aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.checkpatch.conf1
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--.gitignore20
-rw-r--r--Doxyfile.codec.in2
-rw-r--r--Doxyfile.coding.in2
-rw-r--r--Doxyfile.core.in4
-rw-r--r--Doxyfile.ctrl.in2
-rw-r--r--Doxyfile.gb.in2
-rw-r--r--Doxyfile.gsm.in2
-rw-r--r--Doxyfile.isdn.in1716
-rw-r--r--Doxyfile.sim.in1716
-rw-r--r--Doxyfile.usb.in1716
-rw-r--r--Doxyfile.vty.in2
-rw-r--r--Makefile.am143
-rw-r--r--README.md9
-rw-r--r--TODO-RELEASE19
-rw-r--r--configure.ac283
-rwxr-xr-xcontrib/jenkins_amd64.sh10
-rwxr-xr-xcontrib/jenkins_arm.sh3
-rw-r--r--contrib/jenkins_common.sh33
-rw-r--r--contrib/libosmocore.spec.in515
-rwxr-xr-xcontrib/struct_endianness.py (renamed from contrib/struct_endianess.py)8
-rwxr-xr-xcontrib/talloc_count.sh53
-rw-r--r--debian/changelog1814
-rw-r--r--debian/compat2
-rw-r--r--debian/control147
-rw-r--r--debian/copyright18
-rw-r--r--debian/libosmocodec4.install (renamed from debian/libosmocodec0.install)0
-rw-r--r--debian/libosmocore-dev.install1
-rw-r--r--debian/libosmocore-utils.install2
-rw-r--r--debian/libosmocore.dirs1
-rw-r--r--debian/libosmocore21.install (renamed from debian/libosmocore12.install)0
-rw-r--r--debian/libosmogb14.install (renamed from debian/libosmogb9.install)0
-rw-r--r--debian/libosmogsm20.install (renamed from debian/libosmogsm13.install)0
-rw-r--r--debian/libosmoisdn-doc.doc-base7
-rw-r--r--debian/libosmoisdn-doc.install1
-rw-r--r--debian/libosmoisdn0.install1
-rw-r--r--debian/libosmosim-doc.doc-base7
-rw-r--r--debian/libosmosim-doc.install1
-rw-r--r--debian/libosmosim2.install (renamed from debian/libosmosim0.install)0
-rw-r--r--debian/libosmousb-doc.doc-base7
-rw-r--r--debian/libosmousb-doc.install1
-rw-r--r--debian/libosmousb0.install1
-rw-r--r--debian/libosmovty13.install (renamed from debian/libosmovty4.install)0
-rwxr-xr-xdebian/rules23
-rw-r--r--include/Makefile.am186
-rw-r--r--include/osmocom/Makefile.am13
-rw-r--r--include/osmocom/codec/Makefile.am7
-rw-r--r--include/osmocom/codec/codec.h60
-rw-r--r--include/osmocom/codec/ecu.h11
-rw-r--r--include/osmocom/coding/Makefile.am10
-rw-r--r--include/osmocom/coding/gsm0503_amr_dtx.h47
-rw-r--r--include/osmocom/coding/gsm0503_coding.h45
-rw-r--r--include/osmocom/coding/gsm0503_interleaving.h3
-rw-r--r--include/osmocom/coding/gsm0503_parity.h19
-rw-r--r--include/osmocom/core/Makefile.am103
-rw-r--r--include/osmocom/core/application.h9
-rw-r--r--include/osmocom/core/base64.h69
-rw-r--r--include/osmocom/core/bitXXgen.h.tpl31
-rw-r--r--include/osmocom/core/bitcomp.h4
-rw-r--r--include/osmocom/core/bitvec.h9
-rw-r--r--include/osmocom/core/conv.h5
-rw-r--r--include/osmocom/core/counter.h4
-rw-r--r--include/osmocom/core/crcXXgen.h.tpl4
-rw-r--r--include/osmocom/core/crcgen.h4
-rw-r--r--include/osmocom/core/defs.h2
-rw-r--r--include/osmocom/core/endian.h2
-rw-r--r--include/osmocom/core/exec.h25
-rw-r--r--include/osmocom/core/fsm.h20
-rw-r--r--include/osmocom/core/gsmtap.h47
-rw-r--r--include/osmocom/core/gsmtap_util.h39
-rw-r--r--include/osmocom/core/hash.h101
-rw-r--r--include/osmocom/core/hashtable.h141
-rw-r--r--include/osmocom/core/isdnhdlc.h9
-rw-r--r--include/osmocom/core/it_q.h62
-rw-r--r--include/osmocom/core/linuxlist.h256
-rw-r--r--include/osmocom/core/linuxrbtree.h5
-rw-r--r--include/osmocom/core/log2.h184
-rw-r--r--include/osmocom/core/logging.h103
-rw-r--r--include/osmocom/core/loggingrb.h4
-rw-r--r--include/osmocom/core/mnl.h22
-rw-r--r--include/osmocom/core/msgb.h65
-rw-r--r--include/osmocom/core/msgfile.h4
-rw-r--r--include/osmocom/core/netdev.h49
-rw-r--r--include/osmocom/core/netns.h24
-rw-r--r--include/osmocom/core/osmo_io.h231
-rw-r--r--include/osmocom/core/prim.h6
-rw-r--r--include/osmocom/core/rate_ctr.h21
-rw-r--r--include/osmocom/core/select.h41
-rw-r--r--include/osmocom/core/sercomm.h5
-rw-r--r--include/osmocom/core/serial.h5
-rw-r--r--include/osmocom/core/sockaddr_str.h32
-rw-r--r--include/osmocom/core/socket.h156
-rw-r--r--include/osmocom/core/socket_compat.h.tpl10
-rw-r--r--include/osmocom/core/soft_uart.h149
-rw-r--r--include/osmocom/core/stat_item.h60
-rw-r--r--include/osmocom/core/stats.h12
-rw-r--r--include/osmocom/core/stats_tcp.h16
-rw-r--r--include/osmocom/core/strrb.h8
-rw-r--r--include/osmocom/core/talloc.h2
-rw-r--r--include/osmocom/core/tdef.h20
-rw-r--r--include/osmocom/core/thread.h29
-rw-r--r--include/osmocom/core/time_cc.h187
-rw-r--r--include/osmocom/core/timer.h7
-rw-r--r--include/osmocom/core/timer_compat.h4
-rw-r--r--include/osmocom/core/tun.h43
-rw-r--r--include/osmocom/core/use_count.h9
-rw-r--r--include/osmocom/core/utils.h159
-rw-r--r--include/osmocom/core/write_queue.h6
-rw-r--r--include/osmocom/crypt/Makefile.am8
-rw-r--r--include/osmocom/crypt/auth.h52
-rw-r--r--include/osmocom/crypt/kdf.h21
-rw-r--r--include/osmocom/crypt/utran_cipher.h19
-rw-r--r--include/osmocom/ctrl/Makefile.am13
-rw-r--r--include/osmocom/ctrl/control_cmd.h6
-rw-r--r--include/osmocom/ctrl/control_if.h17
-rw-r--r--include/osmocom/ctrl/control_vty.h5
-rw-r--r--include/osmocom/ctrl/ports.h10
-rw-r--r--include/osmocom/gprs/Makefile.am17
-rw-r--r--include/osmocom/gprs/bssgp_bvc_fsm.h71
-rw-r--r--include/osmocom/gprs/frame_relay.h151
-rw-r--r--include/osmocom/gprs/gprs_bssgp.h32
-rw-r--r--include/osmocom/gprs/gprs_bssgp2.h72
-rw-r--r--include/osmocom/gprs/gprs_bssgp_bss.h1
-rw-r--r--include/osmocom/gprs/gprs_bssgp_rim.h274
-rw-r--r--include/osmocom/gprs/gprs_ns.h8
-rw-r--r--include/osmocom/gprs/gprs_ns2.h276
-rw-r--r--include/osmocom/gprs/gprs_ns_frgre.h3
-rw-r--r--include/osmocom/gprs/gprs_rlc.h54
-rw-r--r--include/osmocom/gprs/protocol/Makefile.am8
-rw-r--r--include/osmocom/gprs/protocol/gsm_04_60.h353
-rw-r--r--include/osmocom/gprs/protocol/gsm_08_16.h10
-rw-r--r--include/osmocom/gprs/protocol/gsm_08_18.h199
-rw-r--r--include/osmocom/gprs/protocol/gsm_24_301.h11
-rw-r--r--include/osmocom/gsm/Makefile.am69
-rw-r--r--include/osmocom/gsm/a5.h4
-rw-r--r--include/osmocom/gsm/bsslap.h53
-rw-r--r--include/osmocom/gsm/bssmap_le.h77
-rw-r--r--include/osmocom/gsm/bts_features.h33
-rw-r--r--include/osmocom/gsm/cbsp.h37
-rw-r--r--include/osmocom/gsm/gad.h190
-rw-r--r--include/osmocom/gsm/gsm0502.h90
-rw-r--r--include/osmocom/gsm/gsm0808.h304
-rw-r--r--include/osmocom/gsm/gsm0808_lcs.h48
-rw-r--r--include/osmocom/gsm/gsm0808_utils.h284
-rw-r--r--include/osmocom/gsm/gsm23003.h22
-rw-r--r--include/osmocom/gsm/gsm23236.h60
-rw-r--r--include/osmocom/gsm/gsm29118.h4
-rw-r--r--include/osmocom/gsm/gsm29205.h8
-rw-r--r--include/osmocom/gsm/gsm44021.h8
-rw-r--r--include/osmocom/gsm/gsm48.h73
-rw-r--r--include/osmocom/gsm/gsm48_ie.h12
-rw-r--r--include/osmocom/gsm/gsm48_rest_octets.h11
-rw-r--r--include/osmocom/gsm/gsm_utils.h31
-rw-r--r--include/osmocom/gsm/gsup.h49
-rw-r--r--include/osmocom/gsm/i460_mux.h2
-rw-r--r--include/osmocom/gsm/ipa.h2
-rw-r--r--include/osmocom/gsm/iuup.h129
-rw-r--r--include/osmocom/gsm/l1sap.h13
-rw-r--r--include/osmocom/gsm/lapd_core.h171
-rw-r--r--include/osmocom/gsm/lapdm.h24
-rw-r--r--include/osmocom/gsm/meas_rep.h4
-rw-r--r--include/osmocom/gsm/prim.h7
-rw-r--r--include/osmocom/gsm/protocol/Makefile.am30
-rw-r--r--include/osmocom/gsm/protocol/gsm_03_41.h8
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_08.h650
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_08_gprs.h66
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_11.h10
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_12.h4
-rw-r--r--include/osmocom/gsm/protocol/gsm_04_14.h8
-rw-r--r--include/osmocom/gsm/protocol/gsm_08_08.h198
-rw-r--r--include/osmocom/gsm/protocol/gsm_08_58.h316
-rw-r--r--include/osmocom/gsm/protocol/gsm_09_02.h5
-rw-r--r--include/osmocom/gsm/protocol/gsm_12_21.h173
-rw-r--r--include/osmocom/gsm/protocol/gsm_23_032.h252
-rw-r--r--include/osmocom/gsm/protocol/gsm_23_041.h18
-rw-r--r--include/osmocom/gsm/protocol/gsm_25_415.h222
-rw-r--r--include/osmocom/gsm/protocol/gsm_29_118.h2
-rw-r--r--include/osmocom/gsm/protocol/gsm_44_004.h19
-rw-r--r--include/osmocom/gsm/protocol/gsm_44_060.h252
-rw-r--r--include/osmocom/gsm/protocol/gsm_44_068.h136
-rw-r--r--include/osmocom/gsm/protocol/gsm_44_318.h4
-rw-r--r--include/osmocom/gsm/protocol/gsm_48_071.h118
-rw-r--r--include/osmocom/gsm/protocol/gsm_49_031.h234
-rw-r--r--include/osmocom/gsm/protocol/ipaccess.h38
-rw-r--r--include/osmocom/gsm/rlp.h81
-rw-r--r--include/osmocom/gsm/tlv.h152
-rw-r--r--include/osmocom/isdn/Makefile.am8
-rw-r--r--include/osmocom/isdn/i460_mux.h120
-rw-r--r--include/osmocom/isdn/lapd_core.h192
-rw-r--r--include/osmocom/isdn/v110.h57
-rw-r--r--include/osmocom/isdn/v110_ta.h113
-rw-r--r--include/osmocom/sim/Makefile.am6
-rw-r--r--include/osmocom/sim/class_tables.h4
-rw-r--r--include/osmocom/sim/sim.h87
-rw-r--r--include/osmocom/usb/Makefile.am7
-rw-r--r--include/osmocom/usb/libusb.h115
-rw-r--r--include/osmocom/vty/Makefile.am17
-rw-r--r--include/osmocom/vty/command.h94
-rw-r--r--include/osmocom/vty/cpu_sched_vty.h37
-rw-r--r--include/osmocom/vty/logging.h6
-rw-r--r--include/osmocom/vty/misc.h14
-rw-r--r--include/osmocom/vty/ports.h51
-rw-r--r--include/osmocom/vty/tdef_vty.h2
-rw-r--r--include/osmocom/vty/telnet_interface.h12
-rw-r--r--include/osmocom/vty/vector.h5
-rw-r--r--include/osmocom/vty/vty.h47
-rw-r--r--libosmocodec.pc.in2
-rw-r--r--libosmocoding.pc.in4
-rw-r--r--libosmocore.pc.in6
-rw-r--r--libosmoctrl.pc.in4
-rw-r--r--libosmogb.pc.in4
-rw-r--r--libosmogsm.pc.in4
-rw-r--r--libosmoisdn.pc.in11
-rw-r--r--libosmosim.pc.in4
-rw-r--r--libosmousb.pc.in11
-rw-r--r--libosmovty.pc.in4
-rw-r--r--m4/osmo_ac_code_coverage.m451
-rw-r--r--m4/osmo_ax_code_coverage.m4267
-rwxr-xr-xosmo-release.sh230
-rw-r--r--src/Makefile.am82
-rw-r--r--src/codec/Makefile.am18
-rw-r--r--src/codec/ecu.c20
-rw-r--r--src/codec/ecu_fr.c387
-rw-r--r--src/codec/ecu_fr_old.c166
-rw-r--r--src/codec/gsm610.c160
-rw-r--r--src/codec/gsm620.c55
-rw-r--r--src/codec/gsm660.c166
-rw-r--r--src/codec/gsm690.c117
-rw-r--r--src/coding/Makefile.am24
-rw-r--r--src/coding/gsm0503_amr_dtx.c355
-rw-r--r--src/coding/gsm0503_coding.c1234
-rw-r--r--src/coding/gsm0503_interleaving.c56
-rw-r--r--src/coding/gsm0503_mapping.c4
-rw-r--r--src/coding/gsm0503_parity.c15
-rw-r--r--src/coding/gsm0503_tables.c10
-rw-r--r--src/coding/libosmocoding.map30
-rw-r--r--src/core/Makefile.am166
-rw-r--r--src/core/application.c (renamed from src/application.c)4
-rw-r--r--src/core/backtrace.c (renamed from src/backtrace.c)4
-rw-r--r--src/core/base64.c195
-rw-r--r--src/core/bitcomp.c (renamed from src/bitcomp.c)4
-rw-r--r--src/core/bits.c (renamed from src/bits.c)72
-rw-r--r--src/core/bitvec.c (renamed from src/bitvec.c)68
-rw-r--r--src/core/context.c (renamed from src/context.c)9
-rw-r--r--src/core/conv.c (renamed from src/conv.c)201
-rw-r--r--src/core/conv_acc.c (renamed from src/conv_acc.c)55
-rw-r--r--src/core/conv_acc_generic.c (renamed from src/conv_acc_generic.c)4
-rw-r--r--src/core/conv_acc_neon.c106
-rw-r--r--src/core/conv_acc_neon_impl.h350
-rw-r--r--src/core/conv_acc_sse.c (renamed from src/conv_acc_sse.c)4
-rw-r--r--src/core/conv_acc_sse_avx.c (renamed from src/conv_acc_sse_avx.c)4
-rw-r--r--src/core/conv_acc_sse_impl.h (renamed from src/conv_acc_sse_impl.h)4
-rw-r--r--src/core/counter.c (renamed from src/counter.c)6
-rw-r--r--src/core/crc16.c (renamed from src/crc16.c)0
-rw-r--r--src/core/crcXXgen.c.tpl (renamed from src/crcXXgen.c.tpl)4
-rw-r--r--src/core/exec.c301
-rw-r--r--src/core/fsm.c (renamed from src/fsm.c)42
-rw-r--r--src/core/gsmtap_util.c (renamed from src/gsmtap_util.c)286
-rw-r--r--src/core/isdnhdlc.c (renamed from src/isdnhdlc.c)11
-rw-r--r--src/core/it_q.c269
-rw-r--r--src/core/libosmocore.map631
-rw-r--r--src/core/logging.c (renamed from src/logging.c)763
-rw-r--r--src/core/logging_gsmtap.c (renamed from src/logging_gsmtap.c)18
-rw-r--r--src/core/logging_syslog.c (renamed from src/logging_syslog.c)6
-rw-r--r--src/core/logging_systemd.c117
-rw-r--r--src/core/loggingrb.c (renamed from src/loggingrb.c)4
-rw-r--r--src/core/macaddr.c (renamed from src/macaddr.c)11
-rw-r--r--src/core/mnl.c111
-rw-r--r--src/core/msgb.c (renamed from src/msgb.c)64
-rw-r--r--src/core/msgfile.c (renamed from src/msgfile.c)4
-rw-r--r--src/core/netdev.c962
-rw-r--r--src/core/netns.c208
-rw-r--r--src/core/osmo_io.c1013
-rw-r--r--src/core/osmo_io_internal.h166
-rw-r--r--src/core/osmo_io_poll.c201
-rw-r--r--src/core/osmo_io_uring.c532
-rw-r--r--src/core/panic.c (renamed from src/panic.c)6
-rw-r--r--src/core/plugin.c (renamed from src/plugin.c)6
-rw-r--r--src/core/prbs.c (renamed from src/prbs.c)0
-rw-r--r--src/core/prim.c (renamed from src/prim.c)0
-rw-r--r--src/core/probes.d6
-rw-r--r--src/core/rate_ctr.c (renamed from src/rate_ctr.c)110
-rw-r--r--src/core/rbtree.c (renamed from src/rbtree.c)5
-rw-r--r--src/core/select.c731
-rw-r--r--src/core/sercomm.c (renamed from src/sercomm.c)4
-rw-r--r--src/core/serial.c (renamed from src/serial.c)56
-rw-r--r--src/core/signal.c (renamed from src/signal.c)4
-rw-r--r--src/core/sockaddr_str.c (renamed from src/sockaddr_str.c)165
-rw-r--r--src/core/socket.c2803
-rw-r--r--src/core/soft_uart.c518
-rw-r--r--src/core/stat_item.c463
-rw-r--r--src/core/stat_item_internal.h35
-rw-r--r--src/core/stats.c (renamed from src/stats.c)179
-rw-r--r--src/core/stats_statsd.c (renamed from src/stats_statsd.c)81
-rw-r--r--src/core/stats_tcp.c327
-rw-r--r--src/core/strrb.c (renamed from src/strrb.c)8
-rw-r--r--src/core/tdef.c (renamed from src/tdef.c)47
-rw-r--r--src/core/thread.c56
-rw-r--r--src/core/time_cc.c228
-rw-r--r--src/core/timer.c (renamed from src/timer.c)37
-rw-r--r--src/core/timer_clockgettime.c (renamed from src/timer_clockgettime.c)4
-rw-r--r--src/core/timer_gettimeofday.c (renamed from src/timer_gettimeofday.c)4
-rw-r--r--src/core/tun.c577
-rw-r--r--src/core/use_count.c (renamed from src/use_count.c)37
-rw-r--r--src/core/utils.c (renamed from src/utils.c)654
-rw-r--r--src/core/write_queue.c (renamed from src/write_queue.c)65
-rw-r--r--src/ctrl/Makefile.am8
-rw-r--r--src/ctrl/control_cmd.c106
-rw-r--r--src/ctrl/control_if.c169
-rw-r--r--src/ctrl/control_vty.c21
-rw-r--r--src/ctrl/libosmoctrl.map3
-rw-r--r--src/gb/Makefile.am33
-rw-r--r--src/gb/bssgp_bvc_fsm.c857
-rw-r--r--src/gb/common_vty.c14
-rw-r--r--src/gb/common_vty.h2
-rw-r--r--src/gb/frame_relay.c1051
-rw-r--r--src/gb/gprs_bssgp.c320
-rw-r--r--src/gb/gprs_bssgp2.c486
-rw-r--r--src/gb/gprs_bssgp_bss.c101
-rw-r--r--src/gb/gprs_bssgp_internal.h9
-rw-r--r--src/gb/gprs_bssgp_rim.c1261
-rw-r--r--src/gb/gprs_bssgp_util.c352
-rw-r--r--src/gb/gprs_bssgp_vty.c16
-rw-r--r--src/gb/gprs_ns.c116
-rw-r--r--src/gb/gprs_ns2.c1695
-rw-r--r--src/gb/gprs_ns2_fr.c988
-rw-r--r--src/gb/gprs_ns2_frgre.c616
-rw-r--r--src/gb/gprs_ns2_internal.h503
-rw-r--r--src/gb/gprs_ns2_message.c848
-rw-r--r--src/gb/gprs_ns2_sns.c3106
-rw-r--r--src/gb/gprs_ns2_udp.c597
-rw-r--r--src/gb/gprs_ns2_vc_fsm.c992
-rw-r--r--src/gb/gprs_ns2_vty.c2351
-rw-r--r--src/gb/gprs_ns_vty.c109
-rw-r--r--src/gb/libosmogb.map133
-rw-r--r--src/gsm/Makefile.am35
-rw-r--r--src/gsm/a5.c4
-rw-r--r--src/gsm/abis_nm.c101
-rw-r--r--src/gsm/apn.c2
-rw-r--r--src/gsm/auth_comp128v1.c7
-rw-r--r--src/gsm/auth_comp128v23.c10
-rw-r--r--src/gsm/auth_core.c172
-rw-r--r--src/gsm/auth_milenage.c35
-rw-r--r--src/gsm/auth_tuak.c207
-rw-r--r--src/gsm/auth_xor.c191
-rw-r--r--src/gsm/auth_xor_2g.c81
-rw-r--r--src/gsm/bsslap.c325
-rw-r--r--src/gsm/bssmap_le.c937
-rw-r--r--src/gsm/bts_features.c58
-rw-r--r--src/gsm/cbsp.c310
-rw-r--r--src/gsm/comp128.c4
-rw-r--r--src/gsm/comp128v23.c4
-rw-r--r--src/gsm/gad.c491
-rw-r--r--src/gsm/gea.c4
-rw-r--r--src/gsm/gprs_cipher_core.c4
-rw-r--r--src/gsm/gprs_gea.c4
-rw-r--r--src/gsm/gprs_rlc.c3
-rw-r--r--src/gsm/gsm0341.c4
-rw-r--r--src/gsm/gsm0411_smc.c3
-rw-r--r--src/gsm/gsm0411_smr.c5
-rw-r--r--src/gsm/gsm0411_utils.c6
-rw-r--r--src/gsm/gsm0480.c5
-rw-r--r--src/gsm/gsm0502.c104
-rw-r--r--src/gsm/gsm0808.c1228
-rw-r--r--src/gsm/gsm0808_utils.c903
-rw-r--r--src/gsm/gsm23003.c195
-rw-r--r--src/gsm/gsm23236.c542
-rw-r--r--src/gsm/gsm29118.c4
-rw-r--r--src/gsm/gsm29205.c4
-rw-r--r--src/gsm/gsm44021.c303
-rw-r--r--src/gsm/gsm44068.c121
-rw-r--r--src/gsm/gsm48.c696
-rw-r--r--src/gsm/gsm48049.c2
-rw-r--r--src/gsm/gsm48_arfcn_range_encode.c4
-rw-r--r--src/gsm/gsm48_ie.c305
-rw-r--r--src/gsm/gsm48_rest_octets.c357
-rw-r--r--src/gsm/gsm_04_08_gprs.c4
-rw-r--r--src/gsm/gsm_utils.c80
-rw-r--r--src/gsm/gsup.c146
-rw-r--r--src/gsm/ipa.c64
-rw-r--r--src/gsm/iuup.c1064
-rw-r--r--src/gsm/kasumi.c4
-rw-r--r--src/gsm/kdf.c163
-rw-r--r--src/gsm/kdf/common.h101
-rw-r--r--src/gsm/kdf/crypto.h470
-rw-r--r--src/gsm/kdf/sha1-internal.c307
-rw-r--r--src/gsm/kdf/sha1.c162
-rw-r--r--src/gsm/kdf/sha1.h33
-rw-r--r--src/gsm/kdf/sha1_i.h29
-rw-r--r--src/gsm/kdf/sha256-internal.c231
-rw-r--r--src/gsm/kdf/sha256.c156
-rw-r--r--src/gsm/kdf/sha256.h30
-rw-r--r--src/gsm/kdf/sha256_i.h31
-rw-r--r--src/gsm/lapdm.c437
-rw-r--r--src/gsm/libosmogsm.map212
-rw-r--r--src/gsm/milenage/milenage.c8
-rw-r--r--src/gsm/mncc.c2
-rw-r--r--src/gsm/rlp.c243
-rw-r--r--src/gsm/rsl.c109
-rw-r--r--src/gsm/rxlev_stat.c4
-rw-r--r--src/gsm/sysinfo.c2
-rw-r--r--src/gsm/tlv_parser.c170
-rw-r--r--src/gsm/tuak/KeccakP-1600-3gpp.c176
-rw-r--r--src/gsm/tuak/KeccakP-1600-3gpp.h25
-rw-r--r--src/gsm/tuak/tuak.c372
-rw-r--r--src/gsm/tuak/tuak.h33
-rw-r--r--src/isdn/Makefile.am26
-rw-r--r--src/isdn/i460_mux.c387
-rw-r--r--src/isdn/lapd_core.c (renamed from src/gsm/lapd_core.c)1458
-rw-r--r--src/isdn/libosmoisdn.map52
-rw-r--r--src/isdn/v110.c590
-rw-r--r--src/isdn/v110_ta.c881
-rw-r--r--src/pseudotalloc/Makefile.am3
-rw-r--r--src/pseudotalloc/pseudotalloc.c22
-rw-r--r--src/pseudotalloc/talloc.h3
-rw-r--r--src/select.c398
-rw-r--r--src/sim/Makefile.am31
-rw-r--r--src/sim/card_fs_hpsim.c72
-rw-r--r--src/sim/card_fs_isim.c53
-rw-r--r--src/sim/card_fs_sim.c6
-rw-r--r--src/sim/card_fs_tetra.c4
-rw-r--r--src/sim/card_fs_uicc.c63
-rw-r--r--src/sim/card_fs_usim.c177
-rw-r--r--src/sim/class_tables.c94
-rw-r--r--src/sim/core.c169
-rw-r--r--src/sim/reader.c31
-rw-r--r--src/sim/reader_pcsc.c74
-rw-r--r--src/sim/sim_int.h14
-rw-r--r--src/socket.c1320
-rw-r--r--src/stat_item.c359
-rw-r--r--src/usb/Makefile.am24
-rw-r--r--src/usb/osmo_libusb.c776
-rw-r--r--src/vty/Makefile.am10
-rw-r--r--src/vty/command.c970
-rw-r--r--src/vty/cpu_sched_vty.c682
-rw-r--r--src/vty/fsm_vty.c60
-rw-r--r--src/vty/logging_vty.c310
-rw-r--r--src/vty/stats_vty.c336
-rw-r--r--src/vty/talloc_ctx_vty.c17
-rw-r--r--src/vty/tdef_vty.c17
-rw-r--r--src/vty/telnet_interface.c61
-rw-r--r--src/vty/utils.c114
-rw-r--r--src/vty/vector.c5
-rw-r--r--src/vty/vty.c149
-rw-r--r--tapset/Makefile.am22
-rw-r--r--tapset/libosmocore.stp29
-rw-r--r--tests/Makefile.am586
-rw-r--r--tests/a5/a5_test.c4
-rw-r--r--tests/abis/abis_test.c55
-rw-r--r--tests/abis/abis_test.ok1
-rw-r--r--tests/auth/tuak_test.c309
-rw-r--r--tests/auth/tuak_test.ok48
-rw-r--r--tests/auth/xor2g_test.c77
-rw-r--r--tests/auth/xor2g_test.ok6
-rw-r--r--tests/base64/base64_test.c55
-rw-r--r--tests/base64/base64_test.ok3
-rw-r--r--tests/bitgen/bitgen_test.c85
-rw-r--r--tests/bitgen/bitgen_test.ok260
-rw-r--r--tests/bitvec/bitvec_test.c51
-rw-r--r--tests/bitvec/bitvec_test.ok28
-rw-r--r--tests/bsslap/bsslap_test.c103
-rw-r--r--tests/bsslap/bsslap_test.ok7
-rw-r--r--tests/bssmap_le/bssmap_le_test.c207
-rw-r--r--tests/bssmap_le/bssmap_le_test.ok12
-rw-r--r--tests/codec/codec_ecu_fr_test.c6
-rw-r--r--tests/codec/codec_ecu_fr_test.ok84
-rw-r--r--tests/codec/codec_test.c39
-rw-r--r--tests/codec/codec_test.ok13
-rw-r--r--tests/coding/coding_test.c432
-rw-r--r--tests/coding/coding_test.ok34862
-rw-r--r--tests/conv/conv_gsm0503_test.ok48
-rw-r--r--tests/ctrl/ctrl_test.c13
-rw-r--r--tests/dtx/dtx_gsm0503_test.c266
-rw-r--r--tests/dtx/dtx_gsm0503_test.ok27
-rw-r--r--tests/exec/exec_test.c155
-rw-r--r--tests/exec/exec_test.err1
-rw-r--r--tests/exec/exec_test.ok46
-rw-r--r--tests/fr/fr_test.c6
-rw-r--r--tests/fsm/fsm_dealloc_test.c10
-rw-r--r--tests/fsm/fsm_test.c103
-rw-r--r--tests/fsm/fsm_test.err140
-rw-r--r--tests/gad/gad_test.c143
-rw-r--r--tests/gad/gad_test.ok8
-rw-r--r--tests/gb/bssgp_fc_test.c4
-rw-r--r--tests/gb/gprs_bssgp_rim_test.c799
-rw-r--r--tests/gb/gprs_bssgp_rim_test.ok276
-rw-r--r--tests/gb/gprs_bssgp_test.c6
-rw-r--r--tests/gb/gprs_ns2_test.c670
-rw-r--r--tests/gb/gprs_ns2_test.err139
-rw-r--r--tests/gb/gprs_ns2_test.ok51
-rw-r--r--tests/gb/gprs_ns2_vty.vty89
-rw-r--r--tests/gb/gprs_ns_test.c24
-rw-r--r--tests/gb/gprs_ns_test.err147
-rw-r--r--tests/gb/osmo-ns-dummy.cfg25
-rw-r--r--tests/gb/osmoappdesc.py27
-rw-r--r--tests/gsm0408/gsm0408_test.c922
-rw-r--r--tests/gsm0408/gsm0408_test.err6
-rw-r--r--tests/gsm0408/gsm0408_test.ok446
-rw-r--r--tests/gsm0502/gsm0502_test.c26
-rw-r--r--tests/gsm0808/gsm0808_test.c551
-rw-r--r--tests/gsm0808/gsm0808_test.ok811
-rw-r--r--tests/gsm23003/gsm23003_test.c18
-rw-r--r--tests/gsm23236/gsm23236_test.c616
-rw-r--r--tests/gsm23236/gsm23236_test.ok514
-rw-r--r--tests/gsm29205/gsm29205_test.c6
-rw-r--r--tests/gsm44021/frame_csd_test.c93
-rw-r--r--tests/gsm44021/frame_csd_test.ok31
-rw-r--r--tests/gsm48/rest_octets_test.c116
-rw-r--r--tests/gsm48/rest_octets_test.ok2
-rw-r--r--tests/gsup/gsup_test.c33
-rw-r--r--tests/gsup/gsup_test.err7
-rw-r--r--tests/gsup/gsup_test.ok2
-rw-r--r--tests/i460_mux/i460_mux_test.c399
-rw-r--r--tests/i460_mux/i460_mux_test.ok115
-rw-r--r--tests/it_q/it_q_test.c159
-rw-r--r--tests/it_q/it_q_test.ok22
-rw-r--r--tests/iuup/iuup_test.c767
-rw-r--r--tests/iuup/iuup_test.err55
-rw-r--r--tests/iuup/iuup_test.ok61
-rw-r--r--tests/lapd/lapd_test.c117
-rw-r--r--tests/lapd/lapd_test.ok46
-rw-r--r--tests/logging/logging_gsmtap_test.c64
-rw-r--r--tests/logging/logging_gsmtap_test.err200
-rw-r--r--tests/logging/logging_test.c26
-rw-r--r--tests/logging/logging_test.err2
-rw-r--r--tests/logging/logging_vty_test.c17
-rw-r--r--tests/logging/logging_vty_test.vty162
-rw-r--r--tests/loggingrb/logging_test.err5
-rw-r--r--tests/loggingrb/loggingrb_test.c14
-rw-r--r--tests/msgb/msgb_test.c34
-rw-r--r--tests/msgfile/msgfile_test.c9
-rw-r--r--tests/oap/oap_client_test.c3
-rw-r--r--tests/oap/oap_test.c4
-rw-r--r--tests/osmo-auc-gen/osmo-auc-gen_test.ok68
-rw-r--r--tests/osmo_io/osmo_io_test.c178
-rw-r--r--tests/osmo_io/osmo_io_test.err (renamed from debian/patches/series)0
-rw-r--r--tests/osmo_io/osmo_io_test.ok9
-rw-r--r--tests/rlp/rlp_test.c217
-rw-r--r--tests/rlp/rlp_test.ok35
-rw-r--r--tests/sercomm/sercomm_test.c4
-rw-r--r--tests/sim/sim_test.c4
-rw-r--r--tests/sms/sms_test.c59
-rw-r--r--tests/sms/sms_test.ok16
-rw-r--r--tests/smscb/cbsp_test.c108
-rw-r--r--tests/smscb/cbsp_test.ok2
-rw-r--r--tests/smscb/gsm0341_test.c14
-rw-r--r--tests/smscb/gsm0341_test.ok1
-rw-r--r--tests/smscb/smscb_test.c4
-rw-r--r--tests/sockaddr_str/sockaddr_str_test.c65
-rw-r--r--tests/sockaddr_str/sockaddr_str_test.ok847
-rw-r--r--tests/socket/socket_sctp_test.c234
-rw-r--r--tests/socket/socket_sctp_test.err8
-rw-r--r--tests/socket/socket_sctp_test.ok24
-rw-r--r--tests/socket/socket_test.c347
-rw-r--r--tests/socket/socket_test.err6
-rw-r--r--tests/socket/socket_test.ok29
-rw-r--r--tests/soft_uart/soft_uart_test.c658
-rw-r--r--tests/soft_uart/soft_uart_test.ok278
-rw-r--r--tests/stats/stats_test.c418
-rw-r--r--tests/stats/stats_test.err163
-rw-r--r--tests/stats/stats_test.ok132
-rw-r--r--tests/stats/stats_vty_test.c88
-rw-r--r--tests/stats/stats_vty_test.vty217
-rw-r--r--tests/strrb/strrb_test.c4
-rw-r--r--tests/tdef/tdef_test.c83
-rw-r--r--tests/tdef/tdef_test.err25
-rw-r--r--tests/tdef/tdef_test.ok66
-rw-r--r--tests/tdef/tdef_vty_config_root_test.c (renamed from tests/tdef/tdef_vty_test_config_root.c)14
-rw-r--r--tests/tdef/tdef_vty_config_root_test.vty (renamed from tests/tdef/tdef_vty_test_config_root.vty)0
-rw-r--r--tests/tdef/tdef_vty_config_subnode_test.c (renamed from tests/tdef/tdef_vty_test_config_subnode.c)12
-rw-r--r--tests/tdef/tdef_vty_config_subnode_test.vty (renamed from tests/tdef/tdef_vty_test_config_subnode.vty)0
-rw-r--r--tests/tdef/tdef_vty_dynamic_test.c (renamed from tests/tdef/tdef_vty_test_dynamic.c)10
-rw-r--r--tests/tdef/tdef_vty_dynamic_test.vty (renamed from tests/tdef/tdef_vty_test_dynamic.vty)0
-rw-r--r--tests/testsuite.at211
-rw-r--r--tests/time_cc/time_cc_test.c768
-rw-r--r--tests/time_cc/time_cc_test.ok328
-rw-r--r--tests/timer/clk_override_test.c4
-rw-r--r--tests/timer/timer_test.c6
-rw-r--r--tests/tlv/tlv_test.c155
-rw-r--r--tests/tlv/tlv_test.ok7
-rw-r--r--tests/use_count/use_count_test.c10
-rw-r--r--tests/use_count/use_count_test.err10
-rw-r--r--tests/ussd/ussd_test.c4
-rw-r--r--tests/utils/utils_test.c1187
-rw-r--r--tests/utils/utils_test.ok741
-rw-r--r--tests/v110/frame_test.c54
-rw-r--r--tests/v110/frame_test.ok20
-rw-r--r--tests/v110/ra1_test.c74
-rw-r--r--tests/v110/ra1_test.ok216
-rw-r--r--tests/v110/ta_test.c459
-rw-r--r--tests/v110/ta_test.err270
-rw-r--r--tests/vty/ok_deprecated_logging.cfg3
-rw-r--r--tests/vty/vty_test.c162
-rw-r--r--tests/vty/vty_test.err77
-rw-r--r--tests/vty/vty_test.ok59
-rw-r--r--tests/vty/vty_transcript_test.c228
-rw-r--r--tests/vty/vty_transcript_test.vty123
-rw-r--r--tests/write_queue/wqueue_test.c56
-rw-r--r--utils/Makefile.am38
-rw-r--r--utils/conv_codes_gsm.py130
-rw-r--r--utils/conv_gen.py6
-rw-r--r--utils/gsmtap-logsend.c139
-rwxr-xr-x[-rw-r--r--]utils/gsmtap_logread.py0
-rw-r--r--utils/osmo-aka-verify.c249
-rw-r--r--utils/osmo-arfcn.c13
-rw-r--r--utils/osmo-auc-gen.c87
-rw-r--r--utils/osmo-config-merge.c4
-rw-r--r--utils/osmo-ns-dummy-vty.c325
-rw-r--r--utils/osmo-ns-dummy.c316
-rw-r--r--utils/osmo-sim-test.c470
-rw-r--r--utils/osmo-stat-dummy/Makefile.am10
-rw-r--r--utils/osmo-stat-dummy/README.md12
-rw-r--r--utils/osmo-stat-dummy/osmo-stat-dummy.c335
-rw-r--r--utils/osmo-stat-dummy/osmo-stat-dummy.cfg40
-rw-r--r--utils/osmo-stat-dummy/osmo-stat-dummy.html59
616 files changed, 90819 insertions, 44028 deletions
diff --git a/.checkpatch.conf b/.checkpatch.conf
new file mode 100644
index 00000000..46bf0eb1
--- /dev/null
+++ b/.checkpatch.conf
@@ -0,0 +1 @@
+--exclude ^src/gsm/tuak/KeccakP-1600-3gpp\.(c|h)$
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..7592debf
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+open_collective: osmocom
diff --git a/.gitignore b/.gitignore
index 51c443f3..264e739e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@ install-sh
stamp-h1
libtool
libosmocore-*.tar.*
+*~
# libtool for different platforms like arm-poky-linux-gnueabi-libtool
*-libtool
@@ -36,10 +37,13 @@ libosmocore-*.tar.*
Doxyfile.core
Doxyfile.ctrl
Doxyfile.gsm
+Doxyfile.isdn
Doxyfile.vty
Doxyfile.codec
Doxyfile.coding
Doxyfile.gb
+Doxyfile.sim
+Doxyfile.usb
debian/autoreconf.after
debian/autoreconf.before
@@ -68,7 +72,9 @@ tests/*/*_test
utils/osmo-arfcn
utils/osmo-auc-gen
utils/osmo-config-merge
+utils/osmo-gsmtap-logsend
utils/osmo-sim-test
+utils/osmo-aka-verify
doc/codec
doc/coding
@@ -78,20 +84,32 @@ doc/vty/latex
doc/vty/html
doc/vty/doxygen_sqlite3.db
doc/gsm
+doc/isdn
doc/gb
+doc/sim
+doc/usb
doc/html.tar
doc/*.tag
doc/*.tag.prep
tags
-src/crc*gen.c
+src/core/crc*gen.c
src/gsm/gsm0503_conv.c
src/gsm/*_gen.c
include/osmocom/core/crc*gen.h
include/osmocom/core/bit*gen.h
+include/osmocom/core/socket_compat.h
include/osmocom/gsm/gsm0503.h
tests/conv/gsm0503_test_vectors.c
# vi files
*.sw?
/tests/libsercomstub.a
+
+# code coverage reports
+libosmocore-*-coverage*
+coverage-cobertura.xml
+
+contrib/libosmocore.spec
+/utils/osmo-ns-dummy
+utils/osmo-stat-dummy/osmo-stat-dummy
diff --git a/Doxyfile.codec.in b/Doxyfile.codec.in
index 88f0d27a..40be69fe 100644
--- a/Doxyfile.codec.in
+++ b/Doxyfile.codec.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.coding.in b/Doxyfile.coding.in
index d0e99425..50355de4 100644
--- a/Doxyfile.coding.in
+++ b/Doxyfile.coding.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.core.in b/Doxyfile.core.in
index c2bfcd5d..fb4ceffb 100644
--- a/Doxyfile.core.in
+++ b/Doxyfile.core.in
@@ -610,7 +610,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = @srcdir@/include/osmocom/core @srcdir@/src
+INPUT = @srcdir@/include/osmocom/core @srcdir@/src/core
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.ctrl.in b/Doxyfile.ctrl.in
index 5612a741..6d101dfc 100644
--- a/Doxyfile.ctrl.in
+++ b/Doxyfile.ctrl.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.gb.in b/Doxyfile.gb.in
index b3b2128d..da8e7705 100644
--- a/Doxyfile.gb.in
+++ b/Doxyfile.gb.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.gsm.in b/Doxyfile.gsm.in
index fd893907..c04a1f6a 100644
--- a/Doxyfile.gsm.in
+++ b/Doxyfile.gsm.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmoisdn.tag=../../isdn/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Doxyfile.isdn.in b/Doxyfile.isdn.in
new file mode 100644
index 00000000..9476ab2a
--- /dev/null
+++ b/Doxyfile.isdn.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmoisdn
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom ISDN library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/isdn
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit command for a brief description.)
+
+QT_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @srcdir@/include/osmocom/isdn @srcdir@/src/isdn
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+# IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 1
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE = doc/libosmoisdn.tag
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/Doxyfile.sim.in b/Doxyfile.sim.in
new file mode 100644
index 00000000..0c9dd73d
--- /dev/null
+++ b/Doxyfile.sim.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmosim
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom SIM library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/sim
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit command for a brief description.)
+
+QT_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @srcdir@/include/osmocom/sim @srcdir@/src/sim
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+# IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 1
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE = doc/libosmosim.tag
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/Doxyfile.usb.in b/Doxyfile.usb.in
new file mode 100644
index 00000000..7037713d
--- /dev/null
+++ b/Doxyfile.usb.in
@@ -0,0 +1,1716 @@
+# Doxyfile 1.7.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = libosmousb
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Osmocom USB library"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/usb
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit command for a brief description.)
+
+QT_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = @srcdir@/include/osmocom/usb @srcdir@/src/usb
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+# IMAGE_PATH = images/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 1
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmovty.tag=../../vty/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE = doc/libosmousb.tag
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH = /usr/bin/dot
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/Doxyfile.vty.in b/Doxyfile.vty.in
index 9f56fd7f..8d9fc69e 100644
--- a/Doxyfile.vty.in
+++ b/Doxyfile.vty.in
@@ -1485,7 +1485,7 @@ SKIP_FUNCTION_MACROS = YES
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
-TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
+TAGFILES = doc/libosmocore.tag=../../core/html doc/libosmogsm.tag=../../gsm/html doc/libosmoisdn.tag=../../isdn/html doc/libosmocodec.tag=../../codec/html doc/libosmocoding.tag=../../coding/html doc/libosmoctrl.tag=../../ctrl/html doc/libosmogb.tag=../../gb/html
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
diff --git a/Makefile.am b/Makefile.am
index 2e73f72e..8a8f9636 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,13 +1,25 @@
ACLOCAL_AMFLAGS = -I m4
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
-SUBDIRS = include src src/vty src/codec src/gsm src/coding src/gb src/ctrl src/sim src/pseudotalloc utils tests
+AM_CPPFLAGS = -I$(top_srcdir)/include
+SUBDIRS = \
+ include \
+ src \
+ utils \
+ tapset \
+ tests \
+ $(NULL)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libosmocore.pc libosmocodec.pc libosmovty.pc libosmogsm.pc \
- libosmogb.pc libosmoctrl.pc libosmocoding.pc libosmosim.pc
+ libosmogb.pc libosmoctrl.pc libosmocoding.pc libosmosim.pc \
+ libosmousb.pc libosmoisdn.pc
+
+aclocaldir = $(datadir)/aclocal
+dist_aclocal_DATA = m4/osmo_ac_code_coverage.m4 \
+ m4/osmo_ax_code_coverage.m4
@RELMAKE@
+@CODE_COVERAGE_RULES@
relengdir = $(includedir)
releng_DATA = osmo-release.mk
@@ -22,11 +34,20 @@ $(top_srcdir)/.version:
dist-hook:
echo $(VERSION) > $(distdir)/.tarball-version
-EXTRA_DIST = git-version-gen .version README.md osmo-release.mk osmo-release.sh
+EXTRA_DIST = \
+ .version \
+ README.md \
+ contrib/libosmocore.spec.in \
+ debian \
+ git-version-gen \
+ osmo-release.mk \
+ osmo-release.sh \
+ $(NULL)
HTML = \
$(top_builddir)/doc/core/html/index.html \
$(top_builddir)/doc/gsm/html/index.html \
+ $(top_builddir)/doc/isdn/html/index.html \
$(top_builddir)/doc/vty/html/index.html \
$(top_builddir)/doc/codec/html/index.html \
$(top_builddir)/doc/coding/html/index.html \
@@ -61,7 +82,8 @@ apidoc: $(HTML)
$(top_builddir)/doc/libosmocore.tag.prep: $(top_builddir)/Doxyfile.core \
$(top_srcdir)/include/osmocom/core/*.h \
- $(top_srcdir)/src/*.[hc] \
+ $(top_srcdir)/src/core/*.[hc] \
+ $(top_srcdir)/src/core/crcXXgen.c.tpl \
$(top_srcdir)/src/pseudotalloc/*.[hc]
rm -rf $(top_builddir)/doc/core; mkdir -p $(top_builddir)/doc/core
rm -rf $(top_builddir)/doc/libosmocore.map
@@ -79,6 +101,14 @@ $(top_builddir)/doc/libosmogsm.tag.prep: $(top_builddir)/Doxyfile.gsm \
-$(DOXYGEN) $(top_builddir)/Doxyfile.gsm
touch "$@"
+$(top_builddir)/doc/libosmoisdn.tag.prep: $(top_builddir)/Doxyfile.isdn \
+ $(top_srcdir)/include/osmocom/isdn/*.h \
+ $(top_srcdir)/src/isdn/*.c
+ rm -rf $(top_builddir)/doc/isdn; mkdir -p $(top_builddir)/doc/isdn
+ rm -rf $(top_builddir)/doc/libosmoisdn.map
+ -$(DOXYGEN) $(top_builddir)/Doxyfile.isdn
+ touch "$@"
+
# Don't delete the entire doc/vty, it contains example.xml and vtydoc.xsd (OS#3986)
$(top_builddir)/doc/libosmovty.tag.prep: $(top_builddir)/Doxyfile.vty \
$(top_srcdir)/include/osmocom/vty/*.h \
@@ -120,6 +150,22 @@ $(top_builddir)/doc/libosmogb.tag.prep: $(top_builddir)/Doxyfile.gb \
-$(DOXYGEN) $(top_builddir)/Doxyfile.gb
touch "$@"
+$(top_builddir)/doc/libosmosim.tag.prep: $(top_builddir)/Doxyfile.sim \
+ $(top_srcdir)/include/osmocom/sim/*.h \
+ $(top_srcdir)/src/sim/*.[hc]
+ rm -rf $(top_builddir)/doc/sim; mkdir -p $(top_builddir)/doc/sim
+ rm -rf $(top_builddir)/doc/libosmosim.map
+ -$(DOXYGEN) $(top_builddir)/Doxyfile.sim
+ touch "$@"
+
+$(top_builddir)/doc/libosmousb.tag.prep: $(top_builddir)/Doxyfile.usb \
+ $(top_srcdir)/include/osmocom/usb/*.h \
+ $(top_srcdir)/src/usb/*.[hc]
+ rm -rf $(top_builddir)/doc/usb; mkdir -p $(top_builddir)/doc/usb
+ rm -rf $(top_builddir)/doc/libosmousb.map
+ -$(DOXYGEN) $(top_builddir)/Doxyfile.usb
+ touch "$@"
+
# Build for real once all depending .tag files are in place.
# Depend on the own .tag.prep file to avoid concurrent builds of the same doc
# tree, and to also depend on all the source files listed above.
@@ -134,83 +180,150 @@ $(top_builddir)/doc/libosmogb.tag.prep: $(top_builddir)/Doxyfile.gb \
$(top_builddir)/doc/core/html/index.html: $(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/core; mkdir -p $(top_builddir)/doc/core
$(DOXYGEN) Doxyfile.core
$(top_builddir)/doc/gsm/html/index.html: $(top_builddir)/doc/libosmogsm.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/gsm; mkdir -p $(top_builddir)/doc/gsm
$(DOXYGEN) Doxyfile.gsm
+$(top_builddir)/doc/isdn/html/index.html: $(top_builddir)/doc/libosmoisdn.tag.prep \
+ $(top_builddir)/doc/libosmocore.tag.prep \
+ $(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmovty.tag.prep \
+ $(top_builddir)/doc/libosmocodec.tag.prep \
+ $(top_builddir)/doc/libosmocoding.tag.prep \
+ $(top_builddir)/doc/libosmoctrl.tag.prep \
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
+ rm -rf $(top_builddir)/doc/isdn; mkdir -p $(top_builddir)/doc/isdn
+ $(DOXYGEN) Doxyfile.isdn
+
# Don't delete the entire doc/vty, it contains example.xml and vtydoc.xsd (OS#3986)
$(top_builddir)/doc/vty/html/index.html: $(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/vty/html $(top_builddir)/doc/vty/latex
$(DOXYGEN) Doxyfile.vty
$(top_builddir)/doc/codec/html/index.html: $(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/codec; mkdir -p $(top_builddir)/doc/codec
$(DOXYGEN) Doxyfile.codec
$(top_builddir)/doc/coding/html/index.html: $(top_builddir)/doc/libosmocoding.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmoctrl.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/coding; mkdir -p $(top_builddir)/doc/coding
$(DOXYGEN) Doxyfile.coding
$(top_builddir)/doc/ctrl/html/index.html: $(top_builddir)/doc/libosmoctrl.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
- $(top_builddir)/doc/libosmogb.tag.prep
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/ctrl; mkdir -p $(top_builddir)/doc/ctrl
$(DOXYGEN) Doxyfile.ctrl
$(top_builddir)/doc/gb/html/index.html: $(top_builddir)/doc/libosmogb.tag.prep \
$(top_builddir)/doc/libosmocore.tag.prep \
$(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
$(top_builddir)/doc/libosmovty.tag.prep \
$(top_builddir)/doc/libosmocodec.tag.prep \
$(top_builddir)/doc/libosmocoding.tag.prep \
- $(top_builddir)/doc/libosmoctrl.tag.prep
+ $(top_builddir)/doc/libosmoctrl.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
rm -rf $(top_builddir)/doc/gb; mkdir -p $(top_builddir)/doc/gb
$(DOXYGEN) Doxyfile.gb
+$(top_builddir)/doc/sim/html/index.html: $(top_builddir)/doc/libosmosim.tag.prep \
+ $(top_builddir)/doc/libosmocore.tag.prep \
+ $(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
+ $(top_builddir)/doc/libosmovty.tag.prep \
+ $(top_builddir)/doc/libosmocodec.tag.prep \
+ $(top_builddir)/doc/libosmocoding.tag.prep \
+ $(top_builddir)/doc/libosmoctrl.tag.prep \
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmousb.tag.prep
+ rm -rf $(top_builddir)/doc/sim; mkdir -p $(top_builddir)/doc/sim
+ $(DOXYGEN) Doxyfile.sim
+
+$(top_builddir)/doc/usb/html/index.html: $(top_builddir)/doc/libosmousb.tag.prep \
+ $(top_builddir)/doc/libosmocore.tag.prep \
+ $(top_builddir)/doc/libosmogsm.tag.prep \
+ $(top_builddir)/doc/libosmoisdn.tag.prep \
+ $(top_builddir)/doc/libosmovty.tag.prep \
+ $(top_builddir)/doc/libosmocodec.tag.prep \
+ $(top_builddir)/doc/libosmocoding.tag.prep \
+ $(top_builddir)/doc/libosmoctrl.tag.prep \
+ $(top_builddir)/doc/libosmogb.tag.prep \
+ $(top_builddir)/doc/libosmosim.tag.prep
+ rm -rf $(top_builddir)/doc/usb; mkdir -p $(top_builddir)/doc/usb
+ $(DOXYGEN) Doxyfile.usb
+
if HAVE_DOXYGEN
install-data-hook:
cd $(DESTDIR)$(htmldir) && tar xf html.tar && rm -f html.tar
uninstall-hook:
- cd $(DESTDIR)$(htmldir) && rm -rf {core,gsm,vty,codec,coding,ctrl,gb}
-
-DX_CLEAN = doc/{core,gsm,vty,codec,coding,ctrl,gb}/html/search/* doc/{core,gsm,vty,codec,coding,ctrl,gb}/{html,latex}/* doc/html.tar doc/{core,gsm,vty,codec,coding,ctrl,gb}/doxygen_sqlite3.db doc/*.tag doc/*.tag.prep
+ cd $(DESTDIR)$(htmldir) && rm -rf {core,gsm,isdn,vty,codec,coding,ctrl,gb,sim,usb}
+
+DX_CLEAN = \
+ doc/{core,gsm,isdn,vty,codec,coding,ctrl,gb,sim,usb}/html/search/* \
+ doc/{core,gsm,isdn,vty,codec,coding,ctrl,gb,sim,usb}/{html,latex}/* \
+ doc/{core,gsm,isdn,vty,codec,coding,ctrl,gb,sim,usb}/doxygen_sqlite3.db \
+ doc/*.tag.prep \
+ doc/*.tag \
+ doc/html.tar \
+ $(NULL)
endif
MOSTLYCLEANFILES = $(DX_CLEAN)
diff --git a/README.md b/README.md
index 623804b1..ed9bcd6b 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ communications.
There is no clear scope of it. We simply move all shared code between
the various Osmocom projects in this library to avoid code duplication.
-The libosmcoore.git repository build multiple libraries:
+The libosmocore.git repository build multiple libraries:
* **libosmocore** contains some general-purpose functions like select-loop
abstraction, message buffers, timers, linked lists
@@ -28,7 +28,6 @@ The libosmcoore.git repository build multiple libraries:
* **libosmocodec** contains an implementation of GSM voice codecs
* **libosmocoding** contains an implementation of GSM channel coding
* **libosmosim** contains infrastructure to interface SIM/UICC/USIM cards
-* **libosmotrau** contains encoding/decoding functions for A-bis TRAU frames
Homepage
@@ -42,16 +41,16 @@ GIT Repository
You can clone from the official libosmocore.git repository using
- git clone git://git.osmocom.org/libosmocore.git
+ git clone https://gitea.osmocom.org/osmocom/libosmocore
-There is a cgit interface at <http://git.osmocom.org/libosmocore/>
+There is a web interface at <https://gitea.osmocom.org/osmocom/libosmocore>
Documentation
-------------
Doxygen-generated API documentation is generated during the build
process, but also available online for each of the sub-libraries at
-<http://ftp.osmocom.org/api/latest/libosmocore/>
+<https://ftp.osmocom.org/api/latest/libosmocore/>
Mailing List
------------
diff --git a/TODO-RELEASE b/TODO-RELEASE
index 692bdc18..ce041627 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -7,7 +7,18 @@
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
-core osmo_tdef_get() change val_if_not_present arg from unsigned long to long to allow passing -1
-core struct osmo_tdef fields min_val,max_val added, ABI break (arrays of structs used in programs)
-gsm API added osmo_gsm48_rfpowercap2powerclass()
-gb API added bssgp_bvc_ctx_free()
+core ADD osmo_sock_multiaddr_{add,del}_local_addr()
+core ADD osmo_sock_multiaddr_get_ip_and_port(), osmo_multiaddr_ip_and_port_snprintf(), osmo_sock_multiaddr_get_name_buf()
+core ADD osmo_sock_sctp_get_peer_addr_info()
+core ADD gsmtap_inst_fd2() core, DEPRECATE gsmtap_inst_fd()
+core ADD osmo_sockaddr_str_from_osa() osmo_sockaddr_str_to_osa()
+core behavior change osmo_tdef_fsm_inst_state_chg(): allow millisecond precision
+core ABI change osmo_io_ops now contains a struct of structs, not union of structs
+core ABI change osmo_iofd_set_ioops() now returns a value (error code)
+isdn ABI change add states and flags for external T200 handling
+isdn ADD initial implementation of the V.110 Terminal Adapter
+gsm ABI change add T200 timer states to lapdm_datalink
+gsm ABI change add UI queue to struct lapdm_datalink
+gsm ADD gsup.h: struct osmo_gsup_pdp_info fields pdp_type_nr, pdp_type_org, deprecate pdp_type.
+gsm ABI change gsup.h: Add field pdp_address in struct osmo_gsup_pdp_info shifts the struct, and in turn fields in struct osmo_gsup_message.
+gsm ABI change gsup.h: Add field pco in struct osmo_gsup_message. Length of the struct osmo_gsup_message increase.
diff --git a/configure.ac b/configure.ac
index 6c54e666..6c7126f5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8,6 +8,8 @@ AC_CONFIG_AUX_DIR([.])
AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.6 subdir-objects])
AC_CONFIG_TESTDIR(tests)
+CFLAGS="$CFLAGS -std=gnu11"
+
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -24,6 +26,11 @@ LT_INIT([pic-only disable-static])
AC_CONFIG_MACRO_DIR([m4])
+dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
+AS_CASE(["$LD"],[*clang*],
+ [AS_CASE(["${host_os}"],
+ [*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
+
dnl check for pkg-config
dnl * If pkg-config is missing, we get a "syntax error" for PKG_CHECK_MODULES.
dnl Instead, we want to say that pkg-config and pkg.m4 are missing.
@@ -46,18 +53,23 @@ case $host in
*)
LTLDFLAGS_OSMOGB='-Wl,--version-script=$(srcdir)/libosmogb.map'
LTLDFLAGS_OSMOGSM='-Wl,--version-script=$(srcdir)/libosmogsm.map'
+ LTLDFLAGS_OSMOISDN='-Wl,--version-script=$(srcdir)/libosmoisdn.map'
LTLDFLAGS_OSMOCODING='-Wl,--version-script=$(srcdir)/libosmocoding.map'
+ LTLDFLAGS_OSMOCORE='-Wl,--version-script=$(srcdir)/libosmocore.map'
LTLDFLAGS_OSMOCTRL='-Wl,--version-script=$(srcdir)/libosmoctrl.map'
;;
esac
AC_SUBST(LTLDFLAGS_OSMOGB)
AC_SUBST(LTLDFLAGS_OSMOGSM)
+AC_SUBST(LTLDFLAGS_OSMOISDN)
AC_SUBST(LTLDFLAGS_OSMOCODING)
+AC_SUBST(LTLDFLAGS_OSMOCORE)
AC_SUBST(LTLDFLAGS_OSMOCTRL)
dnl checks for header files
AC_HEADER_STDC
-AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h sys/timerfd.h syslog.h ctype.h netinet/tcp.h netinet/in.h)
+AC_CHECK_HEADERS(execinfo.h poll.h sys/select.h sys/socket.h sys/signalfd.h sys/eventfd.h sys/timerfd.h syslog.h ctype.h netinet/tcp.h netinet/in.h)
+AC_CHECK_DECL(HAVE_SYS_SOCKET_H, AC_SUBST(HAVE_SYS_SOCKET_H, 1), AC_SUBST(HAVE_SYS_SOCKET_H, 0))
# for src/conv.c
AC_FUNC_ALLOCA
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DLOPEN="$LIBS";LIBS=""])
@@ -71,8 +83,28 @@ AC_SUBST(BACKTRACE_LIB)
# check for pthread (PTHREAD_CFLAGS, PTHREAD_LIBS)
AX_PTHREAD
+AC_MSG_CHECKING(for pthread_getname_np(pthread_t, char*, size_t))
+saved_CFLAGS="$CFLAGS"
+saved_LIBS="$LIBS"
+CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+LIBS="$LIBS $PTHREAD_LIBS"
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [#define _GNU_SOURCE
+ #include <pthread.h>],
+ [pthread_getname_np(pthread_self(),"example",0)])],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_PTHREAD_GETNAME_NP,1,
+ [Have function pthread_setname_np(const char*)])],
+ [AC_MSG_RESULT(no)])
+CFLAGS="$saved_CFLAGS"
+LIBS="$saved_LIBS"
+
+
# check for old glibc < 2.17 to get clock_gettime
-AC_SEARCH_LIBS([clock_gettime], [rt posix4], [LIBRARY_RT="$LIBS";LIBS=""])
+AC_SEARCH_LIBS([clock_gettime], [rt posix4],
+ [AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [Define if clock_gettime is available])
+ LIBRARY_RT="$LIBS";LIBS="";])
AC_SUBST(LIBRARY_RT)
AC_ARG_ENABLE(doxygen,
@@ -98,7 +130,7 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
CFLAGS="$saved_CFLAGS"
AC_SUBST(SYMBOL_VISIBILITY)
-AC_CHECK_FUNCS(clock_gettime localtime_r)
+AC_CHECK_FUNCS(localtime_r)
AC_DEFUN([CHECK_TM_INCLUDES_TM_GMTOFF], [
AC_CACHE_CHECK(
@@ -125,13 +157,37 @@ AC_DEFUN([CHECK_TM_INCLUDES_TM_GMTOFF], [
CHECK_TM_INCLUDES_TM_GMTOFF
+# Check if gettid is available (despite not being documented in glibc doc,
+# it requires __USE_GNU on some systems)
+# C compiler is used since __USE_GNU seems to be always defined for g++.
+save_CPPFLAGS=$CPPFLAGS
+AC_LANG_PUSH(C)
+CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
+AC_CHECK_FUNCS([gettid])
+AC_LANG_POP(C)
+CPPFLAGS=$save_CPPFLAGS
+
dnl Check if We need to apply workaround for TLS bug on ARM platform for GCC < 7.3.0:
ARG_ENABLE_DETECT_TLS_GCC_ARM_BUG
dnl Generate the output
AC_CONFIG_HEADER(config.h)
-PKG_CHECK_MODULES(TALLOC, [talloc >= 2.0.1])
+PKG_CHECK_MODULES(TALLOC, [talloc >= 2.1.0])
+
+AC_ARG_ENABLE([uring], [AS_HELP_STRING([--disable-uring], [Build without io_uring support])],
+ [
+ ENABLE_URING=$enableval
+ ],
+ [
+ ENABLE_URING="yes"
+ ])
+AS_IF([test "x$ENABLE_URING" = "xyes"], [
+ PKG_CHECK_MODULES(URING, [liburing >= 0.7])
+ AC_DEFINE([HAVE_URING],[1],[Build with io_uring support])
+])
+AM_CONDITIONAL(ENABLE_URING, test "x$ENABLE_URING" = "xyes")
+AC_SUBST(ENABLE_URING)
AC_ARG_ENABLE([pcsc], [AS_HELP_STRING([--disable-pcsc], [Build without PC/SC support])],
[
@@ -142,10 +198,24 @@ AC_ARG_ENABLE([pcsc], [AS_HELP_STRING([--disable-pcsc], [Build without PC/SC sup
])
AS_IF([test "x$ENABLE_PCSC" = "xyes"], [
PKG_CHECK_MODULES(PCSC, libpcsclite)
+ AC_DEFINE([HAVE_PCSC],[1],[Build with PC/SC support])
])
AM_CONDITIONAL(ENABLE_PCSC, test "x$ENABLE_PCSC" = "xyes")
AC_SUBST(ENABLE_PCSC)
+AC_ARG_ENABLE([libusb], [AS_HELP_STRING([--disable-libusb], [Build without libusb support])],
+ [
+ ENABLE_LIBUSB=$enableval
+ ],
+ [
+ ENABLE_LIBUSB="yes"
+ ])
+AS_IF([test "x$ENABLE_LIBUSB" = "xyes"], [
+ PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
+])
+AM_CONDITIONAL(ENABLE_LIBUSB, test "x$ENABLE_LIBUSB" = "xyes")
+AC_SUBST(ENABLE_LIBUSB)
+
AC_ARG_ENABLE([gnutls], [AS_HELP_STRING([--disable-gnutls], [Do not use GnuTLS fallback for missing getrandom()])],
[ENABLE_GNUTLS=$enableval], [ENABLE_GNUTLS="yes"])
AM_CONDITIONAL(ENABLE_GNUTLS, test x"$ENABLE_GNUTLS" = x"yes")
@@ -160,22 +230,63 @@ then
AC_DEFINE([USE_GNUTLS], [1], [Use GnuTLS as a fallback for missing getrandom()])
fi
+AC_ARG_ENABLE([systemd_logging],
+ [AS_HELP_STRING(
+ [--enable-systemd-logging],
+ [Build with systemd-journal logging support]
+ )],
+ [systemd_logging=$enableval], [systemd_logging="no"])
+AS_IF([test "x$systemd_logging" = "xyes"], [
+ PKG_CHECK_MODULES(SYSTEMD, libsystemd)
+ AC_DEFINE([ENABLE_SYSTEMD_LOGGING], [1], [Enable systemd-journal logging target])
+])
+AM_CONDITIONAL(ENABLE_SYSTEMD_LOGGING, test "x$systemd_logging" = "xyes")
+AC_SUBST(ENABLE_SYSTEMD_LOGGING)
+
+AC_ARG_ENABLE([libmnl],
+ [AS_HELP_STRING(
+ [--disable-libmnl],
+ [Build without netlink socket support via libmnl]
+ )],
+ [mnl=$enableval], [mnl="yes"])
+AS_IF([test "x$mnl" = "xyes"], [
+ PKG_CHECK_MODULES(LIBMNL, libmnl, [AC_SUBST(LIBMNL_PC, [libmnl])])
+ AC_DEFINE([ENABLE_LIBMNL], [1], [Enable netlink socket support via libmnl])
+])
+AM_CONDITIONAL(ENABLE_LIBMNL, test "x$mnl" = "xyes")
+AC_SUBST(ENABLE_LIBMNL)
+
AC_ARG_ENABLE([libsctp], [AS_HELP_STRING([--disable-libsctp], [Do not enable socket multiaddr APIs requiring libsctp])],
[ENABLE_LIBSCTP=$enableval], [ENABLE_LIBSCTP="yes"])
AM_CONDITIONAL(ENABLE_LIBSCTP, test x"$ENABLE_LIBSCTP" = x"yes")
AS_IF([test "x$ENABLE_LIBSCTP" = "xyes"], [
- old_LIBS=$LIBS
- AC_SEARCH_LIBS([sctp_bindx], [sctp], [
- AC_DEFINE(HAVE_LIBSCTP, 1, [Define 1 to enable SCTP support])
- AC_SUBST(HAVE_LIBSCTP, [1])
- if test -n "$ac_lib"; then
- AC_SUBST(LIBSCTP_LIBS, [-l$ac_lib])
- fi
- ], [
- AC_MSG_ERROR([sctp_bindx not found in searched libs])])
- LIBS=$old_LIBS
+ AC_DEFINE(HAVE_LIBSCTP, 1, [Define 1 to enable SCTP support])
+ # Attempt finding .pc, otherwise set manually (<1.0.17 have no .pc file)
+ PKG_CHECK_MODULES(LIBSCTP, libsctp,
+ [AC_SUBST(LIBSCTP_PC, [libsctp])],
+ [
+ AC_MSG_NOTICE([libsctp.pc not found (building against <1.0.17 ?), attempting manual lib lookup])
+ old_LIBS=$LIBS
+ AC_SEARCH_LIBS([sctp_bindx], [sctp], [
+ AC_SUBST(HAVE_LIBSCTP, [1])
+ if test -n "$ac_lib"; then
+ AC_SUBST(LIBSCTP_LIBS, [-l$ac_lib])
+ fi
+ ], [
+ AC_MSG_ERROR([sctp_bindx not found in searched libs])])
+ LIBS=$old_LIBS
+ ])
])
+AC_ARG_ENABLE([sctp-tests], [AS_HELP_STRING([--disable-sctp-tests], [Do not run socket tests requiring system SCTP support])],
+ [ENABLE_SCTP_TESTS=$enableval], [ENABLE_SCTP_TESTS="yes"])
+AM_CONDITIONAL(ENABLE_SCTP_TESTS, test x"$ENABLE_SCTP_TESTS" = x"yes")
+
+AC_ARG_ENABLE([uring-tests], [AS_HELP_STRING([--disable-uring-tests], [Do not run io_uring tests])],
+ [ENABLE_URING_TESTS=$enableval], [ENABLE_URING_TESTS="yes"])
+AM_CONDITIONAL(ENABLE_URING_TESTS, test x"$ENABLE_URING_TESTS" = x"yes")
+AC_SUBST(ENABLE_URING_TESTS)
+
AC_ARG_ENABLE(plugin,
[AS_HELP_STRING(
[--disable-plugin],
@@ -205,15 +316,25 @@ fi
AC_ARG_ENABLE(bsc_fd_check,
[AS_HELP_STRING(
- [--enable-bsc-fd-check],
- [Instrument bsc_register_fd to check that the fd is registered]
+ [--enable-ofd-check],
+ [Instrument osmo_fd_register to check that the fd is registered]
)],
[fd_check=$enableval], [fd_check="no"])
-if test x"$fd_check" = x"no"
+if test x"$fd_check" = x"yes"
then
- AC_DEFINE([BSC_FD_CHECK],[1],[Instrument the bsc_register_fd])
+ AC_DEFINE([OSMO_FD_CHECK], [1], [Instrument the osmo_fd_register])
fi
+AC_ARG_ENABLE([force_io_select],
+ [AS_HELP_STRING(
+ [--enable-force-io-select],
+ [Build with old select I/O instead of poll]
+ )],
+ [force_io_select=$enableval], [force_io_select="no"])
+AS_IF([test "x$force_io_select" = "xyes"], [
+ AC_DEFINE([FORCE_IO_SELECT], [1], [Force the use of select() instead of poll()])
+])
+
AC_ARG_ENABLE(msgfile,
[AS_HELP_STRING(
[--disable-msgfile],
@@ -269,7 +390,7 @@ AC_ARG_ENABLE(embedded,
)],
[embedded=$enableval], [embedded="no"])
-AM_CONDITIONAL(ENABLE_STATS_TEST, true)
+AM_CONDITIONAL(EMBEDDED, false)
AM_CONDITIONAL(ENABLE_SERCOM_STUB, false)
if test x"$embedded" = x"yes"
@@ -283,16 +404,29 @@ then
AM_CONDITIONAL(ENABLE_CTRL, false)
AM_CONDITIONAL(ENABLE_UTILITIES, false)
AM_CONDITIONAL(ENABLE_GB, false)
- AM_CONDITIONAL(ENABLE_GNUTLS, false)
+ AM_CONDITIONAL(ENABLE_LIBMNL, false)
AM_CONDITIONAL(ENABLE_LIBSCTP, false)
+ AM_CONDITIONAL(ENABLE_LIBUSB, false)
AM_CONDITIONAL(ENABLE_PCSC, false)
+ AM_CONDITIONAL(ENABLE_URING, false)
AM_CONDITIONAL(ENABLE_PSEUDOTALLOC, true)
AM_CONDITIONAL(ENABLE_SERCOM_STUB, true)
- AM_CONDITIONAL(ENABLE_STATS_TEST, false)
+ AM_CONDITIONAL(EMBEDDED, true)
AC_DEFINE([USE_GNUTLS], [0])
AC_DEFINE([PANIC_INFLOOP],[1],[Use infinite loop on panic rather than fprintf/abort])
fi
+AC_ARG_ENABLE(log_macros,
+ [AS_HELP_STRING(
+ [--disable-log-macros],
+ [Disable logging macros that are also used internally to print information]
+ )],
+ [log_macros="yes"], [log_macros="no"])
+if test x"$log_macros" == x"yes"
+then
+ AC_DEFINE([LIBOSMOCORE_NO_LOGGING],[1],[Disable logging macros])
+fi
+
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
[--enable-sanitize],
@@ -317,6 +451,7 @@ AC_ARG_ENABLE(werror,
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
+ WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
@@ -331,7 +466,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
AM_PATH_PYTHON
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmo_verify_transcript_vty.py,yes)
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
- AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
+ AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
fi
fi
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
@@ -357,6 +492,54 @@ else
AM_CONDITIONAL(HAVE_SSE4_1, false)
fi
+AC_ARG_ENABLE(neon,
+ [AS_HELP_STRING(
+ [--enable-neon],
+ [Enable ARM NEON instructions support [default=no]]
+ )],
+ [neon=$enableval], [neon="no"])
+AS_IF([test "x$neon" = "xyes"], [
+ AC_DEFINE([HAVE_NEON],, [Support ARM NEON instructions])
+])
+AC_MSG_CHECKING([whether to enable ARM NEON instructions support])
+AC_MSG_RESULT([$neon])
+AM_CONDITIONAL(HAVE_NEON, [test "x$neon" != "xno"])
+
+#
+# SystemTap support
+#
+AC_MSG_CHECKING([whether to include systemtap tracing support])
+AC_ARG_ENABLE([systemtap],
+ [AS_HELP_STRING([--enable-systemtap],
+ [Enable inclusion of systemtap trace support])],
+ [ENABLE_SYSTEMTAP="${enableval}"], [ENABLE_SYSTEMTAP='no'])
+AM_CONDITIONAL([ENABLE_SYSTEMTAP], [test x$ENABLE_SYSTEMTAP = xyes])
+AC_MSG_RESULT(${ENABLE_SYSTEMTAP})
+
+if test "x${ENABLE_SYSTEMTAP}" = xyes; then
+ # Additional configuration for --enable-systemtap is HERE
+ AC_CHECK_PROGS(DTRACE, dtrace)
+ if test -z "$DTRACE"; then
+ AC_MSG_ERROR([dtrace not found])
+ fi
+ AC_CHECK_HEADER([sys/sdt.h], [SDT_H_FOUND='yes'],
+ [SDT_H_FOUND='no';
+ AC_MSG_ERROR([systemtap support needs sys/sdt.h header])])
+ AC_DEFINE([HAVE_SYSTEMTAP], [1], [Define to 1 if using SystemTap probes.])
+ AC_ARG_WITH([tapset-install-dir],
+ [AS_HELP_STRING([--with-tapset-install-dir],
+ [The absolute path where the tapset dir will be installed])],
+ [if test "x${withval}" = x; then
+ ABS_TAPSET_DIR="\$(datadir)/systemtap/tapset"
+ else
+ ABS_TAPSET_DIR="${withval}"
+ fi], [ABS_TAPSET_DIR="\$(datadir)/systemtap/tapset"])
+ AC_SUBST(ABS_TAPSET_DIR)
+fi
+
+
+OSMO_AC_CODE_COVERAGE
+
dnl Check if the compiler supports specified GCC's built-in function
AC_DEFUN([CHECK_BUILTIN_SUPPORT], [
AC_CACHE_CHECK(
@@ -384,6 +567,35 @@ dnl Check if the compiler supports runtime SIMD detection
CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],
[Runtime SIMD detection will be disabled])
+dnl There are some members in struct tcp_info that might not exist on all linux versions
+AC_CHECK_MEMBER([struct tcp_info.tcpi_notsent_bytes],
+ AC_DEFINE([HAVE_TCP_INFO_TCPI_NOTSENT_BYTES],
+ [1],
+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_notsent_bytes member in struct tcp_info]),
+ [],
+ [#include <linux/tcp.h>])
+
+AC_CHECK_MEMBER([struct tcp_info.tcpi_rwnd_limited],
+ AC_DEFINE([HAVE_TCP_INFO_TCPI_RWND_LIMITED],
+ [1],
+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_rwnd_limited member in struct tcp_info]),
+ [],
+ [#include <linux/tcp.h>])
+
+AC_CHECK_MEMBER([struct tcp_info.tcpi_sndbuf_limited],
+ AC_DEFINE([HAVE_TCP_INFO_TCPI_SNDBUF_LIMITED],
+ [1],
+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_sndbuf_limited member in struct tcp_info]),
+ [],
+ [#include <linux/tcp.h>])
+
+AC_CHECK_MEMBER([struct tcp_info.tcpi_reord_seen],
+ AC_DEFINE([HAVE_TCP_INFO_TCPI_REORD_SEEN],
+ [1],
+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_reord_seen member in struct tcp_info]),
+ [],
+ [#include <linux/tcp.h>])
+
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
@@ -393,27 +605,52 @@ AC_OUTPUT(
libosmocoding.pc
libosmovty.pc
libosmogsm.pc
+ libosmoisdn.pc
libosmogb.pc
libosmoctrl.pc
libosmosim.pc
+ libosmousb.pc
include/Makefile
+ include/osmocom/Makefile
+ include/osmocom/codec/Makefile
+ include/osmocom/coding/Makefile
+ include/osmocom/core/Makefile
+ include/osmocom/crypt/Makefile
+ include/osmocom/ctrl/Makefile
+ include/osmocom/gprs/Makefile
+ include/osmocom/gprs/protocol/Makefile
+ include/osmocom/gsm/Makefile
+ include/osmocom/gsm/protocol/Makefile
+ include/osmocom/isdn/Makefile
+ include/osmocom/sim/Makefile
+ include/osmocom/usb/Makefile
+ include/osmocom/vty/Makefile
src/Makefile
+ src/core/Makefile
src/vty/Makefile
src/codec/Makefile
src/coding/Makefile
src/sim/Makefile
+ src/usb/Makefile
src/gsm/Makefile
+ src/isdn/Makefile
src/gb/Makefile
src/ctrl/Makefile
src/pseudotalloc/Makefile
+ tapset/Makefile
tests/Makefile
tests/atlocal
utils/Makefile
+ utils/osmo-stat-dummy/Makefile
Doxyfile.core
Doxyfile.gsm
+ Doxyfile.isdn
Doxyfile.vty
Doxyfile.codec
Doxyfile.coding
Doxyfile.gb
Doxyfile.ctrl
- Makefile)
+ Doxyfile.sim
+ Doxyfile.usb
+ Makefile
+ contrib/libosmocore.spec)
diff --git a/contrib/jenkins_amd64.sh b/contrib/jenkins_amd64.sh
index c79e26ab..f26faa86 100755
--- a/contrib/jenkins_amd64.sh
+++ b/contrib/jenkins_amd64.sh
@@ -3,19 +3,13 @@
. $(dirname "$0")/jenkins_common.sh
-ENABLE_SANITIZE="--enable-sanitize"
-
-if [ "x$label" = "xFreeBSD_amd64" ]; then
- ENABLE_SANITIZE=""
-fi
-
src_dir="$PWD"
build() {
build_dir="$1"
prep_build "$src_dir" "$build_dir"
- "$src_dir"/configure --disable-silent-rules --enable-static $ENABLE_SANITIZE --enable-werror \
+ "$src_dir"/configure --disable-silent-rules --enable-static --enable-sanitize --enable-werror \
--enable-external-tests
run_make
@@ -29,7 +23,7 @@ build .
# do distcheck only once, which is fine from built source tree, since distcheck
# is well separated from the source tree state.
DISTCHECK_CONFIGURE_FLAGS=--enable-external-tests \
- $MAKE distcheck \
+ $MAKE $PARALLEL_MAKE distcheck \
|| cat-testlogs.sh
$MAKE maintainer-clean
diff --git a/contrib/jenkins_arm.sh b/contrib/jenkins_arm.sh
index 8ee90f03..87934158 100755
--- a/contrib/jenkins_arm.sh
+++ b/contrib/jenkins_arm.sh
@@ -19,7 +19,8 @@ build() {
--disable-doxygen \
--disable-shared \
--disable-libsctp \
- --enable-external-tests \
+ --disable-libusb \
+ --disable-libmnl \
CFLAGS="-Os -ffunction-sections -fdata-sections -nostartfiles -nodefaultlibs $WERROR_FLAGS"
$MAKE $PARALLEL_MAKE
diff --git a/contrib/jenkins_common.sh b/contrib/jenkins_common.sh
index fa1d544b..82dd471a 100644
--- a/contrib/jenkins_common.sh
+++ b/contrib/jenkins_common.sh
@@ -10,7 +10,38 @@ fi
osmo-clean-workspace.sh
-verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
+verify_value_string_arrays_are_terminated.py
+
+# Validate enum fields in header are added to tlv_definition in source file (SYS#5891):
+
+verify_gsm0808_tlv_definition() {
+ set +x;
+ enums=$(grep "GSM0808_IE_" include/osmocom/gsm/protocol/gsm_08_08.h | grep "=" | awk '{ print $1 }')
+ counted_enums=$(for f in $enums; do printf "%-60s %s\n" "$f" "$(grep -c "\[$f\]" src/gsm/gsm0808.c)"; done)
+ missing_enums=$(echo "$counted_enums" | grep -v GSM0808_IE_RESERVED | grep "0$" || true)
+ if [ "x$missing_enums" != "x" ]; then
+ echo "Missing IEs in src/gsm/gsm0808.c!"
+ echo "$missing_enums"
+ exit 1
+ fi
+ set -x;
+}
+verify_gsm0808_tlv_definition
+
+verify_gsm_08_05_tlv_definition() {
+ set +x;
+ enums=$(grep "RSL_IE_" include/osmocom/gsm/protocol/gsm_08_58.h | grep -e "=" -e ",$" | awk '{ print $1 }' | tr -d ',')
+ counted_enums=$(for f in $enums; do printf "%-60s %s\n" "$f" "$(grep -c "\[$f\]" src/gsm/rsl.c)"; done)
+ # TODO: Add RSL_IE_SIEMENS_* to the tlv struct definitions.
+ missing_enums=$(echo "$counted_enums" | grep -v RSL_IE_SIEMENS |grep "0$" || true)
+ if [ "x$missing_enums" != "x" ]; then
+ echo "Missing IEs in src/gsm/rsl.c!"
+ echo "$missing_enums"
+ exit 1
+ fi
+ set -x;
+}
+verify_gsm_08_05_tlv_definition
prep_build() {
_src_dir="$1"
diff --git a/contrib/libosmocore.spec.in b/contrib/libosmocore.spec.in
new file mode 100644
index 00000000..60ca3977
--- /dev/null
+++ b/contrib/libosmocore.spec.in
@@ -0,0 +1,515 @@
+#
+# spec file for package libosmocore
+#
+# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+#
+# All modifications and additions to the file contributed by third parties
+# remain the property of their copyright owners, unless otherwise agreed
+# upon. The license for this file, and modifications and additions to the
+# file, is the same license as for the pristine package itself (unless the
+# license for the pristine package is not an Open Source License, in which
+# case the license is the MIT License). An "Open Source License" is a
+# license that conforms to the Open Source Definition (Version 1.9)
+# published by the Open Source Initiative.
+
+Name: libosmocore
+Version: @VERSION@
+Release: 0
+Summary: The Open Source Mobile Communications Core Library
+License: GPL-2.0-only AND GPL-2.0-or-later AND LGPL-2.1-or-later AND AGPL-3.0-or-later
+Group: Productivity/Telephony/Utilities
+Url: https://osmocom.org/projects/libosmocore/wiki/Libosmocore
+Source: %name-%version.tar.xz
+BuildRequires: automake >= 1.6
+BuildRequires: libtool >= 2
+BuildRequires: lksctp-tools-devel
+BuildRequires: pkg-config >= 0.20
+BuildRequires: python3
+BuildRequires: xz
+BuildRequires: pkgconfig(gnutls) >= 2.12.0
+BuildRequires: pkgconfig(libpcsclite)
+BuildRequires: pkgconfig(libusb-1.0)
+BuildRequires: pkgconfig(talloc) >= 2.1.0
+BuildRequires: pkgconfig(libmnl)
+BuildRequires: pkgconfig(libsystemd)
+%if 0%{?centos_ver} != 7
+BuildRequires: pkgconfig(liburing)
+%endif
+
+%description
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project, but which are of
+a more generic nature and thus useful to (at least) other programs
+that Osmocom develops w.r.t. mobile communications.
+
+There is no clear scope of it. It simply houses all code shared
+between OsmocomBB and OpenBSC to avoid code duplication.
+
+%package tools
+Summary: GSM utilities from the osmocore project
+License: GPL-2.0-only AND GPL-2.0-or-later AND LGPL-3.0-or-later AND AGPL-3.0-or-later
+Group: Productivity/Telephony/Utilities
+Provides: %name-utils = %version-%release
+
+%description tools
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project.
+
+This package contains a program for frequency calculation for GSM
+called "osmo-arfcn", and a program called "osmo-auc-gen" that is used
+for testing GSM authentication, as well as "osmo-config-merge", a tool
+for merging Osmocom configuration files.
+
+%package -n libosmocodec4
+Summary: GSM 06.10, 06.20, 06.60, 06.90 codec library
+License: GPL-2.0-or-later
+Group: System/Libraries
+
+%description -n libosmocodec4
+The libosmocodec library contains an implementation of multiple
+GSM codecs:
+
+* GSM 06.10 Full Rate (FR) codec
+* GSM 06.20 Half Rate (HR) codec
+* GSM 06.60 Enhanced Full Range (EFR) codec
+* GSM 06.90 Adaptive Multi-Rate (AMR) codec
+
+%package -n libosmocodec-devel
+Summary: Development files for the Osmocom GSM codec library
+License: GPL-2.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocodec4 = %version
+
+%description -n libosmocodec-devel
+The libosmocodec library contains an implementation of multiple
+GSM codecs.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmocodec.
+
+%package -n libosmocoding0
+Summary: GSM/GPRS/EDGE transcoding routines library
+License: GPL-2.0-or-later
+Group: System/Libraries
+
+%description -n libosmocoding0
+libosmocoding is a library which provides GSM, GPRS and EDGE
+transcoding routines.
+
+The following data types are currently supported: xCCH, PDTCH (CS 1-4
+and MCS 1-9), TCH/FR, TCH/HR, TCH/AFS, RCH/AHS, RACH and SCH.
+
+%package -n libosmocoding-devel
+Summary: Development files for the Osmocom transcoding library
+License: GPL-2.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocodec-devel = %version
+Requires: libosmocoding0 = %version
+Requires: libosmocore-devel = %version
+Requires: libosmogsm-devel = %version
+
+%description -n libosmocoding-devel
+libosmocoding is a library which provides GSM, GPRS and EDGE
+transcoding routines.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmocoding.
+
+%package -n libosmocore21
+Summary: Osmocom core library
+# crc16.c has GPL2-only clauses, the rest (*.c) is GPL-2.0+
+License: GPL-2.0-only AND GPL-2.0-or-later
+Group: System/Libraries
+
+%description -n libosmocore21
+libosmocore is a library with various utility functions shared
+between OpenBSC and OsmocomBB.
+
+%package -n libosmocore-devel
+Summary: Development files for the Osmocom core library
+# crc16.h has GPL2-only clauses, the rest (*.h) is GPL-2.0+
+License: GPL-2.0-only AND GPL-2.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocore21 = %version
+Requires: libtalloc-devel
+Requires: lksctp-tools-devel
+
+%description -n libosmocore-devel
+libosmocore is a library with various utility functions shared
+between OpenBSC and OsmocomBB.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmocore.
+
+%package -n libosmoctrl0
+Summary: Osmocom SNMP-like control interface library
+License: GPL-2.0-or-later
+Group: System/Libraries
+
+%description -n libosmoctrl0
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project.
+
+libosmoctrl is an SNMP-like control interface. In contrast to the VTY
+interface, the control interface is meant to be used by programs.
+
+%package -n libosmoctrl-devel
+Summary: Osmocom control interface library
+License: GPL-2.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocore-devel = %version
+Requires: libosmoctrl0 = %version
+Requires: libosmogsm-devel = %version
+
+%description -n libosmoctrl-devel
+libosmoctrl is an SNMP-like control interface. In contrast to the VTY
+interface, the control interface is meant to be used by programs.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmoctrl.
+
+%package -n libosmogb14
+Summary: Osmocom GPRS Gb Interface (NS/BSSGP) library
+License: AGPL-3.0-or-later
+Group: System/Libraries
+
+%description -n libosmogb14
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project.
+
+The libosmogb library contains a GPRS BSSGP protocol implementation.
+
+%package -n libosmogb-devel
+Summary: Development files for the Osmocom GPRS Gb interface library
+License: AGPL-3.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocore-devel = %version
+Requires: libosmogb14 = %version
+Requires: libosmovty-devel = %version
+
+%description -n libosmogb-devel
+The libosmogb library contains a GPRS BSSGP protocol implementation.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmogb.
+
+%package -n libosmogsm20
+Summary: Osmocom GSM utility library
+License: GPL-2.0-or-later AND AGPL-3.0-or-later
+Group: System/Libraries
+
+%description -n libosmogsm20
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project.
+
+The libosmogsm library in particular is a collection of common code
+used in various GSM related sub-projects inside the Osmocom family of
+projects. It includes A5/1 and A5/2 ciphers, COMP128v1, a LAPDm
+implementation, a GSM TLV parser, SMS utility routines as well as
+protocol definitions for a series of protocols.
+
+%package -n libosmogsm-devel
+Summary: Development files for the Osmocom GSM utility library
+License: GPL-2.0-or-later AND AGPL-3.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocore-devel = %version
+Requires: libosmogsm20 = %version
+Requires: libosmoisdn-devel = %version
+Requires: libosmoisdn0 = %version
+
+%description -n libosmogsm-devel
+The libosmogsm library in particular is a collection of common code
+used in various GSM related sub-projects inside the Osmocom family of
+projects. It includes A5/1 and A5/2 ciphers, COMP128v1, a LAPDm
+implementation, a GSM TLV parser, SMS utility routines as well as
+protocol definitions for a series of protocols.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmogsm.
+
+%package -n libosmoisdn0
+Summary: Osmocom ISDN utility library
+License: GPL-2.0-or-later
+Group: System/Libraries
+
+%description -n libosmoisdn0
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project.
+
+The libosmoisdn library in particular is a collection of common code used in
+various ISDN related sub-projects inside the Osmocom family of projects. It
+includes an I.460 sub-channel multiplex and a generic LAPD core.
+
+%package -n libosmoisdn-devel
+Summary: Development files for the Osmocom ISDN utility library
+License: GPL-2.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocore-devel = %version
+Requires: libosmoisdn0 = %version
+
+%description -n libosmoisdn-devel
+The libosmoisdn library in particular is a collection of common code used in
+various ISDN related sub-projects inside the Osmocom family of projects. It
+includes an I.460 sub-channel multiplex and a generic LAPD core.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmogsm.
+
+%package -n libosmosim2
+Summary: Osmocom SIM card related utility library
+License: GPL-2.0-or-later
+Group: System/Libraries
+
+%description -n libosmosim2
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project.
+
+The libosmosim library in particular contains routines for SIM card
+access.
+
+%package -n libosmosim-devel
+Summary: Development files for the Osmocom SIM card utility library
+License: GPL-2.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocore-devel = %version
+Requires: libosmosim2 = %version
+
+%description -n libosmosim-devel
+The libosmosim library in particular contains routines for SIM card
+access.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmosim.
+
+%package -n libosmovty13
+Summary: Osmocom VTY interface library
+License: GPL-2.0-or-later
+Group: System/Libraries
+
+%description -n libosmovty13
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project.
+
+The libosmovty library implements the interactive command-line on the
+VTY (Virtual TTY), as well as configuration file parsing.
+
+%package -n libosmovty-devel
+Summary: Development files for the Osmocom VTY interface library
+License: GPL-2.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocore-devel = %version
+Requires: libosmovty13 = %version
+
+%description -n libosmovty-devel
+The libosmovty library implements the interactive command-line on the
+VTY (Virtual TTY), as well as configuration file parsing.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmovty.
+
+%package -n libosmousb0
+Summary: Osmocom USB library
+License: GPL-2.0-or-later
+Group: System/Libraries
+
+%description -n libosmousb0
+libosmocore is a package with various utility functions that were
+originally developed as part of the OpenBSC project.
+
+The libosmosub library in particular contains routines for USB device
+access via libusb-1.0, integrated into the libosmocore select event loop.
+
+%package -n libosmousb-devel
+Summary: Development files for the Osmocom USB library
+License: GPL-2.0-or-later
+Group: Development/Libraries/C and C++
+Requires: libosmocore-devel = %version
+Requires: libosmousb0 = %version
+Requires: pkgconfig(libusb-1.0)
+
+%description -n libosmousb-devel
+The libosmosub library in particular contains routines for USB device
+access via libusb-1.0, integrated into the libosmocore select event loop.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libosmousb.
+
+
+%prep
+%setup -q
+
+%build
+echo "%version" >.tarball-version
+autoreconf -fiv
+
+CONFIGURE_FLAGS="
+ --enable-shared \
+ --disable-static \
+ --enable-systemd-logging \
+ --includedir="%_includedir/%name"
+"
+%if 0%{?centos_ver} == 7
+ CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-uring"
+%endif
+
+%configure $CONFIGURE_FLAGS
+make %{?_smp_mflags} V=1
+
+%install
+b="%buildroot"
+make %{?_smp_mflags} install DESTDIR="$b"
+find "$b/%_libdir" -type f -name "*.la" -delete
+
+%check
+make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
+
+%post -n libosmocodec4 -p /sbin/ldconfig
+%postun -n libosmocodec4 -p /sbin/ldconfig
+%post -n libosmocoding0 -p /sbin/ldconfig
+%postun -n libosmocoding0 -p /sbin/ldconfig
+%post -n libosmocore21 -p /sbin/ldconfig
+%postun -n libosmocore21 -p /sbin/ldconfig
+%post -n libosmoctrl0 -p /sbin/ldconfig
+%postun -n libosmoctrl0 -p /sbin/ldconfig
+%post -n libosmogb14 -p /sbin/ldconfig
+%postun -n libosmogb14 -p /sbin/ldconfig
+%post -n libosmogsm20 -p /sbin/ldconfig
+%postun -n libosmogsm20 -p /sbin/ldconfig
+%post -n libosmoisdn0 -p /sbin/ldconfig
+%postun -n libosmoisdn0 -p /sbin/ldconfig
+%post -n libosmosim2 -p /sbin/ldconfig
+%postun -n libosmosim2 -p /sbin/ldconfig
+%post -n libosmovty13 -p /sbin/ldconfig
+%postun -n libosmovty13 -p /sbin/ldconfig
+%post -n libosmousb0 -p /sbin/ldconfig
+%postun -n libosmousb0 -p /sbin/ldconfig
+
+%files tools
+%defattr(-,root,root)
+%_bindir/osmo-*
+
+%files -n libosmocodec4
+%defattr(-,root,root)
+%_libdir/libosmocodec.so.4*
+
+%files -n libosmocodec-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/codec/
+%_libdir/libosmocodec.so
+%_libdir/pkgconfig/libosmocodec.pc
+
+%files -n libosmocoding0
+%defattr(-,root,root)
+%_libdir/libosmocoding.so.0*
+
+%files -n libosmocoding-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/coding/
+%_libdir/libosmocoding.so
+%_libdir/pkgconfig/libosmocoding.pc
+
+%files -n libosmocore21
+%defattr(-,root,root)
+%_libdir/libosmocore.so.21*
+
+%files -n libosmocore-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/core/
+%_libdir/libosmocore.so
+%_libdir/pkgconfig/libosmocore.pc
+%_datadir/aclocal/osmo_ax_code_coverage.m4
+%_datadir/aclocal/osmo_ac_code_coverage.m4
+
+%files -n libosmoctrl0
+%defattr(-,root,root)
+%_libdir/libosmoctrl.so.0*
+
+%files -n libosmoctrl-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/ctrl/
+%_libdir/libosmoctrl.so
+%_libdir/pkgconfig/libosmoctrl.pc
+
+%files -n libosmogb14
+%defattr(-,root,root)
+%_libdir/libosmogb.so.14*
+
+%files -n libosmogb-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/gprs/
+%_libdir/libosmogb.so
+%_libdir/pkgconfig/libosmogb.pc
+
+%files -n libosmogsm20
+%defattr(-,root,root)
+%_libdir/libosmogsm.so.20*
+
+%files -n libosmogsm-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/gsm/
+%_includedir/%name/osmocom/crypt/
+%_libdir/libosmogsm.so
+%_libdir/pkgconfig/libosmogsm.pc
+
+%files -n libosmoisdn0
+%defattr(-,root,root)
+%_libdir/libosmoisdn.so.0*
+
+%files -n libosmoisdn-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/isdn/
+%_libdir/libosmoisdn.so
+%_libdir/pkgconfig/libosmoisdn.pc
+
+%files -n libosmosim2
+%defattr(-,root,root)
+%_libdir/libosmosim.so.2*
+
+%files -n libosmosim-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom/
+%_includedir/%name/osmocom/sim/
+%_libdir/libosmosim.so
+%_libdir/pkgconfig/libosmosim.pc
+
+%files -n libosmovty13
+%defattr(-,root,root)
+%_libdir/libosmovty.so.13*
+
+%files -n libosmovty-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/vty/
+%_includedir/%name/osmo-release.mk
+%_libdir/libosmovty.so
+%_libdir/pkgconfig/libosmovty.pc
+
+%files -n libosmousb0
+%defattr(-,root,root)
+%_libdir/libosmousb.so.0*
+
+%files -n libosmousb-devel
+%defattr(-,root,root)
+%dir %_includedir/%name
+%dir %_includedir/%name/osmocom
+%_includedir/%name/osmocom/usb/
+%_libdir/libosmousb.so
+%_libdir/pkgconfig/libosmousb.pc
+
+%changelog
diff --git a/contrib/struct_endianess.py b/contrib/struct_endianness.py
index be73fbe2..e6cbe00b 100755
--- a/contrib/struct_endianess.py
+++ b/contrib/struct_endianness.py
@@ -17,6 +17,7 @@ re_struct_end = re.compile(r'^}[^;]*;\s*$')
re_substruct_start = re.compile(r'^\s+struct\s*{\s*$')
re_substruct_end = re.compile(r'^\s+}\s*([^;]*\s)[a-zA-Z_][a-zA-Z_0-9]*\s*;\s*$')
+re_unnamed_substruct_end = re.compile(r'^\s+}\s*;\s*$')
re_int_def = re.compile(r'(^\s*((const|unsigned|signed|char|int|long|int[0-9]+_t|uint[0-9]_t)\s+)+\s*)([^;]*;)',
re.DOTALL | re.MULTILINE)
@@ -73,7 +74,8 @@ def section_struct_body(struct_body_lines):
line = struct_body_lines[j]
if (re_substruct_start.fullmatch(line)
- or re_substruct_end.fullmatch(line)):
+ or re_substruct_end.fullmatch(line)
+ or re_unnamed_substruct_end.fullmatch(line)):
end_def()
arbitrary_part.append(line)
j += 1
@@ -251,7 +253,7 @@ def handle_struct_body(body_str):
new_lines = ['#if OSMO_IS_LITTLE_ENDIAN\n']
new_lines.append(body_str)
new_lines.append('#elif OSMO_IS_BIG_ENDIAN\n'
- '/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */\n')
+ '/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */\n')
new_lines.append(big_endian_body_str)
new_lines.append('\n#endif\n')
return ''.join(new_lines)
@@ -259,7 +261,7 @@ def handle_struct_body(body_str):
return body_str
def _check_file(f):
- if not (f.endswith('.h') or f.endswith('.c') or f.endswith('.cpp')):
+ if not f.endswith(('.h', '.c', '.cpp')):
return
# section the file into
diff --git a/contrib/talloc_count.sh b/contrib/talloc_count.sh
new file mode 100755
index 00000000..61aa3f57
--- /dev/null
+++ b/contrib/talloc_count.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Print a summary of how often each named object appears in a talloc report.
+#
+# usage:
+# talloc_count.sh my_talloc_report.txt
+# or:
+# osmo_interact_vty.py -p 4242 -c 'show talloc-context application full' | talloc_count.sh
+#
+# produces output like:
+# 1 struct foo
+# 1 struct log_info
+# 1 struct log_info_cat
+# 21 msgb
+# 1391 SCCP-SCOC(N)[N]
+# 1402 struct osmo_fsm_inst
+# [...]
+
+f="$1"
+
+tmpdir="$(mktemp -d)"
+trap "rm -rf \"$tmpdir\"" EXIT
+
+# without input file, read stdin
+if [ "x$f" = "x" ]; then
+ f="$tmpdir/input"
+ cat > $f
+fi
+
+mangled="$tmpdir/mangled"
+grep contains "$f" \
+ | sed 's/[ \t]*contains.*//' \
+ | sed 's/^[ \t]*//' \
+ | sed 's/[ \t][ \t]*/ /g' \
+ | grep -v '^$' \
+ | grep -v '^[0-9]\+$' \
+ | sed 's/0x[0-9a-fA-F]\+/N/g' \
+ | sed 's/\<[0-9a-fA-F]\+\>/N/g' \
+ | sed 's/[0-9]\+/N/g' \
+ | sort \
+ > "$mangled"
+
+count() {
+ name="$1"
+ nr="$(grep -Fx "$name" "$mangled" | wc -l)"
+ printf "%6d $name\\n" $nr
+}
+
+{
+ cat "$mangled" | uniq | while read type; do
+ count "$type"
+ done
+} | sort -h
diff --git a/debian/changelog b/debian/changelog
index 0e280896..c9184df0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,1817 @@
+libosmocore (1.9.0) unstable; urgency=medium
+
+ [ Oliver Smith ]
+ * gsm0808_dec_channel_type: add missing len check
+ * test_gsm0808_enc_dec_channel_type -> …_speech
+ * gsm0808_chan_indicator: add SPEECH_CTM_TEXT_TELEPHONY
+ * gsm0808_enc/dec_channel_type: support data
+ * Fix typo endianess -> endianness
+ * Run struct_endianness.py
+ * gsm0808_dec_channel_type: fix dec of ch_rate_type
+ * tests: add test_gsm0808_enc_dec_channel_type_sign
+ * gsm0808_enc/dec_channel_type: fix transparent flag
+ * libosmocore.map: add tall_{ctr/msgb}_ctx
+ * rsl: put values for Channel Mode into enums
+ * rsl: RSL_CMOD_CSD_T: make enum values consistent
+ * gsm0808: make CSD enum values consistent with RSL
+ * on_dso_load_select: run after on_dso_load_ctx
+ * debian: set compat level to 10
+ * debian: depend on liburing-dev for debian >= 11
+ * contrib/libosmocore.spec: centos7: disable uring
+ * debian: fix build on ubuntu 20.04 without liburing
+
+ [ Neels Hofmeyr ]
+ * add contrib/talloc_count.sh
+ * add osmo_prim_operation_name()
+ * add gsm0808_amr_modes_from_cfg
+ * improve test output for gsm0808_sc_cfg_from_gsm48_mr_cfg()
+ * fix 'make vty-test' for --disable-gb
+ * fix 'make vty-test' for --disable-external-tests --enable-gb
+ * contrib/talloc_count.sh: improve hexadecimal masking
+ * error log: osmo_sock_init2_multiaddr() v4/v6 mix
+ * logging vty: probe 'print' and 'logging timestamp' cmds
+ * vty: show bug in implicit go_parent_node
+ * vty: fix vty->index for implicit go_parent_node
+ * vty: move struct vty_parent_node to private API
+ * gsm: add osmo_mobile_identity_decode_from_l3_buf()
+ * gsm_04_08_gprs: add IEI "GMM TMSI Based NRI Container"
+ * improve API for osmo_routing_area_id
+
+ [ Max ]
+ * socket: propagate error in osmo_sock_unix_init() to the caller
+ * GSMTAP: fix typo
+ * GSMTAP: add gsmtap_source_init*2()
+ * GSMTAP: add missing parameter docstrings
+ * logging: print talloc report on exit from vty test
+
+ [ Philipp Maier ]
+ * i460_mux.c fix apidoc
+ * i460_mux: make osmo_i460_subchan_count public
+ * gsmtap_util: remove whitespace at the end of line
+ * i460_mux: add define constant for maximum number of subchannels
+ * codec: add define constants for RFC5993 and TS101318
+ * gprs_bssgp_rim: also print NSEI when sending RIM messages
+ * gprs_bssgp_rim: allow sending of encoded RIM messages
+ * gprs_bssgp_rim: add decoder for RIM ROUTING ADDRESS
+
+ [ Vadim Yanitskiy ]
+ * gsm: fix invalid check in gsm48_decode_ssversion()
+ * gsm: add missing features to osmo_bts_features_names[]
+ * gsm: ensure completeness of osmo_bts_features_{descs,names}[]
+ * gsm/{bsslap,bssmap_le}: zero-initialize structs using memset()
+ * msgb: use OSMO_ASSERT in msgb_alloc_headroom[_c]()
+ * gsm: use OSMO_ASSERT() in osmo_iuup_msgb_alloc_c()
+ * debian/control: make libosmocore-doc depend on libosmo{ctrl,gb}-doc
+ * debian/control: fix typo
+ * doxygen: also generate documentation for libosmo{sim,usb}
+ * contrib/struct_endianness.py: simplify file extension check
+ * doxygen: remove documentation for non-existent params
+ * doxygen: fix various typos in commands \param and \returns
+ * gsm0502: add burst length definitions from chapter 5.2
+ * coding: clean up Makefile.am
+ * utils/Makefile.am: remove duplicate libosmogsm.la
+ * utils/Makefile.am: do not overwrite AM_CFLAGS
+ * utils/osmo-stat-dummy/Makefile.am: drop empty variables
+ * gsmtap: add missing entries to gsmtap_type_names[]
+ * tests/v110: assert(user_data_chunk_bits) in test_ra1()
+ * isdn: fix identical operands in v110_adapt_IR8000_to_2400()
+ * gsm_04_08: document/clarify enum gsm48_chan_mode values
+ * gsm_04_08: add more enum gsm48_chan_mode speech values
+ * gsm_04_08: add more enum gsm48_chan_mode data values
+ * gsm0808: handle new enum gsm48_chan_mode speech/data values
+ * tests: make VTY tests depend on the respective binaries
+ * fixup (partial revert): "coding: clean up Makefile.am"
+ * core: remove unnecessary #include <osmocom/core/talloc.h>
+ * libosmocore.map: add missing symbols needed for osmo-qcdiag
+ * coding: fix doxygen doc for _xcch_encode_cB()
+ * copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
+ * coding: use GSM_MACBLOCK_LEN gsm0503_tch_fr_decode()
+ * coding: gsm0503_tch_f96_[de]interleave() not applicable to TCH/F2.4
+ * coding: declare gsm0503_tch_f96_[de]interleave()
+ * coding: fix API doc: TCH/H needs 6 bursts, not 8
+ * coding: fix API doc: TCH/AFS vs TCH/AHS
+ * coding: use gsm0503_tch_hr_decode2() in coding_test
+ * gsm: fix convolutional code definition for TCH/F4.8
+ * coding: implement TCH/F9.6, TCH/[FH]4.8, TCH/H2.4, TCH/F14.4
+ * coding: implement dedicated codec API for FACCH/[FH]
+ * coding: test FACCH/[FH] bitstealing in test_csd()
+ * coding: fix _tch_csd_burst_map(): do not overwrite FACCH
+ * struct osmo_sub_auth_data: remove OSMO_DEPRECATED_OUTSIDE
+ * coding: fix a copy-paste bug in gsm0503_tch_afs_decode_dtx()
+ * gsm: add gsm0502_fn2ccch_block()
+ * gsm0502: cosmetic: use ARRAY_SIZE in gsm0502_fn2ccch_block()
+ * ipa: fix a typo in ipa_ccm_rcvmsg_base(): PING -> PONG
+ * core: fix pointer access in msgb_l[1-4] macros
+ * coding: remove redundant memset()s in gsm0503_tch_fr{96,144}_encode()
+ * coding: implement encoding/decoding API for TCH/F2.4
+ * lapdm: cosmetic: simplify lapdm_phsap_up(), use OSMO_PRIM[_HDR]
+ * gsm_08_08: define GSM0808_SCT_EXT (separately)
+ * gsm48_ie: fix gsm48_encode_bearer_cap(): encode bcap->data.transp
+ * isdn: mux_timeslot_provide_bits(): remove unused 'count'
+ * .gitignore: add include/osmocom/core/socket_compat.h
+ * tests/{v110,gsm44021}: change naming: 'test_' -> '_test'
+ * gsm_12_21.h: add flags for NM_ATT_IPACC_SUPP_FEATURES
+ * gsm_12_21.h: fix typo: NM_IPAC_F_CHANT_P{C->D}CHF
+
+ [ Pau Espin Pedrol ]
+ * gsm_04_60.h: Better describe origin of enum osmo_gprs_nmo
+ * Move libosmogsm TS 44.060 declarations under include/osmocom/gsm/
+ * libosmogb.pc.in: Fix missing dependency on libosmogsm
+ * gsm: gsm_gsmtime2fn(): constify param
+ * cosmetic: stats_tcp: Fix typo in comment
+ * logging: Unregister osmo_fd before closing fd
+ * select: Optimize osmo_fd_get_by_fd
+ * select.c: Clarify osmo_fd_(un)register() API expectations of registered fd
+ * select.c: Clarify osmo_fd_unregister() can only be called on registered osmo_fds
+ * configure.ac: Fix logic enabling osmo_fd fd checks
+ * configure.ac: Fix typo in enable flag description
+ * select.c: osmo_fd_unregister(): Avoid assert hit with old buggy users of the API
+ * tests/Makefile.am: Move system libs at the end of list
+ * tests/Makefile.am: Drop duplicated libosmogb.la in LDADD
+ * tests/Makefile.am: Move LDADD to right position
+ * logging.c: Sanitize calls to osmo_fd_unregister()
+ * Fix parsing of TLV_TYPE_SINGLE_TV
+ * gsm: Add missing TS 24.008 SM layer IEs
+ * gsm_04_08_gprs.h: Add missing GMM IEs for T3302 and T3346
+ * gsm_04_08_gprs.h: Add enum field for GMM 'P-TMSI type' IE
+ * gb: ns2: Rename parameter name in gprs_ns2_nsvc_by_sockaddr_bind()
+ * tlv: Show bug in decoded tlv_parsed for type TLV_TYPE_SINGLE_TV
+ * Fix 'Fix parsing of TLV_TYPE_SINGLE_TV'
+ * ns2: Count transmitted/dropped in each layer implementation
+ * cosmetic: codec/Makefile.am: list sources one file per line
+ * osmo_io: Make name optional, add _set_name() API
+ * socket: Cache errno before calling further functions
+ * gsm0502.h: Document spec number
+ * gsm: Add missing IE definition for GMM Receive N-PDU Number list
+ * exec: osmo_system_nowait2(): Improve logging and error checks
+ * sockaddr_str: Introduce macro OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL
+ * cosmetic: lapdm: Fix typo in comment
+ * tlv: Introduce API msgb_tv32_push()
+ * lapdm: Track fn of primitives in struct lapdm_msg_ctx
+ * gsm: Introduce functions to convert between FN and RFN (Reduced FN)
+ * socket: Add osmo_sock_init flag to enable SCTP ASCONF features
+ * rsl: Introduce new osmocom extension IE RSL_IE_OSMO_ABS_FRAME_NUMBER
+ * lapdm: Append RSL_IE_OSMO_ABS_FRAME_NUMBER to RSLms msgs towards upper layers
+ * Revert "lapdm: Append RSL_IE_OSMO_ABS_FRAME_NUMBER to RSLms msgs towards upper layers"
+ * Revert "rsl: Introduce new osmocom extension IE RSL_IE_OSMO_ABS_FRAME_NUMBER"
+ * lapdm: Update public lapdm_msg_ctx upon CCCH data ind
+ * socket: Avoid aborting socket creation if setsockopt for ASCONF fails
+ * gsm_12_21.h: Introduce packed structs for NM_ATT_IPACC_RLC_CFG{,_2,_3} values
+ * gsm_12_21.h: Introduce packed structs for NM_ATT_IPACC_BSSGP_CFG values
+ * gsm_12_21.h: Introduce packed structs for NM_ATT_IPACC_NS_CFG values
+ * socket: Remove OSMO_SOCK_F_SCTP_ASCONF_SUPPORTED, add osmo_sock_init2_multiaddr2()
+ * socket: Support setsokopt SCTP_INITMSG in osmo_sock_init2_multiaddr2()
+
+ [ Matan Perelman ]
+ * gsm0808_enc_channel_type: Add spare byte
+
+ [ Harald Welte ]
+ * Implement the XOR-2G authentication algorithm
+ * New unit test for XOR-2G authentication
+ * Rename OSMO_AUTH_ALG_XOR to OSMO_AUTH_ALG_XOR_3G
+ * convolutional coding for CSD
+ * Interleaving for CSD
+ * libosmocore.map: Add two missing entries for gsmtap*2() API
+ * Makefile.am: Make libraries depend on .map files
+ * isdn: Add V.110 encoder/decoder
+ * gsm: TS 44.021 modified V.110 frame encoding/decoding support
+ * gsm_08_58: Document IPAC RTP CSD modes in at least a few words
+ * New osmo-gsmtap-logsend utility
+ * gsmtap.h: Define a packet type for encapsulation of GSM RLP frames
+ * rate_ctr: Add rate_ctr_add2() similar to rate_ctr_inc2()
+ * Add osmo_io with initial poll backend
+ * Add osmo_gsm48_si1ro_nch_pos_{encode,decode} functions
+ * cosmetic: Fix spec reference in RSL header file
+ * gsm_08_58.h: Add 'struct rsl_ie_nch_drx_info'
+ * libosmogsm: Support authentication with 256-bit K and/or OP/OPc
+ * libosmogsm: Add OSMO_ASSERT() to ensure correct algorithm
+ * libosmogsm: Ensure MILENAGE + XOR-3G K length is 128 bit
+ * osmo-auc-gen: Convert over to osmo_auth_gen_vec*2 API
+ * libosmogsm: Allow auth API caller to specify RES length
+ * libosmogsm: Factor out the C2 derivation function
+ * libosmogsm: Avoid executing MILENAGE crypto twice (for UMTS and GSM)
+ * libosmogsm: Add support for TUAK authentication algorithm
+ * gsmtap_source_free(): Don't crash if NULL is passed
+
+ [ Daniel Willmann ]
+ * Add libosmocore.map
+ * Add osmo_sockaddr_size() to return the size of the variant used
+ * tests: Add initial osmo_io tests
+ * osmo_io: Avoid read of uninitialized variable
+ * gpsr_ns2_udp: Use osmo_io_fd instead of osmo_fd
+ * osmo_io: Improve handling and documentation of segmentation_cb
+ * osmo_io: Support detecting non-blocking connect()
+ * osmo_io: Consistency - put read/recv callback first in osmo_io_ops
+ * osmo_io: Don't make msg in write_cb const
+ * osmo_io: Remove osmo_iofd_read/write_enable/disable
+ * socket: Ensure fd is not negative in osmo_sock_get_name_buf()
+ * osmo_io: Return early on error in osmo_iofd_register()
+ * osmo_io: Use LOGPIO instead of LOGP
+ * osmo_io: Use bitfield for various boolean flags
+ * osmo_io: Make the test more deterministic between backends
+ * osmo_io: Fix write_enable handling in iofd_txqueue
+ * osmo_io: Remove missing functions from map file
+ * osmo_io: Add osmo_iofd_notify_connected()
+ * osmo_io: Document expectation that segmentation_cb() can modify msgb
+ * osmo_io: Add function to change the maximum length of the tx_queue
+ * cosmetic: Fix doc comment
+ * osmo_io(cosmetic): End in a dot for doxygen AUTO_BRIEF
+ * osmo_io: Fix length calculation in iofd_handle_segmentation()
+ * osmo_io: Ensure correct ownership of msgb when sending
+ * osmo_io: Use MSG_NOSIGNAL to avoid SIGPIPE on write
+ * osmo_io: Add iofd_handle_recv()
+ * osmo_io: Avoid potential double free when sending msgb
+ * osmo_io: Add io_uring backend
+ * ns2: Add VTY option to change the max write queue size for UDP
+ * osmo_io: Change parent of msghdr to iofd (instead of msg)
+ * osmo_io: Use local variable to reference msghdr->msg
+
+ [ Eric ]
+ * fix _thread order
+ * logging: remove log_initialized(void)
+ * gsm48_rest_octets: fix wrong value
+
+ [ Mychaela N. Falconia ]
+ * codec: add osmo_efr_check_sid() function
+ * codec: add SID classification functions per GSM 06.31 & 06.81
+ * codec: add SID preening functions for FR & EFR
+ * codec: add osmo_{fr,efr}_is_any_sid() inline functions
+ * codec: add osmo_gsm611_silence_frame[] datum
+ * codec cosmetic: move old FR ECU code to ecu_fr_old.c
+ * codec: replace GSM-FR ECU with new implementation
+ * coding: fix decoding of EFR triplicated bits
+ * gsm0503_tch_hr_decode(): look at all 8 stealing bits
+ * gsm0503_tch_hr_encode(): accept both TS101318 and RFC5993 payloads
+ * gsm0503_tch_hr_decode2(): new function, emits TS101318 format
+ * libosmocoding.map: export gsm0503_tch_hr_decode2()
+ * coding cosmetic: gsm0503_tch_{fr,hr}_encode(): remove extra spacing
+ * codec: new functions osmo_{fr,efr}_sid_reset()
+ * codec: new function osmo_hr_sid_reset()
+ * coding: gsm0503_tch_{fr,hr}_encode(): add ability to emit BFI
+ * ecu: add is_dtx_pause() method
+
+ [ arehbein ]
+ * core: Check return value of osmo_fd_register()
+ * core: Add function to update osmo_io_ops field for osmo_io_fd
+ * core/osmo_io: Rename variables for readability
+ * core/osmo_io: Fix reception of partial packets
+ * gsm/ipa: Add segmentation callback
+ * Revert "gsm/ipa: Add segmentation callback"
+ * select: Prevent negative index lookup on osmo_fd_lookup.table
+
+ [ Andreas Eversberg ]
+ * ASCI: Add 3GPP TS 44.068 and 44.069 protocol definitions
+ * ASCI: Add IE transcoding according to 3GPP TS 48.008
+ * Added generation of include/osmocom/core/socket_compat.h
+ * ASCI: Add message definition and encoding according to 3GPP TS 48.008
+ * Add support for sending Bter UI frames at lapdm.c
+ * Add support for receiving Bter UI frames at lapdm.c
+ * Add short L3 header to gsm_04_08.h
+ * Fix short L3 header of SI 10 at gsm_04_08.h
+ * ASCI: Add Notification/NCH message to gsm_04_08.h
+ * lapdm: Do not return an error when enqueuing a frame
+ * Add VGCS UPLINK GRANT message structure to gsm_04_08.h
+ * ASCI: Also display group/broadcast call message names
+ * Allow 'configure <cr>' at VTY to enter config mode
+ * ASCI: Add decoding of mobile identity in TALKER INDICATION
+ * ASCI: Add missing check for return value of gsm0808_enc_speech_codec_list2()
+ * ASCI: Add BCC call state definitions
+ * LAPDM: Use correct offset to short header on recevied frame
+ * ASCI: Add definition for TALKER INDICATION and UPLINK RELEASE
+
+ [ Sylvain Munaut ]
+ * gsm: Fix comment for TCH/F4.8 code
+ * gsm: Improve the TCH/H2.4 coding routines
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 13:15:52 +0200
+
+libosmocore (1.8.0) unstable; urgency=medium
+
+ [ Vadim Yanitskiy ]
+ * fix uninitialized err pointer passed to osmo_bssap_le_dec()
+ * gsm0408_test: do not return early in test_bearer_cap()
+ * gsm0408_test: add a testcase for gsm48_decode_bearer_cap()
+ * gsm48_ie: fix coding style: while is not a function
+ * gb: fix uninitialized ptr access in bssgp_encode_rim_pdu()
+ * fsm: add unit tests verifying state timeout s/ms accuracy
+ * fsm: fix state_chg(): pass microseconds to osmo_timer_schedule()
+ * tests/tdef: assert pointer returned by osmo_tdef_get_entry()
+ * gb/gprs_ns: call osmo_timer_del() unconditionally
+ * fsm: osmo_fsm_{event,inst,state}_name(): make *fi pointer const
+ * logging: add a new category DLCSN1 for libosmo-csn1
+ * {gb,sim,usb}: ensure -no-undefined is present in *_la_LDFLAGS
+ * include: use '#pragma once' everywhere
+ * gsm0502: use parentheses in GSM_TDMA_FN_{SUM,SUB} macros
+ * configure.ac: fix 'AM_CONDITIONAL(ENABLE_GNUTLS, false)' listed twice
+ * {gsm,gb}/Makefile.am: drop undefined $GCC_FVISIBILITY_HIDDEN
+ * gsm0502: gsm0502_fn_remap(): use GSM_TDMA_FN_SUB() macro
+ * */Makefile.am: do not mix up AM_CFLAGS with AM_CPPFLAGS
+ * gsm0808: cosmetic: switch is not a function
+ * gsm0808: remove unneeded assignment in enc_speech_codec()
+ * gsm0808: remove redundant assert() in enc_speech_codec()
+ * gsm0808: remove over-defensive assert()s for function parameters
+ * gsm0808: add gsm0808_enc_speech_codec[_list]2()
+ * gsm0808: use new gsm0808_enc_speech_codec[_list]2() API
+ * gsm48_ie: gsm48_decode_freq_list(): make 'cd' argument const
+
+ [ Pau Espin Pedrol ]
+ * iuup: Explicitly mark default case as unexpected with assert
+ * cbsp: avoid potential msgb write overflow in osmo_cbsp_recv_buffered
+ * gsm_23_041.h: Define CBS ETWS Warning Type values
+ * cbsp: Return error if decoding any of the cell id lists fail
+ * tests: Run smscb/gsm0341_test during make check
+ * cbsp: Guard against malformed msgb without l1h,l2h being passed
+ * cbsp: Fix decoding of Fail List
+ * cosmetic: tlv.h: Fix trailing whistespace
+ * tlv.h: Fix TLVP_PRESENT returning a pointer instead of a boolean
+ * gsm: Add BTS feature for Osmux
+ * gsm: rsl: Define new osmocom extension TLV IE to pass Osmux CID
+ * gsm: bts_features: Add missing entries to osmo_bts_features_names
+ * utils.h: protect param with parenthesis in OSMO_BYTES_FOR_BITS()
+ * vty: Allow using hex representations in cmd numeric ranges
+ * socket.h: Reorder sockaddr APIs to have them all together
+ * socket: Introduce API osmo_sockaddr_is_any
+ * gsm: constify several readonly params
+ * ctrl: error if program forgot to initialize the ctr handler before installing cmds
+ * socket.h: Introduce API osmo_sockaddr_netmask_to_prefixlen()
+ * Move src/*.{c,h} to src/core/
+ * src/core/Makefile.am: reformat SOURCES list
+ * Split include/Makefile.am content into subdirs
+ * Makefile.am: Remove unexsiting all_includes variable
+ * Fix all references to config.h
+ * Introduce netns API
+ * Introduce netdev API
+ * Introduce tundev API
+ * configure --enable-libmnl: Add libmnl to libosmocore.pc.in Requires
+ * netdev: Fix compilation building with --disable-libmnl
+ * tun: Fix potential unpaired call to osmo_netns_switch_exit()
+ * gprs_ns2_fr: use osmo_netdev to monitor and operate network device
+ * debian/rules: Fix moved path crc*gen.c
+
+ [ Mychaela Falconia ]
+ * gsm48_ie: fix parsing of Bearer capability IE without octet 3a
+
+ [ Harald Welte ]
+ * sim/class_tables: Add GET IDENTITY, SUSPEND UICC, EXCHANGE CAPABILITIES
+ * allocate VTY port number 4270 for osmo-isdntap
+ * logging.h: Allocate DLM2PA and DLM2UA for libosmo-sigtran
+ * Support building with -Werror=strict-prototypes / -Werror=old-style-definition
+ * Disable -Wstrict-prototypes for logging_vty_add_cmds()
+ * vty/logging.h: Avoid -Werror=pragmas error in C++ code
+ * Add -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition
+ * Fix typos in copyright statements.
+ * gsmtap.h: Add definitions for various ISDN sub-types
+ * create libosmoisdn sub-library
+ * isdndlc: Fix documentation
+
+ [ Oliver Smith ]
+ * gsm0808_enc_aoip_trasp_addr: add length check
+ * utils/osmo-stat-dummy: check for ENABLE_UTILITIES
+ * d/control: libosmocore-dev: depend on libmnl-dev
+ * gsm_08_08.h: fix typo in GSM0808_DATA_FULL_PREF
+
+ [ Alexander Couzens ]
+ * gprs_ns2: add vty `nse <0-65535> restart sns`
+ * gb: add bssgp2_enc_flush_ll encode FLUSH-LL
+
+ [ Neels Hofmeyr ]
+ * enrich API doc for gsm0808_speech_codec
+ * gsm0408_test: do not print errno in expected output
+ * comments: gsm_08_08.h: AMR cfg: explain in much more detail
+ * osmo_tdef_get(): clarify API doc on val_if_not_present
+
+ [ Max ]
+ * Ignore osmo-ns-dummy
+ * Add function to guess AF_UNSPEC address
+ * Add osmo_sockaddr_strs_to_str()
+ * cosmetic: remove trailing space
+ * cosmetic: make linter happy with LAPD code
+ * LAPD: log unknown format value
+ * LAPD: use bool for T200 reset flags
+ * msgb: expand copy test
+ * doc: correct typo in ticket reference
+ * msgb: introduce extended copy functions
+ * Add define for unset Frame Number
+ * LAPD: move tx_hist code into static functions
+ * osmo-ns-dummy: add ctrl interface
+ * jenkins_arm.sh: disable external tests
+ * vty: fix doc typo
+ * telnet_init_dynif: propagate error
+ * telnet_init_dynif: don't allow negative port
+ * rate_ctr: convert to timerfd
+ * rate_ctr: drop rate estimation code
+ * osmo-stat-dummy: add rate counters and statsd tester
+ * ctrl: add optional port to bind command
+ * ASCI: add VBS/VGCS support to BTS features list
+ * SI: add RR short PD message types
+ * Fixup .gitignore
+ * SI: add missing header
+ * Add SI10 support
+
+ [ neels ]
+ * Revert "Add osmo_sockaddr_strs_to_str()"
+ * Revert "Add function to guess AF_UNSPEC address"
+
+ [ Daniel Willmann ]
+ * use_count: Return if uc is NULL
+
+ [ Keith Whyte ]
+ * Fix LCLS-CONNECT-CONTROL generation
+ * Fix Typo in gsm0808_msgt_names[]
+
+ [ Philipp Maier ]
+ * msgb: assert msgb->lXh to be not NULL
+ * msgb: do not use msgb_l4 instead of msgb_sms
+ * bits: fix typo
+ * uitils: add floored and euclidian modulo functions
+ * gsm0408_test: add unittest for gsm_gsmtime2fn()
+ * gsm_utils: improve gsm_gsmtime2fn()
+
+ [ arehbein ]
+ * gb/vty: Show if NSVC is blocked locally by O&M/vty or by remote
+ * libosmocore: Deprecate APIs telnet_init(_dynip)()
+ * libosmocore: Transition to use of 'telnet_init_default'
+
+ [ Eric ]
+ * bitgen test: fix concat macro
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 11:20:41 +0100
+
+libosmocore (1.7.0) unstable; urgency=medium
+
+ [ Vadim Yanitskiy ]
+ * .gitignore: add utils/osmo-aka-verify binary
+ * tests/vty: fix use of GNU 'missing =' extension in designator
+ * debian/control: minimum version of libtalloc-dev must be >= 2.1.0
+ * tests/tdef: rename the binaries to end with '_test'
+ * tdef: fix wrong path in documentation: tests/vty -> tests/tdef
+ * bitvec_read_field(): indicate errors using errno
+ * bitvec_read_field(): fix incorrect bit-shift issue found by UBSan
+ * bitvec_read_field(): optimize by expanding bytenum_from_bitnum()
+ * tests/testsuite.at: ensure empty stderr for the bitvec_test
+ * VTY: enable talloc introspection for OTC_GLOBAL
+ * VTY: implement 'no log gsmtap [HOSTNAME]' command
+ * contrib/libosmocore.spec.in: mention osmo-config-merge in utils
+ * gsm_7bit_encode_n(): use regular malloc() instead of calloc()
+ * tests/logging: ensure both stream and wqueue modes are tested
+ * tests/logging: also test printing the filename information
+ * logging: fix coding style issues in _output_buf()
+ * logging: fix printing of '\0' when filename printed last
+ * tests/logging: merge both logging_test_{stream,wqueue}.err
+ * core/utils.h: add OSMO_LIKELY / OSMO_UNLIKELY macros
+ * core/utils.h: wrap OSMO_ASSERT() with do { ... } while (0)
+ * core/msgb.h: make use of OSMO_LIKELY / OSMO_UNLIKELY
+ * core/utils.h: make use of OSMO_LIKELY in OSMO_ASSERT
+ * libosmocodec: osmo_hr_check_sid(): simplify the logic
+ * contrib/jenkins_amd64.sh: remove FreeBSD specific quirks
+ * bssmap_le: support additional IEs in Perform Location Request
+ * Use internal <osmocom/core/talloc.h> everywhere
+ * coding: fix comments for detect_afs_sid_{first,update,onset}
+ * coding: cosmetic: move 'dtx_prev' to the scope where it's used
+ * coding: use switch statement in gsm0503_tch_a[fh]s_decode_dtx()
+ * coding: properly handle AFS_SID_UPDATE frames in DTX mode
+ * coding: prevent marking FACCH frames as AMR's special DTX frames
+ * tests/dtx: test detection/decoding of A[FH]S_SID_UPDATE
+ * tests/dtx: test tagging of FACCH/[FH] frames
+ * coding: fix decoding of AHS_SID_UPDATE frames (BER ~50%)
+ * coding: do not reset codec ID on receipt of DTX frames
+ * coding: add gsm0503_detect_a[fh]s_dtx_frame2()
+ * coding: separate gsm0503_tch_a[fh]s_decode_inband()
+
+ [ Eric ]
+ * fix isdigit taking unsigned as input
+ * logging: allow disabling macros using a new define: LIBOSMOCORE_NO_LOGGING
+
+ [ Daniel Willmann ]
+ * bssgp_bvc_fsm: Move log message to the correct place
+ * bssgp_bvc_fsm: Add a hook to notify when a reset was acknowledged
+
+ [ Harald Welte ]
+ * logging: Fix memory leak in case async log write queue overflows
+ * write_queue: Document it that caller is responsible if enqueue fails
+ * gsmtap: Add gsmtap_sendmsg_free() as alternative to gsmtap_sendmsg()
+ * Introduce CRC and FSM for IuUP (user plane) as used in 3G RTP data
+ * tcp_stats: fix compilation on CentOS 7
+ * iuup: Fix signed/unsigned loop counter control flow issue
+ * src/conv.c: Align better with Osmocom coding style
+ * bitvec: Fix -Wsign-compare warnings
+ * utils: Fix -Wsign-compare warnings
+ * log_taget_find() should use enum log_target_type, not int
+ * bits.c: Fix -Wsign-compare warnings
+ * socket, select: Fix -Wsign-compare warnings
+ * msgb: Fix -Wsign-compare warnings
+ * osmo_libusb: Fix NULL check in osmo_usb_removed_cb()
+ * osmo_libusb: Use libusb_get_pollfds() to get initial file descriptors
+ * osmo_libusb: Print log message on libusb initialization error
+ * gsm0808: Test if we properly decode a SRVCC cell identifier list
+ * usb: Match device by VID/PID without path/addr if it is unique
+ * clean-up pkg-config files: Make use of "Requires" as documented
+ * debian/control: libosmocore-dev must depend on libsctp-dev and libusb-1.0-0-dev
+ * libosmo{gb,vty}.pc.in: Add talloc to 'Requires'
+ * libosmocore.spec: Make libosmocore-devel require libsctp
+ * libosmovty: Link libosmovty against libpthread
+ * vty: Support platforms that don't support pthread_getname_np()
+ * vty: Add a 'skip-zero' version of 'show stats' and 'show rate-counters'
+ * stats: Functions with no arguments should specify(void)
+ * stats: Avoid NULL pointer deref in allocation failure paths.
+ * stats: don't try to save unknown stats reporter types
+ * fsm_vty: use unsigned int when left-shifting 31 bits!
+ * update git URLs (git -> https; gitea)
+
+ [ Eric Wild ]
+ * logging: make LIBOSMOCORE_NO_LOGGING work as expected
+
+ [ Oliver Smith ]
+ * treewide: remove FSF address
+ * select_main: don't poll forever during shutdown
+ * Cosmetic: linuxlist.h: fix misleading comment
+
+ [ Philipp Maier ]
+ * stats: fix typo
+ * stat_item: tolerate NULL pointer argument in osmo_stat_item_group_free
+ * select: gather statistics for TCP connections
+ * stats_tcp: use a default batch size of 5 instead of 1
+ * stats_tcp: fix stats item identifier
+ * gsm23003: fix docstring for osmo_plmn_from_bcd()
+ * iuup: do not use illegal characters in state/event names.
+ * logging: log to stderr when logging is not initialized
+ * reader: more meaningful null pointer check in get_sw
+
+ [ Pau Espin Pedrol ]
+ * include/: Adapt some headers to match contrib/struct_endianess.py format
+ * logging: Fix Not enough tailroom msgb_put in _output_buf callers
+ * osmo-release.sh: Use variable containing bumpversion path everywhere
+ * iuup: Fix decoding of 1byte-length subflow size fields
+ * iuup: Submit RNL-STATUS-Initialization.ind upon rx of Init
+ * iuup: Improve CRC checksum error logging
+ * gsm: [ABI BREAK] Support CellId SAI, change CellId CGI-PS id number
+ * gsm: lapd_core: Change log line NOTICE->INFO
+ * gsm0808_test: Add new unit test showing dec error
+ * gsm0808: Fix decoding of IE GSM0808_IE_LCS_CLIENT_TYPE
+ * cosmetic: gsm_08_08.h: Add space between assignment sides
+ * gsm0808: Add missing IEs in bss_att_tlvdef
+ * jenkins: Validate IEs are added to tlv_definition
+ * libosmocore.pc.in: put libsctp in Requires.private
+ * configure: Support libsctp < 1.0.17 without libsctp.pc
+ * rsl: Fix tlv_parse of IPAC_DLCX_IND message
+ * jenkins: Validate gsm 08.58 IEs are added to tlv_definition
+ * gsm: Introduce helper rach_tx_integer_raw2val()
+ * cosmetic: logging.h: fix indentation
+ * gsm_12_21.h: Fix abis_nm_avail_state InTest and Failed values
+ * gsm_12_21.h: Add header description pointing to TS files
+ * coding: Refactor function to avoid gcc false positive warn
+ * osmo-arfcn: Fix false positive in gcc 12.1.0
+ * coding: Use ARRAY_SIZE macro
+ * iuup: Rework API to support RFCI IDs != RFCI index
+ * cosmetic: iuup.h: Fix indentation
+ * iuup: Fix IPTIs_present not set to 0 if no IPTIs received
+ * cbsp: Add enum and value string for Cause
+ * iuup: Add missing state to bitmask for st SMpSDU_Data_Transfer_Ready
+ * iuup: Drop unused events
+ * tests/iuup: Showcase IuUP stack not answering subsequent Init msgs
+ * iuup: Fix Handling of subsequent Initialization msgs
+ * vty: command.c: Add assert
+
+ [ Sylvain Munaut ]
+ * conv: Fix the traceback for tail biting codes
+ * build: Disable libusb and libmnl for embedded builds
+
+ [ Neels Hofmeyr ]
+ * PFCP: add DLPFCP and osmo-upf port numbers
+ * ports.h: add osmo-pfcp-tool ports
+ * add osmo_sockaddr_to_str_c(), osmo_sockaddr_to_str_buf2()
+ * log: socket.c: rather use the osmo_sockaddr_str _FMT
+ * add osmo_quote_str_buf3, osmo_escape_str_buf3
+ * add osmo_sockaddr_from/to_octets()
+ * follow-up to osmo_sockaddr_from/to_octets()
+ * BSSAP: HO Request Ack: add missing Codec List (BSS Supported)
+ * add osmo_sockaddr_set_port()
+ * osmo_time_cc: rate_ctr presence should not affect counting
+ * cosmetic tweak in Makefile.am
+
+ [ Michael Iedema ]
+ * stats: use tcp stat names as provided
+
+ [ Karsten Ohme ]
+ * APDU parsing support for GlobalPlatform
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 28 Jun 2022 17:00:51 +0200
+
+libosmocore (1.6.0) unstable; urgency=medium
+
+ [ Pau Espin Pedrol ]
+ * osmo-release.sh: Check configure.ac dependency versions match those in rpm *.spec.in
+ * osmo-release.sh: Drop repeated DRY_RUN early exit
+ * osmo-release.sh: Check LIBVERSION matches rpm *.spec.in
+ * osmo-release.sh: Properly rearrange var init and sanity checks
+ * tests/gb: Fix printf format errors on ARM 32 bits
+ * Skip RPM checks if no *.spec.in available
+ * gsm_08_08.h: Add extra field elements defined in other sections
+ * cosmetic: tlv_parser: fix typo in func description
+ * cosmetic: Fix trailing whitespace
+ * gsm: Support Sending Last EUTRAN PLMN Id in Handover Required
+ * cosmetic: fix typo in comment
+ * gsm0808: Introduce gsm0808_old_bss_to_new_bss_info_att_tlvdef
+ * osmo_timer_pending: Make arg const
+ * gb: Fix naming and export symbol bssgp_enc_rim_pdu
+ * Revert "gb: Fix naming and export symbol bssgp_enc_rim_pdu"
+ * gb: Fix missing exporting symbol bssgp_encode_rim_pdu
+ * vty: Implement missing public API host_config_file()
+ * vty: Introduce API vty_read_config_filep
+ * range_enc_determine_range(): Don't dereference array on size=0
+ * stat,rate_ctr: Introduce new API to get counter at given index
+ * Use new stat item/ctr getter APIs
+ * stat,rate_ctr: Allow setting group name and use it at report time
+ * ns2: Use NSVC bufid in stats report
+ * ipaccess: Add new osmo extension IPAC_PROTO_EXT_PCU
+ * bts_feature: Introduce feature to speak to PCU
+ * msgb_alloc_headroom: Change size args to be uint16_t
+ * ctrl: Pre-calculate required size before allocating msgb
+ * ctrl: Support recovering from short write
+ * cosmetic: gsm_12_21.h: Fix trailing whitespace
+ * Rename osmo dyn ts enums
+ * bts_feature: Introduce feature to confiure dyn ts as sdcch8
+ * Make gcc 11.1.0 false positivies happy
+ * cosmetic: gb/gprs_ns.c: fix trailing whitespace
+ * gb/bssgp: Abort clearly if backward-compat API is used in wrong way
+ * utils: Fix c++ warn in OSMO_STRBUF_APPEND
+ * cosmetic: Fix missing space in comment
+ * gsm_08_58.h: Extend IPA Power Control Params IEs to pass C/I params
+ * gsm: Mark gsm0858_rsl_ul_meas_enc() pointer param const
+ * cosmetic: gsm: meas_rep.h: Fix typo in comment
+ * logging: Change LLAPD category color to purple-like one
+ * {ctrl,vty}/ports.h: Allocate ports for osmo-hnodeb
+ * configure.ac: Depend on talloc 2.1.0
+ * logging: Fix double lock of log_tgt_mutex
+ * contrib/libosmocore.spec.in: Depends on talloc 2.1.0
+ * osmo-release.sh: Blacklist script file from LIBVERS matches
+
+ [ Oliver Smith ]
+ * osmo-release.sh: support epoch
+ * tests/stats: enable logging in test output
+ * stats: log error when missing stats values
+ * Revert "stats: log error when missing stats values"
+ * stat_item: make next_id argument name consistent
+ * stat_item: add comment with struct overview
+ * stat_item: make value ids item specific
+ * stats_test: restore stat_item_get_next asserts
+ * stats: log error when missing stats values (v2)
+ * stats: have_value is a bool, not int
+ * vty: add "show uptime"
+ * vty: add "show pid"
+ * vty: add "shutdown"
+ * vty: show uptime: use timespecsub
+ * vty: clear screen with ^L
+ * vty/ports: prepare formatting for a long define
+ * vty/ports: move pcap server/client ports
+ * tests: add 'make update_exp' target
+ * tests/stats: show how last item sent may be wrong
+ * stats: send real last value if no new values come
+
+ [ Alexander Couzens ]
+ * gprs_bssgp: add support for SGSN oriented RESET
+ * gprs_bssgp: use BVCI_SIGNALLING/BVCI_PTM instead of 0/1
+ * gprs_ns2: don't OSMO_ASSERT() while freeing NS-VC.
+ * gprs_ns2: rework logging of Rx and Tx NS PDU
+ * gprs_ns2: always use the same method to print NSVCs
+ * gprs_ns2_vty: hide dynamic NSE information when ask for persistant only
+ * gprs_ns2_vty: make the `show ns entities` and `show ns binds` look similiar
+ * gprs_ns2: dump_nsvc: correct indention
+ * gprs_ns2: add vty command `nsvc <nsvci> reset`
+ * gprs_ns2: fix memory leaks when receiving SNS or invalid packets
+ * gprs_ns2: vty: remove a white space in `show binds`
+ * gprs_ns2: nsvc_fsm: reorder notification st_alive_on_enter()
+ * gprs_ns2: sns: ensure the sns->alive state is correct
+ * gprs_ns2: sns: remove the initial SNS NSVC if it's not part
+ * gprs_ns2_vty: print a response to vty `nsvc <nsvci> (block|unblock|reset)
+ * gprs_ns2: fix nsvc block and unblock vty command
+ * gprs_ns2: SNS: allow transition missing transition GPRS_SNS_ST_UNCONFIGURED
+ * gprs_ns2: add functions for SNS add/del/change-weight messages
+ * gprs_ns2: sns: fix del bind()
+ * gprs_ns2: vty: fix removing a bind from a SNS
+ * gprs_ns2: rework id strings of nsvcs
+ * gprs_ns2_sns: move selection of the next bind into own function
+ * gprs_ns2_sns: bss: improve validation of configuration
+ * gprs_ns2_sns: refactor local and remote entries into a struct
+ * gprs_ns2_sns: refactor ns2_clear_ipv46_entries_local to use new elems functions
+ * gprs_ns2_sns: use struct ns2_sns_elems in add/update/remove remote_elems
+ * gprs_ns2_sns: move gss->remote specific check out of add_ip4_elem/add_ip6_elem
+ * gprs_ns2_sns: add check for duplicates to add_ip6_elem()
+ * gprs_ns2_sns: refactor ip4_weight_sum/ip6_weight_sum
+ * gprs_ns2_sns: refactor nss_weight_sum_data -> ip46_weight_sum_data
+ * gprs_ns2_sns: replace ns2_sns_type with address family
+ * gprs_ns2_sns: bss: set gss->family
+ * gprs_ns2: use llist_add_tail to keep order
+ * gprs_ns2: fix missing notify towards the NSE when NSVC become blocked
+ * gprs_ns2_vc_fsm: reset the ALIVE response time when stopping test
+ * gprs_ns2: fix crash when changing the MTU
+ * gprs_ns2: fix check of MTU changes for frame relay
+ * gprs_ns2: correct mtu value in the log line
+ * gprs_ns2: use gprs_ns2_free_bind() to clean up a bind
+ * gprs_ns2: fix wrong format string in Tx Size logline
+ * gprs_ns2_vty: dump_nsvc: change output depending on NSVCI
+ * gprs_ns2: ensure the NSE becomes dead when FR link went down
+ * gprs_ns2_udp: don't start the NSVC fsm for SNS
+ * gprs_ns2_sns: refactor SNS failures into a function
+ * gprs_ns2_sns: free the NSE if the SIZE PDU is not valid
+ * gprs_ns2: add recursive anchor to protect against double free
+ * gprs_ns2: move sns_event into internal.h to direct emit events
+ * gprs_ns2_sns: rework sns clean up
+ * gprs_ns2: use an event to free the nsvscs when using SNS
+ * gprs_ns2: gprs_ns2_free_bind() should remove itself before removing nsvcs
+ * gprs_ns2: don't use llist_for_each when freeing an element
+ * gprs_ns2_sns: implement local change weight procedure
+ * gprs_ns2_sns: implement outbound SNS ADD procedures
+ * gprs_ns2_sns: implement outbound SNS DEL procedures
+ * gprs_ns2: also prevent recursive events when SGSN side cleans up
+ * gprs_ns2: calculate the nse->*_sums before notifing the sns fsm
+ * gprs_ns2: add correct filename/linenr to sns failed log message
+ * gprs_ns2: improve reselection protection
+ * gprs_ns2_sns: ensure the SNS fsm behave correct when no signalling NSVCs are present
+ * gprs_ns2: ensure the incoming NSVC is also the outgoing NSVC
+ * ns2: nsvc: reject UNITDATA when the remote BLOCK'ed
+ * ns2: fix a crash when receiving a SIZE while configured
+ * gprs_ns2: fix NS STATUS validation
+ * gprs_ns2: fix a white space
+ * gprs_ns2: nsvc: react on STATUS PDUs with cause code NSVC UNKNOWN/NSVC BLOCKED
+ * vty: add vty_out_uptime() print the uptime to the vty
+ * ns2: nse: add a uptime/downtime to track the last state change
+ * ns2: nsvc: add a uptime/downtime to track the last state change
+ * ns2: message: BLOCK/BLOCK ACK allow to use a given NSVCI instead of using the nsvc nsvci
+ * ns2: ensure the NSVC is in the correct mode for NSVC UNKNOWN/NSVC BLOCKED cause codes
+ * ns2: fsm: add comment don't answer on a STATUS with a STATUS
+ * ns2: message: allow to pass a foreign NSVCI to STATUS PDU
+ * ns2: correct parse a STATUS PDU which was received over a different NSVC
+ * ns2: improve log line when receving a PDU with wrong NSE
+ * ns2: don't forward an invalid RESET PDU to the FSM
+ * ns2: correct parse a BLOCK PDU which was received over a different NSVC
+ * include: add enum for UTRAN cipher
+
+ [ Harald Welte ]
+ * cosmetic: ssn: some more comments
+ * gprs_ns2: Log all transmitted SNS messages
+ * gprs_ns2_sns: Dispatch inbound SNS-ACK to FSM
+ * gprs_ns2: Remove any references to DNS; we use DLNS in NS2.
+ * gprs_ns2: Pass peer/remote sockaddr argument to ns2_create_vc()
+ * TODO-RELEASE: Request increasing _LAST_OSMOVTY_NODE next release
+ * gprs_ns2_sns: Implement error log in case no binds found for NSE
+ * gprs_ns2_sns: Remove TODO (spec agrees, the correct cause code is used)
+ * gprs_ns2_vty: Clarify VTY help string wording
+ * gprs_ns2_sns: refactor ns2_sns_st_size_onenter()
+ * gprs_ns2_sns: Unify handling of SNS-CONFIG for IPv4 + IPv6
+ * gprs_ns2: Encapsulate setting NSE dialect
+ * gprs_ns2_sns: Split allstate action in generic and BSS-specific part
+ * utils: osmo-aka-verify to verify UMTS AKA (SIM side)
+ * osmo-aka-verify: Fix use case with OP and not OPc
+ * gprs_ns2_sns: Support for SGSN-side IP-SNS
+ * gprs_ns2_sns: Add some more OSMO_ASSERT about BSS role
+ * gprs_ns2_sns: Rename BSS-side states to include 'bss' in name
+ * gprs_ns2: Introduce gprs_ns2_create_nse2() for SGSN side SNS
+ * gprs_ns2_vty: Allow creating NSE in sgsn-role
+ * gprs_ns2_sns: Don't clear remote IP endpoints in SGSN role
+ * gprs_ns2_sns: Verify mandatory IE presence in incoming SNS-SIZE
+ * gprs_ns2_sns: SNS-SIZE contains the actual number of local endpoints
+ * gprs_ns2_sns: Assume the SGSN has a very large number of max. NSVC
+ * gprs_ns2_sns: Implement checks during processing of inbound SNS-SIZE
+ * gprs_ns2_vty: Permit VTY configuration of bind->accept_sns
+ * gprs_ns2: dynamic NS-VC + NSE creation for IP-SNS in SGSN role
+ * gprs_ns2_sns: Allow VTY configuration of default binds for IP-SNS
+ * gprs_ns2: Add comments explaining the nsvc->sns_only field
+ * gprs_ns2_vc_fsm: In IP-SNS/ALIVE mode, initial state is ALIVE/UNBLOCKED
+ * gprs_ns2: Actually start Tns-test after SNS-CONFIG creates NS-VC
+ * gprs_ns2_sns: Compute local endpoints before using them
+ * gprs_ns2_sns: remove code duplication in create_missing_nsvcs()
+ * gprs_ns2_sns: Don't create NS-VCs for binds outside the NSE
+ * gprs_ns2_sns: Fix memory leak when creating ip[46]_local arrays
+ * sim: Obtain card ATR when opening the card
+ * sim: Remove 'printf' from library code
+ * frame_relay: Export osmo_fr_network_free()
+ * ns2: Dump frame relay state to VTY during "show ns"
+ * socket: Introduce osmo_sock_set_dscp() to set socket DSCP value
+ * ns2: Fix setting the DSCP value.
+ * socket: Introduce osmo_sock_set_priority() helper function
+ * socket: reduce code duplication, introduce socket_helper_tail()
+ * socket: QoS support for all our socket init functions
+ * socket: IPv6 support for osmo_sock_set_dscp()
+ * gprs_ns2: Fix yet another DSCP vs. TOS mix-up
+ * gprs_ns2_frgre: Ensure DSCP is sin premitted value range
+ * ns2: migrate from osmo_sock_set_dscp() to OSMO_SOCK_F_DSCP()
+ * gprs_ns: Fix another DSCP vs. TOS mistake in old NS code
+ * ns2: Allow setting the socket priority for a UDP bind
+ * Fix ipa_ccm_make_id_resp_from_req
+ * Fix ipa_ccm_make_id_resp_from_req to work at all
+ * ipa_ccm_make_id_resp: Make it work at all
+ * ns2: change the 'priority' setting name to 'socket-priority'
+ * osmo_sock_*_ofd(): Mark OSMO_FD_WRITE on non-blocking connect()
+ * sim: Add osim_card_{reset,close}() API
+ * osmo-auc-gen: Permit specifying the SQN in hex (0x12345) format
+ * copy base64 implementation from mbedtls
+ * base64: Migrate over to osmocom
+ * base64: reformat using Lindent to conform to our coding style
+ * osmo-auc-gen: Print RFC3310 IMS HTTP-AKA style base64 nonce/res
+ * logging: Change stderr + file target to use non-blocking write
+ * logging: Avoid memcpy from stack to msgb in _file_output()
+ * logging: Attempt a synchronous, non-blocking write first (file, stderr)
+ * rate_ctr: Make it safe to call rate_ctr_init() several times
+
+ [ Daniel Willmann ]
+ * stats: Ensure that each osmo_stat_item only reports once per interval
+ * tlv: Fix length returned by t{l16,16l}v_put
+ * stats_vty: Improve generation of osmo counters
+ * ns2: Ignore NSVC with data_weight 0 for data
+ * Aggregate NSVC stats inside the NSE
+ * ns2: Avoid use-after-free when SGSN-side non-persistent SNS-NSE fails
+ * frame_relay, gprs_ns2_fr: Fix log messages, remove unused struct
+
+ [ Vadim Yanitskiy ]
+ * utils/osmo-aka-verify: fix swapped CK/IK arguments
+ * vty/logging: use consistent quiting in warning messages
+ * vty/logging: ensure consistent '%' prefix for warnings
+ * vty/logging: logp: properly handle library specific sub-systems
+ * protocol/gsm_08_58.h: add RSL_CMOD_SP_{GSM4,GSM5,GSM6}
+ * protocol/gsm_08_58.h: add more 'Channel rate and type' values
+ * protocol/gsm_08_58.h: add asymmetric CSD data rates
+ * gsm/abis_nm: add missing NM_OC_IPAC_* value-string entries
+ * rsl: make rsl_dec_chan_nr() more readable, use RSL_CHAN_NR_MASK
+ * gsm_08_58.h: add Osmocom specific Bm/Lm CBITs for VAMOS
+ * gsm_08_58.h: add mask for Osmocom specific VAMOS C-bits
+ * fsm: cosmetic: fix weird spacing in osmo_fsm_inst_alloc()
+ * stats_vty: also show rate counter group name (if present)
+ * bts_features: add feature for BCCH carrier power reduction mode
+ * fix rsl_chan_nr_str_{buf,c}(): enlarge the buffer size
+ * utils: remove misleading comments for osmo_hexdump[_nospc]_c()
+ * utils: introduce osmo_talloc_replace_string_fmt()
+ * gsm_08_58: extend struct abis_rsl_osmo_temp_ovp_acch_cap
+ * Revert "Prevent GCR encoder/decoder functions from being used directly"
+ * gsm/protocol/gsm_44_004.h: fix missing include of 'endian.h'
+ * gsm/protocol/gsm_04_08.h: add gsm48_meas_res_is_valid()
+ * tests/stats: add VTY transcript tests
+ * stats: use llist_add_tail() in osmo_stats_reporter_alloc()
+ * stats: allow configuring reporter's name in the VTY
+ * stats: cosmetic: print 'stats interval' before the reporters
+ * stats: don't mark reporter as 'disable' beforehand
+ * stats: clarify error messages in cfg_no_stats_reporter_{statsd,log}
+
+ [ Neels Hofmeyr ]
+ * add BTS_FEAT_VAMOS
+ * deprecate osmo_bts_feature_name(), add osmo_bts_features_desc()
+ * add osmo_bts_features_names: short BTS feature strings
+ * RR: add VAMOS channel modes
+ * RR: add missing Extended TSC Set IE
+ * gsm48_mr_cfg_from_gsm0808_sc_cfg(): drop bitmask without effect
+ * fix default_timeout type of osmo_tdef_fsm_inst_state_chg default_timeout
+ * add RSL_IE_OSMO_TRAINING_SEQUENCE
+ * add RSL_CMOD_CRT_OSMO_TCH_VAMOS_Bm, RSL_CMOD_CRT_OSMO_TCH_VAMOS_Lm
+ * fixup for gsm48_chan_mode_to_non_vamos()
+ * osmo_select_shutdown_request(): allow finishing pending writes on SIGTERM
+ * add Kc128 to gsm0808 Create Ciphering Command
+ * gsm0808: add Kc128 to Handover Request
+ * add fixme: enforce 8 byte length of Kc
+ * fix api doc of osmo_identifier_sanitize_buf()
+ * vty 'stats reset': do not reset stat_items
+ * utils: add osmo_str_to_int() and osmo_str_to_int64()
+ * add osmo_stat_item_get_group_by_name_idxname()
+ * CTRL: expose stat_item groups on CTRL
+ * stat_item: cosmetic: s/desc/group_desc in osmo_stat_item_group_alloc()
+ * stats_test: assert counter and stat item val counts separately
+ * refactor stat_item: get rid of FIFO and "skipped" error
+ * refactor stat_item: report only changed values
+ * cosmetic: get rid of 3 deprecation warnings
+ * revisit some calls of strtol(), stroul(), strtoull()
+ * add osmo_time_cc, moved from osmo-bsc
+
+ [ Eric ]
+ * kdf: add key derivation functions
+ * vty: allow flushing
+ * gsmtap: allow 127.0.0.x local listeners
+
+ [ Michael Iedema ]
+ * ns2: use same name in ctr_group as stat_item_group
+
+ [ Philipp Maier ]
+ * control_cmd: fix typo
+ * vty: make function cmd_range_match() public
+ * linuxlist: add macro to get last element of a list
+ * command: fix sourcecode formatting
+ * codec: add missing osmo_amr_type_name function.
+ * gsm0503_coding: use ahs tables when encoding ahs codec id
+ * rsl: add new RSL IE to signal temporary overpower
+ * bts_features: Add new feature to indicate support for temporary overpower
+ * gsm_08_58: fix sourcecode formatting
+ * gsm_08_58: fix sourcecode formatting
+
+ [ Keith ]
+ * GPRS: Add PDP_TYPE_N_IETF_IPv4v6
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 13:08:22 +0100
+
+libosmocore (1.5.1) unstable; urgency=medium
+
+ [ Pau Espin Pedrol ]
+ * osmo-release.sh: Fix rc!=0 on TODO-RELEASE file without comment lines
+
+ [ Harald Welte ]
+ * attempt to fix RPM spec file after recent soversion bump
+
+ -- Harald Welte <laforge@osmocom.org> Wed, 24 Feb 2021 09:35:21 +0100
+
+libosmocore (1.5.0) unstable; urgency=medium
+
+ [ Vadim Yanitskiy ]
+ * debian/control: change maintainer to the Osmocom team / mailing list
+ * vty/command: cosmetic: simplify conditions in in config_list_cmd
+ * vty/command: cosmetic: fix formatting of config_help_cmd
+ * vty/command: cosmetic: drop redundant line break
+ * vty/command: fix switch / case coding style in vty_go_parent()
+ * vty/command: cosmetic: swap i and j in vty_dump_element()
+ * gsm0808: add gsm0808_create_sapi_reject_cause()
+ * macaddr: fix osmo_get_macaddr(): return -1 if no device is found
+ * macaddr: fix osmo_macaddr_parse(): return meaningful error codes
+ * lapdm_pad_msgb(): cosmetic: use GSM_MACBLOCK_PADDING
+ * lapd_test: fix wrong comment in lapdm_establish()
+ * lapd_test: rename func=UA (RR) CM Service Request: s/ua/ua_cm/
+ * lapd_test: fix: print all messages to stdout, not stderr
+ * lapd_test: add a test checking SAPI0/SAPI3 prioritization
+ * lapdm: fix SAPI-0/SAPI-3 frame prioritization on DCCH
+ * logging: refactor and simplify log_target_destroy()
+ * logging: fix log_target_destroy(): properly close syslog
+ * logging: fix memleak in log_target_create_file()
+ * vty: fix 'Unsigned compared against 0' generate_cpu_hex_mask()
+ * gsm0808: fix: do not encode invalid encryption algorithm
+ * vty: add program specific attributes to VTY commands
+ * vty: print program specific attributes in the XML reference
+ * vty: cosmetic: drop redundant 'break' statements
+ * vty: cosmetic: s/width/cmd_width/g in vty_describe_command()
+ * vty: cosmetic: fix missing curly braces in vty_describe_command()
+ * vty/command: introduce new attribute CMD_ATTR_IMMEDIATE
+ * vty/command: reflect global attributes in the XML reference
+ * tests: do not ignore stderr of vty_test, also match it
+ * vty: check for duplicate flags in application specific attributes
+ * vty: check for reserved flags in application specific attributes
+ * vty/command: add global command attribute CMD_ATTR_NODE_EXIT
+ * vty/command: add CMD_ATTR_LIB_COMMAND and install() API wrappers
+ * vty: use install_lib_element() and install_lib_element_ve()
+ * vty/command: introduce API for the library specific attributes
+ * vty: introduce and use VTY_CMD_USR_ATTR_NUM
+ * vty/command: introduce a command to list attributes
+ * vty/command: print attribute flags in the output of 'list'
+ * tests/vty: verify 'show vty-attributes' / 'list' commands
+ * vty/command: assign flags to CMD_ATTR_{IMMEDIATE,NODE_EXIT}
+ * vty/command: restrict the use of '.', '!', and '@' as flags
+ * vty: fix vty_dump_element(): do not print empty <attributes>
+ * socket: make the arguments of osmo_sockaddr_cmp() const
+ * gprs_ns2: make struct osmo_sockaddr pointers const
+ * rsl: rsl_chan_nr_str_buf(): use ABIS_RSL_CHAN_NR_CBITS_* macros
+ * logging: introduce 'systemd-journal' target
+ * vty: introduce the expert mode and a command to enable it
+ * vty/command: make some 'struct cmd_element' pointers const
+ * vty/command: introduce vty_dump_xml_ref_mode()
+ * vty/command: add CMD_ATTR_HIDDEN to CMD_ATTR_PUBLIC_MASK
+ * application: do not document unrelated forward-declarations
+ * vty/command: fix: restrict the expert mode to the current session
+ * fix spelling in 'value_string' arrays: existAnt -> existEnt
+ * gsm48: add missing RR cause value definitions
+ * vty/command: add 'hidden only' VTY reference generation mode
+ * bts_features: add missing description for BTS_FEAT_ACCH_REP
+ * core/linuxlist: do not use 'new' as a parameter name
+ * protocol/gsm_08_58.h: add ip.access Power Control structures
+ * protocol/gsm_08_58.h: add Osmocom specific EWMA AVG algo
+ * logging: revert color of LGLOBAL category back to white
+ * gsm_08_58: fix wrong field order in 'struct ipac_preproc_pc_thresh'
+ * gsm_08_58: add flexible array member to 'struct ipac_preproc_ave_cfg'
+ * gsmtap_util: SNR can be negative, use a signed integer
+ * gprs_ns2_sns: always check rc of osmo_sockaddr_str_from_sockaddr()
+ * gprs_ns2_fr: fix resource leaks due to early return in set_ifupdown()
+ * gprs_bssgp: fix uninitialized struct fields in bssgp_create_rim_ri()
+ * bts_features: s/Repeation/Repetition/ in osmo_bts_features_descs[]
+ * gprs_bssgp: abuse gsm48_encode_ra() to encode TAC
+ * fixup: configure.ac: fix: do not define HAVE_NEON unconditionally
+ * gsm_7bit_encode_n(): test encoding of more than 250 septets
+ * gsm_7bit_encode_n(): fix integer overflow in gsm_septets2octets()
+ * gsm0808: use msgb_tv16_put() and osmo_store32be()
+ * bssgp_bvc_fsm: check return value of osmo_fsm_register()
+ * tlv: add msgb_tv32_put(), similar to msgb_tv16_put()
+ * tlv: clarify documentation for msgb_tv{16,32}_put()
+
+ [ Eric ]
+ * vty cpu sched: do not assert if sched impossible
+ * pkgconfig: link to mnl if available
+
+ [ Pau Espin Pedrol ]
+ * vty: Fix cpu-sched VTY node name
+ * sock: osmo_sock_init2_multiaddr: decouple addr resolution from socket creation
+ * socket: multiaddr: Support IPv4 + IPv6 addresses in SCTP associations
+ * socket: Log proper getaddrinfo() error
+ * osmo_sock_get_ip_and_port(): Support IPv6 sockets
+ * l1sap.h: Fix typo in doxygen documentation
+ * osmo_sock_inti2_multiaddr: Fix memleak and free uninitialized mem
+ * socker: Remove AI_ADDRCONFIG from getaddrinfo flags
+ * tests: Split SCTP tests to its own file and run them conditionally
+ * ipa: Fix wrong output log formatting
+ * socket: Allow binding to :: (IPv6) and connecting to IPv4-only on the remote
+ * socket: Add support for AF_INET6 in osmo_sockaddr_to_str_and_uint()
+ * socket: Use AF_UNSPEC instead of PF_UNSPEC calling getaddrinfo
+ * socket: Fix stack-buffer-overflow in osmo_sock_local_ip()
+ * socket: fix wrong ipv6 dst buf size passed in osmo_sock_local_ip
+ * socket: Add some osmo_sockaddr print helpers
+ * tests: Add test to showcase osmo_sock_init2 bug with AF_UNSPEC
+ * socket: Fix bug in osmo_sock_init2(AF_UNSPEC) matching IP versions
+ * logging: Avoid printing OSMO_LOGCOLOR_END if no color was used
+ * osmo_strlcpy: Avoid calling memcpy with size=0
+ * osmo_strlcpy: Clarify length calculation
+ * cosmetic: Fix typo in API doc
+ * gprs_ns2_udp: Avoid dangling freed struct in list if binding fails
+ * vty: Fix left shifting out of range on signed variable
+ * cosmetic: vty: Fix trailing whitespace
+ * gsm: Fix make distcheck with parallel make
+ * contrib: jenkins: Enable parallel make in make distcheck
+ * lapdm: Drop log lines printing fmt=B
+ * lapdm: Split lapd_rx_u() spaghetti into one function per message type
+ * lapdm: Allow SABM L=0 in Timer Recovery State
+ * gb: ns2_sns: Fix missing trailing newline char in log line
+ * gb: ns2_sns: Add missing value_string entry for GPRS_SNS_EV_NO_NSVC
+ * vty: Mark cpu_sched_vty commands with attr immediate
+ * tdef: Introduce OSMO_TDEF_US unit
+ * tests: Fix tdef_test on 32bit platforms
+ * bitvec: Fix left shifting out of range on signed variable
+ * cosmetic: serial: Fix typo in comment
+ * serial: Fix typo in debug log line
+ * serial: Log error if tcgetattr() or tcsetattr() fail
+ * serial: Introduce API osmo_serial_speed_t
+ * Revert "tests: Fix tdef_test on 32bit platforms"
+ * tests: Fix tdef_test on 32bit platforms
+ * statsd report: Fix wrong fmt specificier generating wrong stats
+ * gsm: Add enum for Network Feature Support IE
+ * Include mnl.h iif --enable-libmnl
+ * gb: Import mnl.h iif --enable-libmnl
+ * bssgp: Remove newly added log line warning about NOOP
+ * rest_octets: add Serving Cell Priority Parameters
+ * gsm: si13: Fix encode of EGPRS_PACKET_CHANNEL_REQUEST
+ * si2quater: fix budget calculation for multiple EARFCNs
+ * gsm: append_eutran_neib_cell: Fix SI2quater EARFCN list
+ * GPRS Cell Options (SI13): Add REL-4 CCN_ACTIVE bit
+ * Revert "rest_octets: fix encoding of 3G Early Classmark Sending Restriction"
+ * rest_octets: Fix decoding of SI3 3G Early Classmark Sending Restriction
+ * gitignore: Ignore *~
+ * ctrl: Allow handling CTRL get/set replies in user defined code
+ * Intoduce Packet Switch CGI
+ * ctrl: ports.h: Add OSMO_CTRL_PORT_BSC_NEIGH
+ * gsm: Add missing osmo_*_cmp symbols to libosmogsm.map
+ * gsm: Introduce osmo_{rai,cgi_ps}_cmp() APIs
+ * gprs_bssgp_prim.h: Add missing includes
+ * Revert "gprs_ns2: drop gprs_ns2_vty, rename vty2 -> vty"
+ * ctrl_connection: Initialize write_queue.bfd.fd to -1 during allocation
+ * cosmetic: fix typo in comment
+ * gsm: Fix wrong length in SI13 GPRS Cell Options IE
+ * gsm: bts_features: Introduce BTS_FEAT_CCN
+ * Fix struct bitfields on big endian systems
+ * cosmetic: Move comment one line below in append_gprs_cell_opt
+ * gsm: Fix bitfield order in dtap_header
+ * comsetic: gsm0808_test: Fix trailing whitespace
+ * gsm: Support converting to cgi-ps in gsm0808_cell_id_from_cgi()
+ * gsm0808_utils: Move static function further up in file
+ * gsm: Fix encoding of gsm0808_cell_id_list2 with CGI-PS types
+ * logging: gsmtap: Fill PID field for each message
+ * Introduce osmo_gettid() API
+ * logging: gsmtap: Fix fill PID field not stored in network byte order
+ * logging: gsmtap: Store TID instead of PID in pkt hdr
+ * gsm: Introduce API osmo_gsm48_rest_octets_si13_decode
+ * logging: Allow prefixing thread ID to each log line
+ * tests: Set print_category values explicitly
+ * Drop use of log_set_print_filename() API inside libosmocore
+ * logging: Deprecate API log_set_print_filename
+ * osmo-release.sh: Omit tab whitespace matching debian/control versions
+
+ [ Harald Welte ]
+ * gsmtap: Add definitions for E1/T1 payload (LAPD, TRAU, FR) in GSMTAP
+ * Add VTY + CTRL ports for upcoming OsmoSMLC
+ * gprs_ns2_sns: Fix compilation on Debian 8
+ * ipaccess.h: Add more enum values and 'official' names
+ * ns2: Improve/extend doxygen comments for new ns2 implementation
+ * ns2: Use NULL and not '0' when returning a NULL-Pointer
+ * gprs_ns2: Mark gprs_ns2_validate_* as static
+ * write_queue: Add osmo_wqueue_enqueue_quiet()
+ * write_queue: use msgb_{en,de}queue_count()
+ * write_queue: Re-enqueue msgb if write_cb returns -EAGAIN
+ * logging_vty: set osmo_stderr_target to NULL on "no log stderr"
+ * logging: Introduce MAX_LOG_SIZE for the magic number 4096
+ * bssgp: Don't include RA-ID in BVC-RESET for BVCI=0 (signalling)
+ * bssmap_le/bsslap tests: We must use %td for ptrdiff_t printing
+ * osmo_float_str_to_int: When using strtoll(), use LLONG_{MAX,MIN}
+ * gprs_ns2: Fix parsing of SNS-{ADD,DEL,CHANGE_WEIGHT}
+ * gsm48_rest_octets: Add parser for SI4 rest octets
+ * [cosmetic] rest_octets: Follow coding style regarding {}
+ * ns2: More verbose comments on gprs_ns2_vc_mode
+ * select: Introduce osmo_fd_{read,write}_{enable,disable}()
+ * select: Migrate over to poll()
+ * card_fs_usim.c: Add support for DF.5GS directory
+ * Revert "gsm_04_08: add parser for Mobile Station Classmark 3"
+ * card_fs_usim: Add definitions of DF.ProSe and DF.ACDC
+ * bssgp: Input argument to bssgp_tlv_parse() should be 'const'
+ * ns2: permit multiple nsvci in one nse in VTY
+ * frame_relay: Fix some spelling/language issues
+ * Revert "ns2: permit multiple nsvci in one nse in VTY"
+ * gprs_ns2_vty: Fix VTY documentation errors
+ * frame_relay: Fix null pointer dereference in DLC/PVC delete
+ * frame_relay: Fix error path on ioctl() failure
+ * gprs_ns2: Introduce gprs_ns2_lltype_str() for link layer name
+ * gprs_ns2_fr: Print Frame Relay 'role' in VTY
+ * gprs-ns2: Fix stringification of NS/FR NSVCI
+ * gprs_ns2: Don't return an empty string in case of unknown LL
+ * cosmetic: frame_relay: Fix typos
+ * gprs_ns2: Print link layer and global ALIVE/DEAD state
+ * gprs_ns2_vty: Differentiate 'show ns binds' and 'show ns entities'
+ * gprs_ns2_vty: Show NSVCI in dump_nsvc()
+ * gprs_ns_vty: Unify display of NSVCI on VTY in 'show ns entities'
+ * gprs_ns2_vty: Print all relevant data on each NS-VC
+ * gprs_ns2_udp.c: Fix typo - it's a DSCP and not a DCSP
+ * Integrate libmnl (minimal netlink) library with libosmocore select loop
+ * gprs_ns2_fr: Monitor the kernel net-device link state
+ * gsm_08_18.h: Update enums for message types and IEs with Release 15
+ * gprs_ns2_vty: Re-introduce a 'show ns' command
+ * libosmogb: Add missing LIBMNL_CFLAGS
+ * mnl: Use mnl_socket_open() insatead of mnl_socket_open2()
+ * gprs_ns2_fr: remove include <linux/if.h>
+ * bssgp: Use TLVP_PRES_LEN instead of TLVP_PRESENT
+ * gprs_ns2: Use TLVP_PRES_LEN instead of TLVP_PRESENT
+ * bssgp: Update bssgp_pdu_strings with Release 15
+ * bssgp: Fix typo in BSSGP Message Type enum
+ * Add hlist and hashtable from Linux kernel
+ * Use explicit type-casting in hlist_del() for C++ compatibility
+ * gsm_08_18.h: Add some PDU definitions still missing
+ * hash/log2: Add generic implementations of fls() and fls64()
+ * log2.h: Use uintXX_t instead of kernel specific types
+ * log2.h: Avoid redefining __always_inline
+ * ns2: Accept NS-UNBLOCK-ACK in UNBLOCKED state
+ * Introduce 'osmo_tlv_prot' abstraction for validation of TLV protocols
+ * tlv: Introduce enum with error codes for TLV parser functions
+ * bssgp: Add osmo_tlv_prot_def for BSSGP
+ * gsm_08_18.h: Add #defines for [extended] feature bits
+ * tlv.h: Add msgb_tvlv_put_{16,32}be()
+ * logging: Introduce DLBSSGP logging constant
+ * gb: Add beginnings of a new BSSGP implementation
+ * logging: Assing different 8bit colors to built-in subsystems
+ * bssgp2: Encoding + Decoding functions for BVC and MS flow control
+ * bssgp_bvc_fsm: Add basic BVC flow control rx/tx support
+ * gprs_ns2_fr.c: Skip extraneous FIONBIO
+ * gprs_ns2_fr: guard against race between socket(AF_PACKET) and bind()
+ * gprs_ns2_fr: use ETH_P_HDLC instead of ETH_P_ALL
+ * Fix VTY syntax for newly-introduced NS2 timers
+ * gors_ns2_vty: Fix saving of new NS2 timers
+ * gprs_ns2_fr: Avoid stringop-truncation warning
+ * gprs_ns2_fr: Use OSMO_STRLCPY_ARRAY() where possible
+ * fsm: Add osmo_fsm_inst_broadcast_children()
+ * vty: Fix left shifting out of range on signed variable
+ * lapd_core: Don't dereference data link after sending PRIM_DL_REL
+ * cbsp: Fix encoding of "ETWS Warning Security Info" IE
+ * cbsp: Fix osmo_cbsp_recv_buffered() for KEEP-ALIVE-COMPLETE
+ * cbsp: Fix parsing DCS in decode of WRITE-REPLACE
+ * Add inter-thread queue
+ * tlv_parser: Fix various out-of-bounds accesses
+ * gprs_ns2_fr: reduce duplication between gprs_ns2_fr_connect / connect2
+ * gprs_ns2: Give NS-VC FSMs a proper name/identifier
+ * ns2: delay NS_AFF_CAUSE_RECOVERY until NS-VC for data + sig are unblocked
+ * vty/fsm_vty: Add vty_out_fsm2() + vty_out_fsm_inst2() with prefix
+ * ns2: Properly indent VTY output
+ * ns2: Unify logging context via log macros
+ * logging_vty: Fix saving of "logging print file .. last"
+ * gprs_ns2_test: Fix compilation on Debian 8
+ * Revert "gprs_ns2_vc_fsm: check NSVCI match the NSE"
+ * Revert "gprs_ns2_vc_fsm: check NSEI match the NSE"
+ * ns2: Use proper return value from write_queue callback function
+ * ns2: Log ERROR if we cannot transmit a packet due to ENOBUFS
+ * osmo-ns-dummy: Add simple NS traffic generator
+ * ns2: Work around AF_PACKET socket ENOBUFS problems
+ * ns2: Use named array initializers to avoid mistakes
+ * ns2: encapsulate calls to nsvc->bind->send_vc()
+ * ns2: Increment Rx and Tx byte / packet counters
+ * ns2: Properly report packet drops in FR code
+ * ns2: count number of dropped packets / bytes on transmit
+ * ns2: Implement more rate counters
+ * ns2: Add a rate_ctr for each NS-UNBLOCK
+ * ns2: Move to one common/shared ns2_bind_alloc()
+ * ns2: Memory allocation failures are ENOMEM, not ENOSPC
+ * ns2: Rename nsi->rate_ctr_idx to nsi->nsvc_rate_ctr_idx
+ * ns2: Introduce a per-bind stat_item group with backlog length
+ * ns2: Fix typos in comments
+ * ns2: Print NS-STATUS.ind primitives to the log
+ * ns2: cosmetic: fix indent levels
+ * frame_relay: Send "Fuil Status" ENQUIRY after link recovers
+ * frame_relay: Add status call-backs for link + DLC status changes
+ * frame_relay: Discard received messages for DLC not yet active
+ * frame_relay: cosmetic: Unify log syntax when discarding Rx packets
+ * ns2: Don't automatically re-start FSM at FORCE_UNCONFIGURED
+ * ns2: Stop test procedure when going into unconfigured state
+ * ns2: Don't start sending NS-RESET until FR DLC is available
+ * osmo-ns-dummy: Add "mirror-mode" to mirror back any received packets
+ * initial support for static userspace probes via systemtap
+ * Enable systemd-journald log target in debian + rpm packaging
+ * gb: frame_relay: Factor-out function to set link as dead
+ * gb: frame_relay: Detect link outage on "last receive seq nr == 0"
+ * frame_relay; Fix Q.933 async STATUS at DLC creation
+ * ns2: Name NSVC FSM events consistently
+ * ns2: Don't queue Q.933 LMI messages; only store most recent ones
+ * ns2: improve backlog handling on interface up/down
+ * ns2: Don't try to add packets to the backlog on real errors
+ * README.md: Use https everywhere
+ * ns2_fr: Fix null pointer deref in error path
+ * ns2_fr: Fix heap-use-after-free in error recovery path
+ * BVC FSM: Treat overlapping BVC-RESET as implicit ACK
+ * support for stats static userspace probes via systemtap
+ * CBSP: fix encoding/decoding of keep-alive repetition period
+ * CBSP: Fix encoding of warning period
+
+ [ Neels Hofmeyr ]
+ * comment: mention spec reference in cbsp.[ch]
+ * comment: typo in cbsp.h
+ * bitXXgen: ensure not reading/storing past valid size
+ * bitXXgen: add bitgen_test.c
+ * bitXXgen: add osmo_loadXXbe_ext_2() to get right-adjusted values
+ * add osmo_use_count_to_str_c()
+ * osmo_use_count_to_str: make robust against unused use_count
+ * add osmo_float_str_to_int() and osmo_int_to_float_str_*()
+ * add GAD coding for Location Services
+ * add BSSLAP coding for Location Services
+ * add BSSMAP-LE coding for Location Services
+ * add BSSMAP coding for Location Services
+ * doc tweaks for osmo_float_str_to_int(), osmo_int_to_float_str*()
+ * gad.c: try to workaround warning for "h.type >= 0"
+ * gad.c: fix rc for osmo_gad_enc_ell_point_unc_circle()
+ * gprs_ns2_fr.c: compiler error: replace strncpy() with OSMO_STRLCPY_ARRAY()
+ * fix strncpy bug in gprs_ns2_fr_bind()
+
+ [ Alexander Couzens ]
+ * sockaddr_str: add osmo_sockaddr_str_from_str2() which doesn't set the port
+ * tests/socket: add testcase test_get_ip_and_port
+ * socket: introduce osmo_sock_init_osa & osmo_sock_init_osa_ofd
+ * socket: add osmo_sockaddr_cmp()
+ * gb/gprs_bssgp: remove superfluous whitespace
+ * gb/common_vty: use void *
+ * add osmo_sockaddr_local_ip() to determine the local address for a remote.
+ * Gb: add a second NS implementation
+ * libgb/ns: allow to create NS_ALIVE NSVC
+ * gb/gprs_bssgb: check if talloc failed on btsctx->fc
+ * gb/gprs_bssgb: ensure the fc timer has been stopped when freeing bssgp_bvc
+ * ns2: remove bssgp specific msgb->cb parts
+ * ns2: vty: add missing docs for IPv6 address
+ * ns2: check the specific bit of NS SDU Control bits
+ * ns2: refactor nsvc_by_ functions
+ * ns2: ns2_recv_vc: remove unused parameter nsi
+ * ns2: vty: fix behavior of vtyvc_by_nsei when vtyvc isn't found
+ * ns2: refactor handle_nsip_read/handle_nsfrgre_read
+ * ns2: fix a msg leak when receiving REJECTED messages
+ * gsm_12_21: add osmo_oml_nsvc_address_type for OML NM_ATT_OSMO_NS_LINK_CFG
+ * ns2: parse the return code of gprs_ns2_ip_bind
+ * ns2: refactor ns2_prim_status_ind()
+ * ns2: status ind: add additional flags (first and persistent)
+ * ns2: vty: show all nse
+ * ns2: vty: allow the users (pcu/sgsn) to set a default bind
+ * ns2: vty: on `show ns` add information of NS binds
+ * gprs_ns2_vty_create: remove bind pointer check
+ * gprs_ns2: gprs_ns2_free(): add missing talloc_free
+ * gprs_ns2: fix SNS_ADD for IPv4
+ * gprs_ns2: fix empty prefix in TLV Parse error
+ * gprs_ns2: add gprs_ns2_ip_bind_by_sockaddr() to search for binds by sockaddr
+ * gprs_ns2: gprs_ns2_ip_bind() check if the bind already exists
+ * gprs_ns2: add gprs_ns2_nse_nsei() to get the nsei of a nse
+ * gprs_ns2: add gprs_ns2_free_binds() to free all binds
+ * gprs_ns2: add gprs_ns2_free_nses() to free all NS-E
+ * gprs_ns2: const the return value of gprs_ns2_ip_vc_sockaddr / gprs_ns2_ip_bind_sockaddr
+ * gprs_ns2: rename gprs_ns2_ip_vc_sockaddr -> gprs_ns2_ip_vc_remote
+ * gprs_ns2: add gprs_ns2_ip_vc_local() return the local sockaddr
+ * gprs_ns2: add gprs_ns2_nse_sns_remote() returns the initial SNS address
+ * gprs_ns2: gprs_ns2_ip_vc_remote() the nsvc can be also const
+ * gprs_ns2: add gprs_ns2_nse_foreach_nsvc()
+ * gprs_ns2: Partial revert 48f63867 allow to create NS_ALIVE NSVC
+ * gprs_ns2: add gprs_ns2_ip_vc_equal()
+ * add osmo_sockaddr_to_str_buf/osmo_sockaddr_to_str
+ * gprs_ns2: remove the nsvc fsm timer when cleaning up the fsm
+ * ns2: add value strings for ns2_affecting_cause_prim_str & ns2_prim_str
+ * gprs_ns2: convert gprs_ns2_cause_str into an static inline
+ * libosmocore: change the memory management of NS2
+ * ns2: fixup gprs_ns2_prim_strs
+ * ns2: check if ns_vc_alloc() fails in bind_connect()
+ * ns2: add support for frame relay
+ * ns2: implement link sharing selector
+ * ns2: move LL into public api
+ * ns2: move link layer type into NSE
+ * ns2: remove obsolete type GPRS_NS2_LL_E1
+ * gprs_ns2_fr: remove include <linux/if.h>
+ * gprs_ns2: fix typo in comment
+ * gprs_ns2: use switch() case instead of multiple if in ns2_create_vc()
+ * gprs_ns2_sns: add missing transistion UNCONFIGURED -> SIZE
+ * gprs_ns2_sns: dynamic calculate the maximum NS-VCs
+ * gprs_ns2_sns: add missing S() to allow GPRS_SNS_EV_NO_NSVC happen
+ * gprs_ns2_sns: fix whitespaces and superflous comment
+ * gprs_ns2_sns: correct dynamic calculation
+ * gprs_ns2: introduce NS dialects
+ * gprs_ns2: move allocation of the SNS fsm into create_nse
+ * gprs_ns2: add member name to bind
+ * gprs_ns2: rework gprs_ns2_fr_connect*()
+ * gprs_ns2: add gprs_ns2_free_nsvcs() to free all NS-VC of a NSE
+ * gprs_ns2_sns: rework IP-SNS initial remote
+ * gprs_ns2_sns: use different binds for the initial connection
+ * gprs_ns2_sns: introduce SNS Size/Config retries
+ * gprs_ns2: make nsvc argument const
+ * gprs_ns2: add gprs_ns2_fr_bind_role() to retrieve the fr role
+ * gprs_ns2: on ns2_create_vc parse the tlv before using it
+ * gprs_ns2_vty: udp: fixup dialect changes
+ * gprs_ns2: fr: fix crash when frame relay interface doesn't exists
+ * gprs_ns2: improve handling of TLV errors on new nsvcs
+ * gprs_ns2: don't pass the return code of reject_status_msg
+ * gprs_ns2: fr: implement a write queue
+ * gprs_ns2: fr: setup the device to correct FR/LMI settings
+ * gprs_ns2: add new vty2
+ * gprs_ns2: sns: correct log message when no nsvcs available
+ * gprs_ns2: sns: don't send duplicated packets on retries
+ * gprs_ns2: sns: add log message when size/config retries exhausted
+ * gprs_ns2: don't start unconfigured fsm via FORCE_UNCONFIGURED
+ * gprs_ns2: fix force-unconfigured for IP-SNS NSE
+ * frame_relay: prevent null pointer exception when talloc fails
+ * frame_relay: link_alloc: move log message to the end
+ * gprs_ns2: rework frame relay load distribution function
+ * gprs_ns2_fr: setup_device: allow to setup a new dahdi device
+ * gprs_ns2: fr: check the device state before changing state
+ * gprs_ns2: use zero initialized memory for vty_binds
+ * utils: add osmo-ns-dummy
+ * gprs_ns2: call python vty tests
+ * gprs_ns2: set transfer cap in NS Status primitive
+ * gprs_ns2: check if persistent nsei or nsvc exists when creating dynamic NSE
+ * osmo-ns-dummy: allow to create dynamic NSEs
+ * gprs_ns2_vc: answer UNBLOCK on unblocked nsvc
+ * gprs_ns2: when calling nsvc_force_unconf for a dynamic NSE drop the NSE.
+ * gprs_ns2_sns: clear local and remote entries when SNS failed
+ * gprs_ns2: flag then NSE as dead in nse_free()
+ * gprs_ns2_vc_fsm: fix spaces and remove wrong comment
+ * test: gprs_ns2: don't leak the talloc context
+ * test: gprs_ns2: replace free_bind() with clear_pdus()
+ * test: gprs_ns2: free the nsi after each test
+ * gprs_ns2: add assert on most bind calls
+ * gprs_ns2: allow to use free_vc() with NULL
+ * osmo-ns-dummy: don't leak primitive messages
+ * gprs_ns2: implement BLOCK/UNBLOCK of a NSVC by vty
+ * gprs_ns2: correct handle BLOCK message on initator
+ * gprs_ns2_vc_fsm: rename all event to match RX_ or REQ_
+ * gprs_ns2_vc_fsm: ensure all state is resetted via force_unconf
+ * gprs_ns2_vc_fsm: in RESET accept RESET as ACK
+ * gprs_ns2_vc_fsm: fix transitions for ALIVE state
+ * gprs_ns2_message: remove wrong comment
+ * gprs_ns2_vc_fsm: check NSEI match the NSE
+ * gprs_ns2_vc_fsm: check NSVCI match the NSE
+ * tests: gprs_ns2: add unitdata unit test
+ * gprs_ns2: always pass a tp to gprs_ns2_vc_rx
+ * gprs_ns2_vc_fsm: check NSEI match the NSE
+ * gprs_ns2_vc_fsm: check NSVCI match the NSE
+ * gprs_ns2: drop GPRS_ prefix of gprs_ns2_cs
+ * gprs_ns2: drop prefix of all internal exposed function
+ * gprs_ns2: drop the public prefix gprs_ from all static functions/structs/..
+ * gprs_ns2: refactor: ensure all enums have GPRS_NS2_
+ * gprs_ns2: drop gprs_ns2_vty, rename vty2 -> vty
+ * gprs_ns2: drop gprs_ns2_vty, rename vty2 -> vty
+ * gprs_ns2: remove api call gprs_ns2_dynamic_create_nse
+ * gprs_ns2: introduce gprs_ns2_vty_init_reduced() for the PCU
+ * gprs_ns2: correct vty config write
+ * gprs_ns2: prevent division by zero in load_sharing
+ * gprs_ns2_vc_fsm: reset ALIVE N counter when restarting the test procedure.
+ * gprs_ns2_vc_fsm: rename ST_ALIVE -> ST_RECOVERING
+ * gprs_ns2: unify the handling of **result when bind already present.
+ * gprs_ns2_fr: free_bind(): first do the NULL check before using members
+ * frame_relay: fix NULL pointer deref
+ * gprs_ns2_frgre: set a correct gre pointer for ipv6
+ * gprs_ns2_frgre: check iph/ip6h before passing them to rx functions
+ * gprs_ns2_sns: rework tracking of NS-VC unblocked/alive state
+ * gprs_ns2_sns: add timeout to ST_CONFIG_SGSN
+ * gprs_ns2_sns: reset the N of timeout when entering a new state
+ * gprs_ns2: rename vty-command ip-sns -> ip-sns-remote
+ * gprs_ns2_vty: fix memory leak of vty_binds
+ * gprs_ns2: add signalling & data weights for UDP binds
+ * gprs_ns2_vc_fsm: use CLOCK_MONOTONIC for alive elapsed timer
+ * gprs_ns2_vc_fsm: reset the alive elapsed timeout everytime
+ * gprs_ns2_vty: use strcmp() instead of strncmp()
+ * gprs_ns2_vc_fsm: remove debug line of the alive_timer
+ * gprs_ns2: fix comment of alive NS-VC
+ * gprs_ns2_sns: fix typo in doxygen comment
+ * gprs_ns2: inform the NS user (BSSGP) about the MTU of a NSE
+ * gprs_ns2: rework IP-SNS binds
+ * gprs_ns2_vty: add optional argument signalling and data weights to `nsvc udp`
+ * gprs_ns2: implement a simple load sharing for UDP
+ * gprs_ns2: fix typo in function name ns2_load_sharing_modulo
+ * gprs_ns2: make gprs_ns2_recv_prim() always take msgb ownership
+ * gprs_ns2_sns: rename fsm events to include RX or REQ prefix
+ * gprs_ns2_fr: pass MTU changes to the NSE
+ * gprs_ns2: truncate the NS_STATUS to the MTU
+ * gprs_ns2_message: tx_status: move all cause dependent code into the switch/case
+ * gprs_ns2: add value_string for GPRS_NS2_AFF_CAUSE_SNS_NO_ENDPOINTS
+ * gprs_ns2: ensure no duplicate UDP NSVC can be created
+ * gprs_ns2: free_nse: free the SNS fsm early
+
+ [ Daniel Willmann ]
+ * gsm_08_16.h: Add missing header
+ * gprs_ns2: Make reason const
+ * libosmogb: Add a function to tx BVC RESET by nsei/bvci
+ * Add exported function btsctx_alloc to public header
+ * ns2: Add gprs_ns2_nsvc_state_name() to get the current state of a VC
+ * ns2: Send NSVC representation in NS_AFF_CAUSE_VC_* status indication
+ * ns2: Improve NSVC output
+ * ns2: Split 'initiater' into initiate_{reset,block}
+ * ns2: Add a function to set the NSVC FSM back to unconfigured
+ * ns2: Add a VTY command to reset NSVC FSM
+ * ns2: Fix docs for some NS2 vty commands
+ * logging: Calculate LOG_MAX_{CTX,FILTERS} from the enum
+ * ns2: Add log filtering by NSE/NSEI, fix NSVC filter on receive
+ * ns2: Fix argv index in logging filter VTY commands
+ * Declare osmo_ctx_init() in talloc.h
+ * bssgp: Add SUSPEND_NACK to osmo_pdef_bssgp
+ * logging: Remove duplicate color for DLSMS
+ * ns2_frgre: Fix missing break statement for recv from IPV6
+ * bssgp_bvc_fsm: Handle block request from application correctly
+ * NS2: Fix bind selection of SNS NSVCs
+ * ns2: Add sanity check
+ * Refactor ns2_nsvc_create_ip*
+ * ns2: Fix memory leak in IP-SNS
+ * ns2: Fix assert when removing a bind listen
+ * ns2: Fix incompatible VTY configs when writing config
+ * tests/gb: Add more complex osmo-ns-dummy.cfg
+ * bssgp_bvc_fsm: Set/get maximum BSSGP PDU length
+ * bssgp2_enc_status: Truncate STATUS message to maximum PDU length
+ * bssgp_bvc_fsm: Consistent naming
+
+ [ Philipp Maier ]
+ * command: add library command attribute for libosmo-sccp
+ * command: add library command attribute for libosmo-abis
+ * gsm_08_58: add proprietary IE to signal Repeated ACCH Capability
+ * gsm_04_08: add parser for Mobile Station Classmark 3
+ * gsm_04_08: add parser for Mobile Station Classmark 3
+ * bts_features: add feature BTS_FEAT_ACCH_REP
+ * gsm_08_58: add struct for RSL_IE_OSMO_REP_ACCH_CAP
+ * gsm_08_58: add rxqual field to RSL_IE_OSMO_REP_ACCH_CAP
+ * gprs_bssgp_util: complete bssgp_pdu_strings
+ * gprs_bssgp: add IE parser/generator for RIM Routing Information
+ * gprs_bssgp: add handling for BSSGP RIM primitives
+ * gsm_08_18: add struct to parse RIM PDU Indications
+ * bssgp_rim: add encoder/decoder for NACC related RIM containers
+ * bssgp_rim: move bssgp_parse_rim_ri and bssgp_create_rim_ri to gprs_bssgp_rim
+ * gprs_bssgp: add utilities to send and parse BSSGP rim PDUs
+ * gprs_bssgp_rim: Return with EOPNOTSUPP on unsupported containers
+ * gprs_bssgp_rim: fix bug in dub_tlvp_header()
+ * gprs_bssgp_rim: add value strings for enum bssgp_nacc_cause
+ * gprs_bssgp_rim: add value strings for enum bssgp_ran_inf_app_id
+ * gprs_bssgp_rim: add value strings for enum bssgp_rim_routing_info_discr
+ * gprs_bssgp_rim: add functions to convert a RIM-RI to a string
+ * gprs_bssgp_rim: cosmetic: connect routing identifier strings with "-"
+ * gprs_bssgp: log source and destination RIM routing information
+ * gprs_bssgp: agregate RIM related code in gprs_bssgp_rim.c
+ * gsm48: add compare function for struct gprs_ra_id
+ * gsm_08_58, gsm_44_004: add struct for l1 information
+
+ [ laforge ]
+ * Revert "pkgconfig: link to mnl if available"
+
+ [ Oliver Smith ]
+ * tests/*: fix control reaches end of non-void func
+ * configure.ac: set -std=gnu11
+ * sim: fix gcc 4.9.2 + -std=gnu11 error
+
+ -- Pau Espin Pedrol <pespin@espeweb.net> Tue, 23 Feb 2021 14:03:31 +0100
+
+libosmocore (1.4.0) unstable; urgency=medium
+
+ [ Pau Espin Pedrol ]
+ * bitvec: Fix return value in doc for bitvec_write_field()
+ * gb: Fix typo in bssgp_cause_strings description
+ * logging_vty.c: Avoid acquiring log tgt lock in logging level cmd when not needed
+ * value_string: Switch val from unsigned to int
+ * gsm0503_coding: Fix misleading comment UL vs DL
+ * gsm0503_coding: Fix USF encoding in MCS1-4
+ * gsm0503_tables: Document USF encoding tables
+ * tests/coding: Test decoding of DL EGPRS data packet
+ * tests/coding_test: Fix test_pdtch() results
+ * gsm_04_06.h: Remove repeated egprs header struct definitions
+ * configure.ac: Fix HAVE_CLOCK_GETTIME undef when func in -lrt
+ * Drop old BSC references in fd check configure option
+ * Revert "gsmtap_makemsg_ex: NULL for unknown chan_type"
+ * use_count.h: Fix API doc example
+ * tests/fsm_test.c: Disable use color in logging output
+ * tests: vty: Extend test to do some numeric range validations
+ * vty: Allow 64 bit values in numeric ranges if system supports it
+ * vty: Don't match negative values on purely positive ranges
+ * vty: Introduce support to set cpu-affinity and scheduler policy
+
+ [ Harald Welte ]
+ * usb: Use OSMO_STRLCPY where appropriate
+ * usb: Add osmo_libusb_find_matching_dev_{path,serial}
+ * osmo_libusb: Check return of libusb_get_string_descriptor_ascii()
+ * osmo-sim-test: Recurse through subdirectories
+ * osmo-sim-test: Fall-back to classic SIM
+ * chantype_rsl2gsmtap(): Add entries for CBCH
+ * gsmtap.h: Add definitions for voice inside GSMTAP
+ * Add GSMTAP_CHANNEL_VOICE to gsmtap_gsm_channel_names[] value_string
+ * Add CTRL port number for osmo-mgw
+ * gsmtap: Solve TCH / FACCH confusion once and for all
+ * fixup depreciation warning
+ * socket: Add osmo_sock_mcast_iface_set() to bind multicast to device
+ * libosmosim: Build irrespective of PC/SC support
+ * sim: card_fs_usim.c: Fix FID of EF.EXT4
+ * card_fs_sim: Avoid '/' in file names
+ * osmo-sim-test: Use stderr for error messages
+ * osmo-sim-test: don't print SW in successful case of dump_file()
+ * osmo-sim-test: Optionally dump card files to host filesystem
+ * sim: add osim_file_desc_find_aid()
+ * sim: re-structure how we support cards + applications
+ * osmo-sim-test: Also [attempt to] dump DF.GSM on USIM cards
+ * card_fs_{usim,isim}: Update to 15.7.0 / Release 15
+ * sim: Add HPSIM application support
+ * osmo-sim-test: Avoid double-close
+ * ports.h: Add 4268 for UECUPS VTY
+ * exec: Introduce osmo_system_nowait2() to allow specify a user
+ * select.c: Introduce support for signalfd
+ * timerfd: call osmo_fd_unregister() when closing on read error
+ * gsm0503_parity: Fix compilation with gcc-10
+ * gsm_29_118.h: Fix compilation with gcc-10
+ * sim: When decoding SW, take application specific SW into account
+ * README.md: fix typo (coore -> core)
+ * README.md: We don't build libosmotrau. The latter is in libosmo-abis.git
+ * usb: Add osmo_libusb_find_open_claim() all-in-one API
+ * codec: Add functions for AMR s->d bits and d->s bits
+ * libosmogsm: add Doxygen docs for gsm0502_hop_seq_gen()
+ * Implement ITU-T I.460 multiplex / demultiplex
+ * NS: Optionally disable NS-{RESET,BLOCK,UNBLOCK} when using UDP/IP
+ * NS: replace use of gprs_nsvc_create() with gprs_nsvc_crate2()
+ * lapd/lapdm: print user-defined string name instead of (dl=%p)
+ * lapd_core: Fix log line being about LAPD and not LAPDm
+ * Revert "add osmo_mobile_identity API"
+ * bts_features.h: Introduce BTS_FEAT_PAGING_COORDINATION
+ * gsm0808: Add gsm0808_create_common_id()
+ * gprs_bssgp: Add bssgp_tx_bvc_reset2()
+ * gprs_ns: Set sockaddr_in.sin_family for persistent NSVCs
+ * vty/ports.h: Add VTY port for osmo-e1d
+ * lapd_core: Ensure we always have some tailroom
+ * lapd_core: After calling into L3, check if the state has changed
+ * vty: Avoid ultra-long multi-line strings cluttering talloc reports
+ * gsm0411_{smc,smr}.c: Work around newlib bug
+ * bits.c: Use faster look-up-table approach for osmo_revbytebits_{buf,u8}
+ * i460: Add back-pointer from sub-channel to timeslot
+ * i460: pass more context to call-back functions
+ * i460: Fix bit- and subslots ordering of I.460 mux + demux
+
+ [ Neels Hofmeyr ]
+ * add crcXXgen.c.tpl to EXTRA_DIST
+ * jenkins.sh: simpler invocation of verify_value_string_...
+ * gsm_04_08.h: fix big endian structs
+ * add missing endian.h in gsm_23_041.h
+ * struct_endianess.py: also recognise unnamed substructs
+ * cosmetic: apply changes to match struct_endianess.py output
+ * enable vty xml dumping to stdout
+ * api doc: clarify 'returns' of gsm48_mi_to_string()
+ * api doc: clarify OSMO_NAME_C_IMPL() required FUNC_BUF signature
+ * fix osmo_mi_name_c() to always return talloced strings, via osmo_mi_name_buf()
+ * add gsm23236: MSC pooling: TMSI and NRI utility functions
+ * tlv.h: add msgb_tvl_put() to add a TvLV without the value part
+ * osmo_bcd2str: also validate start_nibble parameter
+ * add osmo_mobile_identity API
+ * add osmo_mobile_identity API
+ * gsm0408_test: allow deprecated API
+ * api comment: fix example of osmo_mobile_identity_encode_msgb
+ * fixup for gsm0808_create_common_id(): add API doc, use new MI API
+
+ [ Philipp Maier ]
+ * l1sap: add measurement related struct members
+ * osmo-sim-test: check tlv_parsed struct tp before access
+ * parity: add amr crc14 definition
+ * conv: add convolutional coder for AMR SID UPDATE frames
+ * gsm690: Fix amr speech bit length table
+ * dtx: add decoding for AMR-DTX frames
+ * exec: osmo_system_nowait2: initalize *pw pointer with NULL
+ * logging: use LOGL_NOTICE when no loglevel is set
+ * logging: do not allow multiple calls of log_init()
+ * gsm0505_amr_dtx: add missing value strings
+ * gsm0808: fix endieness of call identifier
+ * i460_mux: correctly reset subchannels
+ * gsm_08_58: add missing RSL error cause codes
+ * i460_mux: add callback to notify empty tx queue
+
+ [ Vadim Yanitskiy ]
+ * usb/Makefile.am: fix copy-pasted library name: s/libosmosim/libosmousb/
+ * gsm/gsm48049.c: fix use of GNU 'missing =' extension in designator
+ * tdef_vty: do not enforce enum 'node_type' in osmo_tdef_vty_groups_init()
+ * conv: prevent theoretical NULL pointer dereference in osmo_conv_encode()
+ * osmo_libusb: check return value of osmo_fd_register()
+ * exec: prevent uninitialized memory access in osmo_system_nowait()
+ * exec: propogate errors from osmo_environment_[filter|append]
+ * bitvec: make bitvec_free() safe against NULL
+ * tests/bitvec: add a unit test for bitvec_read_field()
+ * bitvec: fix bitvec_unhex(): do not return 1 on success
+ * bitvec: fix misleading description of bitvec_spare_padding()
+ * bitvec: cosmetic: init i only once in bitvec_[un]pack()
+ * bitvec: avoid redundant zero-initialization in bitvec_alloc()
+ * tests/coding: check return value of encoding / decoding functions
+ * tests/coding: reduce verbosity of 8-bit / 11-bit RACH coding tests
+ * tests/coding: cosmetic: use ARRAY_SIZE() macro from utils.h
+ * coding: fix documentation of PDTCH encoding functions
+ * tests/coding: add 11-bit Access Burst samples from a real phone
+ * coding: fix bit ordering in 11-bit RACH coding functions
+ * rest_octets: fix encoding of 3G Early Classmark Sending Restriction
+ * libosmogsm: cosmetic: add spaces before and after PRIu32
+ * bts_features: fix: properly check the result of bitvec_get_bit_pos()
+ * bts_features: introduce osmo_bts_unset_feature()
+ * gsm0502: add TDMA frame number constants and modular arithmetic
+ * utils/gsmtap_logread.py: make it executable
+ * src/Makefile.am: add conv_acc_neon_impl.h to EXTRA_DIST
+ * configure.ac: clarify description of --enable-neon
+ * configure.ac: fix: do not define HAVE_NEON unconditionally
+ * configure.ac: print ARM NEON instructions support status
+
+ [ Eric Wild ]
+ * pcsc: don't leak memory
+
+ [ Alexander Chemeris ]
+ * gb: Fix typos in gprs_ns.c comments
+ * gb: Print signalling and data weights on NS-VC creation.
+ * select: Fix typo in a comment Osmcoom->Osmocom
+ * stats: Move cfg_stats_interval_cmd() function.
+ * stats: Fix documentation for osmo_stats_set_interval()
+ * stats: Support regular stats flush
+ * stats: Change timer to timerfd to make it a true interval timer.
+ * gsm0808: Fix encoding of the SAPI_N_REJECT BSSMAP message.
+ * gsm0808: Make a function to extract Cause IE publicly available.
+ * gsm0808_utils: Fix gsm0808_cause_class() function
+ * gsm0808_utils: Add gsm0808_get_cipher_reject_cause() back with a deprecation notice.
+ * gsm0808: Implement helper functions for CONFUSION BSSMAP message decoding.
+
+ [ Maksim Aristov ]
+ * debian: Change python3 dependency to native arch
+
+ [ Eric ]
+ * configure.ac: fix libtool issue with clang and sanitizer
+ * timer.c: make timers thread safe
+ * pkgconfig/osmocodec/osmocoding: link to talloc
+ * libomsocoding: NEON viterbi acceleration
+
+ [ Kirill Zakharenko ]
+ * statsd: fix rendering for groups with idx==0
+
+ [ Sylvain Munaut ]
+ * libosmogsm: import hopping sequence generation code
+
+ [ Oliver Smith ]
+ * contrib: import RPM spec
+ * gsmtap_makemsg_ex: NULL for unknown chan_type
+ * contrib: integrate RPM spec
+ * Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
+
+ [ Alexander Couzens ]
+ * gprs_ns.h: add missing prototype gprs_ns_rcvmsg()
+ * gprs_ns: fix typo in comment
+ * gprs_ns_frgre.h: add missing declaration of structs
+ * bts_features: add feature BTS_FEAT_IPV6_NSVC
+ * socket: osmo_sock_local_ip: correct doxygen comment
+ * vty: add a define VTY_IPV46_CMD to require a IPv4/6 address
+ * Gb/BSSGP: replace hardcoded Tx into NS library by a callback
+ * gsm 12.21: add osmocom specific NM_ATT_OSMO_NS_LINK_CFG
+ * osmo_sock_init2: improve support for AF_UNSPEC
+ * socket.h: introduce osmo_sockaddr to hold v4 and v6 endpoints
+
+ [ Daniel Willmann ]
+ * rate_ctr: Add functions to reset rate counter (groups)
+ * stat_item: Add function to reset stat items and groups
+ * stats: Add stats commands related to testing
+
+ -- Harald Welte <laforge@osmocom.org> Thu, 13 Aug 2020 11:06:35 +0200
+
+libosmocore (1.3.0) unstable; urgency=medium
+
+ [ Pau Espin Pedrol ]
+ * osmo-release.sh: Add DRY_RUN mode
+ * osmo-release.sh: Verify debian/rules dh_strip lines match LIBVERSION
+ * osmo-release.sh: Verify consistency of dependency versions in configure.ac and debian/control
+ * osmo-release.sh: Check patches under debian/patches apply
+ * osmo-release.sh: Support releasing openbsc.git
+ * vty: Register logp cmd next to logging commands
+ * tdef: Introduce API osmo_tdef_set()
+ * tdef_vty.h: Add missing header dependencies
+ * logging_internal.h: Fix osmo_log_info definition
+ * osmo-release.sh: update TODO-RELEASE for non-lib projects too
+ * logging: Move extern declaration of osmo_log_target_list from logging.h to logging_internal.h
+ * msgb: Allow size==headroom in msgb_alloc_headroom*()
+ * tdef: Introduce min_val and max_val fields
+ * tdef_test: verify case where osmo_tdef_set returns -EEXIST
+ * vty: Optionally Set/replace cfg file during cmd 'write file'
+ * logging: Introduce mutex API to manage log_target in multi-thread envs
+ * socket.c: Move glibc workarounds to same place in addrinfo_helper()
+ * vty: Fix go_parent_cb not called for indented nodes at end of cfg file
+ * tdef: Return correct snprintf value for osmo_tdef_range_str_buf()
+ * socket: Introduce API osmo_sock_init2_multiaddr()
+ * socket: Remove unneeded condition check in osmo_sock_init2_multiaddr()
+ * libosmocore.pc.in: Append -lsctp to Libs.private
+ * socket.c: build multiaddr socket API helpers only if used by public APIs
+ * configure: Introduce --disable-libsctp and error by default if libsctp not found
+ * vty: Return error if cmd returns CMD_WARNING while reading cfg file
+ * cosmetic: gsm_04_08.h: Fix trailing whitespace
+ * gsm_04_08.h: Introduce API osmo_gsm48_rfpowercap2powerclass()
+ * gsm: Fix compilation error under some compilers
+ * gsm: gsm_utils: Fix return type of API ms_class_gmsk_dbm() and add unit tests
+ * gsm: gsm_04_08.h: Allow accessing classmark2 as struct instead of uint32_t
+ * Introduce fields related to DTAP DLCI
+ * osmo-release.sh: Use set -e before applying changes to prepare release
+ * osmo-release.sh: Improve of PKG_CHECK_MODULES from configure.ac
+ * Drop empty file debian/patches/series
+
+ [ Harald Welte ]
+ * codec/ecu_fr: Mark input TCH frame as 'const' as we only read it
+ * context: Add support for [per-thread] global talloc contexts
+ * cbsp: Fix endless loop iteration when decoding cell list IEs
+ * cbsp: Remove printf() statement from early development/debugging
+ * cbsp: Fix decoding of WRITE-REPLACE payload
+ * codec/ecu: Introduce new generic Error Concealment Unit abstraction
+ * gsm_08_58: Add vendor-specific Message Type for ETWS Primary Warning
+ * Introduce BTS_FEAT_ETWS_PN for communicating ETWS PN capability
+ * sim/class_tables: Fix typo in comment
+ * cosmetic: clarify c_iflag in osmo_serial_init()
+ * select: Make file descriptor lists per-thread
+ * 04.80: Deprecate gsm0480_create_ussd_resp()
+ * Check for osmo_fsm_register() error return value
+ * gprs_ns_instantiate(): propagate errors from gprs_sns_init() to caller
+ * osmo-arfcn: Fix '-h' option
+ * utils: exit(2) on unsupported positional arguments on command line
+ * gsup: Introduce OSMO_GSUP_NUM_VECTORS_REQ_IE
+ * gprs_bssgp: Work around gcc-9 claiming "error=stringop-overflow"
+ * libosmocore libusb integration
+ * usb: Import a variety of libusb utility functions from simtrace
+ * debian/control: Add missing libusb-1.0-0-dev dependency
+ * Introduce helper functions for safe fork+exec of processes
+
+ [ Neels Hofmeyr ]
+ * add vty logp command to echo on all log targets
+ * osmo_tdef_get(): allow passing -1 as default timeout
+ * fix: vty crash by logging during VTY_CLOSED event handling
+ * OSMO_SOCKADDR_STR_FMT_ARGS: remove useless condition
+ * OSMO_SOCKADDR_STR_FMT_ARGS: guard against NULL pointer
+ * tdef: fixup osmo_tdef_set()
+ * gsup: add OSMO_GSUP_SUPPORTED_RAT_TYPES_IE and OSMO_GSUP_CURRENT_RAT_TYPE_IE
+ * API doc tweaks (mncc.h, gsm_08_08.h)
+ * add osmo_fsm_set_dealloc_ctx(), to help with use-after-free
+ * fsm: refuse state chg and events after term
+ * add osmo_sockaddr_str_is_nonzero()
+ * test: add OSMO_SOCKADDR_STR_FMT to sockaddr_str_test.c
+ * fix OSMO_SOCKADDR_STR_FMT for IPv6
+ * add osmo_sockaddr_str_cmp()
+ * utils.c: fix various inaccurate API doc about return values
+ * logging.h: define ansi color constants
+ * fix DLSMS logging category color: '[1:38m' isn't actually defined
+ * cosmetic: logging.h: fix comment s/levels/subsystems
+ * osmo_sockaddr_str: API doc: fix 32bit addr mixup of host/network byte order
+ * utils.h: add OSMO_NAME_C_IMPL() macro
+ * fix osmo_escape_str_c() and osmo_quote_str_c()
+ * GSUP: rename E_ROUTING_ERROR to ROUTING_ERROR
+ * fsm.h: add missing include of logging.h
+ * msgb_put: more elaborate logging of head/tailroom failure
+ * utils_test: add osmo_print_n_test()
+ * utils: add osmo_strnchr()
+ * osmo_sockaddr_str: deprecate osmo_sockaddr_str_*_32n()
+ * vty: track parent nodes also for telnet sessions
+ * vty_app_info.is_config_node: add OSMO_DEPRECATED
+ * add osmo_escape_cstr and osmo_quote_cstr
+ * add all missing OSMO_GSUP_TO_MSGT_*() macros
+
+ [ Oliver Smith ]
+ * Cosmetic: l1sap.h: change /* !< to /*!<
+ * logging.h: add L1 SAPI related context and filter
+ * gprs_ns_vty: return success for disabled FR/GRE
+ * debian, utils: switch to python 3
+
+ [ Ruben Undheim ]
+ * MAXPATHLEN set if not defined
+ * No fail if no /proc/cpuinfo
+
+ [ Philipp Maier ]
+ * cosmetic: Move comment to the right place
+ * cosmetic: Add comment on GSM-FR ECU struct
+ * ecu_fr: increase test coverage for FR ECU implementation
+ * gsm0508: add functions to calculate beginning of a block
+
+ [ Vadim Yanitskiy ]
+ * gsm29205_test: fix error: missing braces around initializer
+ * GPRS/BSSGP: introduce bssgp_bvc_ctx_free()
+ * logging/vty: do not print deprecated logging commands to stdout
+ * logging/vty: use LOG_LEVEL_ARGS in logging_vty_add_deprecated_subsys()
+ * logging/vty: fix: actually ignore deprecated logging commands
+ * logging/vty: fix vty_read_file(): do not write warnings to stdin
+ * logging/vty: fix: do not close stderr in vty_close()
+ * libosmovty: properly initialize vty->fd in vty_new()
+ * libosmovty: simplify condition checking vty->fd in vty_close()
+ * core/defs.h: introduce and use OSMO_DEPRECATED_OUTSIDE
+
+ [ Daniel Willmann ]
+ * libosmogsm: add support for XOR authentication
+
+ [ Vasil Velichkov ]
+ * Add code coverage support
+
+ [ Eric Wild ]
+ * sim: allow opening reader# > 0
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 02 Jan 2020 18:42:29 +0100
+
libosmocore (1.2.0) unstable; urgency=medium
[ Harald Welte ]
diff --git a/debian/compat b/debian/compat
index ec635144..f599e28b 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-9
+10
diff --git a/debian/control b/debian/control
index 6c9cfae9..5fb26cbc 100644
--- a/debian/control
+++ b/debian/control
@@ -1,8 +1,9 @@
Source: libosmocore
-Maintainer: Harald Welte <laforge@gnumonks.org>
+Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Section: libs
Priority: optional
-Build-Depends: debhelper (>= 9),
+# liburing-dev: don't try to install it on debian 10 and ubuntu 20.04
+Build-Depends: debhelper (>= 10),
autotools-dev,
autoconf,
automake,
@@ -14,26 +15,32 @@ Build-Depends: debhelper (>= 9),
doxygen,
libpcsclite-dev,
pkg-config,
- libtalloc-dev,
+ libtalloc-dev (>= 2.1.0),
libsctp-dev,
- python (>= 2.7.6)
+ libusb-1.0-0-dev,
+ libmnl-dev,
+ libsystemd-dev,
+ liburing-dev | base-files (<< 11) | ubuntu-keyring (<< 2021),
+ python3:native
Standards-Version: 3.9.8
-Vcs-Git: git://git.osmocom.org/libosmocore.git
-Vcs-Browser: http://git.osmocom.org/libosmocore/
+Vcs-Git: https://gitea.osmocom.org/osmocom/libosmocore
+Vcs-Browser: https://gitea.osmocom.org/osmocom/libosmocore
Homepage: https://projects.osmocom.org/projects/libosmocore
Package: libosmocore
Section: libs
Architecture: any
Multi-Arch: foreign
-Depends: libosmocodec0 (= ${binary:Version}),
+Depends: libosmocodec4 (= ${binary:Version}),
libosmocoding0 (= ${binary:Version}),
- libosmocore12 (= ${binary:Version}),
- libosmogb9 (= ${binary:Version}),
- libosmogsm13 (= ${binary:Version}),
- libosmovty4 (= ${binary:Version}),
+ libosmocore21 (= ${binary:Version}),
+ libosmogb14 (= ${binary:Version}),
+ libosmogsm20 (= ${binary:Version}),
+ libosmoisdn0 (= ${binary:Version}),
+ libosmovty13 (= ${binary:Version}),
libosmoctrl0 (= ${binary:Version}),
- libosmosim0 (= ${binary:Version}),
+ libosmosim2 (= ${binary:Version}),
+ libosmousb0 (= ${binary:Version}),
${misc:Depends}
Description: Open Source MObile COMmunications CORE library (metapackage)
The libraries provided by this package contain various utility functions.
@@ -42,7 +49,7 @@ Description: Open Source MObile COMmunications CORE library (metapackage)
least) other programs that are developed in the sphere of Free Software / Open
Source mobile communication.
-Package: libosmocodec0
+Package: libosmocodec4
Section: libs
Architecture: any
Multi-Arch: same
@@ -68,7 +75,7 @@ Package: libosmocodec-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmocodec0,
+ libosmocodec4,
libjs-jquery
Description: Documentation for the osmo codec library
This is part of the libosmocore "meta"-library. The libosmocore library
@@ -111,7 +118,7 @@ Description: Documentation for the osmo coding library
.
This package contains the documentation for the libosmocoding library.
-Package: libosmocore12
+Package: libosmocore21
Section: libs
Architecture: any
Multi-Arch: same
@@ -125,19 +132,24 @@ Description: Osmo Core library
(at least) other programs that are developed in the sphere of Free Software /
Open Source mobile communication.
.
- The libosmocore12 library in particular is a collection of common code used in
+ The libosmocore library in particular is a collection of common code used in
various sub-projects inside the Osmocom family of projects.
Package: libosmocore-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmocore12,
+ libosmocore21,
libjs-jquery,
libosmocodec-doc,
libosmocoding-doc,
libosmogsm-doc,
- libosmovty-doc
+ libosmoisdn-doc,
+ libosmovty-doc,
+ libosmoctrl-doc,
+ libosmogb-doc,
+ libosmosim-doc,
+ libosmousb-doc
Description: Documentation for the Osmo Core library
This is part of the libosmocore "meta"-library. The libosmocore library
contains various utility functions that were originally developed as part of
@@ -147,7 +159,7 @@ Description: Documentation for the Osmo Core library
.
This package contains the documentation for the libosmocore library.
-Package: libosmogb9
+Package: libosmogb14
Section: libs
Architecture: any
Multi-Arch: same
@@ -168,7 +180,7 @@ Package: libosmogb-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmogb9,
+ libosmogb14,
libjs-jquery
Description: Documentation for the Osmo GPRS Gb library
This is part of the libosmocore "meta"-library. The libosmocore library
@@ -179,7 +191,7 @@ Description: Documentation for the Osmo GPRS Gb library
.
This package contains the documentation for the libosmogb library.
-Package: libosmogsm13
+Package: libosmogsm20
Section: libs
Architecture: any
Multi-Arch: same
@@ -203,7 +215,7 @@ Package: libosmogsm-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmogsm13,
+ libosmogsm20,
libjs-jquery
Description: Documentation for the Osmo GSM utility library
This is part of the libosmocore "meta"-library. The libosmocore library
@@ -214,7 +226,40 @@ Description: Documentation for the Osmo GSM utility library
.
This package contains the documentation for the libosmogsm library.
-Package: libosmovty4
+Package: libosmoisdn0
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: Osmo ISDN utility library
+ This is part of the libosmocore "meta"-library. The libosmocore library
+ contains various utility functions that were originally developed as part of
+ the OpenBSC project, but which are of a more generic nature and thus useful to
+ (at least) other programs that are developed in the sphere of Free Software /
+ Open Source mobile communication.
+ .
+ The libosmoisdn library in particular is a collection of common code used in
+ various ISDN related sub-projects inside the Osmocom family of projects. It
+ includes an I.460 sub-channel multiplex and a generic LAPD core.
+
+Package: libosmoisdn-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends},
+ libosmoisdn0,
+ libjs-jquery
+Description: Documentation for the Osmo ISDN utility library
+ This is part of the libosmocore "meta"-library. The libosmocore library
+ contains various utility functions that were originally developed as part of
+ the OpenBSC project, but which are of a more generic nature and thus useful to
+ (at least) other programs that are developed in the sphere of Free Software /
+ Open Source mobile communication.
+ .
+ This package contains the documentation for the libosmoisdn library.
+
+Package: libosmovty13
Section: libs
Architecture: any
Multi-Arch: same
@@ -235,7 +280,7 @@ Package: libosmovty-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmovty4,
+ libosmovty13,
libjs-jquery
Description: Documentation for the Osmo VTY library
This is part of the libosmocore "meta"-library. The libosmocore library
@@ -277,7 +322,7 @@ Description: Documentation for the Osmocom CTRL library
.
This package contains the documentation for the libosmoctrl library.
-Package: libosmosim0
+Package: libosmosim2
Section: libs
Architecture: any
Multi-Arch: same
@@ -293,12 +338,62 @@ Description: Osmo SIM library
.
The libosmosim library in particular contains routines for SIM card access.
+Package: libosmosim-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends},
+ libosmosim2,
+ libjs-jquery
+Description: Documentation for the Osmo SIM library
+ This is part of the libosmocore "meta"-library. The libosmocore library
+ contains various utility functions that were originally developed as part of
+ the OpenBSC project, but which are of a more generic nature and thus useful to
+ (at least) other programs that are developed in the sphere of Free Software /
+ Open Source mobile communication.
+ .
+ This package contains the documentation for the libosmosim library.
+
+Package: libosmousb0
+Section: libs
+Architecture: any
+Multi-Arch: same
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
+Description: Osmo USB library
+ This is part of the libosmocore "meta"-library. The libosmocore library
+ contains various utility functions that were originally developed as part of
+ the OpenBSC project, but which are of a more generic nature and thus useful to
+ (at least) other programs that are developed in the sphere of Free Software /
+ Open Source mobile communication.
+ .
+ The libosmosub library in particular contains routines for USB device access
+ via libusb-1.0, integrated into the libosmocore select event loop.
+
+Package: libosmousb-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends},
+ libosmousb0,
+ libjs-jquery
+Description: Documentation for the Osmo USB library
+ This is part of the libosmocore "meta"-library. The libosmocore library
+ contains various utility functions that were originally developed as part of
+ the OpenBSC project, but which are of a more generic nature and thus useful to
+ (at least) other programs that are developed in the sphere of Free Software /
+ Open Source mobile communication.
+ .
+ This package contains the documentation for the libosmousb library.
+
Package: libosmocore-dev
Architecture: any
Multi-Arch: same
Section: libdevel
Depends: libosmocore,
- libtalloc-dev,
+ libtalloc-dev (>= 2.1.0),
+ libsctp-dev,
+ libusb-1.0-0-dev,
+ libmnl-dev,
${misc:Depends}
Description: Development headers for Open Source MObile COMmunications CORE library
The header files provided by this package may be used to develop
diff --git a/debian/copyright b/debian/copyright
index b119569a..9281f03d 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,6 +1,6 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: libosmocore
-Source: git://git.osmocom.org/libosmocore.git
+Source: https://gitea.osmocom.org/osmocom/libosmocore
Files: *
Copyright: 2008 Daniel Willmann <daniel@totalueberwachung.de>
@@ -13,30 +13,30 @@ License: GPL-2+
Files: include/osmocom/core/loggingrb.h
include/osmocom/core/strrb.h
- src/strrb.c
- src/loggingrb.c
+ src/core/strrb.c
+ src/core/loggingrb.c
Copyright: 2012-2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
License: GPL-2+
Files: include/osmocom/core/linuxrbtree.h
- src/rbtree.c
+ src/core/rbtree.c
Copyright: 1999 Andrea Arcangeli <andrea@suse.de>
2002 David Woodhouse <dwmw2@infradead.org>
License: GPL-2+
Files: include/osmocom/core/crc16.h
- src/crc16.c
+ src/core/crc16.c
Copyright: 2005 Ben Gardner <bgardner@wabtec.com>
License: GPL-2
-Files: src/utils.c
+Files: src/core/utils.c
Copyright: 2011 Harald Welte <laforge@gnumonks.org>
2011 Sylvain Munaut <tnt@246tNt.com>
2014 Nils O. Selåsdal <noselasd@fiane.dyndns.org>
License: GPL-2+
Files: src/gsm/gsm48_ie.c
- src/gsm/lapd_core.c
+ src/isdn/lapd_core.c
src/gsm/lapdm.c
Copyright: 2008,2010-2011 Harald Welte <laforge@gnumonks.org>
2009-2011 Andreas Eversberg <jolly@eversberg.eu>
@@ -100,8 +100,8 @@ Copyright: 1997,1998 Kunihiro Ishiguro
License: GPL-2+
Files: include/osmocom/core/stats.h
- src/stat_item.c
- src/stats.c
+ src/core/stat_item.c
+ src/core/stats.c
src/vty/stats_vty.c
tests/stats/stats_test.c
Copyright: 2009-2010 by Harald Welte <laforge@gnumonks.org>
diff --git a/debian/libosmocodec0.install b/debian/libosmocodec4.install
index 2676133e..2676133e 100644
--- a/debian/libosmocodec0.install
+++ b/debian/libosmocodec4.install
diff --git a/debian/libosmocore-dev.install b/debian/libosmocore-dev.install
index 944a7bf1..4a7e0afc 100644
--- a/debian/libosmocore-dev.install
+++ b/debian/libosmocore-dev.install
@@ -4,3 +4,4 @@ usr/lib/*/lib*.so
usr/lib/*/lib*.la
usr/lib/*/pkgconfig/*
usr/bin/osmo-release.sh
+usr/share/aclocal/osmo*.m4
diff --git a/debian/libosmocore-utils.install b/debian/libosmocore-utils.install
index d23cc73a..0e411978 100644
--- a/debian/libosmocore-utils.install
+++ b/debian/libosmocore-utils.install
@@ -1,3 +1,5 @@
usr/bin/osmo-arfcn
usr/bin/osmo-auc-gen
+usr/bin/osmo-aka-verify
usr/bin/osmo-config-merge
+usr/bin/osmo-gsmtap-logsend
diff --git a/debian/libosmocore.dirs b/debian/libosmocore.dirs
index 94090a39..9c10e3aa 100644
--- a/debian/libosmocore.dirs
+++ b/debian/libosmocore.dirs
@@ -5,4 +5,5 @@ usr/include/osmocom/codec
usr/include/osmocom/core
usr/include/osmocom/crypt
usr/include/osmocom/gsm
+usr/include/osmocom/isdn
usr/include/osmocom/vty
diff --git a/debian/libosmocore12.install b/debian/libosmocore21.install
index b73331b9..b73331b9 100644
--- a/debian/libosmocore12.install
+++ b/debian/libosmocore21.install
diff --git a/debian/libosmogb9.install b/debian/libosmogb14.install
index 4c474255..4c474255 100644
--- a/debian/libosmogb9.install
+++ b/debian/libosmogb14.install
diff --git a/debian/libosmogsm13.install b/debian/libosmogsm20.install
index 5e617298..5e617298 100644
--- a/debian/libosmogsm13.install
+++ b/debian/libosmogsm20.install
diff --git a/debian/libosmoisdn-doc.doc-base b/debian/libosmoisdn-doc.doc-base
new file mode 100644
index 00000000..bca6e1d0
--- /dev/null
+++ b/debian/libosmoisdn-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmoisdn-doc
+Title: Documentation for the libosmoisdn library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/isdn/html/index.html
+Files: /usr/share/doc/libosmocore/isdn/html/*.html
diff --git a/debian/libosmoisdn-doc.install b/debian/libosmoisdn-doc.install
new file mode 100644
index 00000000..9c985503
--- /dev/null
+++ b/debian/libosmoisdn-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/isdn/
diff --git a/debian/libosmoisdn0.install b/debian/libosmoisdn0.install
new file mode 100644
index 00000000..691a3e0f
--- /dev/null
+++ b/debian/libosmoisdn0.install
@@ -0,0 +1 @@
+usr/lib/*/libosmoisdn*.so.*
diff --git a/debian/libosmosim-doc.doc-base b/debian/libosmosim-doc.doc-base
new file mode 100644
index 00000000..0c2678db
--- /dev/null
+++ b/debian/libosmosim-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmosim-doc
+Title: Documentation for the libosmosim library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/sim/html/index.html
+Files: /usr/share/doc/libosmocore/sim/html/*.html
diff --git a/debian/libosmosim-doc.install b/debian/libosmosim-doc.install
new file mode 100644
index 00000000..2f37b409
--- /dev/null
+++ b/debian/libosmosim-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/sim/
diff --git a/debian/libosmosim0.install b/debian/libosmosim2.install
index 0a780abc..0a780abc 100644
--- a/debian/libosmosim0.install
+++ b/debian/libosmosim2.install
diff --git a/debian/libosmousb-doc.doc-base b/debian/libosmousb-doc.doc-base
new file mode 100644
index 00000000..6121918b
--- /dev/null
+++ b/debian/libosmousb-doc.doc-base
@@ -0,0 +1,7 @@
+Document: libosmousb-doc
+Title: Documentation for the libosmousb library
+Section: Programming
+
+Format: HTML
+Index: /usr/share/doc/libosmocore/usb/html/index.html
+Files: /usr/share/doc/libosmocore/usb/html/*.html
diff --git a/debian/libosmousb-doc.install b/debian/libosmousb-doc.install
new file mode 100644
index 00000000..081159a9
--- /dev/null
+++ b/debian/libosmousb-doc.install
@@ -0,0 +1 @@
+usr/share/doc/libosmocore/usb/
diff --git a/debian/libosmousb0.install b/debian/libosmousb0.install
new file mode 100644
index 00000000..4031b9f4
--- /dev/null
+++ b/debian/libosmousb0.install
@@ -0,0 +1 @@
+usr/lib/*/libosmousb*.so.*
diff --git a/debian/libosmovty4.install b/debian/libosmovty13.install
index fbf6a5fa..fbf6a5fa 100644
--- a/debian/libosmovty4.install
+++ b/debian/libosmovty13.install
diff --git a/debian/rules b/debian/rules
index a9d961c7..db97497c 100755
--- a/debian/rules
+++ b/debian/rules
@@ -25,8 +25,20 @@ override_dh_install:
override_dh_auto_test:
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
+# Set --disable-uring for debian 10 and ubuntu 20.04
override_dh_auto_configure:
- dh_auto_configure -- --enable-static
+ set -x && \
+ CONFIGURE_FLAGS=" \
+ --disable-sctp-tests \
+ --disable-uring-tests \
+ --enable-static \
+ --enable-systemd-logging \
+ "; \
+ distro_v=$$(. /etc/os-release && echo $$VERSION_ID); \
+ if [ "$$distro_v" = 10 ] || [ "$$distro_v" = 20.04 ]; then \
+ CONFIGURE_FLAGS="$$CONFIGURE_FLAGS --disable-uring"; \
+ fi; \
+ dh_auto_configure -- $$CONFIGURE_FLAGS
override_dh_clean:
dh_clean
@@ -40,16 +52,17 @@ override_dh_clean:
$(RM) include/osmocom/core/crc32gen.h
$(RM) include/osmocom/core/crc64gen.h
$(RM) include/osmocom/core/crc8gen.h
- $(RM) src/crc16gen.c
- $(RM) src/crc32gen.c
- $(RM) src/crc64gen.c
- $(RM) src/crc8gen.c
+ $(RM) src/core/crc16gen.c
+ $(RM) src/core/crc32gen.c
+ $(RM) src/core/crc64gen.c
+ $(RM) src/core/crc8gen.c
$(RM) tests/package.m4
$(RM) tests/testsuite
$(RM) -r doc/codec/
$(RM) -r doc/core/
$(RM) -r doc/ctrl/
$(RM) -r doc/gsm/
+ $(RM) -r doc/isdn/
$(RM) -r doc/gb/
$(RM) -r doc/vty/html/
$(RM) -r doc/vty/latex/
diff --git a/include/Makefile.am b/include/Makefile.am
index a82d6ac4..3578a80e 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,185 +1 @@
-BUILT_SOURCES = osmocom/gsm/gsm0503.h
-
-nobase_include_HEADERS = \
- osmocom/codec/ecu.h \
- osmocom/codec/codec.h \
- osmocom/codec/gsm610_bits.h \
- osmocom/core/application.h \
- osmocom/core/backtrace.h \
- osmocom/core/bit16gen.h \
- osmocom/core/bit32gen.h \
- osmocom/core/bit64gen.h \
- osmocom/core/bits.h \
- osmocom/core/bitvec.h \
- osmocom/core/bitcomp.h \
- osmocom/core/byteswap.h \
- osmocom/core/conv.h \
- osmocom/core/counter.h \
- osmocom/core/crc16.h \
- osmocom/core/crc16gen.h \
- osmocom/core/crc32gen.h \
- osmocom/core/crc64gen.h \
- osmocom/core/crc8gen.h \
- osmocom/core/crcgen.h \
- osmocom/core/endian.h \
- osmocom/core/defs.h \
- osmocom/core/fsm.h \
- osmocom/core/gsmtap.h \
- osmocom/core/gsmtap_util.h \
- osmocom/core/isdnhdlc.h \
- osmocom/core/linuxlist.h \
- osmocom/core/linuxrbtree.h \
- osmocom/core/logging.h \
- osmocom/core/loggingrb.h \
- osmocom/core/stats.h \
- osmocom/core/macaddr.h \
- osmocom/core/msgb.h \
- osmocom/core/panic.h \
- osmocom/core/prbs.h \
- osmocom/core/prim.h \
- osmocom/core/process.h \
- osmocom/core/rate_ctr.h \
- osmocom/core/stat_item.h \
- osmocom/core/select.h \
- osmocom/core/sercomm.h \
- osmocom/core/signal.h \
- osmocom/core/socket.h \
- osmocom/core/statistics.h \
- osmocom/core/strrb.h \
- osmocom/core/talloc.h \
- osmocom/core/tdef.h \
- osmocom/core/timer.h \
- osmocom/core/timer_compat.h \
- osmocom/core/utils.h \
- osmocom/core/write_queue.h \
- osmocom/core/sockaddr_str.h \
- osmocom/core/use_count.h \
- osmocom/crypt/auth.h \
- osmocom/crypt/gprs_cipher.h \
- osmocom/ctrl/control_cmd.h \
- osmocom/ctrl/control_if.h \
- osmocom/ctrl/ports.h \
- osmocom/gprs/gprs_bssgp.h \
- osmocom/gprs/gprs_bssgp_bss.h \
- osmocom/gprs/gprs_msgb.h \
- osmocom/gprs/gprs_ns.h \
- osmocom/gprs/gprs_ns_frgre.h \
- osmocom/gprs/gprs_rlc.h \
- osmocom/gprs/protocol/gsm_04_60.h \
- osmocom/gprs/protocol/gsm_08_16.h \
- osmocom/gprs/protocol/gsm_08_18.h \
- osmocom/gsm/a5.h \
- osmocom/gsm/abis_nm.h \
- osmocom/gsm/apn.h \
- osmocom/gsm/bts_features.h \
- osmocom/gsm/cbsp.h \
- osmocom/gsm/comp128.h \
- osmocom/gsm/comp128v23.h \
- osmocom/gsm/bitvec_gsm.h \
- osmocom/gsm/gan.h \
- osmocom/gsm/gsm0341.h \
- osmocom/gsm/gsm0411_smc.h \
- osmocom/gsm/gsm0411_smr.h \
- osmocom/gsm/gsm0411_utils.h \
- osmocom/gsm/gsm0480.h \
- osmocom/gsm/gsm0502.h \
- osmocom/gsm/gsm0503.h \
- osmocom/coding/gsm0503_tables.h \
- osmocom/coding/gsm0503_parity.h \
- osmocom/coding/gsm0503_mapping.h \
- osmocom/coding/gsm0503_interleaving.h \
- osmocom/coding/gsm0503_coding.h \
- osmocom/gsm/gsm0808.h \
- osmocom/gsm/gsm29205.h \
- osmocom/gsm/gsm0808_utils.h \
- osmocom/gsm/gsm23003.h \
- osmocom/gsm/gsm29118.h \
- osmocom/gsm/gsm48.h \
- osmocom/gsm/gsm48_arfcn_range_encode.h \
- osmocom/gsm/gsm48_ie.h \
- osmocom/gsm/gsm48_rest_octets.h \
- osmocom/gsm/gsm_utils.h \
- osmocom/gsm/gsup.h \
- osmocom/gsm/gsup_sms.h \
- osmocom/gsm/ipa.h \
- osmocom/gsm/lapd_core.h \
- osmocom/gsm/lapdm.h \
- osmocom/gsm/meas_rep.h \
- osmocom/gsm/mncc.h \
- osmocom/gsm/prim.h \
- osmocom/gsm/l1sap.h \
- osmocom/gsm/oap.h \
- osmocom/gsm/oap_client.h \
- osmocom/gsm/protocol/gsm_03_40.h \
- osmocom/gsm/protocol/gsm_03_41.h \
- osmocom/gsm/protocol/gsm_04_08.h \
- osmocom/gsm/protocol/gsm_04_08_gprs.h \
- osmocom/gsm/protocol/gsm_04_11.h \
- osmocom/gsm/protocol/gsm_04_12.h \
- osmocom/gsm/protocol/gsm_04_14.h \
- osmocom/gsm/protocol/gsm_04_80.h \
- osmocom/gsm/protocol/gsm_08_08.h \
- osmocom/gsm/protocol/gsm_08_58.h \
- osmocom/gsm/protocol/gsm_09_02.h \
- osmocom/gsm/protocol/gsm_12_21.h \
- osmocom/gsm/protocol/gsm_23_003.h \
- osmocom/gsm/protocol/gsm_23_041.h \
- osmocom/gsm/protocol/gsm_29_118.h \
- osmocom/gsm/protocol/gsm_44_318.h \
- osmocom/gsm/protocol/gsm_48_049.h \
- osmocom/gsm/protocol/ipaccess.h \
- osmocom/gsm/protocol/smpp34_osmocom.h \
- osmocom/gsm/rsl.h \
- osmocom/gsm/rxlev_stat.h \
- osmocom/gsm/sysinfo.h \
- osmocom/gsm/tlv.h \
- osmocom/sim/class_tables.h \
- osmocom/sim/sim.h
-
-if ENABLE_PLUGIN
-nobase_include_HEADERS += osmocom/core/plugin.h
-endif
-
-if ENABLE_MSGFILE
-nobase_include_HEADERS += osmocom/core/msgfile.h
-endif
-
-if ENABLE_SERIAL
-nobase_include_HEADERS += osmocom/core/serial.h
-endif
-
-
-if ENABLE_VTY
-nobase_include_HEADERS += \
- osmocom/vty/buffer.h \
- osmocom/vty/command.h \
- osmocom/vty/logging.h \
- osmocom/vty/stats.h \
- osmocom/vty/misc.h \
- osmocom/vty/telnet_interface.h \
- osmocom/vty/vector.h \
- osmocom/vty/vty.h \
- osmocom/vty/ports.h \
- osmocom/vty/tdef_vty.h \
- osmocom/ctrl/control_vty.h
-endif
-
-noinst_HEADERS = \
- osmocom/gsm/kasumi.h \
- osmocom/gsm/gea.h \
- osmocom/core/logging_internal.h \
- $(NULL)
-
-osmocom/core/bit%gen.h: osmocom/core/bitXXgen.h.tpl
- $(AM_V_GEN)$(MKDIR_P) $(dir $@)
- $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
-
-osmocom/core/crc%gen.h: osmocom/core/crcXXgen.h.tpl
- $(AM_V_GEN)$(MKDIR_P) $(dir $@)
- $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
-
-osmocom/gsm/gsm0503.h: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py
- $(AM_V_GEN)python $(top_srcdir)/utils/conv_gen.py gen_header gsm \
- --target-path $(builddir)/osmocom/gsm
-
-CLEANFILES = osmocom/gsm/gsm0503.h
+SUBDIRS = osmocom
diff --git a/include/osmocom/Makefile.am b/include/osmocom/Makefile.am
new file mode 100644
index 00000000..33538094
--- /dev/null
+++ b/include/osmocom/Makefile.am
@@ -0,0 +1,13 @@
+SUBDIRS = \
+ core \
+ vty \
+ codec \
+ gsm \
+ isdn \
+ crypt \
+ coding \
+ gprs \
+ ctrl \
+ sim \
+ usb \
+ $(NULL)
diff --git a/include/osmocom/codec/Makefile.am b/include/osmocom/codec/Makefile.am
new file mode 100644
index 00000000..63ae4024
--- /dev/null
+++ b/include/osmocom/codec/Makefile.am
@@ -0,0 +1,7 @@
+osmocodec_HEADERS = \
+ ecu.h \
+ codec.h \
+ gsm610_bits.h \
+ $(NULL)
+
+osmocodecdir = $(includedir)/osmocom/codec
diff --git a/include/osmocom/codec/codec.h b/include/osmocom/codec/codec.h
index 6a1bf9fb..c5981f89 100644
--- a/include/osmocom/codec/codec.h
+++ b/include/osmocom/codec/codec.h
@@ -6,6 +6,7 @@
#include <stdbool.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
/* TS 101318 Chapter 5.1: 260 bits + 4bit sig */
#define GSM_FR_BYTES 33
@@ -14,6 +15,10 @@
/* TS 101318 Chapter 5.3: 244 bits + 4bit sig */
#define GSM_EFR_BYTES 31
+/* Number of bytes of an GSM_HR RTP payload */
+#define GSM_HR_BYTES_RTP_RFC5993 (GSM_HR_BYTES + 1)
+#define GSM_HR_BYTES_RTP_TS101318 (GSM_HR_BYTES)
+
extern const uint16_t gsm610_bitorder[]; /* FR */
extern const uint16_t gsm620_unvoiced_bitorder[]; /* HR unvoiced */
extern const uint16_t gsm620_voiced_bitorder[]; /* HR voiced */
@@ -28,6 +33,8 @@ extern const uint16_t gsm690_5_9_bitorder[]; /* AMR 5.9 kbits */
extern const uint16_t gsm690_5_15_bitorder[]; /* AMR 5.15 kbits */
extern const uint16_t gsm690_4_75_bitorder[]; /* AMR 4.75 kbits */
+extern const uint8_t osmo_gsm611_silence_frame[GSM_FR_BYTES];
+
extern const struct value_string osmo_amr_type_names[];
enum osmo_amr_type {
@@ -46,11 +53,19 @@ enum osmo_amr_type {
AMR_NO_DATA = 15,
};
+static inline const char *osmo_amr_type_name(enum osmo_amr_type type)
+{ return get_value_string(osmo_amr_type_names, type); }
+
enum osmo_amr_quality {
AMR_BAD = 0,
AMR_GOOD = 1
};
+extern const uint8_t gsm690_bitlength[AMR_NO_DATA+1];
+
+int osmo_amr_s_to_d(ubit_t *out, const ubit_t *in, uint16_t n_bits, enum osmo_amr_type amr_mode);
+int osmo_amr_d_to_s(ubit_t *out, const ubit_t *in, uint16_t n_bits, enum osmo_amr_type amr_mode);
+
/*! Check if given AMR Frame Type is a speech frame
* \param[in] ft AMR Frame Type
* \returns true if AMR with given Frame Type contains voice, false otherwise
@@ -72,8 +87,53 @@ static inline bool osmo_amr_is_speech(enum osmo_amr_type ft)
}
}
+/* SID ternary classification per GSM 06.31 & 06.81 section 6.1.1 */
+enum osmo_gsm631_sid_class {
+ OSMO_GSM631_SID_CLASS_SPEECH = 0,
+ OSMO_GSM631_SID_CLASS_INVALID = 1,
+ OSMO_GSM631_SID_CLASS_VALID = 2,
+};
+
bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
+bool osmo_efr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
+
+enum osmo_gsm631_sid_class osmo_fr_sid_classify(const uint8_t *rtp_payload);
+enum osmo_gsm631_sid_class osmo_efr_sid_classify(const uint8_t *rtp_payload);
+
+/*! Check if given FR codec frame is any kind of SID, valid or invalid
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \returns true if the frame is an "accepted SID frame" in GSM 06.31
+ * definition, false otherwise.
+ */
+static inline bool osmo_fr_is_any_sid(const uint8_t *rtp_payload)
+{
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_fr_sid_classify(rtp_payload);
+ return sidc != OSMO_GSM631_SID_CLASS_SPEECH;
+}
+
+/*! Check if given EFR codec frame is any kind of SID, valid or invalid
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \returns true if the frame is an "accepted SID frame" in GSM 06.81
+ * definition, false otherwise.
+ */
+static inline bool osmo_efr_is_any_sid(const uint8_t *rtp_payload)
+{
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_efr_sid_classify(rtp_payload);
+ return sidc != OSMO_GSM631_SID_CLASS_SPEECH;
+}
+
+bool osmo_fr_sid_preen(uint8_t *rtp_payload);
+bool osmo_efr_sid_preen(uint8_t *rtp_payload);
+
+void osmo_fr_sid_reset(uint8_t *rtp_payload);
+void osmo_hr_sid_reset(uint8_t *rtp_payload);
+void osmo_efr_sid_reset(uint8_t *rtp_payload);
+
int osmo_amr_rtp_enc(uint8_t *payload, uint8_t cmr, enum osmo_amr_type ft,
enum osmo_amr_quality bfi);
int osmo_amr_rtp_dec(const uint8_t *payload, int payload_len, uint8_t *cmr,
diff --git a/include/osmocom/codec/ecu.h b/include/osmocom/codec/ecu.h
index 99b1430f..64928609 100644
--- a/include/osmocom/codec/ecu.h
+++ b/include/osmocom/codec/ecu.h
@@ -3,18 +3,19 @@
#include <stdint.h>
#include <stdbool.h>
+#include <osmocom/core/defs.h>
#include <osmocom/codec/codec.h>
-/* ECU state for GSM-FR */
+/* ECU state for GSM-FR - deprecated version only! */
struct osmo_ecu_fr_state {
bool subsequent_lost_frame;
uint8_t frame_backup[GSM_FR_BYTES];
};
void osmo_ecu_fr_reset(struct osmo_ecu_fr_state *state, const uint8_t *frame)
- OSMO_DEPRECATED("Use generic ECU abstraction layer instead");
+ OSMO_DEPRECATED_OUTSIDE("Use generic ECU abstraction layer instead");
int osmo_ecu_fr_conceal(struct osmo_ecu_fr_state *state, uint8_t *frame)
- OSMO_DEPRECATED("Use generic ECU abstraction layer instead");
+ OSMO_DEPRECATED_OUTSIDE("Use generic ECU abstraction layer instead");
enum osmo_ecu_codec {
OSMO_ECU_CODEC_HR,
@@ -61,12 +62,16 @@ int osmo_ecu_frame_in(struct osmo_ecu_state *st, bool bfi,
/* generate output data for a substitute/erroneous frame */
int osmo_ecu_frame_out(struct osmo_ecu_state *st, uint8_t *frame_out);
+/* is the stream handled by this ECU currently in a DTX pause? */
+bool osmo_ecu_is_dtx_pause(struct osmo_ecu_state *st);
+
struct osmo_ecu_ops {
struct osmo_ecu_state * (*init)(void *ctx, enum osmo_ecu_codec codec);
void (*destroy)(struct osmo_ecu_state *);
int (*frame_in)(struct osmo_ecu_state *st, bool bfi,
const uint8_t *frame, unsigned int frame_bytes);
int (*frame_out)(struct osmo_ecu_state *st, uint8_t *frame_out);
+ bool (*is_dtx_pause)(struct osmo_ecu_state *st);
};
int osmo_ecu_register(const struct osmo_ecu_ops *ops, enum osmo_ecu_codec codec);
diff --git a/include/osmocom/coding/Makefile.am b/include/osmocom/coding/Makefile.am
new file mode 100644
index 00000000..713b1932
--- /dev/null
+++ b/include/osmocom/coding/Makefile.am
@@ -0,0 +1,10 @@
+osmocoding_HEADERS = \
+ gsm0503_tables.h \
+ gsm0503_parity.h \
+ gsm0503_mapping.h \
+ gsm0503_interleaving.h \
+ gsm0503_coding.h \
+ gsm0503_amr_dtx.h \
+ $(NULL)
+
+osmocodingdir = $(includedir)/osmocom/coding
diff --git a/include/osmocom/coding/gsm0503_amr_dtx.h b/include/osmocom/coding/gsm0503_amr_dtx.h
new file mode 100644
index 00000000..dc22ec7e
--- /dev/null
+++ b/include/osmocom/coding/gsm0503_amr_dtx.h
@@ -0,0 +1,47 @@
+/*! \file gsm0503_amr_dtx.h
+ * GSM TS 05.03 coding
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/defs.h>
+#include <osmocom/core/bits.h>
+
+/*! \addtogroup coding
+ * @{
+ * \file gsm0503_amr_dtx.h */
+
+enum gsm0503_amr_dtx_frames {
+ AMR_OTHER,
+ AFS_SID_FIRST,
+ AFS_SID_UPDATE,
+ AFS_SID_UPDATE_CN,
+ AFS_ONSET,
+ AHS_SID_UPDATE,
+ AHS_SID_UPDATE_CN,
+ AHS_SID_FIRST_P1,
+ AHS_SID_FIRST_P2,
+ AHS_ONSET,
+ AHS_SID_FIRST_INH,
+ AHS_SID_UPDATE_INH,
+};
+
+extern const struct value_string gsm0503_amr_dtx_frame_names[];
+static inline const char *gsm0503_amr_dtx_frame_name(enum gsm0503_amr_dtx_frames frame)
+{
+ return get_value_string(gsm0503_amr_dtx_frame_names, frame);
+}
+
+enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t *ubits)
+ OSMO_DEPRECATED("Use gsm0503_detect_afs_dtx_frame2() instead");
+enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame(int *n_errors, int *n_bits_total, const ubit_t *ubits)
+ OSMO_DEPRECATED("Use gsm0503_detect_ahs_dtx_frame2() instead");
+
+enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame2(int *n_errors, int *n_bits_total,
+ int *mode_id, const sbit_t *sbits);
+enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame2(int *n_errors, int *n_bits_total,
+ int *mode_id, const sbit_t *sbits);
+
+/*! @} */
diff --git a/include/osmocom/coding/gsm0503_coding.h b/include/osmocom/coding/gsm0503_coding.h
index 98038f8f..3c51127e 100644
--- a/include/osmocom/coding/gsm0503_coding.h
+++ b/include/osmocom/coding/gsm0503_coding.h
@@ -50,20 +50,29 @@ int gsm0503_tch_fr_decode(uint8_t *tch_data, const sbit_t *bursts, int net_order
int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len);
int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int *n_errors, int *n_bits_total)
+ OSMO_DEPRECATED("Use gsm0503_tch_hr_decode2() instead");
+int gsm0503_tch_hr_decode2(uint8_t *tch_data, const sbit_t *bursts, int odd,
int *n_errors, int *n_bits_total);
int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
- int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
+ int codec_mode_req, const uint8_t *codec, int codecs, uint8_t ft,
uint8_t cmr);
int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
uint8_t *cmr, int *n_errors, int *n_bits_total);
+int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
+ uint8_t *cmr, int *n_errors, int *n_bits_total, uint8_t *dtx);
int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
- int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr);
+ int codec_mode_req, const uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr);
int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
uint8_t *cmr, int *n_errors, int *n_bits_total);
+int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
+ uint8_t *cmr, int *n_errors, int *n_bits_total, uint8_t *dtx);
int gsm0503_rach_ext_encode(ubit_t *burst, uint16_t ra, uint8_t bsic, bool is_11bit);
int gsm0503_rach_encode(ubit_t *burst, const uint8_t *ra, uint8_t bsic) OSMO_DEPRECATED("Use gsm0503_rach_ext_encode() instead");
@@ -80,4 +89,36 @@ int gsm0503_rach_ext_decode_ber(uint16_t *ra, const sbit_t *burst, uint8_t bsic,
int gsm0503_sch_encode(ubit_t *burst, const uint8_t *sb_info);
int gsm0503_sch_decode(uint8_t *sb_info, const sbit_t *burst);
+int gsm0503_tch_fr96_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_fr96_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_fr48_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_fr48_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_hr48_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_hr48_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_fr24_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_fr24_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_hr24_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_hr24_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_fr144_encode(ubit_t *bursts, const ubit_t *data);
+int gsm0503_tch_fr144_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_fr_facch_encode(ubit_t *bursts, const uint8_t *data);
+int gsm0503_tch_fr_facch_decode(uint8_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_hr_facch_encode(ubit_t *bursts, const uint8_t *data);
+int gsm0503_tch_hr_facch_decode(uint8_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
/*! @} */
diff --git a/include/osmocom/coding/gsm0503_interleaving.h b/include/osmocom/coding/gsm0503_interleaving.h
index 05b5e278..fab4d3d0 100644
--- a/include/osmocom/coding/gsm0503_interleaving.h
+++ b/include/osmocom/coding/gsm0503_interleaving.h
@@ -58,4 +58,7 @@ void gsm0503_mcs8_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
void gsm0503_mcs8_dl_interleave(const ubit_t *hc, const ubit_t *c1,
const ubit_t *c2, ubit_t *hi, ubit_t *di);
+void gsm0503_tch_f96_deinterleave(sbit_t *cB, const sbit_t *iB);
+void gsm0503_tch_f96_interleave(const ubit_t *cB, ubit_t *iB);
+
/*! @} */
diff --git a/include/osmocom/coding/gsm0503_parity.h b/include/osmocom/coding/gsm0503_parity.h
index 28a54443..bda5f99e 100644
--- a/include/osmocom/coding/gsm0503_parity.h
+++ b/include/osmocom/coding/gsm0503_parity.h
@@ -10,14 +10,15 @@
* @{
* \file gsm0503_parity.h */
-const struct osmo_crc64gen_code gsm0503_fire_crc40;
-const struct osmo_crc16gen_code gsm0503_cs234_crc16;
-const struct osmo_crc8gen_code gsm0503_mcs_crc8_hdr;
-const struct osmo_crc16gen_code gsm0503_mcs_crc12;
-const struct osmo_crc8gen_code gsm0503_rach_crc6;
-const struct osmo_crc16gen_code gsm0503_sch_crc10;
-const struct osmo_crc8gen_code gsm0503_tch_fr_crc3;
-const struct osmo_crc8gen_code gsm0503_tch_efr_crc8;
-const struct osmo_crc8gen_code gsm0503_amr_crc6;
+extern const struct osmo_crc64gen_code gsm0503_fire_crc40;
+extern const struct osmo_crc16gen_code gsm0503_cs234_crc16;
+extern const struct osmo_crc8gen_code gsm0503_mcs_crc8_hdr;
+extern const struct osmo_crc16gen_code gsm0503_mcs_crc12;
+extern const struct osmo_crc8gen_code gsm0503_rach_crc6;
+extern const struct osmo_crc16gen_code gsm0503_sch_crc10;
+extern const struct osmo_crc8gen_code gsm0503_tch_fr_crc3;
+extern const struct osmo_crc8gen_code gsm0503_tch_efr_crc8;
+extern const struct osmo_crc8gen_code gsm0503_amr_crc6;
+extern const struct osmo_crc16gen_code gsm0503_amr_crc14;
/*! @} */
diff --git a/include/osmocom/core/Makefile.am b/include/osmocom/core/Makefile.am
new file mode 100644
index 00000000..7c29ca10
--- /dev/null
+++ b/include/osmocom/core/Makefile.am
@@ -0,0 +1,103 @@
+osmocore_HEADERS = \
+ application.h \
+ backtrace.h \
+ base64.h \
+ bit16gen.h \
+ bit32gen.h \
+ bit64gen.h \
+ bits.h \
+ bitvec.h \
+ bitcomp.h \
+ byteswap.h \
+ conv.h \
+ counter.h \
+ crc16.h \
+ crc16gen.h \
+ crc32gen.h \
+ crc64gen.h \
+ crc8gen.h \
+ crcgen.h \
+ endian.h \
+ defs.h \
+ exec.h \
+ fsm.h \
+ gsmtap.h \
+ gsmtap_util.h \
+ hash.h \
+ hashtable.h \
+ isdnhdlc.h \
+ it_q.h \
+ linuxlist.h \
+ linuxrbtree.h \
+ log2.h \
+ logging.h \
+ loggingrb.h \
+ stats.h \
+ macaddr.h \
+ msgb.h \
+ netdev.h \
+ netns.h \
+ osmo_io.h \
+ panic.h \
+ prbs.h \
+ prim.h \
+ process.h \
+ rate_ctr.h \
+ stat_item.h \
+ stats_tcp.h \
+ select.h \
+ sercomm.h \
+ signal.h \
+ socket.h \
+ statistics.h \
+ strrb.h \
+ talloc.h \
+ tdef.h \
+ thread.h \
+ timer.h \
+ timer_compat.h \
+ tun.h \
+ utils.h \
+ write_queue.h \
+ sockaddr_str.h \
+ soft_uart.h \
+ time_cc.h \
+ use_count.h \
+ socket_compat.h \
+ $(NULL)
+
+if ENABLE_PLUGIN
+osmocore_HEADERS += plugin.h
+endif
+
+if ENABLE_MSGFILE
+osmocore_HEADERS += msgfile.h
+endif
+
+if ENABLE_SERIAL
+osmocore_HEADERS += serial.h
+endif
+
+if ENABLE_LIBMNL
+osmocore_HEADERS += mnl.h
+endif
+
+osmocoredir = $(includedir)/osmocom/core
+
+noinst_HEADERS = \
+ logging_internal.h \
+ $(NULL)
+
+bit%gen.h: bitXXgen.h.tpl
+ $(AM_V_GEN)$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
+
+crc%gen.h: crcXXgen.h.tpl
+ $(AM_V_GEN)$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
+
+socket_compat.h: socket_compat.h.tpl
+ $(AM_V_GEN)$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN)sed -e's/XX/$(HAVE_SYS_SOCKET_H)/g' $< > $@
+
+EXTRA_DIST = socket_compat.h.tpl
diff --git a/include/osmocom/core/application.h b/include/osmocom/core/application.h
index edf59ed4..67a59088 100644
--- a/include/osmocom/core/application.h
+++ b/include/osmocom/core/application.h
@@ -2,17 +2,14 @@
#include <osmocom/core/defs.h>
+struct log_info;
+struct log_target;
+
/*!
* \file application.h
* Routines for helping with the osmocom application setup.
*/
-/*! information containing the available logging subsystems */
-struct log_info;
-
-/*! one instance of a logging target (file, stderr, ...) */
-struct log_target;
-
/*! the default logging target, logging to stderr */
extern struct log_target *osmo_stderr_target;
diff --git a/include/osmocom/core/base64.h b/include/osmocom/core/base64.h
new file mode 100644
index 00000000..a6c97206
--- /dev/null
+++ b/include/osmocom/core/base64.h
@@ -0,0 +1,69 @@
+/**
+ * \file base64.h
+ *
+ * \brief RFC 1521 base64 encoding/decoding
+ *
+ * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#pragma once
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Encode a buffer into base64 format
+ *
+ * \param dst destination buffer
+ * \param dlen size of the destination buffer
+ * \param olen number of bytes written
+ * \param src source buffer
+ * \param slen amount of data to be encoded
+ *
+ * \return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
+ * *olen is always updated to reflect the amount
+ * of data that has (or would have) been written.
+ *
+ * \note Call this function with dlen = 0 to obtain the
+ * required buffer size in *olen
+ */
+int osmo_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
+ const unsigned char *src, size_t slen);
+
+/**
+ * \brief Decode a base64-formatted buffer
+ *
+ * \param dst destination buffer (can be NULL for checking size)
+ * \param dlen size of the destination buffer
+ * \param olen number of bytes written
+ * \param src source buffer
+ * \param slen amount of data to be decoded
+ *
+ * \return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
+ * MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
+ * not correct. *olen is always updated to reflect the amount
+ * of data that has (or would have) been written.
+ *
+ * \note Call this function with *dst = NULL or dlen = 0 to obtain
+ * the required buffer size in *olen
+ */
+int osmo_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
+ const unsigned char *src, size_t slen);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/include/osmocom/core/bitXXgen.h.tpl b/include/osmocom/core/bitXXgen.h.tpl
index 6881d87d..ab54ba9c 100644
--- a/include/osmocom/core/bitXXgen.h.tpl
+++ b/include/osmocom/core/bitXXgen.h.tpl
@@ -14,15 +14,13 @@
* but WITHOUT 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.
*/
#pragma once
-/*! load unaligned n-byte integer (little-endian encoding) into uintXX_t
+#include <osmocom/core/utils.h>
+
+/*! load unaligned n-byte integer (little-endian encoding) into uintXX_t, into the least significant octets.
* \param[in] p Buffer where integer is stored
* \param[in] n Number of bytes stored in p
* \returns XX bit unsigned integer
@@ -32,11 +30,14 @@ static inline uintXX_t osmo_loadXXle_ext(const void *p, uint8_t n)
uint8_t i;
uintXX_t r = 0;
const uint8_t *q = (uint8_t *)p;
+ OSMO_ASSERT(n <= sizeof(r));
for(i = 0; i < n; r |= ((uintXX_t)q[i] << (8 * i)), i++);
return r;
}
-/*! load unaligned n-byte integer (big-endian encoding) into uintXX_t
+/*! load unaligned n-byte integer (big-endian encoding) into uintXX_t, into the MOST significant octets.
+ * WARNING: for n < sizeof(uintXX_t), the result is not returned in the least significant octets, as one might expect.
+ * To always return the same value as fed to osmo_storeXXbe_ext() before, use osmo_loadXXbe_ext_2().
* \param[in] p Buffer where integer is stored
* \param[in] n Number of bytes stored in p
* \returns XX bit unsigned integer
@@ -46,10 +47,26 @@ static inline uintXX_t osmo_loadXXbe_ext(const void *p, uint8_t n)
uint8_t i;
uintXX_t r = 0;
const uint8_t *q = (uint8_t *)p;
+ OSMO_ASSERT(n <= sizeof(r));
for(i = 0; i < n; r |= ((uintXX_t)q[i] << (XX - 8* (1 + i))), i++);
return r;
}
+/*! load unaligned n-byte integer (big-endian encoding) into uintXX_t, into the least significant octets.
+ * \param[in] p Buffer where integer is stored
+ * \param[in] n Number of bytes stored in p
+ * \returns XX bit unsigned integer
+ */
+static inline uintXX_t osmo_loadXXbe_ext_2(const void *p, uint8_t n)
+{
+ uint8_t i;
+ uintXX_t r = 0;
+ const uint8_t *q = (uint8_t *)p;
+ OSMO_ASSERT(n <= sizeof(r));
+ for(i = 0; i < n; r |= ((uintXX_t)q[i] << (XX - 8* (1 + i + (sizeof(r) - n)))), i++);
+ return r;
+}
+
/*! store unaligned n-byte integer (little-endian encoding) from uintXX_t
* \param[in] x unsigned XX bit integer
@@ -60,6 +77,7 @@ static inline void osmo_storeXXle_ext(uintXX_t x, void *p, uint8_t n)
{
uint8_t i;
uint8_t *q = (uint8_t *)p;
+ OSMO_ASSERT(n <= sizeof(x));
for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++);
}
@@ -72,6 +90,7 @@ static inline void osmo_storeXXbe_ext(uintXX_t x, void *p, uint8_t n)
{
uint8_t i;
uint8_t *q = (uint8_t *)p;
+ OSMO_ASSERT(n <= sizeof(x));
for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++);
}
diff --git a/include/osmocom/core/bitcomp.h b/include/osmocom/core/bitcomp.h
index 5faa5ea4..21679577 100644
--- a/include/osmocom/core/bitcomp.h
+++ b/include/osmocom/core/bitcomp.h
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/core/bitvec.h b/include/osmocom/core/bitvec.h
index bd107093..1e2fe7b4 100644
--- a/include/osmocom/core/bitvec.h
+++ b/include/osmocom/core/bitvec.h
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -27,7 +23,6 @@
* \file bitvec.h */
#include <stdint.h>
-#include <osmocom/core/talloc.h>
#include <osmocom/core/defs.h>
#include <stdbool.h>
@@ -65,7 +60,7 @@ int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value
int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, unsigned int count);
int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count);
-struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *bvctx);
+struct bitvec *bitvec_alloc(unsigned int size, void *bvctx);
void bitvec_free(struct bitvec *bv);
int bitvec_unhex(struct bitvec *bv, const char *src);
unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer);
@@ -77,7 +72,7 @@ char bit_value_to_char(enum bit_value v);
void bitvec_to_string_r(const struct bitvec *bv, char *str);
void bitvec_zero(struct bitvec *bv);
unsigned bitvec_rl(const struct bitvec *bv, bool b);
-unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits);
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, unsigned int max_bits);
void bitvec_shiftl(struct bitvec *bv, unsigned int n);
int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits);
unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array,
diff --git a/include/osmocom/core/conv.h b/include/osmocom/core/conv.h
index 8b344f4d..84ef0f85 100644
--- a/include/osmocom/core/conv.h
+++ b/include/osmocom/core/conv.h
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \defgroup conv Convolutional encoding and decoding routines
@@ -128,6 +124,7 @@ int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
const sbit_t *input, int n);
int osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
const sbit_t *input);
+int osmo_conv_decode_get_best_end_state(struct osmo_conv_decoder *decoder);
int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
ubit_t *output, int has_flush, int end_state);
diff --git a/include/osmocom/core/counter.h b/include/osmocom/core/counter.h
index dc627918..7b677cb1 100644
--- a/include/osmocom/core/counter.h
+++ b/include/osmocom/core/counter.h
@@ -31,7 +31,7 @@ static inline void osmo_counter_inc(struct osmo_counter *ctr)
}
/*! Get current value of counter */
-OSMO_DEPRECATED("Implement as osmo_stat_item instead")
+OSMO_DEPRECATED_OUTSIDE("Implement as osmo_stat_item instead")
static inline unsigned long osmo_counter_get(struct osmo_counter *ctr)
{
return ctr->value;
@@ -52,7 +52,7 @@ void osmo_counter_free(struct osmo_counter *ctr)
int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data);
-int osmo_counters_count();
+int osmo_counters_count(void);
struct osmo_counter *osmo_counter_get_by_name(const char *name);
diff --git a/include/osmocom/core/crcXXgen.h.tpl b/include/osmocom/core/crcXXgen.h.tpl
index 823f21f2..13a3623e 100644
--- a/include/osmocom/core/crcXXgen.h.tpl
+++ b/include/osmocom/core/crcXXgen.h.tpl
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
diff --git a/include/osmocom/core/crcgen.h b/include/osmocom/core/crcgen.h
index 7cfe8699..24d29e74 100644
--- a/include/osmocom/core/crcgen.h
+++ b/include/osmocom/core/crcgen.h
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
diff --git a/include/osmocom/core/defs.h b/include/osmocom/core/defs.h
index 5e5aa90f..33ffb7bb 100644
--- a/include/osmocom/core/defs.h
+++ b/include/osmocom/core/defs.h
@@ -43,8 +43,10 @@
#if BUILDING_LIBOSMOCORE
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE
+# define OSMO_DEPRECATED_OUTSIDE(text)
#else
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE OSMO_DEPRECATED("For internal use inside libosmocore only.")
+# define OSMO_DEPRECATED_OUTSIDE(text) OSMO_DEPRECATED(text)
#endif
#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE
diff --git a/include/osmocom/core/endian.h b/include/osmocom/core/endian.h
index 6107b12f..426ab680 100644
--- a/include/osmocom/core/endian.h
+++ b/include/osmocom/core/endian.h
@@ -1,7 +1,7 @@
/*! \file endian.h
*
* GNU and FreeBSD have various ways to express the
- * endianess but none of them is similiar enough. This
+ * endianness but none of them is similar enough. This
* will create two defines that allows to decide on the
* endian. The following will be defined to either 0 or
* 1 at the end of the file.
diff --git a/include/osmocom/core/exec.h b/include/osmocom/core/exec.h
new file mode 100644
index 00000000..a4fdcfd3
--- /dev/null
+++ b/include/osmocom/core/exec.h
@@ -0,0 +1,25 @@
+#pragma once
+/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+extern const char *osmo_environment_whitelist[];
+
+int osmo_environment_filter(char **out, size_t out_len, char **in, const char **whitelist);
+int osmo_environment_append(char **out, size_t out_len, char **in);
+int osmo_close_all_fds_above(int last_fd_to_keep);
+int osmo_system_nowait2(const char *command, const char **env_whitelist, char **addl_env, const char *user);
+int osmo_system_nowait(const char *command, const char **env_whitelist, char **addl_env);
diff --git a/include/osmocom/core/fsm.h b/include/osmocom/core/fsm.h
index 269befa5..a1ffd30d 100644
--- a/include/osmocom/core/fsm.h
+++ b/include/osmocom/core/fsm.h
@@ -10,6 +10,7 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
/*! \defgroup fsm Finite State Machine abstraction
* @{
@@ -199,7 +200,7 @@ void osmo_fsm_set_dealloc_ctx(void *ctx);
fmt, ## args)
#define OSMO_T_FMT "%c%u"
-#define OSMO_T_FMT_ARGS(T) ((T) >= 0 ? 'T' : 'X'), ((T) >= 0 ? T : -T)
+#define OSMO_T_FMT_ARGS(T) ((T) >= 0 ? 'T' : 'X'), ((T) >= 0 ? (T) : -(T))
int osmo_fsm_register(struct osmo_fsm *fsm);
void osmo_fsm_unregister(struct osmo_fsm *fsm);
@@ -223,9 +224,9 @@ int osmo_fsm_inst_update_id(struct osmo_fsm_inst *fi, const char *id);
int osmo_fsm_inst_update_id_f(struct osmo_fsm_inst *fi, const char *fmt, ...);
int osmo_fsm_inst_update_id_f_sanitize(struct osmo_fsm_inst *fi, char replace_with, const char *fmt, ...);
-const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event);
-const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi);
-const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state);
+const char *osmo_fsm_event_name(const struct osmo_fsm *fsm, uint32_t event);
+const char *osmo_fsm_inst_name(const struct osmo_fsm_inst *fi);
+const char *osmo_fsm_state_name(const struct osmo_fsm *fsm, uint32_t state);
/*! return the name of the state the FSM instance is currently in. */
static inline const char *osmo_fsm_inst_state_name(struct osmo_fsm_inst *fi)
@@ -325,4 +326,15 @@ void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi,
void *data,
const char *file, int line);
+/*! dispatch an event to all children of an osmocom finite state machine instance
+ *
+ * This is a macro that calls _osmo_fsm_inst_broadcast_children() with the given
+ * parameters as well as the caller's source file and line number for logging
+ * purposes. See there for documentation.
+ */
+#define osmo_fsm_inst_broadcast_children(fi, cause, data) \
+ _osmo_fsm_inst_broadcast_children(fi, cause, data, __FILE__, __LINE__)
+void _osmo_fsm_inst_broadcast_children(struct osmo_fsm_inst *fi, uint32_t event,
+ void *data, const char *file, int line);
+
/*! @} */
diff --git a/include/osmocom/core/gsmtap.h b/include/osmocom/core/gsmtap.h
index 35ba71e5..ebb52960 100644
--- a/include/osmocom/core/gsmtap.h
+++ b/include/osmocom/core/gsmtap.h
@@ -48,6 +48,8 @@
#define GSMTAP_TYPE_OSMOCORE_LOG 0x10 /* libosmocore logging */
#define GSMTAP_TYPE_QC_DIAG 0x11 /* Qualcomm DIAG frame */
#define GSMTAP_TYPE_LTE_NAS 0x12 /* LTE Non-Access Stratum */
+#define GSMTAP_TYPE_E1T1 0x13 /* E1/T1 Lines */
+#define GSMTAP_TYPE_GSM_RLP 0x14 /* GSM RLP frames as per 3GPP TS 24.022 */
/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
@@ -82,8 +84,8 @@
#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_FACCH_F 0x09 /* Actually, it's FACCH/F (signaling) */
+#define GSMTAP_CHANNEL_FACCH_H 0x0a /* Actually, it's FACCH/H (signaling) */
#define GSMTAP_CHANNEL_PACCH 0x0b
#define GSMTAP_CHANNEL_CBCH52 0x0c
#define GSMTAP_CHANNEL_PDTCH 0x0d
@@ -91,6 +93,10 @@
#define GSMTAP_CHANNEL_PDCH GSMTAP_CHANNEL_PDTCH
#define GSMTAP_CHANNEL_PTCCH 0x0e
#define GSMTAP_CHANNEL_CBCH51 0x0f
+#define GSMTAP_CHANNEL_VOICE_F 0x10 /* voice codec payload (FR/EFR/AMR) */
+#define GSMTAP_CHANNEL_VOICE_H 0x11 /* voice codec payload (HR/AMR) */
+#define GSMTAP_CHANNEL_TCH_F GSMTAP_CHANNEL_FACCH_F /* We used the wrong naming in 2008 when we were young */
+#define GSMTAP_CHANNEL_TCH_H GSMTAP_CHANNEL_FACCH_H /* We used the wrong naming in 2008 when we were young */
/* GPRS Coding Scheme CS1..4 */
#define GSMTAP_GPRS_CS_BASE 0x20
@@ -167,6 +173,20 @@
#define GSMTAP_LTE_CH_DTCH 0x06
#define GSMTAP_LTE_CH_MTCH 0x07
+/* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */
+/* sub-types for TYPE_E1T1 */
+#define GSMTAP_E1T1_LAPD 0x01 /* Q.921 LAPD */
+#define GSMTAP_E1T1_FR 0x02 /* Frame Relay */
+#define GSMTAP_E1T1_RAW 0x03 /* raw/transparent B-channel */
+#define GSMTAP_E1T1_TRAU16 0x04 /* 16k TRAU frames; sub-slot 0-3 */
+#define GSMTAP_E1T1_TRAU8 0x05 /* 8k TRAU frames; sub-slot 0-7 */
+#define GSMTAP_E1T1_V5EF 0x06 /* V5 Envelope Function */
+#define GSMTAP_E1T1_X75 0x07 /* X.75 B-channel data */
+#define GSMTAP_E1T1_V120 0x08 /* V.120 B-channel data */
+#define GSMTAP_E1T1_V110 0x09 /* V.110 B-channel data */
+#define GSMTAP_E1T1_H221 0x0a /* H.221 B-channel data */
+#define GSMTAP_E1T1_PPP 0x0b /* PPP */
+
/* flags for the ARFCN */
#define GSMTAP_ARFCN_F_PCS 0x8000
#define GSMTAP_ARFCN_F_UPLINK 0x4000
@@ -318,3 +338,26 @@ struct gsmtap_osmocore_log_hdr {
uint32_t line_nr;/*!< line number */
} src_file;
} __attribute__((packed));
+
+/*! First byte of type==GSMTAP_TYPE_UM sub_type==GSMTAP_CHANNEL_VOICE payload */
+enum gsmtap_um_voice_type {
+ /*! 1 byte TOC + 112 bits (14 octets) = 15 octets payload;
+ * Reference is RFC5993 Section 5.2.1 + 3GPP TS 46.030 Annex B */
+ GSMTAP_UM_VOICE_HR,
+ /*! 33 payload bytes; Reference is RFC3551 Section 4.5.8.1 */
+ GSMTAP_UM_VOICE_FR,
+ /*! 31 payload bytes; Reference is RFC3551 Section 4.5.9 + ETSI TS 101 318 */
+ GSMTAP_UM_VOICE_EFR,
+ /*! 1 byte TOC + 5..31 bytes = 6..32 bytes payload; RFC4867 octet-aligned */
+ GSMTAP_UM_VOICE_AMR,
+ /* TODO: Revisit the types below; their usage; ... */
+ GSMTAP_UM_VOICE_AMR_SID_BAD,
+ GSMTAP_UM_VOICE_AMR_ONSET,
+ GSMTAP_UM_VOICE_AMR_RATSCCH,
+ GSMTAP_UM_VOICE_AMR_SID_UPDATE_INH,
+ GSMTAP_UM_VOICE_AMR_SID_FIRST_P1,
+ GSMTAP_UM_VOICE_AMR_SID_FIRST_P2,
+ GSMTAP_UM_VOICE_AMR_SID_FIRST_INH,
+ GSMTAP_UM_VOICE_AMR_RATSCCH_MARKER,
+ GSMTAP_UM_VOICE_AMR_RATSCCH_DATA,
+};
diff --git a/include/osmocom/core/gsmtap_util.h b/include/osmocom/core/gsmtap_util.h
index f8a12a60..d24ee95f 100644
--- a/include/osmocom/core/gsmtap_util.h
+++ b/include/osmocom/core/gsmtap_util.h
@@ -8,51 +8,54 @@
* @{
* \file gsmtap_util.h */
-uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id);
+uint8_t chantype_rsl2gsmtap2(uint8_t rsl_chantype, uint8_t rsl_link_id, bool user_plane);
+
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id)
+ OSMO_DEPRECATED("Use chantype_rsl2gsmtap2() instead");
+
void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype, uint8_t *link_id);
struct msgb *gsmtap_makemsg_ex(uint8_t type, 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);
+ int8_t snr, const uint8_t *data, unsigned int len);
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);
+ int8_t snr, const uint8_t *data, unsigned int len);
/*! one gsmtap instance */
-struct gsmtap_inst {
- int ofd_wq_mode; /*!< wait queue mode? */
- struct osmo_wqueue wq; /*!< the wait queue */
- struct osmo_fd sink_ofd;/*!< file descriptor */
-};
-
-/*! obtain the file descriptor associated with a gsmtap instance
- * \param[in] gti GSMTAP instance
- * \returns file descriptor of GSMTAP instance */
-static inline int gsmtap_inst_fd(struct gsmtap_inst *gti)
-{
- return gti->wq.bfd.fd;
-}
+struct gsmtap_inst;
+
+int gsmtap_inst_fd(struct gsmtap_inst *gti)
+ OSMO_DEPRECATED("Use gsmtap_inst_fd2() instead");
+
+int gsmtap_inst_fd2(const struct gsmtap_inst *gti);
int gsmtap_source_init_fd(const char *host, uint16_t port);
+int gsmtap_source_init_fd2(const char *local_host, uint16_t local_port, const char *rem_host, uint16_t rem_port);
int gsmtap_source_add_sink_fd(int gsmtap_fd);
struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
int ofd_wq_mode);
+struct gsmtap_inst *gsmtap_source_init2(const char *local_host, uint16_t local_port,
+ const char *rem_host, uint16_t rem_port, int ofd_wq_mode);
+
+void gsmtap_source_free(struct gsmtap_inst *gti);
int gsmtap_source_add_sink(struct gsmtap_inst *gti);
int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg);
+int gsmtap_sendmsg_free(struct gsmtap_inst *gti, struct msgb *msg);
int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, 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,
+ int8_t signal_dbm, int8_t snr, const uint8_t *data,
unsigned int len);
int gsmtap_send(struct gsmtap_inst *gti, 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,
+ int8_t signal_dbm, int8_t snr, const uint8_t *data,
unsigned int len);
extern const struct value_string gsmtap_gsm_channel_names[];
diff --git a/include/osmocom/core/hash.h b/include/osmocom/core/hash.h
new file mode 100644
index 00000000..b45c0361
--- /dev/null
+++ b/include/osmocom/core/hash.h
@@ -0,0 +1,101 @@
+#pragma once
+#include <osmocom/core/log2.h>
+/* Fast hashing routine for ints, longs and pointers.
+ (C) 2002 Nadia Yvette Chambers, IBM */
+
+#include <limits.h>
+#if ULONG_MAX == 4294967295
+#define BITS_PER_LONG 32
+#else
+#define BITS_PER_LONG 64
+#endif
+
+/*
+ * The "GOLDEN_RATIO_PRIME" is used in ifs/btrfs/brtfs_inode.h and
+ * fs/inode.c. It's not actually prime any more (the previous primes
+ * were actively bad for hashing), but the name remains.
+ */
+#if BITS_PER_LONG == 32
+#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_32
+#define hash_long(val, bits) hash_32(val, bits)
+#elif BITS_PER_LONG == 64
+#define hash_long(val, bits) hash_64(val, bits)
+#define GOLDEN_RATIO_PRIME GOLDEN_RATIO_64
+#else
+#error Wordsize not 32 or 64
+#endif
+
+/*
+ * This hash multiplies the input by a large odd number and takes the
+ * high bits. Since multiplication propagates changes to the most
+ * significant end only, it is essential that the high bits of the
+ * product be used for the hash value.
+ *
+ * Chuck Lever verified the effectiveness of this technique:
+ * http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf
+ *
+ * Although a random odd number will do, it turns out that the golden
+ * ratio phi = (sqrt(5)-1)/2, or its negative, has particularly nice
+ * properties. (See Knuth vol 3, section 6.4, exercise 9.)
+ *
+ * These are the negative, (1 - phi) = phi**2 = (3 - sqrt(5))/2,
+ * which is very slightly easier to multiply by and makes no
+ * difference to the hash distribution.
+ */
+#define GOLDEN_RATIO_32 0x61C88647
+#define GOLDEN_RATIO_64 0x61C8864680B583EBull
+
+/*
+ * The _generic versions exist only so lib/test_hash.c can compare
+ * the arch-optimized versions with the generic.
+ *
+ * Note that if you change these, any <asm/hash.h> that aren't updated
+ * to match need to have their HAVE_ARCH_* define values updated so the
+ * self-test will not false-positive.
+ */
+#ifndef HAVE_ARCH__HASH_32
+#define __hash_32 __hash_32_generic
+#endif
+static inline uint32_t __hash_32_generic(uint32_t val)
+{
+ return val * GOLDEN_RATIO_32;
+}
+
+#ifndef HAVE_ARCH_HASH_32
+#define hash_32 hash_32_generic
+#endif
+static inline uint32_t hash_32_generic(uint32_t val, unsigned int bits)
+{
+ /* High bits are more random, so use them. */
+ return __hash_32(val) >> (32 - bits);
+}
+
+#ifndef HAVE_ARCH_HASH_64
+#define hash_64 hash_64_generic
+#endif
+static __always_inline uint32_t hash_64_generic(uint64_t val, unsigned int bits)
+{
+#if BITS_PER_LONG == 64
+ /* 64x64-bit multiply is efficient on all 64-bit processors */
+ return val * GOLDEN_RATIO_64 >> (64 - bits);
+#else
+ /* Hash 64 bits using only 32x32-bit multiply. */
+ return hash_32((uint32_t)val ^ __hash_32(val >> 32), bits);
+#endif
+}
+
+static inline uint32_t hash_ptr(const void *ptr, unsigned int bits)
+{
+ return hash_long((unsigned long)ptr, bits);
+}
+
+/* This really should be called fold32_ptr; it does no hashing to speak of. */
+static inline uint32_t hash32_ptr(const void *ptr)
+{
+ unsigned long val = (unsigned long)ptr;
+
+#if BITS_PER_LONG == 64
+ val ^= (val >> 32);
+#endif
+ return (uint32_t)val;
+}
diff --git a/include/osmocom/core/hashtable.h b/include/osmocom/core/hashtable.h
new file mode 100644
index 00000000..acaf6b91
--- /dev/null
+++ b/include/osmocom/core/hashtable.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Statically sized hash table implementation
+ * (C) 2012 Sasha Levin <levinsasha928@gmail.com>
+ */
+
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/hash.h>
+
+#define DEFINE_HASHTABLE(name, bits) \
+ struct hlist_head name[1 << (bits)] = \
+ { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
+
+#define DECLARE_HASHTABLE(name, bits) \
+ struct hlist_head name[1 << (bits)]
+
+#define HASH_SIZE(name) (ARRAY_SIZE(name))
+#define HASH_BITS(name) ilog2(HASH_SIZE(name))
+
+/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */
+#define hash_min(val, bits) \
+ (sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits))
+
+static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
+{
+ unsigned int i;
+
+ for (i = 0; i < sz; i++)
+ INIT_HLIST_HEAD(&ht[i]);
+}
+
+/**
+ * hash_init - initialize a hash table
+ * @hashtable: hashtable to be initialized
+ *
+ * Calculates the size of the hashtable from the given parameter, otherwise
+ * same as hash_init_size.
+ *
+ * This has to be a macro since HASH_BITS() will not work on pointers since
+ * it calculates the size during preprocessing.
+ */
+#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable))
+
+/**
+ * hash_add - add an object to a hashtable
+ * @hashtable: hashtable to add to
+ * @node: the &struct hlist_node of the object to be added
+ * @key: the key of the object to be added
+ */
+#define hash_add(hashtable, node, key) \
+ hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))])
+
+/**
+ * hash_hashed - check whether an object is in any hashtable
+ * @node: the &struct hlist_node of the object to be checked
+ */
+static inline bool hash_hashed(struct hlist_node *node)
+{
+ return !hlist_unhashed(node);
+}
+
+static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz)
+{
+ unsigned int i;
+
+ for (i = 0; i < sz; i++)
+ if (!hlist_empty(&ht[i]))
+ return false;
+
+ return true;
+}
+
+/**
+ * hash_empty - check whether a hashtable is empty
+ * @hashtable: hashtable to check
+ *
+ * This has to be a macro since HASH_BITS() will not work on pointers since
+ * it calculates the size during preprocessing.
+ */
+#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable))
+
+/**
+ * hash_del - remove an object from a hashtable
+ * @node: &struct hlist_node of the object to remove
+ */
+static inline void hash_del(struct hlist_node *node)
+{
+ hlist_del_init(node);
+}
+
+/**
+ * hash_for_each - iterate over a hashtable
+ * @name: hashtable to iterate
+ * @bkt: integer to use as bucket loop cursor
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ */
+#define hash_for_each(name, bkt, obj, member) \
+ for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+ (bkt)++)\
+ hlist_for_each_entry(obj, &name[bkt], member)
+
+/**
+ * hash_for_each_safe - iterate over a hashtable safe against removal of
+ * hash entry
+ * @name: hashtable to iterate
+ * @bkt: integer to use as bucket loop cursor
+ * @tmp: a &struct hlist_node used for temporary storage
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ */
+#define hash_for_each_safe(name, bkt, tmp, obj, member) \
+ for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\
+ (bkt)++)\
+ hlist_for_each_entry_safe(obj, tmp, &name[bkt], member)
+
+/**
+ * hash_for_each_possible - iterate over all possible objects hashing to the
+ * same bucket
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible(name, obj, member, key) \
+ hlist_for_each_entry(obj, &name[hash_min(key, HASH_BITS(name))], member)
+
+/**
+ * hash_for_each_possible_safe - iterate over all possible objects hashing to the
+ * same bucket safe against removals
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @tmp: a &struct hlist_node used for temporary storage
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible_safe(name, obj, tmp, member, key) \
+ hlist_for_each_entry_safe(obj, tmp,\
+ &name[hash_min(key, HASH_BITS(name))], member)
diff --git a/include/osmocom/core/isdnhdlc.h b/include/osmocom/core/isdnhdlc.h
index 56369bfd..c8cfdc3a 100644
--- a/include/osmocom/core/isdnhdlc.h
+++ b/include/osmocom/core/isdnhdlc.h
@@ -20,14 +20,9 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#ifndef __ISDNHDLC_H__
-#define __ISDNHDLC_H__
+#pragma once
#include <stdint.h>
@@ -80,5 +75,3 @@ extern void osmo_isdnhdlc_out_init(struct osmo_isdnhdlc_vars *hdlc, uint32_t fea
extern int osmo_isdnhdlc_encode(struct osmo_isdnhdlc_vars *hdlc, const uint8_t *src,
uint16_t slen, int *count, uint8_t *dst, int dsize);
-
-#endif /* __ISDNHDLC_H__ */
diff --git a/include/osmocom/core/it_q.h b/include/osmocom/core/it_q.h
new file mode 100644
index 00000000..a28f524e
--- /dev/null
+++ b/include/osmocom/core/it_q.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/select.h>
+#include <pthread.h>
+
+/*! \defgroup osmo_it_q Inter-Thread Queue
+ * @{
+ * \file osmo_it_q.h */
+
+/*! One instance of an inter-thread queue. The user can use this to queue messages
+ * between different threads. The enqueue operation is non-blocking (but of course
+ * grabs a mutex for the actual list operations to safeguard against races). The
+ * receiving thread is woken up by an event_fd which can be registered in the libosmocore
+ * select loop handling. */
+struct osmo_it_q {
+ /* entry in global list of message queues */
+ struct llist_head entry;
+
+ /* the actual list of user structs. HEAD: first in queue; TAIL: last in queue */
+ struct llist_head list;
+ /* A pthread mutex to safeguard accesses to the queue. No rwlock as we always write. */
+ pthread_mutex_t mutex;
+ /* Current count of messages in the queue */
+ unsigned int current_length;
+ /* osmo-fd wrapped eventfd */
+ struct osmo_fd event_ofd;
+
+ /* a user-defined name for this queue */
+ const char *name;
+ /* maximum permitted length of queue */
+ unsigned int max_length;
+ /* read call-back, called for each de-queued message */
+ void (*read_cb)(struct osmo_it_q *q, struct llist_head *item);
+ /* opaque data pointer passed through to call-back function */
+ void *data;
+};
+
+struct osmo_it_q *osmo_it_q_by_name(const char *name);
+
+int _osmo_it_q_enqueue(struct osmo_it_q *queue, struct llist_head *item);
+#define osmo_it_q_enqueue(queue, item, member) \
+ _osmo_it_q_enqueue(queue, &(item)->member)
+
+struct llist_head *_osmo_it_q_dequeue(struct osmo_it_q *queue);
+#define osmo_it_q_dequeue(queue, item, member) do { \
+ struct llist_head *l = _osmo_it_q_dequeue(queue); \
+ if (!l) \
+ *item = NULL; \
+ else \
+ *item = llist_entry(l, typeof(**item), member); \
+} while (0)
+
+
+struct osmo_it_q *osmo_it_q_alloc(void *ctx, const char *name, unsigned int max_length,
+
+ void (*read_cb)(struct osmo_it_q *q, struct llist_head *item),
+ void *data);
+void osmo_it_q_destroy(struct osmo_it_q *q);
+void osmo_it_q_flush(struct osmo_it_q *q);
+
+/*! @} */
diff --git a/include/osmocom/core/linuxlist.h b/include/osmocom/core/linuxlist.h
index 867605e5..2fc3fa75 100644
--- a/include/osmocom/core/linuxlist.h
+++ b/include/osmocom/core/linuxlist.h
@@ -16,6 +16,7 @@
* \file linuxlist.h */
#include <stddef.h>
+#include <stdbool.h>
#ifndef inline
#define inline __inline__
@@ -237,6 +238,12 @@ static inline void llist_splice_init(struct llist_head *llist,
#define llist_last_entry(ptr, type, member) \
llist_entry((ptr)->prev, type, member)
+/*! Return the last element of the list.
+ * \param head the llist head of the list.
+ * \returns last element of the list, head if the list is empty.
+ */
+#define llist_last(head) (head)->prev
+
/*! Get the first element from a list, or NULL.
* \param ptr the list head to take the element from.
* \param type the type of the struct this is embedded in.
@@ -321,8 +328,7 @@ static inline void llist_splice_init(struct llist_head *llist,
pos = llist_entry(pos->member.next, typeof(*pos), member), \
prefetch(pos->member.next))
-/*! Iterate over llist of given type, safe against removal of
- * non-consecutive(!) llist entries.
+/*! Iterate over llist of given type, safe against removal of llist entry.
* \param pos the 'type *' to use as a loop counter.
* \param n another 'type *' to use as temporary storage.
* \param head the head of the list over which to iterate.
@@ -393,6 +399,252 @@ static inline unsigned int llist_count(const struct llist_head *head)
return i;
}
+
+
+/*! Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+ h->next = NULL;
+ h->pprev = NULL;
+}
+
+#define READ_ONCE(x) x
+#define WRITE_ONCE(a, b) a = b
+
+/*! Has node been removed from list and reinitialized?.
+ * \param[in] h: Node to be checked
+ * \return 1 if node is unhashed; 0 if not
+ *
+ * Not that not all removal functions will leave a node in unhashed
+ * state. For example, hlist_nulls_del_init_rcu() does leave the
+ * node in unhashed state, but hlist_nulls_del() does not.
+ */
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+/*! Version of hlist_unhashed for lockless use.
+ * \param[in] n Node to be checked
+ * \return 1 if node is unhashed; 0 if not
+ *
+ * This variant of hlist_unhashed() must be used in lockless contexts
+ * to avoid potential load-tearing. The READ_ONCE() is paired with the
+ * various WRITE_ONCE() in hlist helpers that are defined below.
+ */
+static inline int hlist_unhashed_lockless(const struct hlist_node *h)
+{
+ return !READ_ONCE(h->pprev);
+}
+
+/*!Is the specified hlist_head structure an empty hlist?.
+ * \param[in] h Structure to check.
+ * \return 1 if hlist is empty; 0 if not
+ */
+static inline int hlist_empty(const struct hlist_head *h)
+{
+ return !READ_ONCE(h->first);
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+
+ WRITE_ONCE(*pprev, next);
+ if (next)
+ WRITE_ONCE(next->pprev, pprev);
+}
+
+/*! Delete the specified hlist_node from its list.
+ * \param[in] n: Node to delete.
+ *
+ * Note that this function leaves the node in hashed state. Use
+ * hlist_del_init() or similar instead to unhash @n.
+ */
+static inline void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = (struct hlist_node *)LLIST_POISON1;
+ n->pprev = (struct hlist_node **)LLIST_POISON2;
+}
+
+/*! Delete the specified hlist_node from its list and initialize.
+ * \param[in] n Node to delete.
+ *
+ * Note that this function leaves the node in unhashed state.
+ */
+static inline void hlist_del_init(struct hlist_node *n)
+{
+ if (!hlist_unhashed(n)) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+/*! add a new entry at the beginning of the hlist.
+ * \param[in] n new entry to be added
+ * \param[in] h hlist head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ WRITE_ONCE(n->next, first);
+ if (first)
+ WRITE_ONCE(first->pprev, &n->next);
+ WRITE_ONCE(h->first, n);
+ WRITE_ONCE(n->pprev, &h->first);
+}
+
+/*! add a new entry before the one specified.
+ * @n: new entry to be added
+ * @next: hlist node to add it before, which must be non-NULL
+ */
+static inline void hlist_add_before(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ WRITE_ONCE(n->pprev, next->pprev);
+ WRITE_ONCE(n->next, next);
+ WRITE_ONCE(next->pprev, &n->next);
+ WRITE_ONCE(*(n->pprev), n);
+}
+
+/*! add a new entry after the one specified
+ * \param[in] n new entry to be added
+ * \param[in] prev hlist node to add it after, which must be non-NULL
+ */
+static inline void hlist_add_behind(struct hlist_node *n,
+ struct hlist_node *prev)
+{
+ WRITE_ONCE(n->next, prev->next);
+ WRITE_ONCE(prev->next, n);
+ WRITE_ONCE(n->pprev, &prev->next);
+
+ if (n->next)
+ WRITE_ONCE(n->next->pprev, &n->next);
+}
+
+/*! create a fake hlist consisting of a single headless node.
+ * \param[in] n Node to make a fake list out of
+ *
+ * This makes @n appear to be its own predecessor on a headless hlist.
+ * The point of this is to allow things like hlist_del() to work correctly
+ * in cases where there is no list.
+ */
+static inline void hlist_add_fake(struct hlist_node *n)
+{
+ n->pprev = &n->next;
+}
+
+/*! Is this node a fake hlist?.
+ * \param[in] h Node to check for being a self-referential fake hlist.
+ */
+static inline bool hlist_fake(struct hlist_node *h)
+{
+ return h->pprev == &h->next;
+}
+
+/*!is node the only element of the specified hlist?.
+ * \param[in] n Node to check for singularity.
+ * \param[in] h Header for potentially singular list.
+ *
+ * Check whether the node is the only node of the head without
+ * accessing head, thus avoiding unnecessary cache misses.
+ */
+static inline bool
+hlist_is_singular_node(struct hlist_node *n, struct hlist_head *h)
+{
+ return !n->next && n->pprev == &h->first;
+}
+
+/*! Move an hlist.
+ * \param[in] old hlist_head for old list.
+ * \param[in] new hlist_head for new list.
+ *
+ * Move a list from one list head to another. Fixup the pprev
+ * reference of the first entry if it exists.
+ */
+static inline void hlist_move_list(struct hlist_head *old,
+ struct hlist_head *_new)
+{
+ _new->first = old->first;
+ if (_new->first)
+ _new->first->pprev = &_new->first;
+ old->first = NULL;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos ; pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+#define hlist_entry_safe(ptr, type, member) \
+ ({ typeof(ptr) ____ptr = (ptr); \
+ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
+ })
+
+/*! iterate over list of given type.
+ * \param[out] pos the type * to use as a loop cursor.
+ * \param[in] head the head for your list.
+ * \param[in] member the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(pos, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
+ pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+/*! iterate over a hlist continuing after current point.
+ * \param[out] pos the type * to use as a loop cursor.
+ * \param[in] member the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(pos, member) \
+ for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\
+ pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+/*! iterate over a hlist continuing from current point.
+ * \param[out] pos the type * to use as a loop cursor.
+ * \param[in] member the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(pos, member) \
+ for (; pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+/*! hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry.
+ * \param[out] pos the type * to use as a loop cursor.
+ * \param[out] n a &struct hlist_node to use as temporary storage
+ * \param[in] head the head for your list.
+ * \param[in] member the name of the hlist_node within the struct
+ */
+#define hlist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
+ pos && ({ n = pos->member.next; 1; }); \
+ pos = hlist_entry_safe(n, typeof(*pos), member))
+
+
/*!
* @}
*/
diff --git a/include/osmocom/core/linuxrbtree.h b/include/osmocom/core/linuxrbtree.h
index d3f9fd12..e15317ec 100644
--- a/include/osmocom/core/linuxrbtree.h
+++ b/include/osmocom/core/linuxrbtree.h
@@ -12,11 +12,6 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301, USA.
-
linux/include/linux/rbtree.h
To use rbtrees you'll have to implement your own insert and search cores.
diff --git a/include/osmocom/core/log2.h b/include/osmocom/core/log2.h
new file mode 100644
index 00000000..8c65768f
--- /dev/null
+++ b/include/osmocom/core/log2.h
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Integer base 2 logarithm calculation
+ *
+ * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#pragma once
+#include <stdint.h>
+
+/* from linux/asm-generic/bitops/{fls,fls64}.h - could later be enhanced
+ * with architecture specific optimized versions */
+
+/**
+ * fls - find last (most-significant) bit set
+ * @x: the word to search
+ *
+ * This is defined the same way as ffs.
+ * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
+ */
+static inline __attribute__((always_inline)) int fls(unsigned int x)
+{
+ int r = 32;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff0000u)) {
+ x <<= 16;
+ r -= 16;
+ }
+ if (!(x & 0xff000000u)) {
+ x <<= 8;
+ r -= 8;
+ }
+ if (!(x & 0xf0000000u)) {
+ x <<= 4;
+ r -= 4;
+ }
+ if (!(x & 0xc0000000u)) {
+ x <<= 2;
+ r -= 2;
+ }
+ if (!(x & 0x80000000u)) {
+ x <<= 1;
+ r -= 1;
+ }
+ return r;
+}
+
+/**
+ * fls64 - find last set bit in a 64-bit word
+ * @x: the word to search
+ *
+ * This is defined in a similar way as the libc and compiler builtin
+ * ffsll, but returns the position of the most significant set bit.
+ *
+ * fls64(value) returns 0 if value is 0 or the position of the last
+ * set bit if value is nonzero. The last (most significant) bit is
+ * at position 64.
+ */
+static inline __attribute__((always_inline)) int fls64(uint64_t x)
+{
+ uint32_t h = x >> 32;
+ if (h)
+ return fls(h) + 32;
+ return fls(x);
+}
+
+/*
+ * non-constant log of base 2 calculators
+ * - the arch may override these in asm/bitops.h if they can be implemented
+ * more efficiently than using fls() and fls64()
+ * - the arch is not required to handle n==0 if implementing the fallback
+ */
+#ifndef CONFIG_ARCH_HAS_ILOG2_U32
+static inline __attribute__((const))
+int __ilog2_u32(uint32_t n)
+{
+ return fls(n) - 1;
+}
+#endif
+
+#ifndef CONFIG_ARCH_HAS_ILOG2_U64
+static inline __attribute__((const))
+int __ilog2_u64(uint64_t n)
+{
+ return fls64(n) - 1;
+}
+#endif
+
+/**
+ * const_ilog2 - log base 2 of 32-bit or a 64-bit constant unsigned value
+ * @n: parameter
+ *
+ * Use this where sparse expects a true constant expression, e.g. for array
+ * indices.
+ */
+#define const_ilog2(n) \
+( \
+ __builtin_constant_p(n) ? ( \
+ (n) < 2 ? 0 : \
+ (n) & (1ULL << 63) ? 63 : \
+ (n) & (1ULL << 62) ? 62 : \
+ (n) & (1ULL << 61) ? 61 : \
+ (n) & (1ULL << 60) ? 60 : \
+ (n) & (1ULL << 59) ? 59 : \
+ (n) & (1ULL << 58) ? 58 : \
+ (n) & (1ULL << 57) ? 57 : \
+ (n) & (1ULL << 56) ? 56 : \
+ (n) & (1ULL << 55) ? 55 : \
+ (n) & (1ULL << 54) ? 54 : \
+ (n) & (1ULL << 53) ? 53 : \
+ (n) & (1ULL << 52) ? 52 : \
+ (n) & (1ULL << 51) ? 51 : \
+ (n) & (1ULL << 50) ? 50 : \
+ (n) & (1ULL << 49) ? 49 : \
+ (n) & (1ULL << 48) ? 48 : \
+ (n) & (1ULL << 47) ? 47 : \
+ (n) & (1ULL << 46) ? 46 : \
+ (n) & (1ULL << 45) ? 45 : \
+ (n) & (1ULL << 44) ? 44 : \
+ (n) & (1ULL << 43) ? 43 : \
+ (n) & (1ULL << 42) ? 42 : \
+ (n) & (1ULL << 41) ? 41 : \
+ (n) & (1ULL << 40) ? 40 : \
+ (n) & (1ULL << 39) ? 39 : \
+ (n) & (1ULL << 38) ? 38 : \
+ (n) & (1ULL << 37) ? 37 : \
+ (n) & (1ULL << 36) ? 36 : \
+ (n) & (1ULL << 35) ? 35 : \
+ (n) & (1ULL << 34) ? 34 : \
+ (n) & (1ULL << 33) ? 33 : \
+ (n) & (1ULL << 32) ? 32 : \
+ (n) & (1ULL << 31) ? 31 : \
+ (n) & (1ULL << 30) ? 30 : \
+ (n) & (1ULL << 29) ? 29 : \
+ (n) & (1ULL << 28) ? 28 : \
+ (n) & (1ULL << 27) ? 27 : \
+ (n) & (1ULL << 26) ? 26 : \
+ (n) & (1ULL << 25) ? 25 : \
+ (n) & (1ULL << 24) ? 24 : \
+ (n) & (1ULL << 23) ? 23 : \
+ (n) & (1ULL << 22) ? 22 : \
+ (n) & (1ULL << 21) ? 21 : \
+ (n) & (1ULL << 20) ? 20 : \
+ (n) & (1ULL << 19) ? 19 : \
+ (n) & (1ULL << 18) ? 18 : \
+ (n) & (1ULL << 17) ? 17 : \
+ (n) & (1ULL << 16) ? 16 : \
+ (n) & (1ULL << 15) ? 15 : \
+ (n) & (1ULL << 14) ? 14 : \
+ (n) & (1ULL << 13) ? 13 : \
+ (n) & (1ULL << 12) ? 12 : \
+ (n) & (1ULL << 11) ? 11 : \
+ (n) & (1ULL << 10) ? 10 : \
+ (n) & (1ULL << 9) ? 9 : \
+ (n) & (1ULL << 8) ? 8 : \
+ (n) & (1ULL << 7) ? 7 : \
+ (n) & (1ULL << 6) ? 6 : \
+ (n) & (1ULL << 5) ? 5 : \
+ (n) & (1ULL << 4) ? 4 : \
+ (n) & (1ULL << 3) ? 3 : \
+ (n) & (1ULL << 2) ? 2 : \
+ 1) : \
+ -1)
+
+/**
+ * ilog2 - log base 2 of 32-bit or a 64-bit unsigned value
+ * @n: parameter
+ *
+ * constant-capable log of base 2 calculation
+ * - this can be used to initialise global variables from constant data, hence
+ * the massive ternary operator construction
+ *
+ * selects the appropriately-sized optimised version depending on sizeof(n)
+ */
+#define ilog2(n) \
+( \
+ __builtin_constant_p(n) ? \
+ const_ilog2(n) : \
+ (sizeof(n) <= 4) ? \
+ __ilog2_u32(n) : \
+ __ilog2_u64(n) \
+ )
diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h
index 139d2916..82e686f2 100644
--- a/include/osmocom/core/logging.h
+++ b/include/osmocom/core/logging.h
@@ -11,15 +11,16 @@
#include <osmocom/core/defs.h>
#include <osmocom/core/linuxlist.h>
-/*! Maximum number of logging contexts */
-#define LOG_MAX_CTX 8
-/*! Maximum number of logging filters */
-#define LOG_MAX_FILTERS 8
+extern struct log_info *osmo_log_info;
#ifndef DEBUG
#define DEBUG
#endif
+#ifdef LIBOSMOCORE_NO_LOGGING
+#undef DEBUG
+#endif
+
#ifdef DEBUG
/*! Log a debug message through the Osmocom logging framework
* \param[in] ss logging subsystem (e.g. \ref DLGLOBAL)
@@ -54,11 +55,19 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
* \param[in] fmt format string
* \param[in] args variable argument list
*/
+#ifndef LIBOSMOCORE_NO_LOGGING
#define LOGPC(ss, level, fmt, args...) \
do { \
+ if (!osmo_log_info) { \
+ logp_stub(__FILE__, __LINE__, 1, fmt, ##args); \
+ break; \
+ } \
if (log_check_level(ss, level)) \
logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args); \
} while(0)
+#else
+#define LOGPC(ss, level, fmt, args...)
+#endif
/*! Log through the Osmocom logging framework with explicit source.
* If caller_file is passed as NULL, __FILE__ and __LINE__ are used
@@ -88,8 +97,16 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
* \param[in] fmt format string
* \param[in] args variable argument list
*/
+#ifndef LIBOSMOCORE_NO_LOGGING
#define LOGPSRCC(ss, level, caller_file, caller_line, cont, fmt, args...) \
do { \
+ if (!osmo_log_info) { \
+ if (caller_file) \
+ logp_stub(caller_file, caller_line, cont, fmt, ##args); \
+ else \
+ logp_stub(__FILE__, __LINE__, cont, fmt, ##args); \
+ break; \
+ } \
if (log_check_level(ss, level)) {\
if (caller_file) \
logp2(ss, level, caller_file, caller_line, cont, fmt, ##args); \
@@ -97,6 +114,9 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
logp2(ss, level, __FILE__, __LINE__, cont, fmt, ##args); \
}\
} while(0)
+#else
+#define LOGPSRCC(ss, level, caller_file, caller_line, cont, fmt, args...)
+#endif
/*! different log levels */
#define LOGL_DEBUG 1 /*!< debugging information */
@@ -105,7 +125,7 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
#define LOGL_ERROR 7 /*!< error condition, requires user action */
#define LOGL_FATAL 8 /*!< fatal, program aborted */
-/* logging levels defined by the library itself */
+/* logging subsystems defined by the library itself */
#define DLGLOBAL -1 /*!< global logging */
#define DLLAPD -2 /*!< LAPD implementation */
#define DLINP -3 /*!< (A-bis) Input sub-system */
@@ -125,7 +145,36 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
#define DLMGCP -17 /*!< Osmocom MGCP */
#define DLJIBUF -18 /*!< Osmocom Jitter Buffer */
#define DLRSPRO -19 /*!< Osmocom Remote SIM Protocol */
-#define OSMO_NUM_DLIB 19 /*!< Number of logging sub-systems in libraries */
+#define DLNS -20 /*!< Osmocom NS layer */
+#define DLBSSGP -21 /*!< Osmocom BSSGP layer */
+#define DLNSDATA -22 /*!< Osmocom NS layer data pdus */
+#define DLNSSIGNAL -23 /*!< Osmocom NS layer signal pdus */
+#define DLIUUP -24 /*!< Osmocom IuUP layer */
+#define DLPFCP -25 /*!< Osmocom Packet Forwarding Control Protocol */
+#define DLCSN1 -26 /*!< CSN.1 (Concrete Syntax Notation 1) codec */
+#define DLM2PA -27 /*!< Osmocom M2PA (libosmo-sigtran) */
+#define DLM2UA -28 /*!< Reserved for future Osmocom M2UA (libosmo-sigtran) */
+#define DLIO -29 /*!< Osmocom IO sub-system */
+#define OSMO_NUM_DLIB 29 /*!< Number of logging sub-systems in libraries */
+
+/* Colors that can be used in log_info_cat.color */
+#define OSMO_LOGCOLOR_NORMAL NULL
+#define OSMO_LOGCOLOR_RED "\033[1;31m"
+#define OSMO_LOGCOLOR_GREEN "\033[1;32m"
+#define OSMO_LOGCOLOR_YELLOW "\033[1;33m"
+#define OSMO_LOGCOLOR_BLUE "\033[1;34m"
+#define OSMO_LOGCOLOR_PURPLE "\033[1;35m"
+#define OSMO_LOGCOLOR_CYAN "\033[1;36m"
+#define OSMO_LOGCOLOR_DARKRED "\033[31m"
+#define OSMO_LOGCOLOR_DARKGREEN "\033[32m"
+#define OSMO_LOGCOLOR_DARKYELLOW "\033[33m"
+#define OSMO_LOGCOLOR_DARKBLUE "\033[34m"
+#define OSMO_LOGCOLOR_DARKPURPLE "\033[35m"
+#define OSMO_LOGCOLOR_DARKCYAN "\033[36m"
+#define OSMO_LOGCOLOR_DARKGREY "\033[1;30m"
+#define OSMO_LOGCOLOR_GREY "\033[37m"
+#define OSMO_LOGCOLOR_BRIGHTWHITE "\033[1;37m"
+#define OSMO_LOGCOLOR_END "\033[0;m"
/*! Configuration of single log category / sub-system */
struct log_category {
@@ -142,11 +191,6 @@ struct log_info_cat {
uint8_t enabled; /*!< is this category enabled or not */
};
-/*! Log context information, passed to filter */
-struct log_context {
- void *ctx[LOG_MAX_CTX+1];
-};
-
/*! Indexes to indicate the object currently acted upon.
* Array indexes for the global \a log_context array. */
enum log_ctx_index {
@@ -155,6 +199,7 @@ enum log_ctx_index {
LOG_CTX_BSC_SUBSCR,
LOG_CTX_VLR_SUBSCR,
LOG_CTX_L1_SAPI,
+ LOG_CTX_GB_NSE,
_LOG_CTX_COUNT
};
@@ -168,9 +213,20 @@ enum log_filter_index {
LOG_FLT_BSC_SUBSCR,
LOG_FLT_VLR_SUBSCR,
LOG_FLT_L1_SAPI,
+ LOG_FLT_GB_NSE,
_LOG_FLT_COUNT
};
+/*! Maximum number of logging contexts */
+#define LOG_MAX_CTX _LOG_CTX_COUNT
+/*! Maximum number of logging filters */
+#define LOG_MAX_FILTERS _LOG_FLT_COUNT
+
+/*! Log context information, passed to filter */
+struct log_context {
+ void *ctx[LOG_MAX_CTX+1];
+};
+
/*! Compatibility with older libosmocore versions */
#define LOG_FILTER_ALL (1<<LOG_FLT_ALL)
/*! Compatibility with older libosmocore versions */
@@ -224,6 +280,7 @@ enum log_target_type {
LOG_TGT_TYPE_STDERR, /*!< stderr logging */
LOG_TGT_TYPE_STRRB, /*!< osmo_strrb-backed logging */
LOG_TGT_TYPE_GSMTAP, /*!< GSMTAP network logging */
+ LOG_TGT_TYPE_SYSTEMD, /*!< systemd journal logging */
};
/*! Whether/how to log the source filename (and line number). */
@@ -241,7 +298,7 @@ enum log_filename_pos {
/*! structure representing a logging target */
struct log_target {
- struct llist_head entry; /*!< linked list */
+ struct llist_head entry; /*!< linked list */
/*! Internal data for filtering */
int filter_map;
@@ -257,6 +314,8 @@ struct log_target {
unsigned int use_color:1;
/*! should log messages be prefixed with a timestamp? */
unsigned int print_timestamp:1;
+ /*! should log messages be prefixed with the logger Thread ID? */
+ unsigned int print_tid:1;
/*! DEPRECATED: use print_filename2 instead. */
unsigned int print_filename:1;
/*! should log messages be prefixed with a category name? */
@@ -269,8 +328,11 @@ struct log_target {
union {
struct {
+ /* direct, blocking output via stdio */
FILE *out;
const char *fname;
+ /* indirect output via write_queue and osmo_select_main() */
+ struct osmo_wqueue *wqueue;
} tgt_file;
struct {
@@ -291,6 +353,10 @@ struct log_target {
const char *ident;
const char *hostname;
} tgt_gsmtap;
+
+ struct {
+ bool raw;
+ } sd_journal;
};
/*! call-back function to be called when the logging framework
@@ -332,6 +398,7 @@ struct log_target {
void logp2(int subsys, unsigned int level, const char *file,
int line, int cont, const char *format, ...)
__attribute__ ((format (printf, 6, 7)));
+void logp_stub(const char *file, int line, int cont, const char *format, ...);
int log_init(const struct log_info *inf, void *talloc_ctx);
void log_fini(void);
int log_check_level(int subsys, unsigned int level);
@@ -342,11 +409,13 @@ int log_set_context(uint8_t ctx, void *value);
/* filter on the targets */
void log_set_all_filter(struct log_target *target, int);
-
+int log_cache_enable(void);
+void log_cache_update(int mapped_subsys, uint8_t enabled, uint8_t level);
void log_set_use_color(struct log_target *target, int);
void log_set_print_extended_timestamp(struct log_target *target, int);
void log_set_print_timestamp(struct log_target *target, int);
-void log_set_print_filename(struct log_target *target, int);
+void log_set_print_tid(struct log_target *target, int);
+void log_set_print_filename(struct log_target *target, int) OSMO_DEPRECATED("Use log_set_print_filename2() instead");
void log_set_print_filename2(struct log_target *target, enum log_filename_type lft);
void log_set_print_filename_pos(struct log_target *target, enum log_filename_pos pos);
void log_set_print_category(struct log_target *target, int);
@@ -372,13 +441,17 @@ struct log_target *log_target_create_gsmtap(const char *host, uint16_t port,
const char *ident,
bool ofd_wq_mode,
bool add_sink);
+struct log_target *log_target_create_systemd(bool raw);
+void log_target_systemd_set_raw(struct log_target *target, bool raw);
int log_target_file_reopen(struct log_target *tgt);
+int log_target_file_switch_to_stream(struct log_target *tgt);
+int log_target_file_switch_to_wqueue(struct log_target *tgt);
int log_targets_reopen(void);
void log_add_target(struct log_target *target);
void log_del_target(struct log_target *target);
-struct log_target *log_target_find(int type, const char *fname);
+struct log_target *log_target_find(enum log_target_type type, const char *fname);
void log_enable_multithread(void);
diff --git a/include/osmocom/core/loggingrb.h b/include/osmocom/core/loggingrb.h
index a9fb4047..6d501466 100644
--- a/include/osmocom/core/loggingrb.h
+++ b/include/osmocom/core/loggingrb.h
@@ -11,10 +11,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/core/mnl.h b/include/osmocom/core/mnl.h
new file mode 100644
index 00000000..11c83530
--- /dev/null
+++ b/include/osmocom/core/mnl.h
@@ -0,0 +1,22 @@
+/*! \file select.h
+ * libmnl integration
+ */
+#pragma once
+
+#include <osmocom/core/select.h>
+#include <libmnl/libmnl.h>
+
+/*! osmocom wrapper around libmnl abstraction of netlink socket */
+struct osmo_mnl {
+ /*! osmo-wrapped netlink file descriptor */
+ struct osmo_fd ofd;
+ /*! libmnl socket abstraction */
+ struct mnl_socket *mnls;
+ /*! call-back called for received netlink messages */
+ mnl_cb_t mnl_cb;
+ /*! opaque data provided by user */
+ void *priv;
+};
+
+struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv);
+void osmo_mnl_destroy(struct osmo_mnl *omnl);
diff --git a/include/osmocom/core/msgb.h b/include/osmocom/core/msgb.h
index 1833a6c1..b05d8b6b 100644
--- a/include/osmocom/core/msgb.h
+++ b/include/osmocom/core/msgb.h
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -74,6 +70,8 @@ extern int msgb_resize_area(struct msgb *msg, uint8_t *area,
int old_size, int new_size);
extern struct msgb *msgb_copy(const struct msgb *msg, const char *name);
extern struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name);
+extern struct msgb *msgb_copy_resize(const struct msgb *msg, uint16_t new_len, const char *name);
+extern struct msgb *msgb_copy_resize_c(const void *ctx, const struct msgb *msg, uint16_t new_len, const char *name);
static int msgb_test_invariant(const struct msgb *msg) __attribute__((pure));
/*! Free all msgbs from a queue built with msgb_enqueue().
@@ -129,13 +127,13 @@ static inline struct msgb *msgb_dequeue_count(struct llist_head *queue,
#endif
/*! obtain L1 header of msgb */
-#define msgb_l1(m) ((void *)(m->l1h))
+#define msgb_l1(m) ((void *)((m)->l1h))
/*! obtain L2 header of msgb */
-#define msgb_l2(m) ((void *)(m->l2h))
+#define msgb_l2(m) ((void *)((m)->l2h))
/*! obtain L3 header of msgb */
-#define msgb_l3(m) ((void *)(m->l3h))
+#define msgb_l3(m) ((void *)((m)->l3h))
/*! obtain L4 header of msgb */
-#define msgb_l4(m) ((void *)(m->l4h))
+#define msgb_l4(m) ((void *)((m)->l4h))
/*! obtain SMS header of msgb */
#define msgb_sms(m) msgb_l4(m)
@@ -148,6 +146,7 @@ static inline struct msgb *msgb_dequeue_count(struct llist_head *queue,
*/
static inline unsigned int msgb_l1len(const struct msgb *msgb)
{
+ OSMO_ASSERT(msgb->l1h);
return msgb->tail - (uint8_t *)msgb_l1(msgb);
}
@@ -160,6 +159,7 @@ static inline unsigned int msgb_l1len(const struct msgb *msgb)
*/
static inline unsigned int msgb_l2len(const struct msgb *msgb)
{
+ OSMO_ASSERT(msgb->l2h);
return msgb->tail - (uint8_t *)msgb_l2(msgb);
}
@@ -172,6 +172,7 @@ static inline unsigned int msgb_l2len(const struct msgb *msgb)
*/
static inline unsigned int msgb_l3len(const struct msgb *msgb)
{
+ OSMO_ASSERT(msgb->l3h);
return msgb->tail - (uint8_t *)msgb_l3(msgb);
}
@@ -184,7 +185,8 @@ static inline unsigned int msgb_l3len(const struct msgb *msgb)
*/
static inline unsigned int msgb_l4len(const struct msgb *msgb)
{
- return msgb->tail - (uint8_t *)msgb_sms(msgb);
+ OSMO_ASSERT(msgb->l4h);
+ return msgb->tail - (uint8_t *)msgb_l4(msgb);
}
/*! determine the length of the header
@@ -238,8 +240,12 @@ static inline int msgb_headroom(const struct msgb *msgb)
static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
{
unsigned char *tmp = msgb->tail;
- if (msgb_tailroom(msgb) < (int) len)
- MSGB_ABORT(msgb, "Not enough tailroom msgb_put (%u < %u)\n",
+ if (OSMO_UNLIKELY(msgb_tailroom(msgb) < (int) len))
+ MSGB_ABORT(msgb, "Not enough tailroom msgb_put"
+ " (allocated %u, head at %u, len %u, tailroom %u < want tailroom %u)\n",
+ msgb->data_len - sizeof(struct msgb),
+ msgb->head - msgb->_data,
+ msgb->len,
msgb_tailroom(msgb), len);
msgb->tail += len;
msgb->len += len;
@@ -282,7 +288,7 @@ static inline void msgb_put_u32(struct msgb *msgb, uint32_t word)
*/
static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len)
{
- if (msgb_length(msgb) < len)
+ if (OSMO_UNLIKELY(msgb_length(msgb) < len))
MSGB_ABORT(msgb, "msgb too small to get %u (len %u)\n",
len, msgb_length(msgb));
msgb->tail -= len;
@@ -334,9 +340,14 @@ static inline uint32_t msgb_get_u32(struct msgb *msgb)
*/
static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
{
- if (msgb_headroom(msgb) < (int) len)
- MSGB_ABORT(msgb, "Not enough headroom msgb_push (%u < %u)\n",
- msgb_headroom(msgb), len);
+ if (OSMO_UNLIKELY(msgb_headroom(msgb) < (int) len))
+ MSGB_ABORT(msgb, "Not enough headroom msgb_push"
+ " (allocated %u, head at %u < want headroom %u, len %u, tailroom %u)\n",
+ msgb->data_len - sizeof(struct msgb),
+ msgb->head - msgb->_data,
+ len,
+ msgb->len,
+ msgb_tailroom(msgb));
msgb->data -= len;
msgb->len += len;
return msgb->data;
@@ -392,7 +403,7 @@ static inline unsigned char *msgb_push_tl(struct msgb *msgb, uint8_t tag)
*/
static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
{
- if (msgb_length(msgb) < len)
+ if (OSMO_UNLIKELY(msgb_length(msgb) < len))
MSGB_ABORT(msgb, "msgb too small to pull %u (len %u)\n",
len, msgb_length(msgb));
msgb->len -= len;
@@ -431,7 +442,7 @@ static inline unsigned char *msgb_pull_to_l2(struct msgb *msg)
/*! remove uint8 from front of message
* \param[in] msgb message buffer
- * \returns 8bit value taken from end of msgb
+ * \returns 8bit value taken from the front of msgb
*/
static inline uint8_t msgb_pull_u8(struct msgb *msgb)
{
@@ -441,7 +452,7 @@ static inline uint8_t msgb_pull_u8(struct msgb *msgb)
/*! remove uint16 from front of message
* \param[in] msgb message buffer
- * \returns 16bit value taken from end of msgb
+ * \returns 16bit value taken from the front of msgb
*/
static inline uint16_t msgb_pull_u16(struct msgb *msgb)
{
@@ -451,7 +462,7 @@ static inline uint16_t msgb_pull_u16(struct msgb *msgb)
/*! remove uint32 from front of message
* \param[in] msgb message buffer
- * \returns 32bit value taken from end of msgb
+ * \returns 32bit value taken from the front of msgb
*/
static inline uint32_t msgb_pull_u32(struct msgb *msgb)
{
@@ -483,9 +494,9 @@ static inline void msgb_reserve(struct msgb *msg, int len)
*/
static inline int msgb_trim(struct msgb *msg, int len)
{
- if (len < 0)
+ if (OSMO_UNLIKELY(len < 0))
MSGB_ABORT(msg, "Negative length is not allowed\n");
- if (len > msg->data_len)
+ if (OSMO_UNLIKELY(len > msg->data_len))
return -1;
msg->len = len;
@@ -515,13 +526,13 @@ static inline int msgb_l3trim(struct msgb *msg, int l3len)
* followed by \ref msgb_reserve in order to create a new \ref msgb with
* user-specified amount of headroom.
*/
-static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, int size, int headroom,
+static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, uint16_t size, uint16_t headroom,
const char *name)
{
- osmo_static_assert(size >= headroom, headroom_bigger);
+ OSMO_ASSERT(size >= headroom);
struct msgb *msg = msgb_alloc_c(ctx, size, name);
- if (msg)
+ if (OSMO_LIKELY(msg))
msgb_reserve(msg, headroom);
return msg;
}
@@ -537,13 +548,13 @@ static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, int size, int
* followed by \ref msgb_reserve in order to create a new \ref msgb with
* user-specified amount of headroom.
*/
-static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
+static inline struct msgb *msgb_alloc_headroom(uint16_t size, uint16_t headroom,
const char *name)
{
- osmo_static_assert(size >= headroom, headroom_bigger);
+ OSMO_ASSERT(size >= headroom);
struct msgb *msg = msgb_alloc(size, name);
- if (msg)
+ if (OSMO_LIKELY(msg))
msgb_reserve(msg, headroom);
return msg;
}
diff --git a/include/osmocom/core/msgfile.h b/include/osmocom/core/msgfile.h
index 800b4311..cfa95905 100644
--- a/include/osmocom/core/msgfile.h
+++ b/include/osmocom/core/msgfile.h
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/core/netdev.h b/include/osmocom/core/netdev.h
new file mode 100644
index 00000000..3238ec33
--- /dev/null
+++ b/include/osmocom/core/netdev.h
@@ -0,0 +1,49 @@
+/*! \file netdev.h
+ * network device (interface) convenience functions. */
+
+#pragma once
+#if (!EMBEDDED)
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+
+struct osmo_netdev;
+
+typedef int (*osmo_netdev_ifupdown_ind_cb_t)(struct osmo_netdev *netdev, bool ifupdown);
+typedef int (*osmo_netdev_dev_name_chg_cb_t)(struct osmo_netdev *netdev, const char *new_dev_name);
+typedef int (*osmo_netdev_mtu_chg_cb_t)(struct osmo_netdev *netdev, unsigned int new_mtu);
+
+struct osmo_netdev *osmo_netdev_alloc(void *ctx, const char *name);
+void osmo_netdev_free(struct osmo_netdev *netdev);
+
+int osmo_netdev_register(struct osmo_netdev *netdev);
+int osmo_netdev_unregister(struct osmo_netdev *netdev);
+bool osmo_netdev_is_registered(struct osmo_netdev *netdev);
+
+const char *osmo_netdev_get_name(const struct osmo_netdev *netdev);
+
+void osmo_netdev_set_priv_data(struct osmo_netdev *netdev, void *priv_data);
+void *osmo_netdev_get_priv_data(struct osmo_netdev *netdev);
+
+int osmo_netdev_set_ifindex(struct osmo_netdev *netdev, unsigned int ifindex);
+unsigned int osmo_netdev_get_ifindex(const struct osmo_netdev *netdev);
+
+const char *osmo_netdev_get_dev_name(const struct osmo_netdev *netdev);
+
+int osmo_netdev_set_netns_name(struct osmo_netdev *netdev, const char *netns);
+const char *osmo_netdev_get_netns_name(const struct osmo_netdev *netdev);
+
+void osmo_netdev_set_ifupdown_ind_cb(struct osmo_netdev *netdev, osmo_netdev_ifupdown_ind_cb_t ifupdown_ind_cb);
+void osmo_netdev_set_dev_name_chg_cb(struct osmo_netdev *netdev, osmo_netdev_dev_name_chg_cb_t dev_name_chg_cb);
+void osmo_netdev_set_mtu_chg_cb(struct osmo_netdev *netdev, osmo_netdev_mtu_chg_cb_t mtu_chg_cb);
+
+int osmo_netdev_add_addr(struct osmo_netdev *netdev, const struct osmo_sockaddr *addr, uint8_t prefixlen);
+int osmo_netdev_add_route(struct osmo_netdev *netdev, const struct osmo_sockaddr *dst_addr,
+ uint8_t dst_prefixlen, const struct osmo_sockaddr *gw_addr);
+int osmo_netdev_ifupdown(struct osmo_netdev *netdev, bool ifupdown);
+
+#endif /* (!EMBEDDED) */
+/*! @} */
diff --git a/include/osmocom/core/netns.h b/include/osmocom/core/netns.h
new file mode 100644
index 00000000..5bbf2242
--- /dev/null
+++ b/include/osmocom/core/netns.h
@@ -0,0 +1,24 @@
+/*! \file netns.h
+ * Network namespace convenience functions. */
+
+#pragma once
+#if (!EMBEDDED)
+
+#if defined(__linux__)
+
+#include <signal.h>
+
+struct osmo_netns_switch_state {
+ sigset_t prev_sigmask;
+ int prev_nsfd;
+};
+
+int osmo_netns_open_fd(const char *name);
+int osmo_netns_switch_enter(int nsfd, struct osmo_netns_switch_state *state);
+int osmo_netns_switch_exit(struct osmo_netns_switch_state *state);
+
+
+#endif /* defined(__linux__) */
+
+#endif /* (!EMBEDDED) */
+/*! @} */
diff --git a/include/osmocom/core/osmo_io.h b/include/osmocom/core/osmo_io.h
new file mode 100644
index 00000000..6f4dfa8a
--- /dev/null
+++ b/include/osmocom/core/osmo_io.h
@@ -0,0 +1,231 @@
+/*! \file osmo_io.h
+ * io(_uring) abstraction osmo fd compatibility
+ */
+
+#pragma once
+
+#include <sys/socket.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/utils.h>
+
+/*! \defgroup osmo_io Osmocom I/O interface
+ * @{
+ *
+ * osmo_io is the new (2023) interface for performing asynchronous I/O.
+ * osmo_io encapsulates asynchronous, non-blocking I/O to sockets or other file descriptors
+ * with a submission/completion model.
+ *
+ * For writes, the API user submits write requests, and receives
+ * completion call-backs once the write completes.
+ *
+ * For reads, the API user specifies the size (and headroom) for message buffers, and osmo_io
+ * internally allocates msgb's accordingly. Whenever data arrives at the socket/file descriptor,
+ * osmo_io reads the data into such a msgb and hands it to a read-completion call-back provided
+ * by the API user.
+ *
+ * A given socket/file descriptor is represented by struct osmo_io_fd. osmo_io_fd are named,
+ * i.e. the API user can provide a meaningful name describing the purpose (such as protocol/interface or the
+ * name of the remote peer). This allows osmo_io to log any related [error] messages using this name as
+ * context.
+ *
+ * When implementing some SOCK_STREAM / SOCK_SEQPACKET based client/server transports (such as those on top
+ * of TCP or SCTP), you are most likely better off using the osmo_stream_cli / osmo_stream_srv abstractions
+ * provided by libosmo-netif. They in turn can be used in an osmo_io mode, see the respective documentation.
+ *
+ * If you use osmo_io_fd directly, the life-cycle usually will look as follows:
+ *
+ * 1. open some socket and bind and/or connect it
+ * 2. Allocate an osmo_io_fd using osmo_iofd_setup(), configuring the mode and specifying the call-backs
+ * 3. Registering it with osmo_iofd_register(), which enables reading
+ * 4. Handle inbound data via {read,recvfrom,recvmsg} call-backs; write to it using
+ * osmo_iofd_{write,sendto_sendmsg}_msg()
+ * 5. Eventually un-register it using osmo_iofd_unregister(). Afterwards, you can re-cycle the iofd by
+ * calling osmo_iofd_register() with a new file-descriptor, or free it using osmo_iofd_free().
+ *
+ * \file osmo_io.h */
+
+/*! log macro used for logging information related to the osmo_io_fd.
+ * \param[in] iofd osmo_io_fd about which we're logging
+ * \param[in] level log-level (LOGL_DEBUG, LOGL_INFO, LOGL_NOTICE, LOGL_ERROR, LOGL_FATAL)
+ * \param[in] fmt printf-style format string
+ * \param[in] args arguments to the format string
+ */
+#define LOGPIO(iofd, level, fmt, args...) \
+ LOGP(DLIO, level, "iofd(%s)" fmt, iofd->name, ## args)
+
+struct osmo_io_fd;
+
+/*! The _mode_ of an osmo_io_fd determines if read/write, recvfrom/sendmsg or recvmsg/sendmsg semantics are
+ * used. */
+enum osmo_io_fd_mode {
+ /*! use read() / write() semantics with read_cb/write_cb in osmo_io_ops */
+ OSMO_IO_FD_MODE_READ_WRITE,
+ /*! use recvfrom() / sendto() semantics with recvfrom_cb/sendto_cb in osmo_io_ops */
+ OSMO_IO_FD_MODE_RECVFROM_SENDTO,
+ /*! emulate recvmsg() / sendmsg() semantics with recvmsg_cb/sendto_cb in osmo_io_ops */
+ OSMO_IO_FD_MODE_RECVMSG_SENDMSG,
+};
+
+/*! The back-end used by osmo_io. There can be multiple different back-ends available on a given system;
+ * only one of it is used for all I/O performed via osmo_io in one given process. */
+enum osmo_io_backend {
+ /*! classic back-end using poll(2) and direct read/write/recvfrom/sendto/recvmsg/sendmsg syscalls */
+ OSMO_IO_BACKEND_POLL,
+ /*! back-end using io_uring to perform efficient I/O and reduce syscall overhead */
+ OSMO_IO_BACKEND_IO_URING,
+};
+
+extern const struct value_string osmo_io_backend_names[];
+/*! return the string name of an osmo_io_backend */
+static inline const char *osmo_io_backend_name(enum osmo_io_backend val)
+{ return get_value_string(osmo_io_backend_names, val); }
+
+extern const struct value_string osmo_iofd_mode_names[];
+/*! return the string name of an osmo_io_mode */
+static inline const char *osmo_iofd_mode_name(enum osmo_io_fd_mode val)
+{ return get_value_string(osmo_iofd_mode_names, val); }
+
+/*! I/O operations (call-back functions) related to an osmo_io_fd */
+struct osmo_io_ops {
+ /* mode OSMO_IO_FD_MODE_READ_WRITE: */
+ struct {
+ /*! completion call-back function when something was read from fd. Only valid in
+ * OSMO_IO_FD_MODE_READ_WRITE.
+ * \param[in] iofd osmo_io_fd for which read() has completed.
+ * \param[in] res return value of the read() call, or -errno in case of error.
+ * \param[in] msg message buffer containing the read data. Ownership is transferred to the
+ * call-back, and it must make sure to msgb_free() it eventually! */
+ void (*read_cb)(struct osmo_io_fd *iofd, int res, struct msgb *msg);
+
+ /*! completion call-back function when write issued via osmo_iofd_write_msgb() has completed
+ * on fd. Only valid in OSMO_IO_FD_MODE_READ_WRITE.
+ * \param[in] iofd on which a write() has completed.
+ * \param[in] res return value of the write() call, or -errno in case of error.
+ * \param[in] msg message buffer whose write has completed. Ownership is *not* transferred to the
+ * call-back; it is automatically freed after the call-back terminates! */
+ void (*write_cb)(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg);
+
+ /*! optional call-back function to segment the data at message boundaries.
+ * \param[in] msg message buffer whose data is to be segmented
+ * \returns See full function description.
+ *
+ * This is useful when message boundaries are to be preserved over a SOCK_STREAM transport
+ * socket like TCP. Can be NULL for any application not requiring de-segmentation of
+ * received data.
+ *
+ * The call-back needs to return the size of the next message. If it returns
+ * -EAGAIN or a value larger than msgb_length() (message is incomplete)
+ * osmo_io will wait for more data to be read. Other negative values
+ * cause the msg to be discarded.
+ * If a full message was received (segmentation_cb() returns a value <= msgb_length())
+ * the msgb will be trimmed to size by osmo_io and forwarded to the read call-back. Any
+ * parsing done to the msgb by segmentation_cb() will be preserved for the read_cb()
+ * (e.g. setting lxh or msgb->cb).
+ *
+ * Only one (or none) of both segmentation_cb and segmentation_cb2 shall be set.
+ * Having both set will be considered an error during iofd setup. */
+ int (*segmentation_cb)(struct msgb *msg);
+
+ /*! optional call-back function to segment the data at message boundaries.
+ * \param[in] iofd handling msg
+ * \param[in] msg message buffer whose data is to be segmented
+ * \returns See full function description.
+ *
+ * Same as segmentation_cb above, with an extra parameter to have access to the iofd and its
+ * related functionalities (eg data pointer). This is useful for users requiring to store
+ * global state or access external objects while segmenting.
+ *
+ * The provided iofd shall not be freed by the user during the callback.
+ *
+ * Only one (or none) of both segmentation_cb and segmentation_cb2 shall be set.
+ * Having both set will be considered an error during iofd setup. */
+ int (*segmentation_cb2)(struct osmo_io_fd *iofd, struct msgb *msg);
+ };
+
+ /* mode OSMO_IO_FD_MODE_RECVFROM_SENDTO: */
+ struct {
+ /*! completion call-back function when recvfrom(2) has completed.
+ * Only valid in OSMO_IO_FD_MODE_RECVFROM_SENDTO.
+ * \param[in] iofd osmo_io_fd for which recvfrom() has completed.
+ * \param[in] res return value of the recvfrom() call, or -errno in case of error.
+ * \param[in] msg message buffer containing the read data. Ownership is transferred to the
+ * call-back, and it must make sure to msgb_free() it eventually!
+ * \param[in] saddr socket-address of sender from which data was received. */
+ void (*recvfrom_cb)(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg,
+ const struct osmo_sockaddr *saddr);
+ /*! completion call-back function when sendto() issued via osmo_iofd_sendto_msgb() has
+ * completed on fd. Only valid in OSMO_IO_FD_MODE_RECVFROM_SENDTO.
+ * \param[in] iofd on which a sendto() has completed.
+ * \param[in] res return value of the sendto() call, or -errno in case of error.
+ * \param[in] msg message buffer whose write has completed. Ownership is *not* transferred to the
+ * call-back; it is automatically freed after the call-back terminates!
+ * \param[in] daddr socket-address of destination to which data was sent. */
+ void (*sendto_cb)(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg,
+ const struct osmo_sockaddr *daddr);
+ };
+
+ /* mode OSMO_IO_FD_MODE_RECVMSG_SENDMSG: */
+ struct {
+ /*! completion call-back function when recvmsg(2) has completed.
+ * Only valid in OSMO_IO_FD_MODE_RECVMSG_SENDMSG.
+ * \param[in] iofd osmo_io_fd for which recvmsg() has completed.
+ * \param[in] res return value of the recvmsg() call, or -errno in case of error.
+ * \param[in] msg message buffer containing the read data. Ownership is transferred to the
+ * call-back, and it must make sure to msgb_free() it eventually!
+ * \param[in] msgh msghdr containing metadata related to the recvmsg call. Only valid until
+ * call-back ends. */
+ void (*recvmsg_cb)(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg, const struct msghdr *msgh);
+ /*! completion call-back function when sendmsg() issued via osmo_iofd_sendmsg_msgb() has
+ * completed on fd. Only valid in Only valid in OSMO_IO_FD_MODE_RECVMSG_SENDMSG.
+ * \param[in] iofd on which a sendmsg() has completed.
+ * \param[in] res return value of the sendmsg() call, or -errno in case of error.
+ * \param[in] msg message buffer whose write has completed. Ownership is *not* transferred to the
+ * call-back; it is automatically freed after the call-back terminates! */
+ void (*sendmsg_cb)(struct osmo_io_fd *iofd, int res, struct msgb *msg);
+ };
+};
+
+void osmo_iofd_init(void);
+
+struct osmo_io_fd *osmo_iofd_setup(const void *ctx, int fd, const char *name,
+ enum osmo_io_fd_mode mode, const struct osmo_io_ops *ioops, void *data);
+int osmo_iofd_set_cmsg_size(struct osmo_io_fd *iofd, size_t cmsg_size);
+int osmo_iofd_register(struct osmo_io_fd *iofd, int fd);
+int osmo_iofd_unregister(struct osmo_io_fd *iofd);
+unsigned int osmo_iofd_txqueue_len(struct osmo_io_fd *iofd);
+void osmo_iofd_txqueue_clear(struct osmo_io_fd *iofd);
+int osmo_iofd_close(struct osmo_io_fd *iofd);
+void osmo_iofd_free(struct osmo_io_fd *iofd);
+
+void osmo_iofd_notify_connected(struct osmo_io_fd *iofd);
+
+int osmo_iofd_write_msgb(struct osmo_io_fd *iofd, struct msgb *msg);
+int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendto_flags,
+ const struct osmo_sockaddr *dest);
+int osmo_iofd_sendmsg_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendmsg_flags,
+ const struct msghdr *msgh);
+
+void osmo_iofd_set_alloc_info(struct osmo_io_fd *iofd, unsigned int size, unsigned int headroom);
+void osmo_iofd_set_txqueue_max_length(struct osmo_io_fd *iofd, unsigned int size);
+void *osmo_iofd_get_data(const struct osmo_io_fd *iofd);
+void osmo_iofd_set_data(struct osmo_io_fd *iofd, void *data);
+
+unsigned int osmo_iofd_get_priv_nr(const struct osmo_io_fd *iofd);
+void osmo_iofd_set_priv_nr(struct osmo_io_fd *iofd, unsigned int priv_nr);
+
+int osmo_iofd_get_fd(const struct osmo_io_fd *iofd);
+const char *osmo_iofd_get_name(const struct osmo_io_fd *iofd);
+void osmo_iofd_set_name(struct osmo_io_fd *iofd, const char *name);
+
+int osmo_iofd_set_ioops(struct osmo_io_fd *iofd, const struct osmo_io_ops *ioops);
+void osmo_iofd_get_ioops(struct osmo_io_fd *iofd, struct osmo_io_ops *ioops);
+
+/*! @} */
diff --git a/include/osmocom/core/prim.h b/include/osmocom/core/prim.h
index 99eabff0..8e6b436d 100644
--- a/include/osmocom/core/prim.h
+++ b/include/osmocom/core/prim.h
@@ -30,7 +30,11 @@ enum osmo_prim_operation {
PRIM_OP_CONFIRM, /*!< confirm */
};
-extern const struct value_string osmo_prim_op_names[5];
+extern const struct value_string osmo_prim_op_names[];
+static inline const char *osmo_prim_operation_name(enum osmo_prim_operation val)
+{
+ return get_value_string(osmo_prim_op_names, val);
+}
/*!< The upper 8 byte of the technology, the lower 24 bits for the SAP */
#define _SAP_GSM_SHIFT 24
diff --git a/include/osmocom/core/rate_ctr.h b/include/osmocom/core/rate_ctr.h
index f7e6e225..0a3eb2ce 100644
--- a/include/osmocom/core/rate_ctr.h
+++ b/include/osmocom/core/rate_ctr.h
@@ -61,7 +61,9 @@ struct rate_ctr_group {
const struct rate_ctr_group_desc *desc;
/*! The index of this ctr_group within its class */
unsigned int idx;
- /*! Actual counter structures below */
+ /*! Optional string-based identifier to be used instead of index at report time */
+ char *name;
+ /*! Actual counter structures below. Don't access it directly, use APIs below! */
struct rate_ctr ctr[0];
};
@@ -73,6 +75,9 @@ static inline void rate_ctr_group_upd_idx(struct rate_ctr_group *grp, unsigned i
{
grp->idx = idx;
}
+void rate_ctr_group_set_name(struct rate_ctr_group *grp, const char *name);
+
+struct rate_ctr *rate_ctr_group_get_ctr(struct rate_ctr_group *grp, unsigned int idx);
void rate_ctr_group_free(struct rate_ctr_group *grp);
@@ -81,6 +86,15 @@ void rate_ctr_group_free(struct rate_ctr_group *grp);
* \param inc quantity to increment \a ctr by */
void rate_ctr_add(struct rate_ctr *ctr, int inc);
+/*! Increment the counter by \a inc
+ * \param ctrg \ref rate_ctr_group of counter
+ * \param idx index into \a ctrg counter group
+ * \param inc quantity to increment \a ctr by */
+static inline void rate_ctr_add2(struct rate_ctr_group *ctrg, unsigned int idx, int inc)
+{
+ rate_ctr_add(rate_ctr_group_get_ctr(ctrg, idx), inc);
+}
+
/*! Increment the counter by 1
* \param ctr \ref rate_ctr to increment */
static inline void rate_ctr_inc(struct rate_ctr *ctr)
@@ -93,7 +107,7 @@ static inline void rate_ctr_inc(struct rate_ctr *ctr)
* \param idx index into \a ctrg counter group */
static inline void rate_ctr_inc2(struct rate_ctr_group *ctrg, unsigned int idx)
{
- rate_ctr_inc(&ctrg->ctr[idx]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg, idx));
}
@@ -116,4 +130,7 @@ int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg,
int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data);
+void rate_ctr_reset(struct rate_ctr *ctr);
+void rate_ctr_group_reset(struct rate_ctr_group *ctrg);
+
/*! @} */
diff --git a/include/osmocom/core/select.h b/include/osmocom/core/select.h
index 92904e2f..fc148512 100644
--- a/include/osmocom/core/select.h
+++ b/include/osmocom/core/select.h
@@ -7,6 +7,7 @@
#include <osmocom/core/linuxlist.h>
#include <stdbool.h>
#include <time.h>
+#include <signal.h>
/*! \defgroup select Select loop abstraction
* @{
@@ -18,6 +19,8 @@
#define OSMO_FD_WRITE 0x0002
/*! Indicate interest in exceptions from the file descriptor */
#define OSMO_FD_EXCEPT 0x0004
+/*! Used as when_mask in osmo_fd_update_when() */
+#define OSMO_FD_MASK 0xFFFF
/* legacy naming dating back to early OpenBSC / bsc_hack of 2008 */
#define BSC_FD_READ OSMO_FD_READ
@@ -46,6 +49,24 @@ void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when,
int (*cb)(struct osmo_fd *fd, unsigned int what),
void *data, unsigned int priv_nr);
+void osmo_fd_update_when(struct osmo_fd *ofd, unsigned int when_mask, unsigned int when);
+
+static inline void osmo_fd_read_enable(struct osmo_fd *ofd) {
+ osmo_fd_update_when(ofd, OSMO_FD_MASK, OSMO_FD_READ);
+}
+
+static inline void osmo_fd_read_disable(struct osmo_fd *ofd) {
+ osmo_fd_update_when(ofd, ~OSMO_FD_READ, 0);
+}
+
+static inline void osmo_fd_write_enable(struct osmo_fd *ofd) {
+ osmo_fd_update_when(ofd, OSMO_FD_MASK, OSMO_FD_WRITE);
+}
+
+static inline void osmo_fd_write_disable(struct osmo_fd *ofd) {
+ osmo_fd_update_when(ofd, ~OSMO_FD_WRITE, 0);
+}
+
bool osmo_fd_is_registered(struct osmo_fd *fd);
int osmo_fd_register(struct osmo_fd *fd);
void osmo_fd_unregister(struct osmo_fd *fd);
@@ -68,4 +89,24 @@ int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
const struct timespec *interval);
int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data);
+/* signalfd integration */
+struct osmo_signalfd;
+struct signalfd_siginfo;
+
+typedef void osmo_signalfd_cb(struct osmo_signalfd *osfd, const struct signalfd_siginfo *fdsi);
+
+struct osmo_signalfd {
+ struct osmo_fd ofd;
+ sigset_t sigset;
+ osmo_signalfd_cb *cb;
+ void *data;
+};
+
+struct osmo_signalfd *
+osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data);
+
+void osmo_select_shutdown_request(void);
+int osmo_select_shutdown_requested(void);
+bool osmo_select_shutdown_done(void);
+
/*! @} */
diff --git a/include/osmocom/core/sercomm.h b/include/osmocom/core/sercomm.h
index 072f4d9c..38e6271c 100644
--- a/include/osmocom/core/sercomm.h
+++ b/include/osmocom/core/sercomm.h
@@ -2,8 +2,7 @@
* Osmocom Sercomm HDLC (de)multiplex.
*/
-#ifndef _SERCOMM_H
-#define _SERCOMM_H
+#pragma once
#include <osmocom/core/msgb.h>
@@ -110,5 +109,3 @@ static inline struct msgb *osmo_sercomm_alloc_msgb(unsigned int len)
}
/*! @} */
-
-#endif /* _SERCOMM_H */
diff --git a/include/osmocom/core/serial.h b/include/osmocom/core/serial.h
index 39614a47..0ac29681 100644
--- a/include/osmocom/core/serial.h
+++ b/include/osmocom/core/serial.h
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \defgroup serial Utility functions to deal with serial ports
@@ -32,5 +28,6 @@ int osmo_serial_init(const char *dev, speed_t baudrate);
int osmo_serial_set_baudrate(int fd, speed_t baudrate);
int osmo_serial_set_custom_baudrate(int fd, int baudrate);
int osmo_serial_clear_custom_baudrate(int fd);
+int osmo_serial_speed_t(unsigned int baudrate, speed_t *speed);
/*! @} */
diff --git a/include/osmocom/core/sockaddr_str.h b/include/osmocom/core/sockaddr_str.h
index d47b2a4e..8ec514c7 100644
--- a/include/osmocom/core/sockaddr_str.h
+++ b/include/osmocom/core/sockaddr_str.h
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -31,12 +27,14 @@
#include <stdint.h>
#include <stdbool.h>
#include <arpa/inet.h>
+#include <osmocom/core/defs.h>
struct in_addr;
struct in6_addr;
struct sockaddr_storage;
struct sockaddr_in;
struct sockaddr_in6;
+struct osmo_sockaddr;
/*! \defgroup sockaddr_str IP address/port utilities.
* @{
@@ -61,28 +59,46 @@ struct osmo_sockaddr_str {
* struct osmo_sockaddr_str *my_sockaddr_str = ...;
* printf("got " OSMO_SOCKADDR_STR_FMT "\n", OSMO_SOCKADDR_STR_FMT_ARGS(my_sockaddr_str));
*/
-#define OSMO_SOCKADDR_STR_FMT "%s:%u"
-#define OSMO_SOCKADDR_STR_FMT_ARGS(R) ((R)? (R)->ip : "NULL"), ((R)? (R)->port : 0)
+#define OSMO_SOCKADDR_STR_FMT "%s%s%s:%u"
+#define OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL(R) \
+ ((R)->af == AF_INET6) ? "[" : "", \
+ (R)->ip, \
+ ((R)->af == AF_INET6) ? "]" : "", \
+ (R)->port
+#define OSMO_SOCKADDR_STR_FMT_ARGS(R) \
+ ((R) && (R)->af == AF_INET6) ? "[" : "", \
+ (R) ? (R)->ip : "NULL", \
+ ((R) && (R)->af == AF_INET6) ? "]" : "", \
+ (R) ? (R)->port : 0
bool osmo_sockaddr_str_is_set(const struct osmo_sockaddr_str *sockaddr_str);
bool osmo_sockaddr_str_is_nonzero(const struct osmo_sockaddr_str *sockaddr_str);
+int osmo_sockaddr_str_cmp(const struct osmo_sockaddr_str *a, const struct osmo_sockaddr_str *b);
int osmo_sockaddr_str_from_str(struct osmo_sockaddr_str *sockaddr_str, const char *ip, uint16_t port);
+int osmo_sockaddr_str_from_str2(struct osmo_sockaddr_str *sockaddr_str, const char *ip);
int osmo_sockaddr_str_from_in_addr(struct osmo_sockaddr_str *sockaddr_str, const struct in_addr *addr, uint16_t port);
int osmo_sockaddr_str_from_in6_addr(struct osmo_sockaddr_str *sockaddr_str, const struct in6_addr *addr, uint16_t port);
int osmo_sockaddr_str_from_32(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port);
-int osmo_sockaddr_str_from_32n(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port);
+int osmo_sockaddr_str_from_32h(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port);
int osmo_sockaddr_str_from_sockaddr_in(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in *src);
int osmo_sockaddr_str_from_sockaddr_in6(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in6 *src);
int osmo_sockaddr_str_from_sockaddr(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_storage *src);
+int osmo_sockaddr_str_from_osa(struct osmo_sockaddr_str *sockaddr_str, const struct osmo_sockaddr *src);
int osmo_sockaddr_str_to_in_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in_addr *dst);
int osmo_sockaddr_str_to_in6_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in6_addr *dst);
int osmo_sockaddr_str_to_32(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip);
-int osmo_sockaddr_str_to_32n(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip);
+int osmo_sockaddr_str_to_32h(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip);
int osmo_sockaddr_str_to_sockaddr_in(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in *dst);
int osmo_sockaddr_str_to_sockaddr_in6(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in6 *dst);
int osmo_sockaddr_str_to_sockaddr(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_storage *dst);
+int osmo_sockaddr_str_to_osa(const struct osmo_sockaddr_str *sockaddr_str, struct osmo_sockaddr *dst);
+
+int osmo_sockaddr_str_from_32n(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port)
+ OSMO_DEPRECATED("osmo_sockaddr_str_from_32n() actually uses *host* byte order. Use osmo_sockaddr_str_from_32h() instead");
+int osmo_sockaddr_str_to_32n(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip)
+ OSMO_DEPRECATED("osmo_sockaddr_str_to_32n() actually uses *host* byte order. Use osmo_sockaddr_str_to_32h() instead");
/*! @} */
diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h
index e26ca0d3..ea73cda8 100644
--- a/include/osmocom/core/socket.h
+++ b/include/osmocom/core/socket.h
@@ -2,6 +2,7 @@
* Osmocom socket convenience functions. */
#pragma once
+#if (!EMBEDDED)
/*! \defgroup socket Socket convenience functions
* @{
@@ -11,16 +12,89 @@
#include <stdbool.h>
#include <stddef.h>
-#if (!EMBEDDED)
#include <arpa/inet.h>
+#include <osmocom/core/defs.h>
+
+/*! maximum number of local or remote addresses supported by an osmo_sock instance */
+#define OSMO_SOCK_MAX_ADDRS 32
+
/*! maximum length of a socket name ("r=1.2.3.4:123<->l=5.6.7.8:987") */
#define OSMO_SOCK_NAME_MAXLEN (2 + INET6_ADDRSTRLEN + 1 + 5 + 3 + 2 + INET6_ADDRSTRLEN + 1 + 5 + 1)
-#endif
+
+/*! maximum length of a multi-address socket peer (endpoint) name: (5.6.7.8|::9):987
+ * char '(' + OSMO_STREAM_MAX_ADDRS - 1 addr separators + chars "):" + port buffer + char '\0'
+ */
+#define OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN (INET6_ADDRSTRLEN * OSMO_SOCK_MAX_ADDRS + INET6_ADDRSTRLEN + 2 + 6 + 1)
+/*! maximum length of a multia-address socket name ("r=(::2|1.2.3.4):123<->l=(5.6.7.8|::9):987") */
+#define OSMO_SOCK_MULTIADDR_NAME_MAXLEN (OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN + 7)
+
struct sockaddr_in;
struct sockaddr;
struct osmo_fd;
+struct sctp_paddrinfo;
+
+struct osmo_sockaddr {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_storage sas;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } u;
+};
+
+int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen);
+int osmo_sockaddr_is_any(const struct osmo_sockaddr *addr);
+
+/*! Return the size of the variant used in the address
+ * NOTE: This does not return the size of the in{,6}_addr, but rather the size of the
+ * surrounding sockaddr_in{,6}.
+ * \param[in] addr the osmo_sockaddr to get the size of
+ * \return the size of the struct variant being used. If the value in sa_family is unsupported it will return
+ * the size of struct osmo_sockaddr. Returns 0 if addr is NULL. This way it can simply be a wrapper for sendto()
+ * which can be called with NULL/0 for dest_addr / addrlen (and then behaves like a send() call).
+ */
+static inline socklen_t osmo_sockaddr_size(const struct osmo_sockaddr *addr)
+{
+ if (!addr)
+ return 0;
+
+ switch (addr->u.sa.sa_family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ default:
+ return sizeof(struct osmo_sockaddr);
+ }
+}
+
+unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
+ const struct sockaddr *sa);
+size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
+ const struct sockaddr_in *sin);
+
+const char *osmo_sockaddr_ntop(const struct sockaddr *sa, char *dst);
+uint16_t osmo_sockaddr_port(const struct sockaddr *sa);
+void osmo_sockaddr_set_port(struct sockaddr *sa, uint16_t port);
+
+int osmo_sockaddr_local_ip(struct osmo_sockaddr *local_ip,
+ const struct osmo_sockaddr *remote_ip);
+int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
+ const struct osmo_sockaddr *b);
+
+int osmo_sockaddr_to_octets(uint8_t *dst, size_t dst_maxlen, const struct osmo_sockaddr *os);
+int osmo_sockaddr_from_octets(struct osmo_sockaddr *os, const void *src, size_t src_len);
+int osmo_sockaddr_from_str_and_uint(struct osmo_sockaddr *osa_out, const char *ipstr, uint16_t port);
+
+int osmo_sockaddr_netmask_to_prefixlen(const struct osmo_sockaddr *addr);
+
+const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *sockaddr);
+char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len,
+ const struct osmo_sockaddr *sockaddr);
+int osmo_sockaddr_to_str_buf2(char *buf, size_t buf_len, const struct osmo_sockaddr *sockaddr);
+char *osmo_sockaddr_to_str_c(void *ctx, const struct osmo_sockaddr *sockaddr);
/* flags for osmo_sock_init. */
/*! connect the socket to a remote peer */
@@ -36,8 +110,14 @@ struct osmo_fd;
/*! use SO_REUSEADDR on UDP ports (required for multicast) */
#define OSMO_SOCK_F_UDP_REUSEADDR (1 << 5)
-/*! maximum number of local or remote addresses supported by an osmo_sock instance */
-#define OSMO_SOCK_MAX_ADDRS 32
+/*! use OSMO_SOCK_F_DSCP(x) to set IP DSCP 'x' for packets transmitted on the socket */
+#define OSMO_SOCK_F_DSCP(x) (((x)&0x3f) << 24)
+#define GET_OSMO_SOCK_F_DSCP(f) (((f) >> 24) & 0x3f)
+
+/*! use OSMO_SOCK_F_PRIO(x) to set priority 'x' for packets transmitted on the socket */
+#define OSMO_SOCK_F_PRIO(x) (((x)&0xff) << 16)
+#define GET_OSMO_SOCK_F_PRIO(f) (((f) >> 16) & 0xff)
+
int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
const char *host, uint16_t port, unsigned int flags);
@@ -46,9 +126,48 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
const char *local_host, uint16_t local_port,
const char *remote_host, uint16_t remote_port, unsigned int flags);
+struct osmo_sock_init2_multiaddr_pars {
+ union {
+ struct {
+ uint8_t version; /* set to 0 */
+ struct {
+ bool set;
+ bool abort_on_failure;
+ uint32_t value;
+ } sockopt_auth_supported;
+ struct {
+ bool set;
+ bool abort_on_failure;
+ uint32_t value;
+ } sockopt_asconf_supported;
+ struct {
+ bool set;
+ bool abort_on_failure;
+ bool num_ostreams_present;
+ bool max_instreams_present;
+ bool max_attempts_present;
+ bool max_init_timeo_present;
+ uint16_t num_ostreams_value;
+ uint16_t max_instreams_value;
+ uint16_t max_attempts_value;
+ uint16_t max_init_timeo_value;
+ } sockopt_initmsg;
+ } sctp;
+ };
+};
int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
- const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port, unsigned int flags);
+ const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port, unsigned int flags)
+ OSMO_DEPRECATED_OUTSIDE("Use osmo_sock_init2_multiaddr2() instead");
+int osmo_sock_init2_multiaddr2(uint16_t family, uint16_t type, uint8_t proto,
+ const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
+ const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port,
+ unsigned int flags, struct osmo_sock_init2_multiaddr_pars *pars);
+
+int osmo_sock_init_osa(uint16_t type, uint8_t proto,
+ const struct osmo_sockaddr *local,
+ const struct osmo_sockaddr *remote,
+ unsigned int flags);
int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
const char *host, uint16_t port, unsigned int flags);
@@ -57,16 +176,14 @@ int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
const char *local_host, uint16_t local_port,
const char *remote_host, uint16_t remote_port, unsigned int flags);
+int osmo_sock_init_osa_ofd(struct osmo_fd *ofd, int type, int proto,
+ const struct osmo_sockaddr *local,
+ const struct osmo_sockaddr *remote,
+ unsigned int flags);
+
int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
uint8_t proto, unsigned int flags);
-int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen);
-
-unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
- const struct sockaddr *sa);
-size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
- const struct sockaddr_in *sin);
-
int osmo_sock_unix_init(uint16_t type, uint8_t proto,
const char *socket_path, unsigned int flags);
@@ -83,12 +200,27 @@ int osmo_sock_get_local_ip_port(int fd, char *port, size_t len);
int osmo_sock_get_remote_ip(int fd, char *host, size_t len);
int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len);
+int osmo_sock_multiaddr_get_ip_and_port(int fd, int ip_proto, char *ip, size_t *ip_cnt, size_t ip_len,
+ char *port, size_t port_len, bool local);
+int osmo_multiaddr_ip_and_port_snprintf(char *str, size_t str_len,
+ const char *ip, size_t ip_cnt, size_t ip_len,
+ const char *portbuf);
+int osmo_sock_multiaddr_get_name_buf(char *str, size_t str_len, int fd, int sk_proto);
+int osmo_sock_multiaddr_add_local_addr(int sfd, const char **addrs, size_t addrs_cnt);
+int osmo_sock_multiaddr_del_local_addr(int sfd, const char **addrs, size_t addrs_cnt);
int osmo_sock_mcast_loop_set(int fd, bool enable);
int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl);
int osmo_sock_mcast_all_set(int fd, bool enable);
+int osmo_sock_mcast_iface_set(int fd, const char *ifname);
int osmo_sock_mcast_subscribe(int fd, const char *grp_addr);
int osmo_sock_local_ip(char *local_ip, const char *remote_ip);
+int osmo_sock_set_dscp(int fd, uint8_t dscp);
+int osmo_sock_set_priority(int fd, int prio);
+
+int osmo_sock_sctp_get_peer_addr_info(int fd, struct sctp_paddrinfo *pinfo, size_t *pinfo_cnt);
+
+#endif /* (!EMBEDDED) */
/*! @} */
diff --git a/include/osmocom/core/socket_compat.h.tpl b/include/osmocom/core/socket_compat.h.tpl
new file mode 100644
index 00000000..43bee9ee
--- /dev/null
+++ b/include/osmocom/core/socket_compat.h.tpl
@@ -0,0 +1,10 @@
+#define HAVE_STRUCT_SOCKADDR_STORAGE XX
+
+#if HAVE_STRUCT_SOCKADDR_STORAGE
+ #include <sys/socket.h>
+#else
+struct sockaddr_storage {
+ unsigned short ss_family;
+ char __data[128 - sizeof(unsigned short)];
+};
+#endif
diff --git a/include/osmocom/core/soft_uart.h b/include/osmocom/core/soft_uart.h
new file mode 100644
index 00000000..afc6ad6d
--- /dev/null
+++ b/include/osmocom/core/soft_uart.h
@@ -0,0 +1,149 @@
+#pragma once
+
+/*! \file soft_uart.h
+ * Software UART implementation. */
+/*
+ * (C) 2022 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+
+/*! Parity mode.
+ * https://en.wikipedia.org/wiki/Parity_bit */
+enum osmo_soft_uart_parity_mode {
+ OSMO_SUART_PARITY_NONE, /*!< No parity bit */
+ OSMO_SUART_PARITY_EVEN, /*!< Even parity */
+ OSMO_SUART_PARITY_ODD, /*!< Odd parity */
+ OSMO_SUART_PARITY_MARK, /*!< Always 1 */
+ OSMO_SUART_PARITY_SPACE, /*!< Always 0 */
+ _OSMO_SUART_PARITY_NUM
+};
+
+/*! Flags passed to the application. */
+enum osmo_soft_uart_flags {
+ OSMO_SUART_F_FRAMING_ERROR = (1 << 0), /*!< Framing error occurred */
+ OSMO_SUART_F_PARITY_ERROR = (1 << 1), /*!< Parity error occurred */
+ OSMO_SUART_F_BREAK = (1 << 2), /*!< Break condition (not implemented) */
+};
+
+/*! Modem status "line" flags.
+ * https://en.wikipedia.org/wiki/RS-232#Data_and_control_signals */
+enum osmo_soft_uart_status {
+ OSMO_SUART_STATUS_F_DTR = (1 << 0), /*!< Data Terminal Ready */
+ OSMO_SUART_STATUS_F_DCD = (1 << 1), /*!< Data Carrier Detect */
+ OSMO_SUART_STATUS_F_DSR = (1 << 2), /*!< Data Set Ready */
+ OSMO_SUART_STATUS_F_RI = (1 << 3), /*!< Ring Indicator */
+ OSMO_SUART_STATUS_F_RTS_RTR = (1 << 4), /*!< Request To Send or Ready To Receive */
+ OSMO_SUART_STATUS_F_CTS = (1 << 5), /*!< Clear To Send */
+};
+
+/*! Flow control mode.
+ * https://en.wikipedia.org/wiki/Flow_control_(data)#Hardware_flow_control */
+enum osmo_soft_uart_flow_ctrl_mode {
+ /*! No flow control */
+ OSMO_SUART_FLOW_CTRL_NONE,
+ /*! DTR/DSR flow control: Tx if DSR is active and drop DTR if cannot Rx anymore. */
+ OSMO_SUART_FLOW_CTRL_DTR_DSR,
+ /*! RTS/CTS flow control: Tx if CTS is active and drop RTS if cannot Rx anymore.
+ * The technically correct name would be RTR/CTS, because the RTS signal actually
+ * indicates readiness to *receive* data (Ready To Receive), and not really used
+ * to request a transmission (Request To Send) nowadays. Alternatively, the RTS
+ * signal can be interpreted as "Request To Send to me". */
+ OSMO_SUART_FLOW_CTRL_RTS_CTS,
+};
+
+/*! Configuration for a soft-UART. */
+struct osmo_soft_uart_cfg {
+ /*! Number of data bits (typically 5, 6, 7 or 8). */
+ uint8_t num_data_bits;
+ /*! Number of stop bits (typically 1 or 2). */
+ uint8_t num_stop_bits;
+ /*! Parity mode (none, even, odd, space, mark). */
+ enum osmo_soft_uart_parity_mode parity_mode;
+ /*! Size of the receive buffer; UART will buffer up to that number
+ * of characters before calling the receive call-back. */
+ unsigned int rx_buf_size;
+ /*! Receive timeout; UART will flush the receive buffer via the receive call-back
+ * after indicated number of milliseconds, even if it is not full yet. */
+ unsigned int rx_timeout_ms;
+
+ /*! Opaque application-private data; passed to call-backs. */
+ void *priv;
+
+ /*! Receive call-back of the application.
+ *
+ * Called if at least one of the following conditions is met:
+ * a) rx_buf_size characters were received (Rx buffer is full);
+ * b) rx_timeout_ms expired and Rx buffer is not empty;
+ * c) a parity or framing error is occurred.
+ *
+ * \param[in] priv opaque application-private data.
+ * \param[in] rx_data msgb holding the received data.
+ * Must be free()ed by the application.
+ * \param[in] flags bit-mask of OSMO_SUART_F_*. */
+ void (*rx_cb)(void *priv, struct msgb *rx_data, unsigned int flags);
+
+ /*! Transmit call-back of the application.
+ *
+ * The implementation is expected to provide at most tx_data->data_len
+ * characters (the actual amount is determined by the number of requested
+ * bits and the effective UART configuration).
+ *
+ * \param[in] priv opaque application-private data.
+ * \param[inout] tx_data msgb for writing to be transmitted data. */
+ void (*tx_cb)(void *priv, struct msgb *tx_data);
+
+ /*! Modem status line change call-back.
+ * \param[in] priv opaque application-private data.
+ * \param[in] status updated status; bit-mask of OSMO_SUART_STATUS_F_*. */
+ void (*status_change_cb)(void *priv, unsigned int status);
+
+ /*! "Hardware" flow control mode. */
+ enum osmo_soft_uart_flow_ctrl_mode flow_ctrl_mode;
+};
+
+extern const struct osmo_soft_uart_cfg osmo_soft_uart_default_cfg;
+
+struct osmo_soft_uart;
+
+struct osmo_soft_uart *osmo_soft_uart_alloc(void *ctx, const char *name,
+ const struct osmo_soft_uart_cfg *cfg);
+void osmo_soft_uart_free(struct osmo_soft_uart *suart);
+int osmo_soft_uart_configure(struct osmo_soft_uart *suart, const struct osmo_soft_uart_cfg *cfg);
+
+const char *osmo_soft_uart_get_name(const struct osmo_soft_uart *suart);
+void osmo_soft_uart_set_name(struct osmo_soft_uart *suart, const char *name);
+
+int osmo_soft_uart_set_rx(struct osmo_soft_uart *suart, bool enable);
+int osmo_soft_uart_set_tx(struct osmo_soft_uart *suart, bool enable);
+
+int osmo_soft_uart_rx_ubits(struct osmo_soft_uart *suart, const ubit_t *ubits, size_t n_ubits);
+int osmo_soft_uart_tx_ubits(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits);
+
+unsigned int osmo_soft_uart_get_status(const struct osmo_soft_uart *suart);
+int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status);
+void osmo_soft_uart_set_status_line(struct osmo_soft_uart *suart,
+ enum osmo_soft_uart_status line,
+ bool active);
+
+void osmo_soft_uart_flush_rx(struct osmo_soft_uart *suart);
diff --git a/include/osmocom/core/stat_item.h b/include/osmocom/core/stat_item.h
index 806173ab..7f6857c0 100644
--- a/include/osmocom/core/stat_item.h
+++ b/include/osmocom/core/stat_item.h
@@ -6,6 +6,7 @@
#include <stdint.h>
+#include <osmocom/core/defs.h>
#include <osmocom/core/linuxlist.h>
struct osmo_stat_item_desc;
@@ -13,30 +14,16 @@ struct osmo_stat_item_desc;
#define OSMO_STAT_ITEM_NOVALUE_ID 0
#define OSMO_STAT_ITEM_NO_UNIT NULL
-/*! Individual entry in value FIFO */
-struct osmo_stat_item_value {
- int32_t id; /*!< identifier of value */
- int32_t value; /*!< actual value */
-};
-
-/*! data we keep for each actual item */
-struct osmo_stat_item {
- /*! back-reference to the item description */
- const struct osmo_stat_item_desc *desc;
- /*! the index of the freshest value */
- int32_t last_value_index;
- /*! offset to the freshest value in the value FIFO */
- int16_t last_offs;
- /*! value FIFO */
- struct osmo_stat_item_value values[0];
-};
+/*! data we keep for each actual item. Access via API functions only.
+ * (This struct was made opaque after libosmocore 1.5.1)*/
+struct osmo_stat_item;
/*! Statistics item description */
struct osmo_stat_item_desc {
const char *name; /*!< name of the item */
const char *description;/*!< description of the item */
const char *unit; /*!< unit of a value */
- unsigned int num_values;/*!< number of values to store in FIFO */
+ unsigned int num_values;/*!< DEPRECATED, this value is ignored after libosmocore version 1.5.1 */
int32_t default_value; /*!< default value */
};
@@ -62,13 +49,15 @@ struct osmo_stat_item_group {
const struct osmo_stat_item_group_desc *desc;
/*! The index of this value group within its class */
unsigned int idx;
+ /*! Optional string-based identifier to be used instead of index at report time */
+ char *name;
/*! Actual counter structures below */
struct osmo_stat_item *items[0];
};
struct osmo_stat_item_group *osmo_stat_item_group_alloc(
void *ctx,
- const struct osmo_stat_item_group_desc *desc,
+ const struct osmo_stat_item_group_desc *group_desc,
unsigned int idx);
static inline void osmo_stat_item_group_udp_idx(
@@ -76,7 +65,8 @@ static inline void osmo_stat_item_group_udp_idx(
{
grp->idx = idx;
}
-
+struct osmo_stat_item *osmo_stat_item_group_get_item(struct osmo_stat_item_group *grp, unsigned int idx);
+void osmo_stat_item_group_set_name(struct osmo_stat_item_group *statg, const char *name);
void osmo_stat_item_group_free(struct osmo_stat_item_group *statg);
void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value);
@@ -87,18 +77,14 @@ int osmo_stat_item_init(void *tall_ctx);
struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
const char *name, const unsigned int idx);
+struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idxname(const char *group_name, const char *idx_name);
const struct osmo_stat_item *osmo_stat_item_get_by_name(
const struct osmo_stat_item_group *statg, const char *name);
-int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *idx, int32_t *value);
-
-/*! Get the last (freshest) value */
-static int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item);
-
-int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx);
+int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item);
-int osmo_stat_item_discard_all(int32_t *idx);
+void osmo_stat_item_flush(struct osmo_stat_item *item);
typedef int (*osmo_stat_item_handler_t)(
struct osmo_stat_item_group *, struct osmo_stat_item *, void *);
@@ -110,8 +96,20 @@ int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data);
-static inline int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item)
-{
- return item->values[item->last_offs].value;
-}
+void osmo_stat_item_reset(struct osmo_stat_item *item);
+void osmo_stat_item_group_reset(struct osmo_stat_item_group *statg);
+
+const struct osmo_stat_item_desc *osmo_stat_item_get_desc(struct osmo_stat_item *item);
+
+/* DEPRECATION: up until libosmocore 1.5.1, this API also defined
+ * - struct osmo_stat_item_value
+ * - osmo_stat_item_get_next()
+ * - osmo_stat_item_discard()
+ * - osmo_stat_item_discard_all()
+ * Despite our principle of never breaking API compatibility, we have decided to remove these, because there are no
+ * known users. These items were never practically used/usable outside of libosmocore since the generic stats reporter
+ * (stats.c) was introduced.
+ * We also decided to make struct osmo_stat_item opaque to allow future changes of the struct without API breakage.
+ */
+
/*! @} */
diff --git a/include/osmocom/core/stats.h b/include/osmocom/core/stats.h
index e01016d4..a034a616 100644
--- a/include/osmocom/core/stats.h
+++ b/include/osmocom/core/stats.h
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -73,6 +69,7 @@ struct osmo_stats_reporter {
char *bind_addr_str; /*!< local bind IP address */
int dest_port; /*!< destination (UDP) port */
int mtu; /*!< Maximum Transmission Unit */
+ unsigned int flush_period; /*!< period between regular flushes */
/*! Maximum class/index to report. FIXME: More details! */
enum osmo_stats_class max_class;
@@ -87,7 +84,8 @@ struct osmo_stats_reporter {
int fd; /*!< file descriptor of socket */
struct msgb *buffer; /*!< message buffer for log output */
int agg_enabled; /*!< is aggregation enabled? */
- int force_single_flush;
+ int force_single_flush; /*!< set to 1 to force a flush (send even unchanged stats values) */
+ unsigned int flush_period_counter; /*!< count sends between forced flushes */
struct llist_head list;
int (*open)(struct osmo_stats_reporter *srep);
@@ -106,10 +104,11 @@ struct osmo_stats_config {
int interval;
};
+extern struct llist_head osmo_stats_reporter_list;
extern struct osmo_stats_config *osmo_stats_config;
void osmo_stats_init(void *ctx);
-int osmo_stats_report();
+int osmo_stats_report(void);
int osmo_stats_set_interval(int interval);
@@ -129,6 +128,7 @@ int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix);
int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep);
int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep);
+int osmo_stats_reporter_set_flush_period(struct osmo_stats_reporter *srep, unsigned int period);
/* reporter creation */
struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name);
diff --git a/include/osmocom/core/stats_tcp.h b/include/osmocom/core/stats_tcp.h
new file mode 100644
index 00000000..9bc7111a
--- /dev/null
+++ b/include/osmocom/core/stats_tcp.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#define TCP_STATS_DEFAULT_INTERVAL 0 /* secs */
+#define TCP_STATS_DEFAULT_BATCH_SIZE 5 /* sockets per interval */
+
+struct osmo_tcp_stats_config {
+ /* poll interval in seconds, use osmo_stats_tcp_set_interval() to manipulate this value */
+ int interval;
+ /* specify how many sockets are processed when the interval timer expires */
+ int batch_size;
+};
+extern struct osmo_tcp_stats_config *osmo_tcp_stats_config;
+
+int osmo_stats_tcp_osmo_fd_register(const struct osmo_fd *fd, const char *name);
+int osmo_stats_tcp_osmo_fd_unregister(const struct osmo_fd *fd);
+int osmo_stats_tcp_set_interval(int interval);
diff --git a/include/osmocom/core/strrb.h b/include/osmocom/core/strrb.h
index b87239da..92d6a2f2 100644
--- a/include/osmocom/core/strrb.h
+++ b/include/osmocom/core/strrb.h
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -30,8 +26,6 @@
#include <stdbool.h>
#include <stdint.h>
-#include <osmocom/core/talloc.h>
-
/*! A structure representing an osmocom string ringbuffer */
#define RB_MAX_MESSAGE_SIZE 240
@@ -42,7 +36,7 @@ struct osmo_strrb {
char **buffer; /*!< storage for messages */
};
-struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size);
+struct osmo_strrb *osmo_strrb_create(void *talloc_ctx, size_t rb_size);
bool osmo_strrb_is_empty(const struct osmo_strrb *rb);
const char *osmo_strrb_get_nth(const struct osmo_strrb *rb,
unsigned int string_index);
diff --git a/include/osmocom/core/talloc.h b/include/osmocom/core/talloc.h
index c68a56cf..f15cd2a2 100644
--- a/include/osmocom/core/talloc.h
+++ b/include/osmocom/core/talloc.h
@@ -25,3 +25,5 @@ extern __thread struct osmo_talloc_contexts *osmo_ctx;
* to the various _c functions like msgb_alloc_c() */
#define OTC_GLOBAL (osmo_ctx->global)
#define OTC_SELECT (osmo_ctx->select)
+
+int osmo_ctx_init(const char *id);
diff --git a/include/osmocom/core/tdef.h b/include/osmocom/core/tdef.h
index 54819d95..402d0102 100644
--- a/include/osmocom/core/tdef.h
+++ b/include/osmocom/core/tdef.h
@@ -40,6 +40,7 @@ enum osmo_tdef_unit {
OSMO_TDEF_MS, /*!< milliseconds */
OSMO_TDEF_M, /*!< minutes */
OSMO_TDEF_CUSTOM, /*!< unspecified unit, explained in osmo_tdef.desc. */
+ OSMO_TDEF_US, /*!< microseconds */
};
extern const struct value_string osmo_tdef_unit_names[];
@@ -120,11 +121,13 @@ struct osmo_tdef_state_timeout {
const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state,
const struct osmo_tdef_state_timeout *timeouts_array);
-/*! Call osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer(), depending on the timeouts_array, tdefs and
- * default_timeout.
+/*! Call osmo_fsm_inst_state_chg[_ms]() or osmo_fsm_inst_state_chg_keep_timer[_ms](),
+ * depending on the timeouts_array, tdefs and default_timeout.
*
- * A T timer configured in sub-second precision is rounded up to the next full second. A timer in unit =
- * OSMO_TDEF_CUSTOM is applied as if the unit is in seconds (i.e. this macro does not make sense for custom units!).
+ * A timer defined with sub-millisecond precision (e.g OSMO_TDEF_US) is rounded up to the next full millisecond.
+ * A timer value defined in units higher than millisecond (e.g. OSMO_TDEF_S, OSMO_TDEF_M) is converted to milliseconds.
+ * A timer in unit = OSMO_TDEF_CUSTOM is applied as if the unit is in seconds (i.e. this macro does not make sense
+ * for custom units!).
*
* See osmo_tdef_get_state_timeout() and osmo_tdef_get().
*
@@ -152,16 +155,17 @@ const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state
* \param[in] state State number to transition to.
* \param[in] timeouts_array Array of struct osmo_tdef_state_timeout[32] to look up state in.
* \param[in] tdefs Array of struct osmo_tdef (last entry zero initialized) to look up T in.
- * \param[in] default_timeout If a T is set in timeouts_array, but no timeout value is configured for T, then use this
- * default timeout value as fallback, or pass -1 to abort the program.
- * \return Return value from osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer().
+ * \param[in] default_timeout If a T is set in timeouts_array, but no timeout value is configured for T,
+ * then use this default timeout value (in seconds) as fallback,
+ * or pass a negative number to abort the program.
+ * \return Return value from osmo_fsm_inst_state_chg[_ms]() or osmo_fsm_inst_state_chg_keep_timer[_ms]().
*/
#define osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout) \
_osmo_tdef_fsm_inst_state_chg(fi, state, timeouts_array, tdefs, default_timeout, \
__FILE__, __LINE__)
int _osmo_tdef_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t state,
const struct osmo_tdef_state_timeout *timeouts_array,
- const struct osmo_tdef *tdefs, unsigned long default_timeout,
+ const struct osmo_tdef *tdefs, signed long default_timeout,
const char *file, int line);
/*! Manage timer definitions in named groups.
diff --git a/include/osmocom/core/thread.h b/include/osmocom/core/thread.h
new file mode 100644
index 00000000..d857268d
--- /dev/null
+++ b/include/osmocom/core/thread.h
@@ -0,0 +1,29 @@
+/*! \file thread.h
+ * Compatibility header with some thread related helpers
+ */
+/*
+ * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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.
+ *
+ */
+
+/*! \defgroup thread Osmocom thread helpers
+ * @{
+ * \file thread.h */
+
+#pragma once
+
+#include <sys/types.h>
+
+pid_t osmo_gettid(void);
diff --git a/include/osmocom/core/time_cc.h b/include/osmocom/core/time_cc.h
new file mode 100644
index 00000000..36fdee46
--- /dev/null
+++ b/include/osmocom/core/time_cc.h
@@ -0,0 +1,187 @@
+/*! \file time_cc.h
+ * Report the cumulative counter of time for which a flag is true as rate counter.
+ */
+/* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/timer.h>
+
+/*! \defgroup time_cc Cumulative counter of time as rate counter.
+ * @{
+ * \file time_cc.h
+ */
+
+struct osmo_tdef;
+struct rate_ctr;
+
+/*! Configuration for osmo_time_cc.
+ * Report the cumulative counter of time for which a flag is true as rate counter.
+ * For example, for each second that the flag is true, increment a rate counter.
+ *
+ * The flag to be monitored is reported by osmo_time_cc_set_flag().
+ *
+ * The granularity defines how much time one rate counter increment represents:
+ * the default configuration is gran_usec = 1000000, i.e. one rate counter increment represents one second.
+ *
+ * Reporting as rate counter is configurable by round_threshold_usec and forget_sum_usec, examples:
+ *
+ * round_threshold_usec:
+ * - To get "ceil()" behavior, set round_threshold_usec = 1. This increments the rate counter for each gran_usec period
+ * where the flag was seen true, even if it was true for only a very short fraction of a gran_usec period.
+ * - To get "round()" behavior, set round_threshold_usec = half of gran_usec. The rate counter increments when the flag
+ * has been true for 0.5 of a gran_usec (and then again at 1.5 * gran_usec) of 'true' flag. round_threshold_usec = 0
+ * is a special value that means to use half of gran_usec.
+ * - To get "floor()" behavior, set round_threshold_usec >= gran_usec. The rate counter increments when reaching full
+ * gran_usec periods of the flag being true.
+ *
+ * forget_sum_usec:
+ * This is a tradeoff between the accuracy of the reported rate counter and making sure that the events reported are not
+ * irrelevantly long ago.
+ * - To keep sub-granularity-period surplus time forever, set forget_sum_usec = 0.
+ * - To keep surplus time for up to a minute, set forget_sum_usec = 60000000 (60 seconds).
+ * - To get rid of "leftover" time (almost) immediately after the flag goes false, set forget_sum_usec = 1.
+ * - If gran_usec is set to one second and forget_sum_usec is set to one minute, the reported rate counter has a
+ * possible inaccuracy of 1/60th, but makes sure that no timings older than a minute affect the current reports.
+ *
+ * Reporting modes in detail:
+ *
+ * The rate_ctr increments when the cumulative counter passes round_threshold_usec (default: half of gran_usec).
+ *
+ * sum ^
+ * | ________
+ * | /
+ * | /
+ * | /
+ * 3*gran --+--------------------------------------+
+ * | /:
+ * | / :
+ * | - - - - - - - - - - - - - - - - - / :
+ * | /. :
+ * | / . :
+ * 2*gran --+--------------------------------+ . :
+ * | /: . :
+ * | / : . :
+ * | - - - - - - - - - -_________/ : . :
+ * | / . : . :
+ * | / . : . :
+ * 1*gran --+-----------------+ . : . :
+ * | /: . : . :
+ * | / : . : . :
+ * | - - - - - - -/ : . : . :
+ * | /. : . : . :
+ * | ....-------' . : . : . :
+ * 0 +------------------------------------------------------------------------> elapsed time
+ * . : . : . :
+ * _ _ _______ ____________
+ * flag: __| |_| |____| . : |_______|. : . : |__________
+ * f t f t f t . : f t. : . : f
+ * round_threshold_usec : . : . : . :
+ * = 1 usec: 0 1 . :2 . :3 . :4 = "ceil()"
+ * = 0 == gran_usec/2: 0 1 : 2 : 3 : = "round()"
+ * >= gran_usec: 0 1 2 3 = "floor()"
+ *
+ */
+struct osmo_time_cc_cfg {
+ /*! Granularity in microseconds: nr of microseconds that one rate_ctr increment represents. A typical value is
+ * gran_usec = 1000000, meaning one rate counter increment represents one second. When zero, use 1000000. */
+ uint64_t gran_usec;
+ /*! Nr of microseconds above n * gran_usec at which to trigger a counter increment. When zero, use half a
+ * gran_usec. */
+ uint64_t round_threshold_usec;
+ /*! Forget counted sub-gran time after the flag was false for this long. */
+ uint64_t forget_sum_usec;
+ /*! Rate counter to report to, or NULL to not use it. */
+ struct rate_ctr *rate_ctr;
+
+ /*! Update gran_usec from this T timer value, or zero to not use any T timer. */
+ int T_gran;
+ /*! Update round_threshold_usec from this T timer value, or zero to not use any T timer. */
+ int T_round_threshold;
+ /*! Update forget_sum_usec from this T timer value, or zero to not use any T timer. */
+ int T_forget_sum;
+ /*! Look up T_gran and T_forget_sum in this list of timers, or NULL to not use any T timers. */
+ struct osmo_tdef *T_defs;
+};
+
+/*! Report the cumulative counter of time for which a flag is true as rate counter.
+ * See also osmo_time_cc_cfg for details on configuring.
+ *
+ * Usage:
+ *
+ * struct my_obj {
+ * struct osmo_time_cc flag_cc;
+ * };
+ *
+ * void my_obj_init(struct my_obj *my_obj)
+ * {
+ * osmo_time_cc_init(&my_obj->flag_cc);
+ * my_obj->flag_cc.cfg = (struct osmo_time_cc_cfg){
+ * .gran_usec = 1000000,
+ * .forget_sum_usec = 60000000,
+ * .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, MY_CTR_IDX),
+ * };
+ * // optional: set initial flag state, default is 'false':
+ * // osmo_time_cc_set_flag(&my_obj->flag_cc, false);
+ * }
+ *
+ * void my_obj_event(struct my_obj *my_obj, bool flag)
+ * {
+ * osmo_time_cc_set_flag(&my_obj->flag_cc, flag);
+ * }
+ *
+ * void my_obj_destruct(struct my_obj *my_obj)
+ * {
+ * osmo_time_cc_cleanup(&my_obj->flag_cc);
+ * }
+ */
+struct osmo_time_cc {
+ struct osmo_time_cc_cfg cfg;
+
+ bool flag_state;
+
+ /*! Overall cumulative sum. Does not get reset for the entire lifetime of an osmo_time_cc.
+ * (Informational only, not used by the osmo_time_cc implementation.) */
+ uint64_t total_sum;
+
+ struct osmo_timer_list timer;
+
+ /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc instance started counting. */
+ uint64_t start_time;
+ /*! CLOCK_MONOTONIC reading in microseconds, at the time when the osmo_time_cc last evaluated the flag state and
+ * possibly added to the cumulated sum. */
+ uint64_t last_counted_time;
+
+ /*! Internal cumulative counter of time that flag_state was true. It may get reset to zero regularly, depending
+ * on cfg.forget_sum_usec. This is the basis for incrementing cfg.rate_ctr. */
+ uint64_t sum;
+ /*! The amount of time that already reported cfg.rate_ctr increments account for. This may be ahead of or behind
+ * 'sum', depending on cfg.round_threshold_usec. */
+ uint64_t reported_sum;
+};
+
+void osmo_time_cc_init(struct osmo_time_cc *tc);
+void osmo_time_cc_set_flag(struct osmo_time_cc *tc, bool flag);
+void osmo_time_cc_cleanup(struct osmo_time_cc *tc);
+
+/*! @} */
diff --git a/include/osmocom/core/timer.h b/include/osmocom/core/timer.h
index 19797662..16338dad 100644
--- a/include/osmocom/core/timer.h
+++ b/include/osmocom/core/timer.h
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \defgroup timer Osmocom timers
@@ -75,7 +71,7 @@ void osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microse
void osmo_timer_del(struct osmo_timer_list *timer);
-int osmo_timer_pending(struct osmo_timer_list *timer);
+int osmo_timer_pending(const struct osmo_timer_list *timer);
int osmo_timer_remaining(const struct osmo_timer_list *timer,
const struct timeval *now,
@@ -84,6 +80,7 @@ int osmo_timer_remaining(const struct osmo_timer_list *timer,
* internal timer list management
*/
struct timeval *osmo_timers_nearest(void);
+int osmo_timers_nearest_ms(void);
void osmo_timers_prepare(void);
int osmo_timers_update(void);
int osmo_timers_check(void);
diff --git a/include/osmocom/core/timer_compat.h b/include/osmocom/core/timer_compat.h
index 916f5684..fd52ae36 100644
--- a/include/osmocom/core/timer_compat.h
+++ b/include/osmocom/core/timer_compat.h
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \defgroup timer Osmocom timers
diff --git a/include/osmocom/core/tun.h b/include/osmocom/core/tun.h
new file mode 100644
index 00000000..86bd8df0
--- /dev/null
+++ b/include/osmocom/core/tun.h
@@ -0,0 +1,43 @@
+/*! \file tun.h
+ * tunnel network device convenience functions. */
+
+#pragma once
+#if (!EMBEDDED)
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/netdev.h>
+
+struct osmo_tundev;
+
+/* callback user gets ownership of the msgb and is expected to free it. */
+typedef int (*osmo_tundev_data_ind_cb_t)(struct osmo_tundev *tun, struct msgb *msg);
+
+struct osmo_tundev *osmo_tundev_alloc(void *ctx, const char *name);
+void osmo_tundev_free(struct osmo_tundev *tundev);
+int osmo_tundev_open(struct osmo_tundev *tundev);
+int osmo_tundev_close(struct osmo_tundev *tundev);
+bool osmo_tundev_is_open(struct osmo_tundev *tundev);
+
+void osmo_tundev_set_priv_data(struct osmo_tundev *tundev, void *priv_data);
+void *osmo_tundev_get_priv_data(struct osmo_tundev *tundev);
+
+void osmo_tundev_set_data_ind_cb(struct osmo_tundev *tundev, osmo_tundev_data_ind_cb_t data_ind_cb);
+
+const char *osmo_tundev_get_name(const struct osmo_tundev *tundev);
+
+int osmo_tundev_set_dev_name(struct osmo_tundev *tundev, const char *dev_name);
+const char *osmo_tundev_get_dev_name(const struct osmo_tundev *tundev);
+
+int osmo_tundev_set_netns_name(struct osmo_tundev *tundev, const char *netns);
+const char *osmo_tundev_get_netns_name(const struct osmo_tundev *tundev);
+
+struct osmo_netdev *osmo_tundev_get_netdev(struct osmo_tundev *tundev);
+
+int osmo_tundev_send(struct osmo_tundev *tundev, struct msgb *msg);
+
+#endif /* (!EMBEDDED) */
+/*! @} */
diff --git a/include/osmocom/core/use_count.h b/include/osmocom/core/use_count.h
index 6a4bf1f3..532d8b14 100644
--- a/include/osmocom/core/use_count.h
+++ b/include/osmocom/core/use_count.h
@@ -19,10 +19,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
@@ -130,8 +126,9 @@ typedef int (* osmo_use_count_cb_t )(struct osmo_use_count_entry *use_count_entr
* int foo_use_cb(struct osmo_use_count_entry *use_count_entry, int32_t old_use_count, const char *file, int line)
* {
* struct foo *foo = use_count_entry->use_count->talloc_object;
- * if (osmo_use_count_total(&use_count_entry->use_count) == 0)
+ * if (osmo_use_count_total(use_count_entry->use_count) == 0)
* talloc_free(foo);
+ * return 0;
* }
*
* // The function name is a convenient use token:
@@ -215,6 +212,8 @@ int _osmo_use_count_get_put(struct osmo_use_count *uc, const char *use, int32_t
const char *file, int line);
const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc);
+int osmo_use_count_to_str_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc);
+char *osmo_use_count_to_str_c(void *ctx, const struct osmo_use_count *uc);
int32_t osmo_use_count_total(const struct osmo_use_count *uc);
int32_t osmo_use_count_by(const struct osmo_use_count *uc, const char *use);
diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h
index 601bb565..92bea59f 100644
--- a/include/osmocom/core/utils.h
+++ b/include/osmocom/core/utils.h
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
+#include <string.h>
#include <osmocom/core/backtrace.h>
#include <osmocom/core/talloc.h>
@@ -21,6 +22,8 @@
#define OSMO_MAX(a, b) ((a) >= (b) ? (a) : (b))
/*! Return the minimum of two specified values */
#define OSMO_MIN(a, b) ((a) >= (b) ? (b) : (a))
+/*! Return a typical cmp result for comparable entities a and b. */
+#define OSMO_CMP(a, b) ((a) < (b)? -1 : ((a) > (b)? 1 : 0))
/*! Stringify the name of a macro x, e.g. an FSM event name.
* Note: if nested within another preprocessor macro, this will
* stringify the value of x instead of its name. */
@@ -30,14 +33,23 @@
/*! Make a value_string entry from an enum value name */
#define OSMO_VALUE_STRING(x) { x, #x }
/*! Number of bytes necessary to store given BITS */
-#define OSMO_BYTES_FOR_BITS(BITS) ((BITS + 8 - 1) / 8)
+#define OSMO_BYTES_FOR_BITS(BITS) (((BITS) + 7) / 8)
/*! Copy a C-string into a sized buffer using sizeof to detect buffer's size */
#define OSMO_STRLCPY_ARRAY(array, src) osmo_strlcpy(array, src, sizeof(array))
+/*! Branch prediction optimizations */
+#if defined(__GNUC__)
+#define OSMO_LIKELY(exp) __builtin_expect(!!(exp), 1)
+#define OSMO_UNLIKELY(exp) __builtin_expect(!!(exp), 0)
+#else
+#define OSMO_LIKELY(exp) exp
+#define OSMO_UNLIKELY(exp) exp
+#endif
+
/*! A mapping between human-readable string and numeric value */
struct value_string {
- unsigned int value; /*!< numeric value */
+ uint32_t value; /*!< numeric value */
const char *str; /*!< human-readable string */
};
@@ -52,8 +64,9 @@ char osmo_bcd2char(uint8_t bcd);
uint8_t osmo_char2bcd(char c);
int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex);
+int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_nibble, int end_nibble, bool allow_hex);
-int osmo_hexparse(const char *str, uint8_t *b, int max_len);
+int osmo_hexparse(const char *str, uint8_t *b, unsigned int max_len);
char *osmo_ubit_dump_buf(char *buf, size_t buf_len, const uint8_t *bits, unsigned int len);
char *osmo_ubit_dump(const uint8_t *bits, unsigned int len);
@@ -98,9 +111,11 @@ do { \
* the predicate evaluates to false (0).
*/
#define OSMO_ASSERT(exp) \
- if (!(exp)) { \
+do { \
+ if (OSMO_UNLIKELY(!(exp))) { \
osmo_panic("Assert failed %s %s:%d\n", #exp, __FILE__, __LINE__); \
- }
+ } \
+} while (0); /* some code invokes OSMO_ASSERT() without the semicolon */
/*! duplicate a string using talloc and release its prior content (if any)
* \param[in] ctx Talloc context to use for allocation
@@ -113,6 +128,8 @@ static inline void osmo_talloc_replace_string(void *ctx, char **dst, const char
*dst = talloc_strdup(ctx, newstr);
}
+void osmo_talloc_replace_string_fmt(void *ctx, char **dst, const char *fmt, ...);
+
/*! Append to a string and re-/allocate if necessary.
* \param[in] ctx Talloc context to use for initial allocation.
* \param[in,out] dest char* to re-/allocate and append to.
@@ -136,6 +153,7 @@ uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len);
uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len);
size_t osmo_strlcpy(char *dst, const char *src, size_t siz);
+const char *osmo_strnchr(const char *str, size_t str_size, char c);
bool osmo_is_hexstr(const char *str, int min_digits, int max_digits,
bool require_even);
@@ -144,11 +162,18 @@ bool osmo_identifier_valid(const char *str);
bool osmo_separated_identifiers_valid(const char *str, const char *sep_chars);
void osmo_identifier_sanitize_buf(char *str, const char *sep_chars, char replace_with);
+size_t osmo_escape_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len);
+char *osmo_escape_cstr_c(void *ctx, const char *str, int in_len);
+size_t osmo_quote_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len);
+char *osmo_quote_cstr_c(void *ctx, const char *str, int in_len);
+
const char *osmo_escape_str(const char *str, int len);
+int osmo_escape_str_buf3(char *buf, size_t bufsize, const char *str, int in_len);
char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_len);
const char *osmo_escape_str_buf(const char *str, int in_len, char *buf, size_t bufsize);
char *osmo_escape_str_c(const void *ctx, const char *str, int in_len);
const char *osmo_quote_str(const char *str, int in_len);
+int osmo_quote_str_buf3(char *buf, size_t bufsize, const char *str, int in_len);
char *osmo_quote_str_buf2(char *buf, size_t bufsize, const char *str, int in_len);
const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bufsize);
char *osmo_quote_str_c(const void *ctx, const char *str, int in_len);
@@ -157,6 +182,18 @@ int osmo_print_n(char *buf, size_t bufsize, const char *str, size_t n);
uint32_t osmo_isqrt32(uint32_t x);
+/*! Floored Modulo (See also: Daan Leijen, Division and Modulus for Computer Scientists).
+ * \param[in] x dividend.
+ * \param[in] y divisor.
+ * \returns remainder of x divided by y. */
+#define OSMO_MOD_FLR(x, y) (((x) > 0 && (y) < 0) || ((x) < 0 && (y) > 0) ? (x) % (y) + (y) : (x) % (y))
+
+/*! Euclidean Modulo (See also: Daan Leijen, Division and Modulus for Computer Scientists).
+ * \param[in] x dividend.
+ * \param[in] y divisor.
+ * \returns remainder of x divided by y. */
+#define OSMO_MOD_EUC(x, y) ((x) % (y) < 0 ? (y) > 0 ? (x) % (y) + (y) : (x) % (y) - (y) : (x) % (y))
+
char osmo_luhn(const char* in, int in_len);
/*! State for OSMO_STRBUF_APPEND() and OSMO_STRBUF_PRINTF(). See there for examples. */
@@ -212,9 +249,9 @@ struct osmo_strbuf {
#define OSMO_STRBUF_APPEND(STRBUF, func, args...) do { \
if (!(STRBUF).pos) \
(STRBUF).pos = (STRBUF).buf; \
- size_t _sb_remain = (STRBUF).buf ? (STRBUF).len - ((STRBUF).pos - (STRBUF).buf) : 0; \
+ size_t _sb_remain = OSMO_STRBUF_REMAIN(STRBUF); \
int _sb_l = func((STRBUF).pos, _sb_remain, ##args); \
- if (_sb_l < 0 || _sb_l > _sb_remain) \
+ if (_sb_l < 0 || (size_t)_sb_l > _sb_remain) \
(STRBUF).pos = (STRBUF).buf + (STRBUF).len; \
else if ((STRBUF).pos) \
(STRBUF).pos += _sb_l; \
@@ -244,6 +281,38 @@ struct osmo_strbuf {
#define OSMO_STRBUF_PRINTF(STRBUF, fmt, args...) \
OSMO_STRBUF_APPEND(STRBUF, snprintf, fmt, ##args)
+/*! Get remaining space for characters and terminating nul in the given struct osmo_strbuf.
+ * \param[in] sb the string buffer to get the remaining space for.
+ * \returns remaining space in the given struct osmo_strbuf. */
+static inline size_t _osmo_strbuf_remain(const struct osmo_strbuf *sb)
+{
+ if (OSMO_UNLIKELY(sb == NULL || sb->buf == NULL))
+ return 0;
+ if (sb->pos == NULL)
+ return sb->len;
+ return sb->len - (sb->pos - sb->buf);
+}
+
+/*! Return remaining space for characters and terminating nul in the given struct osmo_strbuf. */
+#define OSMO_STRBUF_REMAIN(STRBUF) \
+ _osmo_strbuf_remain(&(STRBUF))
+
+/*! Get number of actual characters (without terminating nul) in the given struct osmo_strbuf.
+ * \param[in] sb the string buffer to get the number of characters for.
+ * \returns number of actual characters (without terminating nul). */
+static inline size_t _osmo_strbuf_char_count(const struct osmo_strbuf *sb)
+{
+ if (OSMO_UNLIKELY(sb == NULL || sb->buf == NULL))
+ return 0;
+ if (sb->pos == NULL || sb->pos <= sb->buf)
+ return 0;
+ return OSMO_MIN((size_t)(sb->pos - sb->buf), sb->len - 1);
+}
+
+/*! Return number of actual characters contained in struct osmo_strbuf (without terminating nul). */
+#define OSMO_STRBUF_CHAR_COUNT(STRBUF) \
+ _osmo_strbuf_char_count(&(STRBUF))
+
/*! Like OSMO_STRBUF_APPEND(), but for function signatures that return the char* buffer instead of a length.
* When using this function, the final STRBUF.chars_needed may not reflect the actual number of characters needed, since
* that number cannot be obtained from this kind of function signature.
@@ -255,7 +324,7 @@ struct osmo_strbuf {
#define OSMO_STRBUF_APPEND_NOLEN(STRBUF, func, args...) do { \
if (!(STRBUF).pos) \
(STRBUF).pos = (STRBUF).buf; \
- size_t _sb_remain = (STRBUF).buf ? (STRBUF).len - ((STRBUF).pos - (STRBUF).buf) : 0; \
+ size_t _sb_remain = OSMO_STRBUF_REMAIN(STRBUF); \
if (_sb_remain) { \
func((STRBUF).pos, _sb_remain, ##args); \
} \
@@ -267,6 +336,80 @@ struct osmo_strbuf {
(STRBUF).chars_needed += _sb_l; \
} while(0)
+void osmo_strbuf_drop_tail(struct osmo_strbuf *sb, size_t n_chars);
+/* Convenience macro. struct osmo_strbuf are typically static to a function scope. Avoid having to type '&', same as
+ * with all the other OSMO_STRBUF_* API. */
+#define OSMO_STRBUF_DROP_TAIL(STRBUF, N_CHARS) osmo_strbuf_drop_tail(&(STRBUF), N_CHARS)
+
+void osmo_strbuf_added_tail(struct osmo_strbuf *sb, size_t n_chars);
+/* Convenience macro. struct osmo_strbuf are typically static to a function scope. Avoid having to type '&', same as
+ * with all the other OSMO_STRBUF_* API. */
+#define OSMO_STRBUF_ADDED_TAIL(STRBUF, N_CHARS) osmo_strbuf_added_tail(&(STRBUF), N_CHARS)
+
bool osmo_str_startswith(const char *str, const char *startswith_str);
+int osmo_float_str_to_int(int64_t *val, const char *str, unsigned int precision);
+int osmo_int_to_float_str_buf(char *buf, size_t buflen, int64_t val, unsigned int precision);
+char *osmo_int_to_float_str_c(void *ctx, int64_t val, unsigned int precision);
+
+int osmo_str_to_int64(int64_t *result, const char *str, int base, int64_t min_val, int64_t max_val);
+int osmo_str_to_int(int *result, const char *str, int base, int min_val, int max_val);
+
+/*! Translate a buffer function to a talloc context function.
+ * This is the full function body of a char *foo_name_c(void *ctx, val...) function, implemented by an
+ * int foo_name_buf(buf, buflen, val...) function:
+ *
+ * char *foo_name_c(void *ctx, example_t arg)
+ * {
+ * OSMO_NAME_C_IMPL(ctx, 64, "ERROR", foo_name_buf, arg)
+ * }
+ *
+ * Return a talloc'd string containing the result of the given foo_name_buf() function, or ON_ERROR on error in the called
+ * foo_name_buf() function.
+ *
+ * If ON_ERROR is NULL, the function returns NULL on error rc from FUNC_BUF. Take care: returning NULL in printf() like
+ * formats (LOGP()) makes the program crash. If ON_ERROR is non-NULL, it must be a string constant, which is not
+ * returned directly, but written to an allocated string buffer first.
+ *
+ * \param[in] INITIAL_BUFSIZE Which size to first talloc from ctx -- a larger size makes a reallocation less likely, a
+ * smaller size allocates less unused bytes, zero allocates once but still runs the string composition twice.
+ * \param[in] ON_ERROR String constant to copy on error rc returned by FUNC_BUF, or NULL to return NULL.
+ * \param[in] FUNC_BUF Name of a function with signature int foo_buf(char *buf, size_t buflen, ...).
+ * The function must return the strlen() that it would write to a sufficiently large buffer or
+ * negative on error, like snprintf().
+ * \param[in] FUNC_BUF_ARGS Additional arguments to pass to FUNC_BUF after the buf and buflen.
+ */
+#define OSMO_NAME_C_IMPL(CTX, INITIAL_BUFSIZE, ON_ERROR, FUNC_BUF, FUNC_BUF_ARGS...) \
+ size_t _len = INITIAL_BUFSIZE; \
+ int _needed; \
+ char *_str = NULL; \
+ if ((INITIAL_BUFSIZE) > 0) { \
+ _str = (char*)talloc_named_const(CTX, _len, __func__); \
+ OSMO_ASSERT(_str); \
+ } \
+ _needed = FUNC_BUF(_str, _len, ## FUNC_BUF_ARGS); \
+ if (_needed < 0) \
+ goto OSMO_NAME_C_on_error; \
+ if ((unsigned int) _needed < _len) \
+ return _str; \
+ _len = _needed + 1; \
+ if (_str) \
+ talloc_free(_str); \
+ _str = (char*)talloc_named_const(CTX, _len, __func__); \
+ OSMO_ASSERT(_str); \
+ _needed = FUNC_BUF(_str, _len, ## FUNC_BUF_ARGS); \
+ if (_needed < 0) \
+ goto OSMO_NAME_C_on_error; \
+ return _str; \
+OSMO_NAME_C_on_error: \
+ /* Re-using and re-sizing above allocated buf ends up in very complex code. Just free and strdup. */ \
+ if (_str) \
+ talloc_free(_str); \
+ if (!(ON_ERROR)) \
+ return NULL; \
+ _str = talloc_strdup(CTX, ON_ERROR); \
+ OSMO_ASSERT(_str); \
+ talloc_set_name_const(_str, __func__); \
+ return _str;
+
/*! @} */
diff --git a/include/osmocom/core/write_queue.h b/include/osmocom/core/write_queue.h
index 071621d1..fe762829 100644
--- a/include/osmocom/core/write_queue.h
+++ b/include/osmocom/core/write_queue.h
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -53,6 +49,8 @@ struct osmo_wqueue {
void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length);
void osmo_wqueue_clear(struct osmo_wqueue *queue);
int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data);
+int osmo_wqueue_enqueue_quiet(struct osmo_wqueue *queue, struct msgb *data);
+size_t osmo_wqueue_set_maxlen(struct osmo_wqueue *queue, unsigned int len);
int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what);
/*! @} */
diff --git a/include/osmocom/crypt/Makefile.am b/include/osmocom/crypt/Makefile.am
new file mode 100644
index 00000000..fa391bb3
--- /dev/null
+++ b/include/osmocom/crypt/Makefile.am
@@ -0,0 +1,8 @@
+osmocrypt_HEADERS = \
+ auth.h \
+ gprs_cipher.h \
+ kdf.h \
+ utran_cipher.h \
+ $(NULL)
+
+osmocryptdir = $(includedir)/osmocom/crypt
diff --git a/include/osmocom/crypt/auth.h b/include/osmocom/crypt/auth.h
index c653b616..1499ef85 100644
--- a/include/osmocom/crypt/auth.h
+++ b/include/osmocom/crypt/auth.h
@@ -30,12 +30,41 @@ enum osmo_auth_algo {
OSMO_AUTH_ALG_COMP128v1,
OSMO_AUTH_ALG_COMP128v2,
OSMO_AUTH_ALG_COMP128v3,
- OSMO_AUTH_ALG_XOR,
+ OSMO_AUTH_ALG_XOR_3G,
OSMO_AUTH_ALG_MILENAGE,
+ OSMO_AUTH_ALG_XOR_2G,
+ OSMO_AUTH_ALG_TUAK,
_OSMO_AUTH_ALG_NUM,
};
+/* Backwards-compatibility. We used to call XOR-3G just "XOR" which became ambiguous when
+ * we started to add XOR-2G support. */
+#define OSMO_AUTH_ALG_XOR OSMO_AUTH_ALG_XOR_3G
/*! permanent (secret) subscriber auth data */
+struct osmo_sub_auth_data2 {
+ enum osmo_sub_auth_type type;
+ enum osmo_auth_algo algo;
+ union {
+ struct {
+ /* See 3GPP TS 33.102 Section 9.3.7 Length of authentication parameters */
+ uint8_t opc[32]; /*!< operator invariant value */
+ uint8_t opc_len; /*!< OPc length (in bytes): 16 or 32 */
+ uint8_t k[32]; /*!< secret key of the subscriber */
+ uint8_t k_len; /*!< K length (in bytes): 16 or 32 */
+ uint8_t amf[2];
+ uint64_t sqn; /*!< sequence number (in: prev sqn; out: used sqn) */
+ int opc_is_op; /*!< is the OPC field OPC (0) or OP (1) ? */
+ unsigned int ind_bitlen; /*!< nr of bits not in SEQ, only SQN */
+ unsigned int ind; /*!< which IND slot to use an SQN from */
+ uint64_t sqn_ms; /*!< sqn from AUTS (output value only) */
+ } umts;
+ struct {
+ uint8_t ki[OSMO_A5_MAX_KEY_LEN_BYTES]; /*!< secret key */
+ } gsm;
+ } u;
+};
+
+/* deprecated older structure without support for 32-byte K/OP[c] */
struct osmo_sub_auth_data {
enum osmo_sub_auth_type type;
enum osmo_auth_algo algo;
@@ -63,7 +92,7 @@ struct osmo_auth_vector {
uint8_t ck[OSMO_A5_MAX_KEY_LEN_BYTES]; /*!< ciphering key */
uint8_t ik[OSMO_A5_MAX_KEY_LEN_BYTES]; /*!< integrity key */
uint8_t res[16]; /*!< authentication result */
- uint8_t res_len; /*!< length (in bytes) of res */
+ uint8_t res_len; /*!< length (in bytes) of res: 4..16 bytes */
uint8_t kc[8]; /*!< Kc for GSM encryption (A5) */
uint8_t sres[4]; /*!< authentication result for GSM */
uint32_t auth_types; /*!< bitmask of OSMO_AUTH_TYPE_* */
@@ -78,22 +107,32 @@ struct osmo_auth_impl {
/*! callback for generate authentication vectors */
int (*gen_vec)(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand);
- /* callback for generationg auth vectors + re-sync */
+ /*! callback for generating auth vectors + re-sync */
int (*gen_vec_auts)(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *auts, const uint8_t *rand_auts,
const uint8_t *_rand);
};
int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud, const uint8_t *_rand);
+ struct osmo_sub_auth_data *aud, const uint8_t *_rand)
+ OSMO_DEPRECATED_OUTSIDE("Use osmo_auth_gen_vec2 instead");
+
+int osmo_auth_gen_vec2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud, const uint8_t *_rand);
int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
struct osmo_sub_auth_data *aud,
const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand)
+ OSMO_DEPRECATED_OUTSIDE("Use osmo_auth_gen_vec_auts2 instead");
+
+int osmo_auth_gen_vec_auts2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
const uint8_t *_rand);
int osmo_auth_register(struct osmo_auth_impl *impl);
@@ -106,5 +145,6 @@ const char *osmo_auth_alg_name(enum osmo_auth_algo alg);
enum osmo_auth_algo osmo_auth_alg_parse(const char *name);
void osmo_auth_c3(uint8_t kc[], const uint8_t ck[], const uint8_t ik[]);
+void osmo_auth_c2(uint8_t sres[4], const uint8_t *res, size_t res_len, uint8_t sres_deriv_func);
/* @} */
diff --git a/include/osmocom/crypt/kdf.h b/include/osmocom/crypt/kdf.h
new file mode 100644
index 00000000..4a3b3b2e
--- /dev/null
+++ b/include/osmocom/crypt/kdf.h
@@ -0,0 +1,21 @@
+#pragma once
+
+/*! \defgroup kdf key derivation functions
+ * @{
+ * \file kdf.h */
+
+#include <stdint.h>
+
+void osmo_kdf_kc128(const uint8_t* ck, const uint8_t* ik, uint8_t* kc128);
+
+void osmo_kdf_kasme(const uint8_t *ck, const uint8_t *ik, const uint8_t* plmn_id,
+ const uint8_t *sqn, const uint8_t *ak, uint8_t *kasme);
+
+void osmo_kdf_enb(const uint8_t *kasme, uint32_t ul_count, uint8_t *kenb);
+
+void osmo_kdf_nh(const uint8_t *kasme, const uint8_t *sync_input, uint8_t *nh);
+
+void osmo_kdf_nas(uint8_t algo_type, uint8_t algo_id, const uint8_t *kasme, uint8_t *knas);
+
+
+/* @} */
diff --git a/include/osmocom/crypt/utran_cipher.h b/include/osmocom/crypt/utran_cipher.h
new file mode 100644
index 00000000..7dab3bc9
--- /dev/null
+++ b/include/osmocom/crypt/utran_cipher.h
@@ -0,0 +1,19 @@
+/*! \file utran_cipher.h */
+
+#pragma once
+
+/* 3GPP TS 25.413 § 9.2.1.11 */
+enum osmo_utran_integrity_algo {
+ OSMO_UTRAN_UIA1 = 0,
+ OSMO_UTRAN_UIA2 = 1,
+ _OSMO_UTRAN_UIA_NUM
+};
+
+/* 3GPP TS 25.413 § 9.2.1.12 */
+enum osmo_utran_encryption_algo {
+ OSMO_UTRAN_UEA0 = 0,
+ OSMO_UTRAN_UEA1 = 1,
+ OSMO_UTRAN_UEA2 = 2,
+ _OSMO_UTRAN_UEA_NUM
+};
+
diff --git a/include/osmocom/ctrl/Makefile.am b/include/osmocom/ctrl/Makefile.am
new file mode 100644
index 00000000..416e3ab4
--- /dev/null
+++ b/include/osmocom/ctrl/Makefile.am
@@ -0,0 +1,13 @@
+osmoctrl_HEADERS = \
+ control_cmd.h \
+ control_if.h \
+ ports.h \
+ $(NULL)
+
+if ENABLE_VTY
+osmoctrl_HEADERS += \
+ control_vty.h \
+ $(NULL)
+endif
+
+osmoctrldir = $(includedir)/osmocom/ctrl
diff --git a/include/osmocom/ctrl/control_cmd.h b/include/osmocom/ctrl/control_cmd.h
index 276a7def..e4f78fa2 100644
--- a/include/osmocom/ctrl/control_cmd.h
+++ b/include/osmocom/ctrl/control_cmd.h
@@ -24,6 +24,7 @@ enum ctrl_node_type {
CTRL_NODE_TS, /* TS specific (net.btsN.trxM.tsI.) */
CTRL_NODE_FSM, /* Finite State Machine (description) */
CTRL_NODE_FSM_INST, /* Finite State Machine (instance) */
+ CTRL_NODE_LCHAN, /* LCHAN specific (net.btsN.trxM.tsI.lchanL) */
_LAST_CTRL_NODE
};
@@ -123,6 +124,7 @@ int ctrl_cmd_def_send(struct ctrl_cmd_def *cd);
int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data);
int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd);
+/* ctrl_cmd_send is also declared in control_if.h, but legacy openbsc.git expects it here */
int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd);
int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd);
struct ctrl_cmd *ctrl_cmd_parse3(void *ctx, struct msgb *msg, bool *parse_failed);
@@ -234,9 +236,7 @@ static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \
* \param[in] cmdname symbol name of the command related function
* \param[in] cmdstr string name exposed on CTRL
* \param[in] dtype name of outer struct of user data
- * \param[in] element name of field within \a dtype
- * \param[in] min minimum permitted integer value
- * \param[in] max maximum permitted integer value */
+ * \param[in] element name of field within \a dtype */
#define CTRL_CMD_DEFINE_STRING(cmdname, cmdstr, dtype, element) \
CTRL_HELPER_GET_STRING(cmdname, dtype, element) \
CTRL_HELPER_SET_STRING(cmdname, dtype, element) \
diff --git a/include/osmocom/ctrl/control_if.h b/include/osmocom/ctrl/control_if.h
index 5fa9588d..e262d9e2 100644
--- a/include/osmocom/ctrl/control_if.h
+++ b/include/osmocom/ctrl/control_if.h
@@ -9,6 +9,7 @@ int ctrl_parse_get_num(vector vline, int i, long *num);
typedef int (*ctrl_cmd_lookup)(void *data, vector vline, int *node_type,
void **node_data, int *i);
+typedef void (*ctrl_cmd_reply_cb)(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data);
struct ctrl_handle {
struct osmo_fd listen_fd;
@@ -18,10 +19,17 @@ struct ctrl_handle {
/* List of control connections */
struct llist_head ccon_list;
+
+ /* User defined GET/SET REPLY handler. User can set cmd->defer to 1 in
+ order to own and keep the cmd pointer and free it after the function
+ returns. "data" param is the user data pointer supplied during
+ ctrl_handle allocation */
+ ctrl_cmd_reply_cb reply_cb;
};
-int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd);
+int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd) OSMO_DEPRECATED("Use ctrl_cmd_send2() instead.");
+int ctrl_cmd_send2(struct ctrl_connection *ccon, struct ctrl_cmd *cmd);
int ctrl_cmd_send_trap(struct ctrl_handle *ctrl, const char *name, char *value);
struct ctrl_handle *ctrl_handle_alloc(void *ctx, void *data, ctrl_cmd_lookup lookup);
struct ctrl_handle *ctrl_handle_alloc2(void *ctx, void *data,
@@ -29,19 +37,20 @@ struct ctrl_handle *ctrl_handle_alloc2(void *ctx, void *data,
unsigned int node_count);
struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port,
ctrl_cmd_lookup lookup);
+struct ctrl_handle *ctrl_interface_setup2(void *data, uint16_t default_port, ctrl_cmd_lookup lookup,
+ unsigned int node_count);
struct ctrl_handle *ctrl_interface_setup_dynip(void *data,
const char *bind_addr,
uint16_t port,
- ctrl_cmd_lookup lookup);
+ ctrl_cmd_lookup lookup) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE;
struct ctrl_handle *ctrl_interface_setup_dynip2(void *data,
const char *bind_addr,
uint16_t port,
ctrl_cmd_lookup lookup,
- unsigned int node_count);
+ unsigned int node_count) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE;
struct ctrl_connection *osmo_ctrl_conn_alloc(void *ctx, void *data);
int ctrl_cmd_handle(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data);
struct ctrl_cmd *ctrl_cmd_exec_from_string(struct ctrl_handle *ch, const char *cmdstr);
int ctrl_lookup_register(ctrl_cmd_lookup lookup);
-
int ctrl_handle_msg(struct ctrl_handle *ctrl, struct ctrl_connection *ccon, struct msgb *msg);
diff --git a/include/osmocom/ctrl/control_vty.h b/include/osmocom/ctrl/control_vty.h
index af9ee991..a6cd012e 100644
--- a/include/osmocom/ctrl/control_vty.h
+++ b/include/osmocom/ctrl/control_vty.h
@@ -2,6 +2,8 @@
#pragma once
+#include <stdint.h>
+
/* Add the 'ctrl' section to VTY, containing the 'bind' command. */
int ctrl_vty_init(void *ctx);
@@ -9,3 +11,6 @@ int ctrl_vty_init(void *ctx);
* This should be fed to ctrl_interface_setup() once the configuration has been
* read. */
const char *ctrl_vty_get_bind_addr(void);
+
+/* Returns configured port passed to the 'line ctrl'/'bind' command or default_port. */
+uint16_t ctrl_vty_get_bind_port(uint16_t default_port);
diff --git a/include/osmocom/ctrl/ports.h b/include/osmocom/ctrl/ports.h
index 25d2491b..b4bceef3 100644
--- a/include/osmocom/ctrl/ports.h
+++ b/include/osmocom/ctrl/ports.h
@@ -12,6 +12,7 @@
#define OSMO_CTRL_PORT_TRX 4236
/* 4237 used by VTY interface */
#define OSMO_CTRL_PORT_BTS 4238
+#define OSMO_CTRL_PORT_BSC_NEIGH 4248 /* osmo-bsc Neighbor Resloution Service */
#define OSMO_CTRL_PORT_NITB_BSC 4249
#define OSMO_CTRL_PORT_BSC_NAT 4250
#define OSMO_CTRL_PORT_SGSN 4251
@@ -24,4 +25,13 @@
#define OSMO_CTRL_PORT_GBPROXY 4263
/* 4264 used by VTY interface */
#define OSMO_CTRL_PORT_CBC 4265
+/* 4266 used by D-GSM mDNS */
+#define OSMO_CTRL_PORT_MGW 4267
+#define OSMO_CTRL_PORT_SMLC 4272
+/* 4273 used by VTY interface */
+#define OSMO_CTRL_PORT_HNODEB 4274
+/* 4275: OSMO_VTY_PORT_UPF */
+#define OSMO_CTRL_PORT_UPF 4276
+/* 4277: OSMO_VTY_PORT_PFCP_TOOL */
+#define OSMO_CTRL_PORT_PFCP_TOOL 4278
/* When adding/changing port numbers, keep docs and wiki in sync. See above. */
diff --git a/include/osmocom/gprs/Makefile.am b/include/osmocom/gprs/Makefile.am
new file mode 100644
index 00000000..289be578
--- /dev/null
+++ b/include/osmocom/gprs/Makefile.am
@@ -0,0 +1,17 @@
+SUBDIRS = protocol
+
+osmogprs_HEADERS = \
+ frame_relay.h \
+ bssgp_bvc_fsm.h \
+ gprs_bssgp.h \
+ gprs_bssgp2.h \
+ gprs_bssgp_bss.h \
+ gprs_bssgp_rim.h \
+ gprs_msgb.h \
+ gprs_ns.h \
+ gprs_ns_frgre.h \
+ gprs_ns2.h \
+ gprs_rlc.h \
+ $(NULL)
+
+osmogprsdir = $(includedir)/osmocom/gprs
diff --git a/include/osmocom/gprs/bssgp_bvc_fsm.h b/include/osmocom/gprs/bssgp_bvc_fsm.h
new file mode 100644
index 00000000..9d3a6205
--- /dev/null
+++ b/include/osmocom/gprs/bssgp_bvc_fsm.h
@@ -0,0 +1,71 @@
+#pragma once
+#include <stdint.h>
+
+struct gprs_ns2_inst;
+struct osmo_fsm_inst;
+struct gprs_ra_id;
+struct bssgp2_flow_ctrl;
+
+enum bssp_ptp_bvc_fsm_state {
+ BSSGP_BVCFSM_S_NULL,
+ BSSGP_BVCFSM_S_BLOCKED,
+ BSSGP_BVCFSM_S_WAIT_RESET_ACK,
+ BSSGP_BVCFSM_S_UNBLOCKED,
+};
+
+enum bssgp_ptp_bvc_fsm_event {
+ /* Rx of BSSGP PDUs from the remote side; 'data' is 'struct tlv_parsed', and
+ * the assumption is that the caller has already validated all mandatory IEs
+ * are present and of sufficient length */
+ BSSGP_BVCFSM_E_RX_BLOCK,
+ BSSGP_BVCFSM_E_RX_BLOCK_ACK,
+ BSSGP_BVCFSM_E_RX_UNBLOCK,
+ BSSGP_BVCFSM_E_RX_UNBLOCK_ACK,
+ BSSGP_BVCFSM_E_RX_RESET,
+ BSSGP_BVCFSM_E_RX_RESET_ACK,
+ BSSGP_BVCFSM_E_RX_FC_BVC,
+ BSSGP_BVCFSM_E_RX_FC_BVC_ACK,
+ /* Requests of the local user */
+ BSSGP_BVCFSM_E_REQ_BLOCK, /* data: uint8_t *cause */
+ BSSGP_BVCFSM_E_REQ_UNBLOCK,
+ BSSGP_BVCFSM_E_REQ_RESET, /* data: uint8_t *cause */
+ BSSGP_BVCFSM_E_REQ_FC_BVC, /* data: struct bssgp2_flow_ctrl */
+};
+
+struct bssgp_bvc_fsm_ops {
+ /* call-back notifying the user of a state change */
+ void (*state_chg_notification)(uint16_t nsei, uint16_t bvci, int old_state, int new_state,
+ void *priv);
+ /* call-back notifying the user of a BVC-RESET event */
+ void (*reset_notification)(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
+ uint16_t cell_id, uint8_t cause, void *priv);
+ void (*rx_fc_bvc)(uint16_t nsei, uint16_t bvci, const struct bssgp2_flow_ctrl *fc, void *priv);
+ void (*reset_ack_notification)(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id,
+ uint16_t cell_id, uint8_t cause, void *priv);
+};
+
+struct osmo_fsm_inst *
+bssgp_bvc_fsm_alloc_sig_bss(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint32_t features);
+
+struct osmo_fsm_inst *
+bssgp_bvc_fsm_alloc_ptp_bss(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint16_t bvci,
+ const struct gprs_ra_id *ra_id, uint16_t cell_id);
+
+struct osmo_fsm_inst *
+bssgp_bvc_fsm_alloc_sig_sgsn(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint32_t features);
+
+struct osmo_fsm_inst *
+bssgp_bvc_fsm_alloc_ptp_sgsn(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint16_t bvci);
+
+void bssgp_bvc_fsm_set_ops(struct osmo_fsm_inst *fi, const struct bssgp_bvc_fsm_ops *ops, void *ops_priv);
+
+bool bssgp_bvc_fsm_is_unblocked(struct osmo_fsm_inst *fi);
+
+uint8_t bssgp_bvc_fsm_get_block_cause(struct osmo_fsm_inst *fi);
+
+uint32_t bssgp_bvc_fsm_get_features_advertised(struct osmo_fsm_inst *fi);
+uint32_t bssgp_bvc_fsm_get_features_received(struct osmo_fsm_inst *fi);
+uint32_t bssgp_bvc_fsm_get_features_negotiated(struct osmo_fsm_inst *fi);
+
+void bssgp_bvc_fsm_set_max_pdu_len(struct osmo_fsm_inst *fi, uint16_t max_pdu_len);
+uint16_t bssgp_bvc_fsm_get_max_pdu_len(const struct osmo_fsm_inst *fi); \ No newline at end of file
diff --git a/include/osmocom/gprs/frame_relay.h b/include/osmocom/gprs/frame_relay.h
new file mode 100644
index 00000000..81b42a69
--- /dev/null
+++ b/include/osmocom/gprs/frame_relay.h
@@ -0,0 +1,151 @@
+/*! \file frame_relay.h */
+
+/* (C) 2020 Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/utils.h>
+
+#include <stdint.h>
+
+struct osmo_tdef;
+struct msgb;
+struct vty;
+
+enum osmo_fr_role {
+ FR_ROLE_USER_EQUIPMENT,
+ FR_ROLE_NETWORK_EQUIPMENT,
+};
+
+/* 48.016 § 6.1.4.2 default maximum information field size of 1600 octets */
+#define FRAME_RELAY_MTU 1600
+/* FR DLC header is 2 byte */
+#define FRAME_RELAY_SDU (FRAME_RELAY_MTU - 2)
+
+extern const struct value_string osmo_fr_role_names[];
+
+static inline const char *osmo_fr_role_str(enum osmo_fr_role role) {
+ return get_value_string(osmo_fr_role_names, role);
+}
+
+struct osmo_fr_network {
+ struct llist_head links;
+
+ unsigned int n391; /* full status polling counter */
+ unsigned int n392; /* error threshold */
+ unsigned int n393; /* monitored events count */
+
+ struct osmo_tdef *T_defs; /* T391, T392 */
+};
+
+struct osmo_fr_dlc;
+
+/* Frame Relay Link */
+struct osmo_fr_link {
+ /* list in osmo_fr_network.links */
+ struct llist_head list;
+ struct osmo_fr_network *net;
+ enum osmo_fr_role role;
+ /* human-readable name */
+ const char *name;
+
+ /* value of the last received send sequence number field in the
+ * link integrity verification information element */
+ uint8_t last_rx_seq;
+
+ /* value of the send sequence number field of the last link
+ * integrity verification information element sent */
+ uint8_t last_tx_seq;
+
+ struct osmo_timer_list t391;
+ struct osmo_timer_list t392;
+
+ unsigned int polling_count;
+ unsigned int err_count;
+ unsigned int succeed;
+ /* the type of the last status enquiry */
+ uint8_t expected_rep;
+ bool state;
+
+ /* list of data link connections at this link */
+ struct llist_head dlc_list;
+
+ /* optional call-back to be called for each PDU received on an unknown DLC */
+ int (*unknown_dlc_rx_cb)(void *cb_data, struct msgb *msg);
+ void *unknown_dlc_rx_cb_data;
+
+ /* call-back to be called for transmitting on the underlying hardware */
+ int (*tx_cb)(void *data, struct msgb *msg);
+ /* optional call-back to be called each time the status changes active/inactive */
+ void (*status_cb)(struct osmo_fr_link *link, void *cb_data, bool active);
+ void *cb_data;
+};
+
+/* Frame Relay Data Link Connection */
+struct osmo_fr_dlc {
+ /* entry in fr_link.dlc_list */
+ struct llist_head list;
+ struct osmo_fr_link *link;
+
+ uint16_t dlci;
+
+ /* is this DLC marked active for traffic? */
+ bool active;
+ /* was this DLC newly added? */
+ bool add;
+ /* is this DLC about to be destroyed */
+ bool del;
+
+ /* The local state needs to be transferred to the USER;
+ * NET must wait until USER confirms it implicitly by a seq number check */
+ bool state_send;
+
+ /* call-back to be called for each PDU received on this DLC */
+ int (*rx_cb)(void *cb_data, struct msgb *msg);
+ /* optional call-back to be called each time the status changes active/inactive */
+ void (*status_cb)(struct osmo_fr_dlc *dlc, void *cb_data, bool active);
+ void *cb_data;
+};
+
+/* allocate a frame relay network */
+struct osmo_fr_network *osmo_fr_network_alloc(void *ctx);
+void osmo_fr_network_free(struct osmo_fr_network *net);
+void osmo_fr_network_dump_vty(struct vty *vty, const struct osmo_fr_network *net);
+
+/* allocate a frame relay link in a given network */
+struct osmo_fr_link *osmo_fr_link_alloc(struct osmo_fr_network *net, enum osmo_fr_role role, const char *name);
+
+/* free a frame link in a given network */
+void osmo_fr_link_free(struct osmo_fr_link *link);
+
+/* allocate a data link connectoin on a given framerelay link */
+struct osmo_fr_dlc *osmo_fr_dlc_alloc(struct osmo_fr_link *link, uint16_t dlci);
+void osmo_fr_dlc_free(struct osmo_fr_dlc *dlc);
+
+struct osmo_fr_dlc *osmo_fr_dlc_by_dlci(struct osmo_fr_link *link, uint16_t dlci);
+
+int osmo_fr_rx(struct msgb *msg);
+int osmo_fr_tx_dlc(struct msgb *msg);
diff --git a/include/osmocom/gprs/gprs_bssgp.h b/include/osmocom/gprs/gprs_bssgp.h
index 0f87333a..6c043327 100644
--- a/include/osmocom/gprs/gprs_bssgp.h
+++ b/include/osmocom/gprs/gprs_bssgp.h
@@ -10,13 +10,32 @@
#include <osmocom/gsm/prim.h>
#include <osmocom/gprs/protocol/gsm_08_18.h>
+#include <osmocom/gprs/protocol/gsm_24_301.h>
+#include <osmocom/gprs/gprs_bssgp_rim.h>
/* gprs_bssgp_util.c */
+
+#define BSSGP_PDUF_UL 0x0001 /* PDU may occur in uplink */
+#define BSSGP_PDUF_DL 0x0002 /* PDU may occur in downlink */
+#define BSSGP_PDUF_SIG 0x0004 /* PDU may occur on Signaling BVC */
+#define BSSGP_PDUF_PTP 0x0008 /* PDU may occur on PTP BVC */
+#define BSSGP_PDUF_PTM 0x0010 /* PDU may occur on PTM BVC */
+
+extern const struct osmo_tlv_prot_def osmo_pdef_bssgp;
+
+/*! return the PDU type flags (UL/DL/SIG/PTP/PTM) of specified PDU type */
+static inline uint32_t bssgp_pdu_type_flags(uint8_t pdu_type) {
+ return osmo_tlv_prot_msgt_flags(&osmo_pdef_bssgp, pdu_type);
+}
+
+typedef int (*bssgp_bvc_send)(void *ctx, struct msgb *msg);
extern struct gprs_ns_inst *bssgp_nsi;
+void bssgp_set_bssgp_callback(bssgp_bvc_send ns_send, void *data);
struct msgb *bssgp_msgb_alloc(void);
struct msgb *bssgp_msgb_copy(const struct msgb *msg, const char *name);
const char *bssgp_cause_str(enum gprs_bssgp_cause cause);
const char *bssgp_pdu_str(enum bssgp_pdu_type pdu);
+int bssgp_tx_bvc_reset_nsei_bvci(uint16_t nsei, uint16_t bvci, enum gprs_bssgp_cause cause, const struct gprs_ra_id *ra_id, uint16_t cell_id);
/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */
int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei,
uint16_t bvci, uint16_t ns_bvci);
@@ -38,6 +57,8 @@ enum bssgp_prim {
PRIM_NM_BVC_BLOCK,
PRIM_NM_BVC_UNBLOCK,
PRIM_NM_STATUS,
+
+ PRIM_BSSGP_RIM_PDU_TRANSFER,
};
struct osmo_bssgp_prim {
@@ -55,6 +76,7 @@ struct osmo_bssgp_prim {
struct {
uint8_t suspend_ref;
} resume;
+ struct bssgp_ran_information_pdu rim_pdu;
} u;
};
@@ -103,11 +125,15 @@ struct bssgp_bvc_ctx {
/*! default bucket leak rate of per-MS bucket in octests/s */
uint32_t r_default_ms;
+ /*! BSS or SGSN. This defines the local state. */
+ bool is_sgsn;
/* we might want to add this as a shortcut later, avoiding the NSVC
* lookup for every packet, similar to a routing cache */
//struct gprs_nsvc *nsvc;
};
extern struct llist_head bssgp_bvc_ctxts;
+/* Create a BTS Context with BVCI+NSEI */
+struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
/* Find a BTS Context based on parsed RA ID and Cell ID */
struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid);
/* Find a BTS context based on BVCI+NSEI tuple */
@@ -157,7 +183,7 @@ int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
uint16_t cid);
/* Wrapper around TLV parser to parse BSSGP IEs */
-static inline int bssgp_tlv_parse(struct tlv_parsed *tp, uint8_t *buf, int len)
+static inline int bssgp_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len)
{
return tlv_parse(tp, &tvlv_att_def, buf, len, 0, 0);
}
@@ -209,11 +235,11 @@ int bssgp_fc_in(struct bssgp_flow_control *fc, struct msgb *msg,
int bssgp_fc_ms_init(struct bssgp_flow_control *fc_ms, uint16_t bvci,
uint16_t nsei, uint32_t max_queue_depth);
-void bssgp_flush_all_queues();
+void bssgp_flush_all_queues(void);
void bssgp_fc_flush_queue(struct bssgp_flow_control *fc);
/* gprs_bssgp_vty.c */
int bssgp_vty_init(void);
-void bssgp_set_log_ss(int ss);
+void bssgp_set_log_ss(int ss) OSMO_DEPRECATED("Use DLBSSGP instead!\n");
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx);
diff --git a/include/osmocom/gprs/gprs_bssgp2.h b/include/osmocom/gprs/gprs_bssgp2.h
new file mode 100644
index 00000000..53e76e3a
--- /dev/null
+++ b/include/osmocom/gprs/gprs_bssgp2.h
@@ -0,0 +1,72 @@
+#pragma once
+#include <stdint.h>
+
+#include <osmocom/gprs/protocol/gsm_08_18.h>
+#include <osmocom/gprs/gprs_ns2.h>
+
+struct bssgp2_flow_ctrl;
+struct gprs_ns2_inst;
+struct gprs_ra_id;
+struct msgb;
+
+struct bssgp2_flow_ctrl {
+ uint8_t tag;
+ /* maximum bucket size (Bmax) in bytes */
+ uint64_t bucket_size_max;
+ /*! bucket leak rate in _bytes_ per second */
+ uint64_t bucket_leak_rate;
+ /* percentage how full the given bucket is */
+ uint8_t bucket_full_ratio;
+ bool bucket_full_ratio_present;
+ union {
+ /*! FC-BVC specifi members */
+ struct {
+ /*! default maximum bucket size per MS in bytes */
+ uint64_t bmax_default_ms;
+ /*! default bucket leak rate (R) for MS flow control bucket */
+ uint64_t r_default_ms;
+
+ /*! average milliseconds of queueing delay for a BVC */
+ uint32_t measurement;
+ bool measurement_present;
+ } bvc;
+ /*! FC-MS specifi members */
+ struct {
+ /*! TLLI of the MS */
+ uint32_t tlli;
+ } ms;
+ } u;
+};
+
+
+int bssgp2_nsi_tx_ptp(struct gprs_ns2_inst *nsi, uint16_t nsei, uint16_t bvci,
+ struct msgb *msg, uint32_t lsp);
+
+int bssgp2_nsi_tx_sig(struct gprs_ns2_inst *nsi, uint16_t nsei, struct msgb *msg, uint32_t lsp);
+
+struct msgb *bssgp2_enc_bvc_block(uint16_t bvci, enum gprs_bssgp_cause cause);
+
+struct msgb *bssgp2_enc_bvc_block_ack(uint16_t bvci);
+
+struct msgb *bssgp2_enc_bvc_unblock(uint16_t bvci);
+
+struct msgb *bssgp2_enc_bvc_unblock_ack(uint16_t bvci);
+
+struct msgb *bssgp2_enc_bvc_reset(uint16_t bvci, enum gprs_bssgp_cause cause,
+ const struct gprs_ra_id *ra_id, uint16_t cell_id,
+ const uint8_t *feat_bm, const uint8_t *ext_feat_bm);
+
+struct msgb *bssgp2_enc_bvc_reset_ack(uint16_t bvci, const struct gprs_ra_id *ra_id, uint16_t cell_id,
+ const uint8_t *feat_bm, const uint8_t *ext_feat_bm);
+
+struct msgb *bssgp2_enc_flush_ll(uint32_t tlli, uint16_t old_bvci,
+ const uint16_t *new_bvci, const uint16_t *nsei);
+struct msgb *bssgp2_enc_status(uint8_t cause, const uint16_t *bvci, const struct msgb *orig_msg, uint16_t max_pdu_len);
+
+
+int bssgp2_dec_fc_bvc(struct bssgp2_flow_ctrl *fc, const struct tlv_parsed *tp);
+struct msgb *bssgp2_enc_fc_bvc(const struct bssgp2_flow_ctrl *fc, enum bssgp_fc_granularity *gran);
+struct msgb *bssgp2_enc_fc_bvc_ack(uint8_t tag);
+int bssgp2_dec_fc_ms(struct bssgp2_flow_ctrl *fc, struct tlv_parsed *tp);
+struct msgb *bssgp2_enc_fc_ms(const struct bssgp2_flow_ctrl *fc, enum bssgp_fc_granularity *gran);
+struct msgb *bssgp2_enc_fc_ms_ack(uint32_t tlli, uint8_t tag);
diff --git a/include/osmocom/gprs/gprs_bssgp_bss.h b/include/osmocom/gprs/gprs_bssgp_bss.h
index f07ab526..ab62b669 100644
--- a/include/osmocom/gprs/gprs_bssgp_bss.h
+++ b/include/osmocom/gprs/gprs_bssgp_bss.h
@@ -57,6 +57,7 @@ int bssgp_tx_bvc_block(struct bssgp_bvc_ctx *bctx, uint8_t cause);
int bssgp_tx_bvc_unblock(struct bssgp_bvc_ctx *bctx);
int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause);
+int bssgp_tx_bvc_reset2(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause, bool add_cell_id);
int bssgp_tx_ul_ud(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
const uint8_t *qos_profile, struct msgb *llc_pdu);
diff --git a/include/osmocom/gprs/gprs_bssgp_rim.h b/include/osmocom/gprs/gprs_bssgp_rim.h
new file mode 100644
index 00000000..10ea58bd
--- /dev/null
+++ b/include/osmocom/gprs/gprs_bssgp_rim.h
@@ -0,0 +1,274 @@
+/*! \file gprs_bssgp.h
+ * GPRS BSSGP RIM protocol implementation as per 3GPP TS 48.018. */
+/*
+ * (C) 2020-2021 by sysmocom - s.f.m.c. GmbH
+ * Author: Philipp Maier <pmaier@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gprs/protocol/gsm_08_18.h>
+#include <osmocom/gprs/protocol/gsm_24_301.h>
+
+enum bssgp_rim_routing_info_discr {
+ BSSGP_RIM_ROUTING_INFO_GERAN,
+ BSSGP_RIM_ROUTING_INFO_UTRAN,
+ BSSGP_RIM_ROUTING_INFO_EUTRAN,
+};
+
+extern const struct value_string bssgp_rim_routing_info_discr_strs[];
+
+/*! Obtain a human-readable string for NACC Cause code */
+static inline const char *bssgp_rim_routing_info_discr_str(enum bssgp_rim_routing_info_discr val)
+{ return get_value_string(bssgp_rim_routing_info_discr_strs, val); }
+
+/*! BSSGP RIM Routing information, see also 3GPP TS 48.018, section 11.3.70 */
+struct bssgp_rim_routing_info {
+ enum bssgp_rim_routing_info_discr discr;
+ union {
+ struct {
+ struct gprs_ra_id raid;
+ uint16_t cid;
+ } geran;
+ struct {
+ struct gprs_ra_id raid;
+ uint16_t rncid;
+ } utran;
+ struct {
+ struct osmo_eutran_tai tai;
+ /* See also 3GPP TS 36.413 9.2.1.37 and 3GPP TS 36.401 */
+ uint8_t global_enb_id[8];
+ uint8_t global_enb_id_len;
+ } eutran;
+ };
+};
+
+/* The encoded result of the rim routing information is, depending on the
+ * address type (discr) of variable length. */
+#define BSSGP_RIM_ROUTING_INFO_MAXLEN 14
+
+char *bssgp_rim_ri_name_buf(char *buf, size_t buf_len, const struct bssgp_rim_routing_info *ri);
+const char *bssgp_rim_ri_name(const struct bssgp_rim_routing_info *ri);
+int bssgp_parse_rim_ri(struct bssgp_rim_routing_info *ri, const uint8_t *buf, unsigned int len);
+int bssgp_parse_rim_ra(struct bssgp_rim_routing_info *ri, const uint8_t *buf, unsigned int len, uint8_t discr);
+int bssgp_create_rim_ri(uint8_t *buf, const struct bssgp_rim_routing_info *ri);
+
+/* 3GPP TS 48.018, table 11.3.63.1.1: RAN-INFORMATION-REQUEST Application Container coding for NACC */
+struct bssgp_ran_inf_req_app_cont_nacc {
+ struct osmo_cell_global_id_ps reprt_cell;
+};
+
+int bssgp_dec_ran_inf_req_app_cont_nacc(struct bssgp_ran_inf_req_app_cont_nacc *cont, const uint8_t *buf, size_t len);
+int bssgp_enc_ran_inf_req_app_cont_nacc(uint8_t *buf, size_t len, const struct bssgp_ran_inf_req_app_cont_nacc *cont);
+
+/* Length of NACC system information, see also: 3GPP TS 48.018 11.3.63.2.1 */
+#define BSSGP_RIM_SI_LEN 21
+#define BSSGP_RIM_PSI_LEN 22
+
+/* 3GPP TS 48.018, table 11.3.63.2.1.a: RAN-INFORMATION Application Container coding for NACC */
+struct bssgp_ran_inf_app_cont_nacc {
+ struct osmo_cell_global_id_ps reprt_cell;
+ bool type_psi;
+ uint8_t num_si;
+
+ /* Pointer to system information messages */
+ const uint8_t *si[127];
+};
+
+int bssgp_dec_ran_inf_app_cont_nacc(struct bssgp_ran_inf_app_cont_nacc *cont, const uint8_t *buf, size_t len);
+int bssgp_enc_ran_inf_app_cont_nacc(uint8_t *buf, size_t len, const struct bssgp_ran_inf_app_cont_nacc *cont);
+
+/* 3GPP TS 48.018, table 11.3.64.1.b, NACC Cause coding */
+enum bssgp_nacc_cause {
+ BSSGP_NACC_CAUSE_UNSPEC,
+ BSSGP_NACC_CAUSE_SYNTAX_ERR,
+ BSSGP_NACC_CAUSE_RPRT_CELL_MISSMTCH,
+ BSSGP_NACC_CAUSE_SIPSI_TYPE_ERR,
+ BSSGP_NACC_CAUSE_SIPSI_LEN_ERR,
+ BSSGP_NACC_CAUSE_SIPSI_SET_ERR,
+};
+
+extern const struct value_string bssgp_nacc_cause_strs[];
+
+/*! Obtain a human-readable string for NACC Cause code */
+static inline const char *bssgp_nacc_cause_str(enum bssgp_nacc_cause val)
+{ return get_value_string(bssgp_nacc_cause_strs, val); }
+
+/* 3GPP TS 48.018, table 11.3.64.1.a, Application Error Container coding for NACC */
+struct bssgp_app_err_cont_nacc {
+ enum bssgp_nacc_cause nacc_cause;
+
+ /* Pointer to errornous application container */
+ const uint8_t *err_app_cont;
+ size_t err_app_cont_len;
+};
+
+int bssgp_dec_app_err_cont_nacc(struct bssgp_app_err_cont_nacc *cont, const uint8_t *buf, size_t len);
+int bssgp_enc_app_err_cont_nacc(uint8_t *buf, size_t len, const struct bssgp_app_err_cont_nacc *cont);
+
+/* 3GPP TS 48.018, table 11.3.61.b: RIM Application Identity coding */
+enum bssgp_ran_inf_app_id {
+ BSSGP_RAN_INF_APP_ID_NACC = 1,
+ BSSGP_RAN_INF_APP_ID_SI3 = 2,
+ BSSGP_RAN_INF_APP_ID_MBMS = 3,
+ BSSGP_RAN_INF_APP_ID_SON = 4,
+ BSSGP_RAN_INF_APP_ID_UTRA_SI = 5,
+};
+
+extern const struct value_string bssgp_ran_inf_app_id_strs[];
+
+/*! Obtain a human-readable string for RIM Application Identity code */
+static inline const char *bssgp_ran_inf_app_id_str(enum bssgp_ran_inf_app_id val)
+{ return get_value_string(bssgp_ran_inf_app_id_strs, val); }
+
+/* 3GPP TS 48.018, table 11.3.62a.1.b: RAN-INFORMATION-REQUEST RIM Container Contents */
+struct bssgp_ran_inf_req_rim_cont {
+ enum bssgp_ran_inf_app_id app_id;
+ uint32_t seq_num;
+ struct bssgp_rim_pdu_ind pdu_ind;
+ uint8_t prot_ver;
+
+ /* Nested application container */
+ union {
+ struct bssgp_ran_inf_req_app_cont_nacc app_cont_nacc;
+ /* TODO: add containers for Si3, MBMS, SON, UTRA-SI */
+ } u;
+
+ /* Pointer to SON-transfer application identity, only present if app_id is indicating "son-transfer",
+ * see also 3GPP TS 48.018, section 11.3.108 and 3GPP TS 36.413 annex B.1.1 */
+ const uint8_t *son_trans_app_id;
+ size_t son_trans_app_id_len;
+};
+
+int bssgp_dec_ran_inf_req_rim_cont(struct bssgp_ran_inf_req_rim_cont *cont, const uint8_t *buf, size_t len);
+int bssgp_enc_ran_inf_req_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_req_rim_cont *cont);
+
+/* 3GPP TS 48.018, table 11.3.62a.2.b: RAN-INFORMATION RIM Container Contents */
+struct bssgp_ran_inf_rim_cont {
+ enum bssgp_ran_inf_app_id app_id;
+ uint32_t seq_num;
+ struct bssgp_rim_pdu_ind pdu_ind;
+ uint8_t prot_ver;
+ bool app_err;
+
+ /* Nested application container */
+ union {
+ struct bssgp_ran_inf_app_cont_nacc app_cont_nacc;
+ struct bssgp_app_err_cont_nacc app_err_cont_nacc;
+ /* TODO: add containers for Si3, MBMS, SON, UTRA-SI */
+ } u;
+
+ /* Pointer to SON-transfer application identity, only present if app_id is indicating "son-transfer",
+ * see also 3GPP TS 48.018, section 11.3.108 and 3GPP TS 36.413 annex B.1.1 */
+ const uint8_t *son_trans_app_id;
+ size_t son_trans_app_id_len;
+};
+
+int bssgp_dec_ran_inf_rim_cont(struct bssgp_ran_inf_rim_cont *cont, const uint8_t *buf, size_t len);
+int bssgp_enc_ran_inf_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_rim_cont *cont);
+
+/* 3GPP TS 48.018, table 11.3.62a.3.b: RAN-INFORMATION-ACK RIM Container Contents */
+struct bssgp_ran_inf_ack_rim_cont {
+ enum bssgp_ran_inf_app_id app_id;
+ uint32_t seq_num;
+ uint8_t prot_ver;
+
+ /* Pointer to SON-transfer application identity, only present if app_id is indicating "son-transfer",
+ * see also 3GPP TS 48.018, section 11.3.108 and 3GPP TS 36.413 annex B.1.1 */
+ const uint8_t *son_trans_app_id;
+ size_t son_trans_app_id_len;
+};
+
+int bssgp_dec_ran_inf_ack_rim_cont(struct bssgp_ran_inf_ack_rim_cont *cont, const uint8_t *buf, size_t len);
+int bssgp_enc_ran_inf_ack_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_ack_rim_cont *cont);
+
+/* 3GPP TS 48.018, table 11.3.62a.4.b: RAN-INFORMATION-ERROR RIM Container Contents */
+struct bssgp_ran_inf_err_rim_cont {
+ enum bssgp_ran_inf_app_id app_id;
+ uint8_t cause;
+ uint8_t prot_ver;
+
+ /* Pointer to (encoded) errornous PDU,
+ * see also: 3GPP TS 48.018, section 11.3.24 */
+ const uint8_t *err_pdu;
+ size_t err_pdu_len;
+
+ /* Pointer to SON-transfer application identity, only present if app_id is indicating "son-transfer",
+ * see also 3GPP TS 48.018, section 11.3.108 and 3GPP TS 36.413 annex B.1.1 */
+ const uint8_t *son_trans_app_id;
+ size_t son_trans_app_id_len;
+};
+
+int bssgp_dec_ran_inf_err_rim_cont(struct bssgp_ran_inf_err_rim_cont *cont, const uint8_t *buf, size_t len);
+int bssgp_enc_ran_inf_err_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_err_rim_cont *cont);
+
+/* 3GPP TS 48.018, table 11.3.62a.5.b: RAN-INFORMATION-APPLICATION-ERROR RIM Container Contents */
+struct bssgp_ran_inf_app_err_rim_cont {
+ enum bssgp_ran_inf_app_id app_id;
+ uint32_t seq_num;
+ struct bssgp_rim_pdu_ind pdu_ind;
+ uint8_t prot_ver;
+
+ /* Nested application container */
+ union {
+ struct bssgp_app_err_cont_nacc app_err_cont_nacc;
+ /* TODO: add containers for Si3, MBMS, SON, UTRA-SI */
+ } u;
+};
+
+int bssgp_dec_ran_inf_app_err_rim_cont(struct bssgp_ran_inf_app_err_rim_cont *cont, const uint8_t *buf, size_t len);
+int bssgp_enc_ran_inf_app_err_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_app_err_rim_cont *cont);
+
+/* Chapter 10.6.1: RAN-INFORMATION-REQUEST */
+struct bssgp_ran_information_pdu {
+ struct bssgp_rim_routing_info routing_info_dest;
+ struct bssgp_rim_routing_info routing_info_src;
+
+ /* Encoded variant of the RIM container */
+ uint8_t rim_cont_iei;
+ const uint8_t *rim_cont;
+ unsigned int rim_cont_len;
+
+ /* Decoded variant of the RIM container */
+ bool decoded_present;
+ union {
+ struct bssgp_ran_inf_req_rim_cont req_rim_cont;
+ struct bssgp_ran_inf_rim_cont rim_cont;
+ struct bssgp_ran_inf_ack_rim_cont ack_rim_cont;
+ struct bssgp_ran_inf_err_rim_cont err_rim_cont;
+ struct bssgp_ran_inf_app_err_rim_cont app_err_rim_cont;
+ } decoded;
+
+ /* When receiving a PDU from BSSGP the encoded variant of the RIM
+ * container will always be present. The decoded variant will be
+ * present in addition whenever BSSGP was able to decode the container.
+ *
+ * When sending a PDU to BSSGP, then the decoded variant is used when
+ * it is available. The encoded variant (if present) will be ignored
+ * then. */
+};
+
+int bssgp_parse_rim_pdu(struct bssgp_ran_information_pdu *pdu, const struct msgb *msg);
+struct msgb *bssgp_encode_rim_pdu(const struct bssgp_ran_information_pdu *pdu);
+
+int bssgp_tx_rim(const struct bssgp_ran_information_pdu *pdu, uint16_t nsei);
+int bssgp_tx_rim_encoded(struct msgb *msg, uint16_t nsei);
diff --git a/include/osmocom/gprs/gprs_ns.h b/include/osmocom/gprs/gprs_ns.h
index 02faa506..af25825e 100644
--- a/include/osmocom/gprs/gprs_ns.h
+++ b/include/osmocom/gprs/gprs_ns.h
@@ -97,6 +97,8 @@ struct gprs_ns_inst {
uint32_t remote_ip;
uint16_t remote_port;
int dscp;
+ /*! IPA compatibility: NS-RESET/BLOCK/UNBLOCK even on IP-SNS */
+ bool use_reset_block_unblock;
} nsip;
/*! NS-over-FR-over-GRE-over-IP specific bits */
struct {
@@ -186,6 +188,11 @@ struct sockaddr_in;
/* main function for higher layers (BSSGP) to send NS messages */
int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg);
+/* Receive incoming NS message from underlying transport layer */
+int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
+ struct sockaddr_in *saddr, enum gprs_ns_ll ll);
+
+
int gprs_ns_tx_alive(struct gprs_nsvc *nsvc);
int gprs_ns_tx_alive_ack(struct gprs_nsvc *nsvc);
int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause);
@@ -195,7 +202,6 @@ int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc);
/* Listen for incoming GPRS packets via NS/FR/GRE */
int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi);
-struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci);
struct gprs_nsvc *gprs_nsvc_create2(struct gprs_ns_inst *nsi, uint16_t nsvci,
uint8_t sig_weight, uint8_t data_weight);
void gprs_nsvc_delete(struct gprs_nsvc *nsvc);
diff --git a/include/osmocom/gprs/gprs_ns2.h b/include/osmocom/gprs/gprs_ns2.h
new file mode 100644
index 00000000..7c7e2211
--- /dev/null
+++ b/include/osmocom/gprs/gprs_ns2.h
@@ -0,0 +1,276 @@
+/*! \file gprs_ns2.h */
+
+
+#pragma once
+
+#include <stdint.h>
+#include <netinet/in.h>
+
+#include <osmocom/core/prim.h>
+#include <osmocom/gprs/protocol/gsm_08_16.h>
+#include <osmocom/gprs/frame_relay.h>
+
+struct osmo_sockaddr;
+struct osmo_sockaddr_str;
+struct osmo_fr_network;
+
+struct gprs_ns2_inst;
+struct gprs_ns2_nse;
+struct gprs_ns2_vc;
+struct gprs_ns2_vc_bind;
+struct gprs_ns2_vc_driver;
+struct gprs_ns_ie_ip4_elem;
+struct gprs_ns_ie_ip6_elem;
+
+enum gprs_ns2_vc_mode {
+ /*! The VC will use RESET/BLOCK/UNBLOCK to start the connection and do ALIVE/ACK.
+ * This is what is needed for Frame Relay transport, and if you use a R97/R99 Gb
+ * interface over an IP transport (never standardized by 3GPP) */
+ GPRS_NS2_VC_MODE_BLOCKRESET,
+ /*! The VC will only use ALIVE/ACK (no RESET/BLOCK/UNBLOCK), which is for Gb-IP
+ * interface compliant to 3GPP Rel=4 or later. */
+ GPRS_NS2_VC_MODE_ALIVE,
+};
+
+enum gprs_ns2_dialect {
+ GPRS_NS2_DIALECT_UNDEF,
+ GPRS_NS2_DIALECT_STATIC_ALIVE,
+ GPRS_NS2_DIALECT_STATIC_RESETBLOCK,
+ GPRS_NS2_DIALECT_IPACCESS,
+ GPRS_NS2_DIALECT_SNS,
+};
+
+/*! Osmocom NS link layer types */
+enum gprs_ns2_ll {
+ GPRS_NS2_LL_UNDEF, /*!< undefined, used by vty */
+ GPRS_NS2_LL_UDP, /*!< NS/UDP/IP */
+ GPRS_NS2_LL_FR, /*!< NS/FR */
+ GPRS_NS2_LL_FR_GRE, /*!< NS/FR/GRE/IP */
+};
+
+/*! Osmocom NS primitives according to 48.016 5.2 Service primitives */
+enum gprs_ns2_prim {
+ GPRS_NS2_PRIM_UNIT_DATA,
+ GPRS_NS2_PRIM_CONGESTION,
+ GPRS_NS2_PRIM_STATUS,
+};
+
+extern const struct value_string gprs_ns2_prim_strs[];
+extern const struct value_string gprs_ns2_lltype_strs[];
+
+/*! Obtain a human-readable string for NS primitives */
+static inline const char *gprs_ns2_prim_str(enum gprs_ns2_prim val)
+{ return get_value_string(gprs_ns2_prim_strs, val); }
+
+/*! Obtain a human-readable string for NS link-layer type */
+static inline const char *gprs_ns2_lltype_str(enum gprs_ns2_ll val)
+{ return get_value_string(gprs_ns2_lltype_strs, val); }
+
+/*! Osmocom NS primitives according to 48.016 5.2.2.4 Service primitives */
+enum gprs_ns2_congestion_cause {
+ GPRS_NS2_CONG_CAUSE_BACKWARD_BEGIN,
+ GPRS_NS2_CONG_CAUSE_BACKWARD_END,
+ GPRS_NS2_CONG_CAUSE_FORWARD_BEGIN,
+ GPRS_NS2_CONG_CAUSE_FORWARD_END,
+};
+
+/*! Osmocom NS primitives according to 48.016 5.2.2.6 Service primitives */
+enum gprs_ns2_affecting_cause {
+ GPRS_NS2_AFF_CAUSE_VC_FAILURE,
+ GPRS_NS2_AFF_CAUSE_VC_RECOVERY,
+ GPRS_NS2_AFF_CAUSE_FAILURE,
+ GPRS_NS2_AFF_CAUSE_RECOVERY,
+ /* osmocom own causes */
+ GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED,
+ GPRS_NS2_AFF_CAUSE_SNS_FAILURE,
+ GPRS_NS2_AFF_CAUSE_SNS_NO_ENDPOINTS,
+ GPRS_NS2_AFF_CAUSE_MTU_CHANGE,
+};
+
+extern const struct value_string gprs_ns2_aff_cause_prim_strs[];
+
+/*! Obtain a human-readable string for NS affecting cause in primitives */
+static inline const char *gprs_ns2_aff_cause_prim_str(enum gprs_ns2_affecting_cause val)
+{ return get_value_string(gprs_ns2_aff_cause_prim_strs, val); }
+
+/*! Osmocom NS primitives according to 48.016 5.2.2.7 Service primitives */
+enum gprs_ns2_change_ip_endpoint {
+ GRPS_NS2_ENDPOINT_NO_CHANGE,
+ GPRS_NS2_ENDPOINT_REQUEST_CHANGE,
+ GPRS_NS2_ENDPOINT_CONFIRM_CHANGE,
+};
+
+extern const struct value_string gprs_ns2_cause_strs[];
+
+/*! Obtain a human-readable string for NS primitives */
+static inline const char *gprs_ns2_cause_str(enum ns_cause val)
+{ return get_value_string(gprs_ns2_cause_strs, val); }
+
+struct osmo_gprs_ns2_prim {
+ struct osmo_prim_hdr oph;
+
+ uint16_t nsei;
+ uint16_t bvci;
+
+ union {
+ struct {
+ enum gprs_ns2_change_ip_endpoint change;
+ uint32_t link_selector;
+ /* TODO: implement resource distribution
+ * add place holder for the link selector */
+ long long _resource_distribution_placeholder1;
+ long long _resource_distribution_placeholder2;
+ long long _resource_distribution_placeholder3;
+ } unitdata;
+ struct {
+ enum gprs_ns2_congestion_cause cause;
+ } congestion;
+ struct {
+ enum gprs_ns2_affecting_cause cause;
+ char *nsvc;
+ /* 48.016 5.2.2.6 transfer capability */
+ int transfer;
+ /* osmocom specific */
+ /* Persistent NSE/NSVC are configured by vty */
+ bool persistent;
+ /* Only true on the first time it's available.
+ * Allow the BSSGP layer to reset persistent NSE */
+ bool first;
+ /* MTU of a NS SDU. It's the lowest MTU of all (alive & dead) NSVCs */
+ uint16_t mtu;
+ } status;
+ } u;
+};
+
+/* instance */
+struct gprs_ns2_inst *gprs_ns2_instantiate(void *ctx, osmo_prim_cb cb, void *cb_data);
+void gprs_ns2_free(struct gprs_ns2_inst *inst);
+
+/* Entrypoint for primitives from the NS USER */
+int gprs_ns2_recv_prim(struct gprs_ns2_inst *nsi, struct osmo_prim_hdr *oph);
+
+/*! a callback to iterate over all NSVC */
+typedef int (*gprs_ns2_foreach_nsvc_cb)(struct gprs_ns2_vc *nsvc, void *ctx);
+
+int gprs_ns2_nse_foreach_nsvc(struct gprs_ns2_nse *nse,
+ gprs_ns2_foreach_nsvc_cb cb, void *cb_data);
+struct gprs_ns2_nse *gprs_ns2_nse_by_nsei(struct gprs_ns2_inst *nsi, uint16_t nsei);
+struct gprs_ns2_nse *gprs_ns2_create_nse(struct gprs_ns2_inst *nsi, uint16_t nsei,
+ enum gprs_ns2_ll linklayer,
+ enum gprs_ns2_dialect dialect);
+struct gprs_ns2_nse *gprs_ns2_create_nse2(struct gprs_ns2_inst *nsi, uint16_t nsei,
+ enum gprs_ns2_ll linklayer,
+ enum gprs_ns2_dialect dialect, bool local_sgsn_role);
+uint16_t gprs_ns2_nse_nsei(struct gprs_ns2_nse *nse);
+void gprs_ns2_free_nse(struct gprs_ns2_nse *nse);
+void gprs_ns2_free_nses(struct gprs_ns2_inst *nsi);
+
+/* create vc */
+void gprs_ns2_free_nsvc(struct gprs_ns2_vc *nsvc);
+void gprs_ns2_free_nsvcs(struct gprs_ns2_nse *nse);
+struct gprs_ns2_vc *gprs_ns2_nsvc_by_nsvci(struct gprs_ns2_inst *nsi, uint16_t nsvci);
+
+/* generic VL driver */
+struct gprs_ns2_vc_bind *gprs_ns2_bind_by_name(struct gprs_ns2_inst *nsi,
+ const char *name);
+
+/* IP VL driver */
+int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
+ const char *name,
+ const struct osmo_sockaddr *local,
+ int dscp,
+ struct gprs_ns2_vc_bind **result);
+struct gprs_ns2_vc_bind *gprs_ns2_ip_bind_by_sockaddr(struct gprs_ns2_inst *nsi,
+ const struct osmo_sockaddr *sockaddr);
+
+/* FR VL driver */
+struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
+ struct gprs_ns2_inst *nsi,
+ const char *netif);
+const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind);
+enum osmo_fr_role gprs_ns2_fr_bind_role(struct gprs_ns2_vc_bind *bind);
+int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
+ const char *name,
+ const char *netif,
+ struct osmo_fr_network *fr_network,
+ enum osmo_fr_role fr_role,
+ struct gprs_ns2_vc_bind **result);
+int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind);
+struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind, uint16_t dlci);
+struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
+ struct gprs_ns2_nse *nse,
+ uint16_t nsvci,
+ uint16_t dlci);
+struct gprs_ns2_vc *gprs_ns2_fr_connect2(struct gprs_ns2_vc_bind *bind,
+ uint16_t nsei,
+ uint16_t nsvci,
+ uint16_t dlci);
+
+/* create a VC connection */
+struct gprs_ns2_vc *gprs_ns2_ip_connect(struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *remote,
+ struct gprs_ns2_nse *nse,
+ uint16_t nsvci);
+
+struct gprs_ns2_vc *gprs_ns2_ip_connect2(struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *remote,
+ uint16_t nsei,
+ uint16_t nsvci,
+ enum gprs_ns2_dialect dialect);
+struct gprs_ns2_vc *gprs_ns2_ip_connect_inactive(struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *remote,
+ struct gprs_ns2_nse *nse,
+ uint16_t nsvci);
+void gprs_ns2_ip_bind_set_sns_weight(struct gprs_ns2_vc_bind *bind,
+ uint8_t signalling, uint8_t data);
+
+void gprs_ns2_free_bind(struct gprs_ns2_vc_bind *bind);
+void gprs_ns2_free_binds(struct gprs_ns2_inst *nsi);
+
+/* create a VC SNS connection */
+int gprs_ns2_sns_count(struct gprs_ns2_nse *nse);
+int gprs_ns2_sns_add_endpoint(struct gprs_ns2_nse *nse,
+ const struct osmo_sockaddr *saddr);
+int gprs_ns2_sns_del_endpoint(struct gprs_ns2_nse *nse,
+ const struct osmo_sockaddr *saddr);
+int gprs_ns2_sns_add_bind(struct gprs_ns2_nse *nse, struct gprs_ns2_vc_bind *bind);
+int gprs_ns2_sns_del_bind(struct gprs_ns2_nse *nse, struct gprs_ns2_vc_bind *bind);
+const struct osmo_sockaddr *gprs_ns2_nse_sns_remote(struct gprs_ns2_nse *nse);
+
+const struct osmo_sockaddr *gprs_ns2_ip_vc_remote(const struct gprs_ns2_vc *nsvc);
+const struct osmo_sockaddr *gprs_ns2_ip_vc_local(const struct gprs_ns2_vc *nsvc);
+bool gprs_ns2_ip_vc_equal(const struct gprs_ns2_vc *nsvc,
+ const struct osmo_sockaddr *local,
+ const struct osmo_sockaddr *remote,
+ uint16_t nsvci);
+const struct osmo_sockaddr *gprs_ns2_ip_bind_sockaddr(struct gprs_ns2_vc_bind *bind);
+int gprs_ns2_is_ip_bind(struct gprs_ns2_vc_bind *bind);
+int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp);
+int gprs_ns2_ip_bind_set_priority(struct gprs_ns2_vc_bind *bind, uint8_t priority);
+struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_bind(
+ struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *rem_addr);
+
+int gprs_ns2_frgre_bind(struct gprs_ns2_inst *nsi,
+ const char *name,
+ const struct osmo_sockaddr *local,
+ int dscp,
+ struct gprs_ns2_vc_bind **result);
+int gprs_ns2_is_frgre_bind(struct gprs_ns2_vc_bind *bind);
+uint16_t gprs_ns2_fr_nsvc_dlci(const struct gprs_ns2_vc *nsvc);
+
+struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_nse(
+ struct gprs_ns2_nse *nse,
+ const struct osmo_sockaddr *sockaddr);
+void gprs_ns2_start_alive_all_nsvcs(struct gprs_ns2_nse *nse);
+
+/* VC information */
+const char *gprs_ns2_ll_str(struct gprs_ns2_vc *nsvc);
+char *gprs_ns2_ll_str_buf(char *buf, size_t buf_len, struct gprs_ns2_vc *nsvc);
+char *gprs_ns2_ll_str_c(const void *ctx, struct gprs_ns2_vc *nsvc);
+const char *gprs_ns2_nsvc_state_name(struct gprs_ns2_vc *nsvc);
+
+/* vty */
+int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi);
+
+/*! @} */
diff --git a/include/osmocom/gprs/gprs_ns_frgre.h b/include/osmocom/gprs/gprs_ns_frgre.h
index d48ce086..8cf54c76 100644
--- a/include/osmocom/gprs/gprs_ns_frgre.h
+++ b/include/osmocom/gprs/gprs_ns_frgre.h
@@ -2,4 +2,7 @@
#pragma once
+struct gprs_nsvc;
+struct msgb;
+
int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg);
diff --git a/include/osmocom/gprs/gprs_rlc.h b/include/osmocom/gprs/gprs_rlc.h
index b74f9e40..060424ae 100644
--- a/include/osmocom/gprs/gprs_rlc.h
+++ b/include/osmocom/gprs/gprs_rlc.h
@@ -1,54 +1,4 @@
-/*! \file gprs_rlc.h */
-
#pragma once
-#include <stdint.h>
-
-/*! Structure for CPS coding and puncturing scheme (TS 04.60 10.4.8a) */
-struct egprs_cps {
- uint8_t bits;
- uint8_t mcs;
- uint8_t p[2];
-};
-
-/*! CPS puncturing table selection (TS 04.60 10.4.8a) */
-enum egprs_cps_punc {
- EGPRS_CPS_P1,
- EGPRS_CPS_P2,
- EGPRS_CPS_P3,
- EGPRS_CPS_NONE = -1,
-};
-
-/*! EGPRS header types (TS 04.60 10.0a.2) */
-enum egprs_hdr_type {
- EGPRS_HDR_TYPE1,
- EGPRS_HDR_TYPE2,
- EGPRS_HDR_TYPE3,
-};
-
-enum osmo_gprs_cs {
- OSMO_GPRS_CS_NONE,
- OSMO_GPRS_CS1,
- OSMO_GPRS_CS2,
- OSMO_GPRS_CS3,
- OSMO_GPRS_CS4,
- OSMO_GPRS_MCS1,
- OSMO_GPRS_MCS2,
- OSMO_GPRS_MCS3,
- OSMO_GPRS_MCS4,
- OSMO_GPRS_MCS5,
- OSMO_GPRS_MCS6,
- OSMO_GPRS_MCS7,
- OSMO_GPRS_MCS8,
- OSMO_GPRS_MCS9,
- _NUM_OSMO_GPRS_CS
-};
-
-int egprs_get_cps(struct egprs_cps *cps, uint8_t type, uint8_t bits);
-
-int osmo_gprs_ul_block_size_bits(enum osmo_gprs_cs cs);
-int osmo_gprs_dl_block_size_bits(enum osmo_gprs_cs cs);
-int osmo_gprs_ul_block_size_bytes(enum osmo_gprs_cs cs);
-int osmo_gprs_dl_block_size_bytes(enum osmo_gprs_cs cs);
-enum osmo_gprs_cs osmo_gprs_ul_cs_by_block_bytes(uint8_t block_size);
-enum osmo_gprs_cs osmo_gprs_dl_cs_by_block_bytes(uint8_t block_size);
+#pragma message "Header osmocom/gprs/gprs_rlc.h is deprecated, include osmocom/gsm/protocol/gsm_44_060.h instead"
+#include <osmocom/gsm/protocol/gsm_44_060.h>
diff --git a/include/osmocom/gprs/protocol/Makefile.am b/include/osmocom/gprs/protocol/Makefile.am
new file mode 100644
index 00000000..e69527d0
--- /dev/null
+++ b/include/osmocom/gprs/protocol/Makefile.am
@@ -0,0 +1,8 @@
+osmogprsproto_HEADERS = \
+ gsm_04_60.h \
+ gsm_08_16.h \
+ gsm_08_18.h \
+ gsm_24_301.h \
+ $(NULL)
+
+osmogprsprotodir = $(includedir)/osmocom/gprs/protocol
diff --git a/include/osmocom/gprs/protocol/gsm_04_60.h b/include/osmocom/gprs/protocol/gsm_04_60.h
index f592b14f..c2c11a7d 100644
--- a/include/osmocom/gprs/protocol/gsm_04_60.h
+++ b/include/osmocom/gprs/protocol/gsm_04_60.h
@@ -1,353 +1,4 @@
-/*! \file gsm_04_60.h
- * General Packet Radio Service (GPRS).
- * Radio Link Control / Medium Access Control (RLC/MAC) protocol
- * 3GPP TS 04.60 version 8.27.0 Release 1999
- */
-
#pragma once
-#include <stdint.h>
-#include <osmocom/core/endian.h>
-
-#if OSMO_IS_LITTLE_ENDIAN == 1
-/* TS 04.60 10.3a.4.1.1 */
-struct gprs_rlc_ul_header_egprs_1 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t r:1,
- si:1,
- cv:4,
- tfi_hi:2;
- uint8_t tfi_lo:3,
- bsn1_hi:5;
- uint8_t bsn1_lo:6,
- bsn2_hi:2;
- uint8_t bsn2_lo:8;
- uint8_t cps:5,
- rsb:1,
- pi:1,
- spare_hi:1;
- uint8_t spare_lo:6,
- dummy:2;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:2, cv:4, si:1, r:1;
- uint8_t bsn1_hi:5, tfi_lo:3;
- uint8_t bsn2_hi:2, bsn1_lo:6;
- uint8_t bsn2_lo:8;
- uint8_t spare_hi:1, pi:1, rsb:1, cps:5;
- uint8_t dummy:2, spare_lo:6;
-#endif
-} __attribute__ ((packed));
-
-/* TS 04.60 10.3a.4.2.1 */
-struct gprs_rlc_ul_header_egprs_2 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t r:1,
- si:1,
- cv:4,
- tfi_hi:2;
- uint8_t tfi_lo:3,
- bsn1_hi:5;
- uint8_t bsn1_lo:6,
- cps_hi:2;
- uint8_t cps_lo:1,
- rsb:1,
- pi:1,
- spare_hi:5;
- uint8_t spare_lo:5,
- dummy:3;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:2, cv:4, si:1, r:1;
- uint8_t bsn1_hi:5, tfi_lo:3;
- uint8_t cps_hi:2, bsn1_lo:6;
- uint8_t spare_hi:5, pi:1, rsb:1, cps_lo:1;
- uint8_t dummy:3, spare_lo:5;
-#endif
-} __attribute__ ((packed));
-
-/* TS 04.60 10.3a.4.3.1 */
-struct gprs_rlc_ul_header_egprs_3 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t r:1,
- si:1,
- cv:4,
- tfi_hi:2;
- uint8_t tfi_lo:3,
- bsn1_hi:5;
- uint8_t bsn1_lo:6,
- cps_hi:2;
- uint8_t cps_lo:2,
- spb:2,
- rsb:1,
- pi:1,
- spare:1,
- dummy:1;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:2, cv:4, si:1, r:1;
- uint8_t bsn1_hi:5, tfi_lo:3;
- uint8_t cps_hi:2, bsn1_lo:6;
- uint8_t dummy:1, spare:1, pi:1, rsb:1, spb:2, cps_lo:2;
-#endif
-} __attribute__ ((packed));
-
-struct gprs_rlc_dl_header_egprs_1 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t usf:3,
- es_p:2,
- rrbp:2,
- tfi_hi:1;
- uint8_t tfi_lo:4,
- pr:2,
- bsn1_hi:2;
- uint8_t bsn1_mid:8;
- uint8_t bsn1_lo:1,
- bsn2_hi:7;
- uint8_t bsn2_lo:3,
- cps:5;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
- uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
- uint8_t bsn1_mid:8;
- uint8_t bsn2_hi:7, bsn1_lo:1;
- uint8_t cps:5, bsn2_lo:3;
-#endif
-} __attribute__ ((packed));
-
-struct gprs_rlc_dl_header_egprs_2 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t usf:3,
- es_p:2,
- rrbp:2,
- tfi_hi:1;
- uint8_t tfi_lo:4,
- pr:2,
- bsn1_hi:2;
- uint8_t bsn1_mid:8;
- uint8_t bsn1_lo:1,
- cps:3,
- dummy:4;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
- uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
- uint8_t bsn1_mid:8;
- uint8_t dummy:4, cps:3, bsn1_lo:1;
-#endif
-} __attribute__ ((packed));
-
-struct gprs_rlc_dl_header_egprs_3 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t usf:3,
- es_p:2,
- rrbp:2,
- tfi_hi:1;
- uint8_t tfi_lo:4,
- pr:2,
- bsn1_hi:2;
- uint8_t bsn1_mid:8;
- uint8_t bsn1_lo:1,
- cps:4,
- spb:2,
- dummy:1;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
- uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
- uint8_t bsn1_mid:8;
- uint8_t dummy:1, spb:2, cps:4, bsn1_lo:1;
-#endif
-} __attribute__ ((packed));
-#else
-/* TS 04.60 10.3a.4.1.1 */
-struct gprs_rlc_ul_header_egprs_1 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t tfi_hi:2,
- cv:4,
- si:1,
- r:1;
- uint8_t bsn1_hi:5,
- tfi_lo:3;
- uint8_t bsn2_hi:2,
- bsn1_lo:6;
- uint8_t bsn2_lo:8;
- uint8_t spare_hi:1,
- pi:1,
- rsb:1,
- cps:5;
- uint8_t dummy:2,
- spare_lo:6;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t r:1, si:1, cv:4, tfi_hi:2;
- uint8_t tfi_lo:3, bsn1_hi:5;
- uint8_t bsn1_lo:6, bsn2_hi:2;
- uint8_t bsn2_lo:8;
- uint8_t cps:5, rsb:1, pi:1, spare_hi:1;
- uint8_t spare_lo:6, dummy:2;
-#endif
-} __attribute__ ((packed));
-
-/* TS 04.60 10.3a.4.2.1 */
-struct gprs_rlc_ul_header_egprs_2 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t tfi_hi:2,
- cv:4,
- si:1,
- r:1;
- uint8_t bsn1_hi:5,
- tfi_lo:3;
- uint8_t cps_hi:2,
- bsn1_lo:6;
- uint8_t spare_hi:5,
- pi:1,
- rsb:1,
- cps_lo:1;
- uint8_t dummy:3,
- spare_lo:5;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t r:1, si:1, cv:4, tfi_hi:2;
- uint8_t tfi_lo:3, bsn1_hi:5;
- uint8_t bsn1_lo:6, cps_hi:2;
- uint8_t cps_lo:1, rsb:1, pi:1, spare_hi:5;
- uint8_t spare_lo:5, dummy:3;
-#endif
-} __attribute__ ((packed));
-
-/* TS 04.60 10.3a.4.3.1 */
-struct gprs_rlc_ul_header_egprs_3 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t tfi_hi:2,
- cv:4,
- si:1,
- r:1;
- uint8_t bsn1_hi:5,
- tfi_lo:3;
- uint8_t cps_hi:2,
- bsn1_lo:6;
- uint8_t dummy:1,
- spare:1,
- pi:1,
- rsb:1,
- spb:2,
- cps_lo:2;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t r:1, si:1, cv:4, tfi_hi:2;
- uint8_t tfi_lo:3, bsn1_hi:5;
- uint8_t bsn1_lo:6, cps_hi:2;
- uint8_t cps_lo:2, spb:2, rsb:1, pi:1, spare:1, dummy:1;
-#endif
-} __attribute__ ((packed));
-
-struct gprs_rlc_dl_header_egprs_1 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t tfi_hi:1,
- rrbp:2,
- es_p:2,
- usf:3;
- uint8_t bsn1_hi:2,
- pr:2,
- tfi_lo:4;
- uint8_t bsn1_mid:8;
- uint8_t bsn2_hi:7,
- bsn1_lo:1;
- uint8_t cps:5,
- bsn2_lo:3;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t usf:3, es_p:2, rrbp:2, tfi_hi:1;
- uint8_t tfi_lo:4, pr:2, bsn1_hi:2;
- uint8_t bsn1_mid:8;
- uint8_t bsn1_lo:1, bsn2_hi:7;
- uint8_t bsn2_lo:3, cps:5;
-#endif
-} __attribute__ ((packed));
-
-struct gprs_rlc_dl_header_egprs_2 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t tfi_hi:1,
- rrbp:2,
- es_p:2,
- usf:3;
- uint8_t bsn1_hi:2,
- pr:2,
- tfi_lo:4;
- uint8_t bsn1_mid:8;
- uint8_t dummy:4,
- cps:3,
- bsn1_lo:1;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t usf:3, es_p:2, rrbp:2, tfi_hi:1;
- uint8_t tfi_lo:4, pr:2, bsn1_hi:2;
- uint8_t bsn1_mid:8;
- uint8_t bsn1_lo:1, cps:3, dummy:4;
-#endif
-} __attribute__ ((packed));
-
-struct gprs_rlc_dl_header_egprs_3 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t tfi_hi:1,
- rrbp:2,
- es_p:2,
- usf:3;
- uint8_t bsn1_hi:2,
- pr:2,
- tfi_lo:4;
- uint8_t bsn1_mid:8;
- uint8_t dummy:1,
- spb:2,
- cps:4,
- bsn1_lo:1;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- uint8_t usf:3, es_p:2, rrbp:2, tfi_hi:1;
- uint8_t tfi_lo:4, pr:2, bsn1_hi:2;
- uint8_t bsn1_mid:8;
- uint8_t bsn1_lo:1, cps:4, spb:2, dummy:1;
-#endif
-} __attribute__ ((packed));
-#endif
-
-/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */
-enum osmo_gprs_nmo {
- GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */
- GPRS_NMO_II = 1, /* all paging on CCCH */
- GPRS_NMO_III = 2, /* no paging coordination */
-};
-
-/* TS 04.60 12.24 */
-struct osmo_gprs_cell_options {
- enum osmo_gprs_nmo nmo;
- /* T3168: wait for packet uplink assignment message */
- uint32_t t3168; /* in milliseconds */
- /* T3192: wait for release of the TBF after reception of the final block */
- uint32_t t3192; /* in milliseconds */
- uint32_t drx_timer_max;/* in seconds */
- uint32_t bs_cv_max;
- uint8_t supports_egprs_11bit_rach;
- bool ctrl_ack_type_use_block; /* use PACKET CONTROL ACKNOWLEDGMENT */
-
- uint8_t ext_info_present;
- struct {
- uint8_t egprs_supported;
- uint8_t use_egprs_p_ch_req;
- uint8_t bep_period;
- uint8_t pfc_supported;
- uint8_t dtm_supported;
- uint8_t bss_paging_coordination;
- } ext_info;
-};
-
-/* TS 04.60 Table 12.9.2 */
-struct osmo_gprs_power_ctrl_pars {
- uint8_t alpha;
- uint8_t t_avg_w;
- uint8_t t_avg_t;
- uint8_t pc_meas_chan;
- uint8_t n_avg_i;
-};
+#pragma message "Header osmocom/gprs/protocol/gsm_04_60.h is deprecated, include osmocom/gsm/protocol/gsm_44_060.h instead"
+#include <osmocom/gsm/protocol/gsm_44_060.h>
diff --git a/include/osmocom/gprs/protocol/gsm_08_16.h b/include/osmocom/gprs/protocol/gsm_08_16.h
index 95efcb6d..f98f68dd 100644
--- a/include/osmocom/gprs/protocol/gsm_08_16.h
+++ b/include/osmocom/gprs/protocol/gsm_08_16.h
@@ -6,6 +6,8 @@
#pragma once
#include <stdint.h>
+#include <arpa/inet.h>
+#include <osmocom/core/utils.h>
/*! \addtogroup libgb
* @{
@@ -26,6 +28,14 @@ struct gprs_ns_ie_ip4_elem {
uint8_t data_weight;
} __attribute__ ((packed));
+/*! Section 10.3.2d List of IP6 Elements */
+struct gprs_ns_ie_ip6_elem {
+ struct in6_addr ip_addr;
+ uint16_t udp_port;
+ uint8_t sig_weight;
+ uint8_t data_weight;
+} __attribute__ ((packed));
+
extern const struct value_string gprs_ns_pdu_strings[];
/*! NS PDU Type (TS 08.16, Section 10.3.7, Table 14) */
diff --git a/include/osmocom/gprs/protocol/gsm_08_18.h b/include/osmocom/gprs/protocol/gsm_08_18.h
index af6caf32..1152eb6c 100644
--- a/include/osmocom/gprs/protocol/gsm_08_18.h
+++ b/include/osmocom/gprs/protocol/gsm_08_18.h
@@ -1,24 +1,32 @@
/*! \file gsm_08_18.h */
+/* Updated to reflect TS 48.018 version 15.0.0 Release 15 */
#pragma once
#include <stdint.h>
+#include <osmocom/core/endian.h>
/*! Fixed BVCI definitions (Section 5.4.1) */
#define BVCI_SIGNALLING 0x0000
#define BVCI_PTM 0x0001
+/* typo backwards compatiblity */
+#define BSSGP_PDUT_RA_CAPA_UDPATE BSSGP_PDUT_RA_CAPA_UPDATE
+
/*! BSSGP PDU types (Section 11.3.26 / Table 11.27) */
enum bssgp_pdu_type {
/* PDUs between RL and BSSGP SAPs */
BSSGP_PDUT_DL_UNITDATA = 0x00,
BSSGP_PDUT_UL_UNITDATA = 0x01,
BSSGP_PDUT_RA_CAPABILITY = 0x02,
- BSSGP_PDUT_PTM_UNITDATA = 0x03,
+ /* PDUs between MBMS SAPs */
+ BSSGP_PDUT_PTM_UNITDATA = 0x03, /* reserved in later specs */
+ BSSGP_PDUT_DL_MMBS_UNITDATA = 0x04,
+ BSSGP_PDUT_UL_MMBS_UNITDATA = 0x05,
/* PDUs between GMM SAPs */
BSSGP_PDUT_PAGING_PS = 0x06,
BSSGP_PDUT_PAGING_CS = 0x07,
- BSSGP_PDUT_RA_CAPA_UDPATE = 0x08,
+ BSSGP_PDUT_RA_CAPA_UPDATE = 0x08,
BSSGP_PDUT_RA_CAPA_UPDATE_ACK = 0x09,
BSSGP_PDUT_RADIO_STATUS = 0x0a,
BSSGP_PDUT_SUSPEND = 0x0b,
@@ -27,6 +35,11 @@ enum bssgp_pdu_type {
BSSGP_PDUT_RESUME = 0x0e,
BSSGP_PDUT_RESUME_ACK = 0x0f,
BSSGP_PDUT_RESUME_NACK = 0x10,
+ BSSGP_PDUT_PAGING_PS_REJECT = 0x11,
+ BSSGP_PDUT_DUMMY_PAGING_PS = 0x12,
+ BSSGP_PDUT_DUMMY_PAGING_PS_RESP = 0x13,
+ BSSGP_PDUT_MS_REGISTR_ENQ = 0x14,
+ BSSGP_PDUT_MS_REGISTR_ENQ_RESP = 0x15,
/* PDus between NM SAPs */
BSSGP_PDUT_BVC_BLOCK = 0x20,
BSSGP_PDUT_BVC_BLOCK_ACK = 0x21,
@@ -41,8 +54,11 @@ enum bssgp_pdu_type {
BSSGP_PDUT_FLUSH_LL = 0x2a,
BSSGP_PDUT_FLUSH_LL_ACK = 0x2b,
BSSGP_PDUT_LLC_DISCARD = 0x2c,
+ BSSGP_PDUT_FLOW_CONTROL_PFC = 0x2d,
+ BSSGP_PDUT_FLOW_CONTROL_PFC_ACK = 0x2e,
BSSGP_PDUT_SGSN_INVOKE_TRACE = 0x40,
BSSGP_PDUT_STATUS = 0x41,
+ BSSGP_PDUT_OVERLOAD = 0x42,
/* PDUs between PFM SAP's */
BSSGP_PDUT_DOWNLOAD_BSS_PFC = 0x50,
BSSGP_PDUT_CREATE_BSS_PFC = 0x51,
@@ -52,6 +68,35 @@ enum bssgp_pdu_type {
BSSGP_PDUT_MODIFY_BSS_PFC_ACK = 0x55,
BSSGP_PDUT_DELETE_BSS_PFC = 0x56,
BSSGP_PDUT_DELETE_BSS_PFC_ACK = 0x57,
+ BSSGP_PDUT_DELETE_BSS_PFC_REQ = 0x58,
+ BSSGP_PDUT_PS_HO_REQUIRED = 0x59,
+ BSSGP_PDUT_PS_HO_REQUIRED_ACK = 0x5a,
+ BSSGP_PDUT_PS_HO_REQUIRED_NACK = 0x5b,
+ BSSGP_PDUT_PS_HO_REQUEST = 0x5c,
+ BSSGP_PDUT_PS_HO_REQUEST_ACK = 0x5d,
+ BSSGP_PDUT_PS_HO_REQUEST_NACK = 0x5e,
+ BSSGP_PDUT_PS_HO_COMPLETE = 0x91,
+ BSSGP_PDUT_PS_HO_CANCEL = 0x92,
+ BSSGP_PDUT_PS_HO_COMPLETE_ACK = 0x93,
+ /* PDUs between LCS SAPs */
+ BSSGP_PDUT_PERFORM_LOC_REQ = 0x60,
+ BSSGP_PDUT_PERFORM_LOC_RESP = 0x61,
+ BSSGP_PDUT_PERFORM_LOC_ABORT = 0x62,
+ BSSGP_PDUT_POSITION_COMMAND = 0x63,
+ BSSGP_PDUT_POSITION_RESPONSE = 0x64,
+ /* PDUs between RIM SAPs */
+ BSSGP_PDUT_RAN_INFO = 0x70,
+ BSSGP_PDUT_RAN_INFO_REQ = 0x71,
+ BSSGP_PDUT_RAN_INFO_ACK = 0x72,
+ BSSGP_PDUT_RAN_INFO_ERROR = 0x73,
+ BSSGP_PDUT_RAN_INFO_APP_ERROR = 0x74,
+ /* PDUs between MBMS SAPs */
+ BSSGP_PDUT_MBMS_START_REQ = 0x80,
+ BSSGP_PDUT_MBMS_START_RESP = 0x81,
+ BSSGP_PDUT_MBMS_STOP_REQ = 0x82,
+ BSSGP_PDUT_MBMS_STOP_RESP = 0x83,
+ BSSGP_PDUT_MBMS_UPDATE_REQ = 0x84,
+ BSSGP_PDUT_MBMS_UPDATE_RESP = 0x85,
};
/*! BSSGP User-Data header (Section 10.2.1 and 10.2.2) */
@@ -68,7 +113,7 @@ struct bssgp_normal_hdr {
uint8_t data[0]; /*!< optional/conditional IEs as TLVs */
};
-/*! BSSGP Information Element Identifiers */
+/*! BSSGP Information Element Identifiers (Section 11.3 / Table 11.3) */
enum bssgp_iei_type {
BSSGP_IE_ALIGNMENT = 0x00,
BSSGP_IE_BMAX_DEFAULT_MS = 0x01,
@@ -116,6 +161,105 @@ enum bssgp_iei_type {
BSSGP_IE_FEATURE_BITMAP = 0x3b,
BSSGP_IE_BUCKET_FULL_RATIO = 0x3c,
BSSGP_IE_SERVICE_UTRAN_CCO = 0x3d,
+ BSSGP_IE_NSEI = 0x3e,
+ BSSGP_IE_RRLP_APDU = 0x3f,
+ BSSGP_IE_LCS_QOS = 0x40,
+ BSSGP_IE_LCS_CLIENT_TYPE = 0x41,
+ BSSGP_IE_REQUESTED_GPS_AST_DATA = 0x42,
+ BSSGP_IE_LOCATION_TYPE = 0x43,
+ BSSGP_IE_LOCATION_ESTIMATE = 0x44,
+ BSSGP_IE_POSITIONING_DATA = 0x45,
+ BSSGP_IE_DECIPHERING_KEYS = 0x46,
+ BSSGP_IE_LCS_PRIORITY = 0x47,
+ BSSGP_IE_LCS_CAUSE = 0x48,
+ BSSGP_IE_LCS_CAPABILITY = 0x49,
+ BSSGP_IE_RRLP_FLAGS = 0x4a,
+ BSSGP_IE_RIM_APP_IDENTITY = 0x4b,
+ BSSGP_IE_RIM_SEQ_NR = 0x4c,
+ BSSGP_IE_RIM_REQ_APP_CONTAINER = 0x4d,
+ BSSGP_IE_RAN_INFO_APP_CONTAINER = 0x4e,
+ BSSGP_IE_RIM_PDU_INDICATIONS = 0x4f,
+ BSSGP_IE_PFC_FLOW_CTRL_PARAMS = 0x52,
+ BSSGP_IE_GLOBAL_CN_ID = 0x53,
+ BSSGP_IE_RIM_ROUTING_INFO = 0x54,
+ BSSGP_IE_RIM_PROTOCOL_VERSION = 0x55,
+ BSSGP_IE_APP_ERROR_CONTAINER = 0x56,
+ BSSGP_IE_RI_REQ_RIM_CONTAINER = 0x57,
+ BSSGP_IE_RI_RIM_CONTAINER = 0x58,
+ BSSGP_IE_RI_APP_ERROR_RIM_CONT = 0x59,
+ BSSGP_IE_RI_ACK_RIM_CONTAINER = 0x5a,
+ BSSGP_IE_RI_ERROR_RIM_COINTAINER= 0x5b,
+ BSSGP_IE_TMGI = 0x5c,
+ BSSGP_IE_MBMS_SESSION_ID = 0x5d,
+ BSSGP_IE_MBMS_SESSION_DURATION = 0x5e,
+ BSSGP_IE_MBMS_SA_ID_LIST = 0x5f,
+ BSSGP_IE_MBMS_RESPONSE = 0x60,
+ BSSGP_IE_MBMS_RA_LIST = 0x61,
+ BSSGP_IE_MBMS_SESSION_INFO = 0x62,
+ BSSGP_IE_MBMS_STOP_CAUSE = 0x63,
+ BSSGP_IE_SBSS_TO_TBSS_TR_CONT = 0x64,
+ BSSGP_IE_TBSS_TO_SBSS_TR_CONT = 0x65,
+ BSSGP_IE_NAS_CONT_FOR_PS_HO = 0x66,
+ BSSGP_IE_PFC_TO_BE_SETUP_LIST = 0x67,
+ BSSGP_IE_LIST_OF_SETUP_PFC = 0x68,
+ BSSGP_IE_EXT_FEATURE_BITMAP = 0x69,
+ BSSGP_IE_SRC_TO_TGT_TR_CONT = 0x6a,
+ BSSGP_IE_TGT_TO_SRC_TR_CONT = 0x6b,
+ BSSGP_IE_NC_ID = 0x6c,
+ BSSGP_IE_PAGE_MODE = 0x6d,
+ BSSGP_IE_CONTAINER_ID = 0x6e,
+ BSSGP_IE_GLOBAL_TFI = 0x6f,
+ BSSGP_IE_IMEI = 0x70,
+ BSSGP_IE_TIME_TO_MBMS_DATA_XFR = 0x71,
+ BSSGP_IE_MBMS_SESSION_REP_NR = 0x72,
+ BSSGP_IE_INTER_RAT_HO_INFO = 0x73,
+ BSSGP_IE_PS_HO_COMMAND = 0x74,
+ BSSGP_IE_PS_HO_INDICATIONS = 0x75,
+ BSSGP_IE_SI_PSI_CONTAINER = 0x76,
+ BSSGP_IE_ACTIVE_PFC_LIST = 0x77,
+ BSSGP_IE_VELOCITY_DATA = 0x78,
+ BSSGP_IE_DTM_HO_COMMAND = 0x79,
+ BSSGP_IE_CS_INDICATION = 0x7a,
+ BSSGP_IE_RQD_GANNS_AST_DATA = 0x7b,
+ BSSGP_IE_GANSS_LOCATION_TYPE = 0x7c,
+ BSSGP_IE_GANSS_POSITIONING_DATA = 0x7d,
+ BSSGP_IE_FLOW_CTRL_GRANULARITY = 0x7e,
+ BSSGP_IE_ENB_ID = 0x7f,
+ BSSGP_IE_EUTRAN_IRAT_HO_INFO = 0x80,
+ BSSGP_IE_SUB_PID4RAT_FREQ_PRIO = 0x81,
+ BSSGP_IE_REQ4IRAT_HO_INFO = 0x82,
+ BSSGP_IE_RELIABLE_IRAT_HO_INFO = 0x83,
+ BSSGP_IE_SON_TRANSFER_APP_ID = 0x84,
+ BSSGP_IE_CSG_ID = 0x85,
+ BSSGP_IE_TAC = 0x86,
+ BSSGP_IE_REDIRECT_ATTEMPT_FLAG = 0x87,
+ BSSGP_IE_REDIRECTION_INDICATION = 0x88,
+ BSSGP_IE_REDIRECTION_COMPLETED = 0x89,
+ BSSGP_IE_UNCONF_SEND_STATE_VAR = 0x8a,
+ BSSGP_IE_IRAT_MEASUREMENT_CONF = 0x8b,
+ BSSGP_IE_SCI = 0x8c,
+ BSSGP_IE_GGSN_PGW_LOCATION = 0x8d,
+ BSSGP_IE_SELECTED_PLMN_ID = 0x8e,
+ BSSGP_IE_PRIO_CLASS_IND = 0x8f,
+ BSSGP_IE_SOURCE_CELL_ID = 0x90,
+ BSSGP_IE_IRAT_MEAS_CFG_E_EARFCN = 0x91,
+ BSSGP_IE_EDRX_PARAMETERS = 0x92,
+ BSSGP_IE_T_UNTIL_NEXT_PAGING = 0x93,
+ BSSGP_IE_COVERAGE_CLASS = 0x98,
+ BSSGP_IE_PAGING_ATTEMPT_INFO = 0x99,
+ BSSGP_IE_EXCEPTION_REPORT_FLAG = 0x9a,
+ BSSGP_IE_OLD_RA_ID = 0x9b,
+ BSSGP_IE_ATTACH_IND = 0x9c,
+ BSSGP_IE_PLMN_ID = 0x9d,
+ BSSGP_IE_MME_QUERY = 0x9e,
+ BSSGP_IE_SGSN_GROUP_ID = 0x9f,
+ BSSGP_IE_ADDITIONAL_PTMSI = 0xa0,
+ BSSGP_IE_UE_USAGE_TYPE = 0xa1,
+ BSSGP_IE_MLAT_TIMER = 0xa2,
+ BSSGP_IE_MLAT_TA = 0xa3,
+ BSSGP_IE_MS_SYNC_ACCURACY = 0xa4,
+ BSSGP_IE_BTS_RX_ACCURACY_LVL = 0xa5,
+ BSSGP_IE_TA_REQ = 0xa6,
};
/*! Cause coding (Section 11.3.8 / Table 11.10) */
@@ -178,3 +322,52 @@ enum gprs_bssgp_cause {
BSSGP_CAUSE_DTM_HO_MSC_ERR = 0x4a,
BSSGP_CAUSE_INVAL_CSG_CELL = 0x4b,
};
+
+/* Feature Bitmap (Section 11.3.45) */
+#define BSSGP_FEAT_PFC 0x01 /* Packet Flow Context */
+#define BSSGP_FEAT_CBL 0x02 /* Current Bucket Level */
+#define BSSGP_FEAT_INR 0x04 /* Inter-NSE re-routing */
+#define BSSGP_FEAT_LCS 0x08 /* Location Services */
+#define BSSGP_FEAT_RIM 0x10 /* RAN Inoformation Management */
+#define BSSGP_FEAT_PFC_FC 0x20 /* PFC Flow Control */
+#define BSSGP_FEAT_ERS 0x40 /* Enhanced Radio Status */
+#define BSSGP_FEAT_MBMS 0x80 /* Multimedia Broadcast */
+
+/* Extended Feature Bitmap (Section 11.3.84) */
+#define BSSGP_XFEAT_PSHO 0x01 /* PS Handover */
+#define BSSGP_XFEAT_GBIT 0x02 /* Gigabit Interface */
+#define BSSGP_XFEAT_MOCN 0x04 /* Multi-Operator CN */
+#define BSSGP_XFEAT_CSPS 0x08 /* CS/PS coordination enhancements */
+#define BSSGP_XFEAT_ECIoT 0x10 /* EC-GSM-IoT */
+#define BSSGP_XFEAT_DCN 0x20 /* Dedicated CN */
+#define BSSGP_XFEAT_eDRX 0x40 /* eDRX */
+#define BSSGP_XFEAT_MSAD 0x80 /* MS-assisted Dedicated CN selection */
+
+/* Flow Control Granularity (Section 11.3.102) */
+enum bssgp_fc_granularity {
+ BSSGP_FC_GRAN_100 = 0,
+ BSSGP_FC_GRAN_1000 = 1,
+ BSSGP_FC_GRAN_10000 = 2,
+ BSSGP_FC_GRAN_100000 = 3,
+};
+
+/* RAN-INFORMATION-REQUEST PDU Type Extension
+ * 3GPP TS 48.018, table 11.3.65.1 */
+enum bssgp_rim_pdu_type {
+ RIM_PDU_TYPE_STOP = 0,
+ RIM_PDU_TYPE_SING_REP = 1,
+ RIM_PDU_TYPE_MULT_REP = 2,
+};
+
+/* RIM PDU Indications
+ * 3GPP TS 48.018, section 11.3.65.0 */
+struct bssgp_rim_pdu_ind {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t ack_requested:1,
+ pdu_type_ext:3,
+ reserved:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved:4, pdu_type_ext:3, ack_requested:1;
+#endif
+} __attribute__ ((packed));
diff --git a/include/osmocom/gprs/protocol/gsm_24_301.h b/include/osmocom/gprs/protocol/gsm_24_301.h
new file mode 100644
index 00000000..d4bcd87e
--- /dev/null
+++ b/include/osmocom/gprs/protocol/gsm_24_301.h
@@ -0,0 +1,11 @@
+/*! \file gsm_24_301.h */
+
+#pragma once
+
+/*! Tracking area TS 24.301, section 9.9.3.32 */
+struct osmo_eutran_tai {
+ uint16_t mcc;
+ uint16_t mnc;
+ bool mnc_3_digits;
+ uint16_t tac;
+};
diff --git a/include/osmocom/gsm/Makefile.am b/include/osmocom/gsm/Makefile.am
new file mode 100644
index 00000000..5678a51e
--- /dev/null
+++ b/include/osmocom/gsm/Makefile.am
@@ -0,0 +1,69 @@
+SUBDIRS = protocol
+
+BUILT_SOURCES = gsm0503.h
+
+osmogsm_HEADERS = \
+ a5.h \
+ abis_nm.h \
+ apn.h \
+ bts_features.h \
+ cbsp.h \
+ comp128.h \
+ comp128v23.h \
+ bitvec_gsm.h \
+ gan.h \
+ gsm0341.h \
+ gsm0411_smc.h \
+ gsm0411_smr.h \
+ gsm0411_utils.h \
+ gsm0480.h \
+ gsm0502.h \
+ gsm0503.h \
+ bsslap.h \
+ bssmap_le.h \
+ gad.h \
+ gsm0808.h \
+ gsm0808_lcs.h \
+ gsm29205.h \
+ gsm0808_utils.h \
+ gsm23003.h \
+ gsm23236.h \
+ gsm29118.h \
+ gsm44021.h \
+ gsm48.h \
+ gsm48_arfcn_range_encode.h \
+ gsm48_ie.h \
+ gsm48_rest_octets.h \
+ gsm_utils.h \
+ gsup.h \
+ gsup_sms.h \
+ i460_mux.h \
+ ipa.h \
+ iuup.h \
+ lapd_core.h \
+ lapdm.h \
+ meas_rep.h \
+ mncc.h \
+ prim.h \
+ l1sap.h \
+ oap.h \
+ oap_client.h \
+ rlp.h \
+ rsl.h \
+ rxlev_stat.h \
+ sysinfo.h \
+ tlv.h \
+ $(NULL)
+
+osmogsmdir = $(includedir)/osmocom/gsm
+
+noinst_HEADERS = \
+ kasumi.h \
+ gea.h \
+ $(NULL)
+
+gsm0503.h: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py
+ $(AM_V_GEN)python3 $(top_srcdir)/utils/conv_gen.py gen_header gsm \
+ --target-path $(builddir)/
+
+CLEANFILES = gsm0503.h
diff --git a/include/osmocom/gsm/a5.h b/include/osmocom/gsm/a5.h
index fa63246d..1b26842f 100644
--- a/include/osmocom/gsm/a5.h
+++ b/include/osmocom/gsm/a5.h
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
diff --git a/include/osmocom/gsm/bsslap.h b/include/osmocom/gsm/bsslap.h
new file mode 100644
index 00000000..56f2e6c8
--- /dev/null
+++ b/include/osmocom/gsm/bsslap.h
@@ -0,0 +1,53 @@
+/*! \addtogroup bsslap
+ * @{
+ * \file bsslap.h
+ * Message encoding and decoding for 3GPP TS 48.071 BSS LCS Assistance Protocol (BSSLAP).
+ */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#pragma once
+
+#include <osmocom/gsm/protocol/gsm_48_071.h>
+#include <osmocom/gsm/protocol/gsm_49_031.h>
+
+struct msgb;
+
+struct osmo_bsslap_err {
+ int rc;
+ enum bsslap_msgt msg_type;
+ enum bsslap_iei iei;
+ enum lcs_cause cause;
+ char *logmsg;
+};
+
+extern const struct value_string osmo_bsslap_msgt_names[];
+static inline const char *osmo_bsslap_msgt_name(enum bsslap_msgt val)
+{ return get_value_string(osmo_bsslap_msgt_names, val); }
+
+extern const struct value_string osmo_bsslap_iei_names[];
+static inline const char *osmo_bsslap_iei_name(enum bsslap_iei val)
+{ return get_value_string(osmo_bsslap_iei_names, val); }
+
+int osmo_bsslap_enc(struct msgb *msg, const struct bsslap_pdu *pdu);
+int osmo_bsslap_dec(struct bsslap_pdu *pdu,
+ struct osmo_bsslap_err **err, void *err_ctx,
+ const uint8_t *data, size_t len);
+
+/*! @} */
diff --git a/include/osmocom/gsm/bssmap_le.h b/include/osmocom/gsm/bssmap_le.h
new file mode 100644
index 00000000..113d4bd1
--- /dev/null
+++ b/include/osmocom/gsm/bssmap_le.h
@@ -0,0 +1,77 @@
+/*! \addtogroup bssmap_le
+ * @{
+ * \file bssmap_le.h
+ * Message encoding and decoding for 3GPP TS 49.031 BSSMAP-LE.
+ */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#pragma once
+
+#include <osmocom/gsm/protocol/gsm_49_031.h>
+
+struct osmo_bsslap_err;
+struct osmo_gad_err;
+
+struct osmo_bssmap_le_err {
+ int rc;
+ enum bssmap_le_msgt msg_type;
+ enum bssmap_le_iei iei;
+ enum lcs_cause cause;
+ struct osmo_bsslap_err *bsslap_err;
+ struct osmo_gad_err *gad_err;
+ char *logmsg;
+};
+
+struct osmo_bssap_le_err {
+ int rc;
+ struct osmo_bssmap_le_err *bssmap_le_err;
+ void *dtap_err;
+ char *logmsg;
+};
+
+enum bssmap_le_msgt osmo_bssmap_le_msgt(const uint8_t *data, uint8_t len);
+
+extern const struct value_string osmo_bssmap_le_msgt_names[];
+static inline const char *osmo_bssmap_le_msgt_name(enum bssmap_le_msgt val)
+{ return get_value_string(osmo_bssmap_le_msgt_names, val); }
+
+extern const struct value_string osmo_bssmap_le_iei_names[];
+static inline const char *osmo_bssmap_le_iei_name(enum bssmap_le_iei val)
+{ return get_value_string(osmo_bssmap_le_iei_names, val); }
+
+int osmo_lcs_cause_enc(struct msgb *msg, const struct lcs_cause_ie *lcs_cause);
+int osmo_lcs_cause_dec(struct lcs_cause_ie *lcs_cause,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *data, uint8_t len);
+
+int osmo_bssap_le_pdu_to_str_buf(char *buf, size_t buflen, const struct bssap_le_pdu *bssap_le);
+char *osmo_bssap_le_pdu_to_str_c(void *ctx, const struct bssap_le_pdu *bssap_le);
+
+struct msgb *osmo_bssap_le_enc(const struct bssap_le_pdu *pdu);
+int osmo_bssap_le_dec(struct bssap_le_pdu *pdu, struct osmo_bssap_le_err **err, void *err_ctx, struct msgb *msg);
+
+uint8_t osmo_bssmap_le_ie_enc_location_type(struct msgb *msg, const struct bssmap_le_location_type *location_type);
+int osmo_bssmap_le_ie_dec_location_type(struct bssmap_le_location_type *lt,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len);
+
+/*! @} */
diff --git a/include/osmocom/gsm/bts_features.h b/include/osmocom/gsm/bts_features.h
index 7ead0203..cf1db4ac 100644
--- a/include/osmocom/gsm/bts_features.h
+++ b/include/osmocom/gsm/bts_features.h
@@ -7,7 +7,7 @@
/* N. B: always add new features to the end of the list (right before _NUM_BTS_FEAT) to avoid breaking compatibility
with BTS compiled against earlier version of this header. Also make sure that the description strings
- gsm_bts_features_descs[] in gsm_data.c are also updated accordingly! */
+ osmo_bts_features_{descs,names}[] in bts_features.c are also updated accordingly! */
enum osmo_bts_features {
BTS_FEAT_HSCSD,
BTS_FEAT_GPRS,
@@ -24,12 +24,33 @@ enum osmo_bts_features {
BTS_FEAT_SPEECH_F_AMR,
BTS_FEAT_SPEECH_H_AMR,
BTS_FEAT_ETWS_PN,
+ BTS_FEAT_PAGING_COORDINATION, /* BTS hands CS paging to PCU/PACCH */
+ BTS_FEAT_IPV6_NSVC,
+ BTS_FEAT_ACCH_REP,
+ BTS_FEAT_CCN, /* Is CCN supported by the cell? TS 44.060 sec 8.8.2 */
+ BTS_FEAT_VAMOS, /* Is the BTS VAMOS capable? */
+ BTS_FEAT_ABIS_OSMO_PCU, /* BTS supports forwarding data to PCUIF over IPA OML multiplex */
+ BTS_FEAT_BCCH_POWER_RED,
+ BTS_FEAT_DYN_TS_SDCCH8, /* Osmo Dynamic TS supports configured as SDCCH8 */
+ BTS_FEAT_ACCH_TEMP_OVP, /* FACCH/SACCH Temporary overpower */
+ BTS_FEAT_OSMUX, /* Osmux (Osmocom RTP muxing) support */
+ BTS_FEAT_VBS, /* Voice Broadcast Service support, 3GPP TS 43.069 */
+ BTS_FEAT_VGCS, /* Voice Group Call Service support, 3GPP TS 44.068 */
_NUM_BTS_FEAT
};
extern const struct value_string osmo_bts_features_descs[];
-const char *osmo_bts_feature_name(enum osmo_bts_features feature);
+static inline const char *osmo_bts_features_desc(enum osmo_bts_features val)
+{ return get_value_string(osmo_bts_features_descs, val); }
+
+const char *osmo_bts_feature_name(enum osmo_bts_features feature)
+ OSMO_DEPRECATED("Use osmo_bts_features_desc() instead");
+
+extern const struct value_string osmo_bts_features_names[];
+
+static inline const char *osmo_bts_features_name(enum osmo_bts_features val)
+{ return get_value_string(osmo_bts_features_names, val); }
static inline int osmo_bts_set_feature(struct bitvec *features, enum osmo_bts_features feature)
{
@@ -37,8 +58,14 @@ static inline int osmo_bts_set_feature(struct bitvec *features, enum osmo_bts_fe
return bitvec_set_bit_pos(features, feature, 1);
}
+static inline int osmo_bts_unset_feature(struct bitvec *features, enum osmo_bts_features feature)
+{
+ OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES);
+ return bitvec_set_bit_pos(features, feature, 0);
+}
+
static inline bool osmo_bts_has_feature(const struct bitvec *features, enum osmo_bts_features feature)
{
OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES);
- return bitvec_get_bit_pos(features, feature);
+ return bitvec_get_bit_pos(features, feature) == ONE;
}
diff --git a/include/osmocom/gsm/cbsp.h b/include/osmocom/gsm/cbsp.h
index 90516cb7..efa4ce6f 100644
--- a/include/osmocom/gsm/cbsp.h
+++ b/include/osmocom/gsm/cbsp.h
@@ -6,8 +6,8 @@
#include <osmocom/gsm/gsm0808_utils.h>
/* Definitions for parsed / abstract representation of messages in the
- * CBSP (Cell Broadcast Service Protocol). Data here is *not* formatted
- * like the * on-the-wire format. Any similarities are coincidetial ;) */
+ * CBSP (Cell Broadcast Service Protocol, 3GPP TS 48.049). Data here is *not* formatted
+ * like the on-the-wire format. Any similarities are coincidential ;) */
/* Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
*
@@ -24,10 +24,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Decoded 8.2.3 Message Content */
@@ -75,7 +71,7 @@ struct osmo_cbsp_fail_ent {
struct llist_head list; /* entry in a fail_list below */
enum CELL_IDENT id_discr;
union gsm0808_cell_id_u cell_id;
- uint8_t cause;
+ uint8_t cause; /* enum osmo_cbsp_cause */
};
@@ -245,6 +241,30 @@ struct osmo_cbsp_error_ind {
enum cbsp_channel_ind *channel_ind;
};
+/* 8.2.13 Cause */
+enum osmo_cbsp_cause {
+ OSMO_CBSP_CAUSE_PARAM_NOT_RECOGNISED = 0,
+ OSMO_CBSP_CAUSE_PARAM_VALUE_INVALID,
+ OSMO_CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED,
+ OSMO_CBSP_CAUSE_CELL_ID_NOT_VALID,
+ OSMO_CBSP_CAUSE_UNRECOGNISED_MESSAGE,
+ OSMO_CBSP_CAUSE_MISSING_MANDATORY_ELEMENT,
+ OSMO_CBSP_CAUSE_BSC_CAPACITY_EXCEEDED,
+ OSMO_CBSP_CAUSE_CELL_MEMORY_EXCEEDED,
+ OSMO_CBSP_CAUSE_BSC_MEMORY_EXCEEDED,
+ OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_SUPPORTED,
+ OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_OPERATIONAL,
+ OSMO_CBSP_CAUSE_INCOMPATIBLE_DRX_PARAM,
+ OSMO_CBSP_CAUSE_EXT_CHAN_NOT_SUPPORTED,
+ OSMO_CBSP_CAUSE_MSG_REF_ALREADY_USED,
+ OSMO_CBSP_CAUSE_UNSPECIFIED_ERROR,
+ OSMO_CBSP_CAUSE_LAI_OR_LAC_NOT_VALID,
+};
+extern const struct value_string osmo_cbsp_cause_names[];
+static inline const char *osmo_cbsp_cause_name(enum osmo_cbsp_cause cause)
+{
+ return get_value_string(osmo_cbsp_cause_names, cause);
+}
/* decoded CBSP message */
struct osmo_cbsp_decoded {
@@ -283,7 +303,7 @@ struct osmo_cbsp_decoded {
} u;
};
-extern const __thread char *osmo_cbsp_errstr;
+extern __thread const char *osmo_cbsp_errstr;
struct msgb *osmo_cbsp_msgb_alloc(void *ctx, const char *name);
struct msgb *osmo_cbsp_encode(void *ctx, const struct osmo_cbsp_decoded *in);
@@ -292,3 +312,4 @@ void osmo_cbsp_init_struct(struct osmo_cbsp_decoded *cbsp, enum cbsp_msg_type ms
struct osmo_cbsp_decoded *osmo_cbsp_decoded_alloc(void *ctx, enum cbsp_msg_type msg_type);
int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb **tmp_msg);
+int osmo_cbsp_segmentation_cb(struct msgb *msg);
diff --git a/include/osmocom/gsm/gad.h b/include/osmocom/gsm/gad.h
new file mode 100644
index 00000000..57d0e37a
--- /dev/null
+++ b/include/osmocom/gsm/gad.h
@@ -0,0 +1,190 @@
+/*! \addtogroup gad
+ * @{
+ * \file gad.h
+ * Message encoding and decoding for 3GPP TS 23.032 GAD: Universal Geographical Area Description.
+ */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/gsm/protocol/gsm_23_032.h>
+#include <osmocom/core/utils.h>
+
+struct msgb;
+
+struct osmo_gad_ell_point {
+ /*! Latitude in micro degrees (degrees * 1e6), -90'000'000 (S) .. 90'000'000 (N). */
+ int32_t lat;
+ /*! Longitude in micro degrees (degrees * 1e6), -180'000'000 (W) .. 180'000'000 (E). */
+ int32_t lon;
+};
+
+struct osmo_gad_ell_point_unc_circle {
+ /*! Latitude in micro degrees (degrees * 1e6), -90'000'000 (S) .. 90'000'000 (N). */
+ int32_t lat;
+ /*! Longitude in micro degrees (degrees * 1e6), -180'000'000 (W) .. 180'000'000 (E). */
+ int32_t lon;
+ /*! Uncertainty circle radius in millimeters (m * 1e3), 0 .. 18'000'000. */
+ uint32_t unc;
+};
+
+struct osmo_gad_ell_point_unc_ellipse {
+ /*! Latitude in micro degrees (degrees * 1e6), -90'000'000 (S) .. 90'000'000 (N). */
+ int32_t lat;
+ /*! Longitude in micro degrees (degrees * 1e6), -180'000'000 (W) .. 180'000'000 (E). */
+ int32_t lon;
+ /*! Uncertainty ellipsoid radius of major axis in millimeters, 0 .. 18'000'000.
+ * Coding of uncertainty is non-linear, use osmo_gad_dec_unc(osmo_gad_enc_unc(val)) to clamp. */
+ uint32_t unc_semi_major;
+ /*! Uncertainty ellipsoid radius of minor axis in millimeters, 0 .. 18'000'000.
+ * Coding of uncertainty is non-linear, use osmo_gad_dec_unc(osmo_gad_enc_unc(val)) to clamp. */
+ uint32_t unc_semi_minor;
+ /*! Major axis orientation in degrees (DEG), 0 (N) .. 90 (E) .. 179 (SSE). */
+ uint8_t major_ori;
+ /*! Confidence in percent, 0 = no information, 1..100%, 101..128 = no information. */
+ uint8_t confidence;
+};
+
+struct osmo_gad_polygon {
+ uint8_t num_points;
+ struct osmo_gad_ell_point point[15];
+};
+
+struct osmo_gad_ell_point_alt {
+ /*! latitude in micro degrees (degrees * 1e6), -90'000'000 (S) .. 90'000'000 (N). */
+ int32_t lat;
+ /*! longitude in micro degrees (degrees * 1e6), -180'000'000 (W) .. 180'000'000 (E). */
+ int32_t lon;
+ /*! Altitude in meters, -32767 (depth) .. 32767 (height) */
+ int16_t alt;
+};
+
+struct osmo_gad_ell_point_alt_unc_ell {
+ /*! latitude in micro degrees (degrees * 1e6), -90'000'000 (S) .. 90'000'000 (N). */
+ int32_t lat;
+ /*! longitude in micro degrees (degrees * 1e6), -180'000'000 (W) .. 180'000'000 (E). */
+ int32_t lon;
+ /*! Altitude in meters, -32767 (depth) .. 32767 (height) */
+ int16_t alt;
+ /*! Uncertainty ellipsoid radius of major axis in millimeters, 0 .. 18'000'000.
+ * Coding of uncertainty is non-linear, use osmo_gad_dec_unc(osmo_gad_enc_unc(val)) to clamp. */
+ uint32_t unc_semi_major;
+ /*! Uncertainty ellipsoid radius of minor axis in millimeters, 0 .. 18'000'000.
+ * Coding of uncertainty is non-linear, use osmo_gad_dec_unc(osmo_gad_enc_unc(val)) to clamp. */
+ uint32_t unc_semi_minor;
+ /*! Major axis orientation in degrees (DEG), 0 (N) .. 90 (E) .. 179 (SSE). */
+ uint8_t major_ori;
+ /*! Uncertainty altitude in millimeters, 0 .. 990'000.
+ * Coding of uncertainty altitude is non-linear, and distinct from the non-altitude uncertainty coding. Use
+ * osmo_gad_dec_unc_alt(osmo_gad_enc_unc_alt(val)) to clamp. */
+ int32_t unc_alt;
+ /*! Confidence in percent, 0 = no information, 1..100%, 101..128 = no information. */
+ uint8_t confidence;
+};
+
+struct osmo_gad_ell_arc {
+ /*! latitude in micro degrees (degrees * 1e6), -90'000'000 (S) .. 90'000'000 (N). */
+ int32_t lat;
+ /*! longitude in micro degrees (degrees * 1e6), -180'000'000 (W) .. 180'000'000 (E). */
+ int32_t lon;
+ /*! inner circle radius in mm (m * 1e3) */
+ uint32_t inner_r;
+ /*! Uncertainty circle radius in millimeters, 0 .. 18'000'000.
+ * Coding of uncertainty is non-linear, use osmo_gad_dec_unc(osmo_gad_enc_unc(val)) to clamp. */
+ uint32_t unc_r;
+ /*! Offset angle of first arc edge in degrees from North clockwise (eastwards), 0..359.
+ * Angle is coded in increments of 2 degrees. */
+ uint16_t ofs_angle;
+ /*! Included angle defining the angular width of the arc, in degrees clockwise, 1..360.
+ * Angle is coded in increments of 2 degrees. */
+ uint16_t incl_angle;
+ /*! Confidence in percent, 0 = no information, 1..100%, 101..128 = no information. */
+ uint8_t confidence;
+};
+
+struct osmo_gad_ha_ell_point_alt_unc_ell {
+ /*! latitude in micro degrees (degrees * 1e6), -90'000'000 (S) .. 90'000'000 (N). */
+ int32_t lat;
+ /*! longitude in micro degrees (degrees * 1e6), -180'000'000 (W) .. 180'000'000 (E). */
+ int32_t lon;
+ /*! Altitude in millimeters, -500'000 (depth) .. 10'000'000 (height) */
+ int32_t alt;
+ /*! Uncertainty ellipsoid radius of major axis in millimeters, 0 .. 46'491.
+ * Coding of high-accuracy uncertainty is non-linear, use osmo_gad_dec_ha_unc(osmo_gad_enc_ha_unc(val)) to
+ * clamp. */
+ uint32_t unc_semi_major;
+ /*! Uncertainty ellipsoid radius of minor axis in millimeters, 0 .. 46'491.
+ * Coding of high-accuracy uncertainty is non-linear, use osmo_gad_dec_ha_unc(osmo_gad_enc_ha_unc(val)) to
+ * clamp. */
+ uint32_t unc_semi_minor;
+ /*! Major axis orientation in degrees (DEG), 0 (N) .. 90 (E) .. 179 (SSE). */
+ uint8_t major_ori;
+ /*! Horizontal confidence in percent, 0 = no information, 1..100%, 101..128 = no information. */
+ uint8_t h_confidence;
+ /*! High-Accuracy uncertainty altitude */
+ int32_t unc_alt;
+ /*! Vertical confidence in percent, 0 = no information, 1..100%, 101..128 = no information. */
+ uint8_t v_confidence;
+};
+
+struct osmo_gad {
+ enum gad_type type;
+ union {
+ struct osmo_gad_ell_point ell_point;
+ struct osmo_gad_ell_point_unc_circle ell_point_unc_circle;
+ struct osmo_gad_ell_point_unc_ellipse ell_point_unc_ellipse;
+ struct osmo_gad_polygon polygon;
+ struct osmo_gad_ell_point_alt ell_point_alt;
+ struct osmo_gad_ell_point_alt_unc_ell ell_point_alt_unc_ell;
+ struct osmo_gad_ell_arc ell_arc;
+ struct osmo_gad_ell_point_unc_ellipse ha_ell_point_unc_ellipse;
+ struct osmo_gad_ha_ell_point_alt_unc_ell ha_ell_point_alt_unc_ell;
+ };
+};
+
+struct osmo_gad_err {
+ int rc;
+ enum gad_type type;
+ char *logmsg;
+};
+
+extern const struct value_string osmo_gad_type_names[];
+static inline const char *osmo_gad_type_name(enum gad_type val)
+{ return get_value_string(osmo_gad_type_names, val); }
+
+int osmo_gad_raw_write(struct msgb *msg, const union gad_raw *gad_raw);
+int osmo_gad_raw_read(union gad_raw *gad_raw, struct osmo_gad_err **err, void *err_ctx, const uint8_t *data, uint8_t len);
+
+int osmo_gad_enc(union gad_raw *gad_raw, const struct osmo_gad *gad);
+int osmo_gad_dec(struct osmo_gad *gad, struct osmo_gad_err **err, void *err_ctx, const union gad_raw *gad_raw);
+
+int osmo_gad_to_str_buf(char *buf, size_t buflen, const struct osmo_gad *gad);
+char *osmo_gad_to_str_c(void *ctx, const struct osmo_gad *gad);
+
+uint32_t osmo_gad_enc_lat(int32_t deg_1e6);
+int32_t osmo_gad_dec_lat(uint32_t lat);
+uint32_t osmo_gad_enc_lon(int32_t deg_1e6);
+int32_t osmo_gad_dec_lon(uint32_t lon);
+uint8_t osmo_gad_enc_unc(uint32_t mm);
+uint32_t osmo_gad_dec_unc(uint8_t unc);
+/*! @} */
diff --git a/include/osmocom/gsm/gsm0502.h b/include/osmocom/gsm/gsm0502.h
index c9901dfd..0ac13873 100644
--- a/include/osmocom/gsm/gsm0502.h
+++ b/include/osmocom/gsm/gsm0502.h
@@ -1,4 +1,7 @@
-/*! \file gsm0502.h */
+/*! \defgroup gsm0502 GSM 05.02 / 3GPP TS 45.002
+ * @{
+ * \file gsm0502.h
+ */
#pragma once
@@ -7,9 +10,82 @@
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
+/* 4.3.3 TDMA frame number : constants and modular arithmetic */
+#define GSM_TDMA_FN_DURATION_nS 4615384 /* in 1e−9 seconds (approx) */
+#define GSM_TDMA_FN_DURATION_uS 4615 /* in 1e-6 seconds (approx) */
+
+#define GSM_TDMA_SUPERFRAME (26 * 51)
+#define GSM_TDMA_HYPERFRAME (2048 * GSM_TDMA_SUPERFRAME)
+
+/*! Return the sum of two specified TDMA frame numbers (summation) */
+#define GSM_TDMA_FN_SUM(a, b) \
+ (((a) + (b)) % GSM_TDMA_HYPERFRAME)
+/*! Return the difference of two specified TDMA frame numbers (subtraction) */
+#define GSM_TDMA_FN_SUB(a, b) \
+ (((a) + GSM_TDMA_HYPERFRAME - (b)) % GSM_TDMA_HYPERFRAME)
+/*! Return the *minimum* difference of two specified TDMA frame numbers (distance) */
+#define GSM_TDMA_FN_DIFF(a, b) \
+ OSMO_MIN(GSM_TDMA_FN_SUB(a, b), GSM_TDMA_FN_SUB(b, a))
+
+/*! Increment the given TDMA frame number by 1 and return the result (like ++fn) */
+#define GSM_TDMA_FN_INC(fn) \
+ ((fn) = GSM_TDMA_FN_SUM((fn), 1))
+/*! Decrement the given TDMA frame number by 1 and return the result (like --fn) */
+#define GSM_TDMA_FN_DEC(fn) \
+ ((fn) = GSM_TDMA_FN_SUB((fn), 1))
+
+/* 5.2.3.1 Normal burst for GMSK (1 bit per symbol) */
+#define GSM_NBITS_NB_GMSK_TAIL 3
+#define GSM_NBITS_NB_GMSK_PAYLOAD (2 * 58)
+#define GSM_NBITS_NB_GMSK_TRAIN_SEQ 26
+#define GSM_NBITS_NB_GMSK_BURST 148 /* without guard period */
+
+/* 5.2.3.3 Normal burst for 8-PSK (3 bits per symbol) */
+#define GSM_NBITS_NB_8PSK_TAIL (GSM_NBITS_NB_GMSK_TAIL * 3)
+#define GSM_NBITS_NB_8PSK_PAYLOAD (GSM_NBITS_NB_GMSK_PAYLOAD * 3)
+#define GSM_NBITS_NB_8PSK_TRAIN_SEQ (GSM_NBITS_NB_GMSK_TRAIN_SEQ * 3)
+#define GSM_NBITS_NB_8PSK_BURST (GSM_NBITS_NB_GMSK_BURST * 3)
+
+/* 5.2.5 Synchronization burst (also GMSK) */
+#define GSM_NBITS_SB_GMSK_TAIL GSM_NBITS_NB_GMSK_TAIL
+#define GSM_NBITS_SB_GMSK_PAYLOAD (2 * 39)
+#define GSM_NBITS_SB_GMSK_ETRAIN_SEQ 64
+#define GSM_NBITS_SB_GMSK_BURST GSM_NBITS_NB_GMSK_BURST
+
+/* 5.2.6 Dummy burst (also GMSK) */
+#define GSM_NBITS_DB_GMSK_TAIL GSM_NBITS_NB_GMSK_TAIL
+#define GSM_NBITS_DB_GMSK_MIXED 142
+#define GSM_NBITS_DB_GMSK_BURST GSM_NBITS_NB_GMSK_BURST
+
+/* 5.2.7 Access burst (also GMSK) */
+#define GSM_NBITS_AB_GMSK_ETAIL 8
+#define GSM_NBITS_AB_GMSK_SYNCH_SEQ 41
+#define GSM_NBITS_AB_GMSK_PAYLOAD 36
+#define GSM_NBITS_AB_GMSK_TAIL GSM_NBITS_NB_GMSK_TAIL
+#define GSM_NBITS_AB_GMSK_BURST GSM_NBITS_NB_GMSK_BURST
+
+/*! Compare the given TDMA FNs, taking the wrapping into account.
+ * \param[in] fn1 First TDMA Fn value to compare.
+ * \param[in] fn2 Second TDMA Fn value to compare.
+ * \returns similarly to memcmp(), -1 if fn1 goes before fn2;
+ * 0 if fn1 equals fn2;
+ * 1 if fn1 goes after fn2. */
+static inline int gsm0502_fncmp(uint32_t fn1, uint32_t fn2)
+{
+ const uint32_t thresh = GSM_TDMA_HYPERFRAME / 2;
+
+ if (fn1 == fn2)
+ return 0;
+ if ((fn1 < fn2 && (fn2 - fn1) < thresh) ||
+ (fn1 > fn2 && (fn1 - fn2) > thresh))
+ return -1;
+
+ return 1;
+}
+
/* Table 5 Clause 7 TS 05.02 */
static inline unsigned int
-gsm0502_get_n_pag_blocks(struct gsm48_control_channel_descr *chan_desc)
+gsm0502_get_n_pag_blocks(const struct gsm48_control_channel_descr *chan_desc)
{
if (chan_desc->ccch_conf == RSL_BCCH_CCCH_CONF_1_C)
return 3 - chan_desc->bs_ag_blks_res;
@@ -34,7 +110,7 @@ gsm0502_get_paging_group(uint64_t imsi, unsigned int bs_cc_chans,
}
unsigned int
-gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi);
+gsm0502_calc_paging_group(const struct gsm48_control_channel_descr *chan_desc, uint64_t imsi);
enum gsm0502_fn_remap_channel {
FN_REMAP_TCH_F,
@@ -47,3 +123,11 @@ enum gsm0502_fn_remap_channel {
};
uint32_t gsm0502_fn_remap(uint32_t fn, enum gsm0502_fn_remap_channel channel);
+
+uint16_t gsm0502_hop_seq_gen(const struct gsm_time *t,
+ uint8_t hsn, uint8_t maio,
+ size_t n, const uint16_t *ma);
+
+int gsm0502_fn2ccch_block(uint32_t fn);
+
+/*! @} */
diff --git a/include/osmocom/gsm/gsm0808.h b/include/osmocom/gsm/gsm0808.h
index 373b4341..80ef6836 100644
--- a/include/osmocom/gsm/gsm0808.h
+++ b/include/osmocom/gsm/gsm0808.h
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -30,11 +26,11 @@
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/socket_compat.h>
+
#define BSSMAP_MSG_SIZE 1024
#define BSSMAP_MSG_HEADROOM 512
-struct sockaddr_storage;
-
struct msgb;
struct gsm0808_cell_id_list2;
@@ -54,13 +50,35 @@ struct msgb *gsm0808_create_clear_command2(uint8_t cause, bool csfb_ind);
struct msgb *gsm0808_create_clear_complete(void);
struct msgb *gsm0808_create_cipher(const struct gsm0808_encrypt_info *ei,
const uint8_t *cipher_response_mode);
+
+struct gsm0808_cipher_mode_command {
+ struct gsm0808_encrypt_info ei;
+
+ /*! 3GPP TS 48.008 3.2.2.34 Cipher Response Mode, optional IE */
+ bool cipher_response_mode_present;
+ /*! 3GPP TS 48.008 3.2.2.34 Cipher Response Mode:
+ * 0 - IMEISV must not be included by the Mobile Station;
+ * 1 - IMEISV must be included by the Mobile Station.
+ */
+ uint8_t cipher_response_mode;
+
+ bool kc128_present;
+ uint8_t kc128[16];
+
+ /* more items are defined in the spec and may be added later */
+ bool more_items; /*< always set this to false */
+};
+struct msgb *gsm0808_create_cipher2(const struct gsm0808_cipher_mode_command *cmc);
+
struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id);
struct msgb *gsm0808_create_cipher_reject(enum gsm0808_cause cause);
struct msgb *gsm0808_create_cipher_reject_ext(enum gsm0808_cause_class class, uint8_t ext);
-struct msgb *gsm0808_create_classmark_request();
+struct msgb *gsm0808_create_classmark_request(void);
struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len,
const uint8_t *cm3, uint8_t cm3_len);
-struct msgb *gsm0808_create_sapi_reject(uint8_t link_id);
+struct msgb *gsm0808_create_sapi_reject_cause(uint8_t link_id, uint16_t cause);
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
+ OSMO_DEPRECATED("Use gsm0808_create_sapi_reject_cause() instead");
struct msgb *gsm0808_create_ass(const struct gsm0808_channel_type *ct,
const uint16_t *cic,
const struct sockaddr_storage *ss,
@@ -104,6 +122,9 @@ struct msgb *gsm0808_create_lcls_conn_ctrl(enum gsm0808_lcls_config config,
enum gsm0808_lcls_control control);
struct msgb *gsm0808_create_lcls_conn_ctrl_ack(enum gsm0808_lcls_status status);
struct msgb *gsm0808_create_lcls_notification(enum gsm0808_lcls_status status, bool break_req);
+struct msgb *gsm0808_create_common_id(const char *imsi,
+ const struct osmo_plmn_id *selected_plmn_id,
+ const struct osmo_plmn_id *last_used_eutran_plnm_id);
/*! 3GPP TS 48.008 §3.2.2.5.8 Old BSS to New BSS information */
@@ -121,6 +142,9 @@ struct gsm0808_old_bss_to_new_bss_info {
uint8_t field;
} current_channel_type_2;
+ bool last_eutran_plmn_id_present;
+ struct osmo_plmn_id last_eutran_plmn_id;
+
/* more items are defined in the spec and may be added later */
bool more_items; /*< always set this to false */
};
@@ -190,7 +214,12 @@ struct gsm0808_handover_request {
uint8_t global_call_reference_len;
/* more items are defined in the spec and may be added later */
- bool more_items; /*!< always set this to false */
+ bool more_items; /*!< set this to true iff any fields below are used */
+
+ bool kc128_present;
+ uint8_t kc128[16];
+
+ bool more_items2; /*!< always set this to false */
};
struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_request *params);
@@ -217,8 +246,12 @@ struct gsm0808_handover_request_ack {
const struct sockaddr_storage *aoip_transport_layer;
+ bool more_items; /*!< set this to true iff any fields below are used */
+
+ struct gsm0808_speech_codec_list codec_list_bss_supported; /*< omit when .len == 0 */
+
/* more items are defined in the spec and may be added later */
- bool more_items; /*!< always set this to false */
+ bool more_items2; /*!< always set this to false */
};
struct msgb *gsm0808_create_handover_request_ack2(const struct gsm0808_handover_request_ack *params);
@@ -236,8 +269,8 @@ struct gsm0808_handover_command {
};
struct msgb *gsm0808_create_handover_command(const struct gsm0808_handover_command *params);
-struct msgb *gsm0808_create_handover_detect();
-struct msgb *gsm0808_create_handover_succeeded();
+struct msgb *gsm0808_create_handover_detect(void);
+struct msgb *gsm0808_create_handover_succeeded(void);
struct gsm0808_handover_complete {
bool rr_cause_present;
@@ -250,7 +283,7 @@ struct gsm0808_handover_complete {
bool chosen_encr_alg_present;
uint8_t chosen_encr_alg;
-
+
bool chosen_channel_present;
uint8_t chosen_channel;
@@ -299,10 +332,248 @@ struct gsm0808_handover_performed {
};
struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_performed *params);
+/*! 3GPP TS 48.008 §3.2.1.50 VGCS/VBS SETUP */
+struct gsm0808_vgcs_vbs_setup {
+ struct gsm0808_group_callref callref;
+
+ bool priority_present;
+ struct gsm0808_priority priority;
+
+ bool vgcs_feature_flags_present;
+ struct gsm0808_vgcs_feature_flags flags;
+};
+struct msgb *gsm0808_create_vgcs_vbs_setup(const struct gsm0808_vgcs_vbs_setup *params);
+
+/*! 3GPP TS 48.008 §3.2.1.51 VGCS/VBS SETUP ACK */
+struct gsm0808_vgcs_vbs_setup_ack {
+ bool vgcs_feature_flags_present;
+ struct gsm0808_vgcs_feature_flags flags;
+};
+struct msgb *gsm0808_create_vgcs_vbs_setup_ack(const struct gsm0808_vgcs_vbs_setup_ack *params);
+
+/*! 3GPP TS 48.008 §3.2.1.52 VGCS/VBS SETUP REFUSE */
+struct msgb *gsm0808_create_vgcs_vbs_setup_refuse(enum gsm0808_cause cause);
+
+/*! 3GPP TS 48.008 §3.2.1.53 VGCS/VBS ASSIGNMENT REQUEST */
+struct gsm0808_vgcs_vbs_assign_req {
+ struct gsm0808_channel_type channel_type;
+ enum gsm0808_assignment_requirement ass_req;
+ struct gsm0808_cell_id cell_identifier;
+ struct gsm0808_group_callref callref;
+
+ bool priority_present;
+ struct gsm0808_priority priority;
+
+ bool cic_present;
+ uint16_t cic;
+
+ bool downlink_dtx_flag_present;
+ enum gsm0808_downlink_dtx_flag downlink_dtx_flag;
+
+ bool encryption_information_present;
+ struct gsm0808_encrypt_info encryption_information;
+
+ bool vstk_rand_present;
+ uint8_t vstk_rand[5];
+
+ bool vstk_present;
+ uint8_t vstk[16];
+
+ bool cils_present;
+ struct gsm0808_cell_id_list_segment cils;
+
+ bool aoip_transport_layer_present;
+ struct sockaddr_storage aoip_transport_layer;
+
+ bool call_id_present;
+ uint32_t call_id;
+
+ bool codec_list_present;
+ struct gsm0808_speech_codec_list codec_list_msc_preferred;
+};
+struct msgb *gsm0808_create_vgcs_vbs_assign_req(const struct gsm0808_vgcs_vbs_assign_req *params);
+
+/*! 3GPP TS 48.008 §3.2.1.54 VGCS/VBS ASSIGNMENT RESULT */
+struct gsm0808_vgcs_vbs_assign_res {
+ struct gsm0808_channel_type channel_type;
+ struct gsm0808_cell_id cell_identifier;
+
+ bool chosen_channel_present;
+ uint8_t chosen_channel;
+
+ bool cic_present;
+ uint16_t cic;
+
+ bool circuit_pool_present;
+ uint8_t circuit_pool;
+
+ bool aoip_transport_layer_present;
+ struct sockaddr_storage aoip_transport_layer;
+
+ bool codec_present;
+ struct gsm0808_speech_codec codec_msc_chosen;
+
+ bool call_id_present;
+ uint32_t call_id;
+};
+struct msgb *gsm0808_create_vgcs_vbs_assign_res(const struct gsm0808_vgcs_vbs_assign_res *params);
+
+/*! 3GPP TS 48.008 §3.2.1.55 VGCS/VBS ASSIGNMENT FAILURE */
+struct gsm0808_vgcs_vbs_assign_fail {
+ enum gsm0808_cause cause;
+
+ bool circuit_pool_present;
+ uint8_t circuit_pool;
+
+ bool cpl_present;
+ struct gsm0808_circuit_pool_list cpl;
+
+ bool codec_list_present;
+ struct gsm0808_speech_codec_list codec_list_bss_supported;
+};
+struct msgb *gsm0808_create_vgcs_vbs_assign_fail(const struct gsm0808_vgcs_vbs_assign_fail *params);
+
+/*! 3GPP TS 48.008 §3.2.1.57 (VGCS) UPLINK REQUEST */
+struct gsm0808_uplink_request {
+ bool talker_priority_present;
+ enum gsm0808_talker_priority talker_priority;
+
+ bool cell_identifier_present;
+ struct gsm0808_cell_id cell_identifier;
+
+ bool l3_present;
+ struct gsm0808_layer_3_information l3;
+
+ bool mi_present;
+ struct osmo_mobile_identity mi;
+};
+struct msgb *gsm0808_create_uplink_request(const struct gsm0808_uplink_request *params);
+
+/*! 3GPP TS 48.008 §3.2.1.58 (VGCS) UPLINK REQUEST ACKNOWLEDGE */
+struct gsm0808_uplink_request_ack {
+ bool talker_priority_present;
+ enum gsm0808_talker_priority talker_priority;
+
+ bool emerg_set_ind_present;
+
+ bool talker_identity_present;
+ struct gsm0808_talker_identity talker_identity;
+};
+struct msgb *gsm0808_create_uplink_request_ack(const struct gsm0808_uplink_request_ack *params);
+
+/*! 3GPP TS 48.008 §3.2.1.59 (VGCS) UPLINK REQUEST CONFIRM */
+struct gsm0808_uplink_request_cnf {
+ struct gsm0808_cell_id cell_identifier;
+
+ bool talker_identity_present;
+ struct gsm0808_talker_identity talker_identity;
+
+ /* mandatory! */
+ struct gsm0808_layer_3_information l3;
+};
+struct msgb *gsm0808_create_uplink_request_cnf(const struct gsm0808_uplink_request_cnf *params);
+
+/*! 3GPP TS 48.008 §3.2.1.59a (VGCS) UPLINK APPLICATION DATA */
+struct gsm0808_uplink_app_data {
+ struct gsm0808_cell_id cell_identifier;
+ struct gsm0808_layer_3_information l3;
+ bool bt_ind;
+};
+struct msgb *gsm0808_create_uplink_app_data(const struct gsm0808_uplink_app_data *params);
+
+/*! 3GPP TS 48.008 §3.2.1.60 (VGCS) UPLINK RELEASE INDICATION */
+struct gsm0808_uplink_release_ind {
+ enum gsm0808_cause cause;
+
+ bool talker_priority_present;
+ enum gsm0808_talker_priority talker_priority;
+};
+struct msgb *gsm0808_create_uplink_release_ind(const struct gsm0808_uplink_release_ind *params);
+
+/*! 3GPP TS 48.008 §3.2.1.61 (VGCS) UPLINK REJECT COMMAND */
+struct gsm0808_uplink_reject_cmd {
+ enum gsm0808_cause cause;
+
+ bool current_talker_priority_present;
+ enum gsm0808_talker_priority current_talker_priority;
+ bool rejected_talker_priority_present;
+ enum gsm0808_talker_priority rejected_talker_priority;
+
+ bool talker_identity_present;
+ struct gsm0808_talker_identity talker_identity;
+};
+struct msgb *gsm0808_create_uplink_reject_cmd(const struct gsm0808_uplink_reject_cmd *params);
+
+/*! 3GPP TS 48.008 §3.2.1.62 (VGCS) UPLINK RELEASE COMMAND */
+struct msgb *gsm0808_create_uplink_release_cmd(const enum gsm0808_cause cause);
+
+/*! 3GPP TS 48.008 §3.2.1.63 (VGCS) UPLINK SEIZED COMMAND */
+struct gsm0808_uplink_seized_cmd {
+ enum gsm0808_cause cause;
+
+ bool talker_priority_present;
+ enum gsm0808_talker_priority talker_priority;
+
+ bool emerg_set_ind_present;
+
+ bool talker_identity_present;
+ struct gsm0808_talker_identity talker_identity;
+};
+struct msgb *gsm0808_create_uplink_seized_cmd(const struct gsm0808_uplink_seized_cmd *params);
+
+/*! 3GPP TS 48.008 §3.2.1.78 VGCS ADDITIONAL INFORMATION */
+struct msgb *gsm0808_create_vgcs_additional_info(const struct gsm0808_talker_identity *ti);
+
+/*! 3GPP TS 48.008 §3.2.1.79 VGCS/VBS AREA CELL INFO */
+struct gsm0808_vgcs_vbs_area_cell_info {
+ struct gsm0808_cell_id_list_segment cils;
+
+ bool ass_req_present;
+ enum gsm0808_assignment_requirement ass_req;
+};
+struct msgb *gsm0808_create_vgcs_vbs_area_cell_info(const struct gsm0808_vgcs_vbs_area_cell_info *params);
+
+/*! 3GPP TS 48.008 §3.2.1.80 VGCS/VBS ASSIGNMENT STATUS */
+struct gsm0808_vgcs_vbs_assign_stat {
+ /* established cells */
+ bool cils_est_present;
+ struct gsm0808_cell_id_list_segment cils_est;
+
+ /* cells to be established */
+ bool cils_tbe_present;
+ struct gsm0808_cell_id_list_segment cils_tbe;
+
+ /* released cells - no user present */
+ bool cils_rel_present;
+ struct gsm0808_cell_id_list_segment cils_rel;
+
+ /* not established cells - no establishment possible */
+ bool cils_ne_present;
+ struct gsm0808_cell_id_list_segment cils_ne;
+
+ bool cell_status_present;
+ enum gsm0808_vgcs_vbs_cell_status cell_status;
+};
+struct msgb *gsm0808_create_vgcs_vbs_assign_stat(const struct gsm0808_vgcs_vbs_assign_stat *params);
+
+/*! 3GPP TS 48.008 §3.2.1.81 VGCS SMS */
+struct msgb *gsm0808_create_vgcs_sms(const struct gsm0808_sms_to_vgcs *sms);
+
+/*! 3GPP TS 48.008 §3.2.1.82 (VGCS/VBS) NOTIFICATION DATA */
+struct gsm0808_notification_data {
+ struct gsm0808_application_data app_data;
+ struct gsm0808_data_identity data_ident;
+
+ bool msisdn_present;
+ char msisdn[MSISDN_MAXLEN + 1];
+};
+struct msgb *gsm0808_create_notification_data(const struct gsm0808_notification_data *parms);
+
struct msgb *gsm0808_create_dtap(struct msgb *msg, uint8_t link_id);
void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id);
const struct tlv_definition *gsm0808_att_tlvdef(void);
+extern const struct tlv_definition gsm0808_old_bss_to_new_bss_info_att_tlvdef;
/*! Parse BSSAP TLV structure using \ref tlv_parse */
#define osmo_bssap_tlv_parse(dec, buf, len) tlv_parse(dec, gsm0808_att_tlvdef(), buf, len, 0, 0)
@@ -315,6 +586,13 @@ const char *gsm0808_bssap_name(uint8_t msg_type);
const char *gsm0808_cause_name(enum gsm0808_cause cause);
const char *gsm0808_cause_class_name(enum gsm0808_cause_class class);
+/*! Parse Cause TLV 3GPP TS 08.08 §3.2.2.5
+ * \returns Cause value */
+enum gsm0808_cause gsm0808_get_cause(const struct tlv_parsed *tp);
+
+const char *gsm0808_diagnostics_octet_location_str(uint8_t pointer);
+const char *gsm0808_diagnostics_bit_location_str(uint8_t bit_pointer);
+
extern const struct value_string gsm0808_lcls_config_names[];
extern const struct value_string gsm0808_lcls_control_names[];
extern const struct value_string gsm0808_lcls_status_names[];
diff --git a/include/osmocom/gsm/gsm0808_lcs.h b/include/osmocom/gsm/gsm0808_lcs.h
new file mode 100644
index 00000000..71665013
--- /dev/null
+++ b/include/osmocom/gsm/gsm0808_lcs.h
@@ -0,0 +1,48 @@
+/*! \addtogroup gsm0808
+ * @{
+ * \file gsm0808_lcs.h
+ *
+ * Declarations that depend on both gsm0808.h and bssmap_le.h: LCS related message coding.
+ * (This file prevents circular dependency between struct definitions for BSSMAP messages, since BSSMAP references
+ * struct lcs_cause and struct bssmap_le_location_type, and BSSMAP-LE references gsm0808_cause.
+ */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#pragma once
+
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/bssmap_le.h>
+
+struct gsm0808_perform_location_request {
+ struct bssmap_le_location_type location_type;
+ struct osmo_mobile_identity imsi;
+
+ bool more_items; /*!< always set this to false */
+};
+struct msgb *gsm0808_create_perform_location_request(const struct gsm0808_perform_location_request *params);
+
+struct gsm0808_perform_location_response {
+ bool location_estimate_present;
+ union gad_raw location_estimate;
+
+ struct lcs_cause_ie lcs_cause;
+};
+struct msgb *gsm0808_create_perform_location_response(const struct gsm0808_perform_location_response *params);
+
+int gsm0808_enc_lcs_cause(struct msgb *msg, const struct lcs_cause_ie *lcs_cause);
+struct msgb *gsm0808_create_perform_location_abort(const struct lcs_cause_ie *lcs_cause);
+
+/*! @} */
diff --git a/include/osmocom/gsm/gsm0808_utils.h b/include/osmocom/gsm/gsm0808_utils.h
index ccdf5ed9..dbe2abf9 100644
--- a/include/osmocom/gsm/gsm0808_utils.h
+++ b/include/osmocom/gsm/gsm0808_utils.h
@@ -31,6 +31,8 @@ struct sockaddr_storage;
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/core/endian.h>
/*! (225-1)/2 is the maximum number of elements in a cell identifier list. */
#define GSM0808_CELL_ID_LIST2_MAXLEN 127
@@ -44,6 +46,9 @@ union gsm0808_cell_id_u {
uint16_t ci;
struct osmo_location_area_id lai_and_lac;
uint16_t lac;
+ struct osmo_service_area_id sai;
+ /* osmocom specific: */
+ struct osmo_cell_global_id_ps global_ps;
};
/*! Parsed representation of Cell Identifier IE (3GPP TS 48.008 3.2.2.17) */
@@ -59,6 +64,164 @@ struct gsm0808_cell_id_list2 {
unsigned int id_list_len;
};
+/*! Packed representation of a Priority IE (GGPP TS 48.008 3.2.2.18) */
+struct gsm0808_priority {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t pvi:1, /* Preemption Vulnerability indicator */
+ qa:1, /* Queuing allowed indicator */
+ priority_level:4, /* Priority level: 1 == hightest, 14 == lowest */
+ pci:1, /* Preemption Capability indicator */
+ spare:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare:1, pci:1, priority_level:4, qa:1, pvi:1;
+#endif
+} __attribute__ ((packed));
+
+/*! Packed representation of a VGCS Feature Flags IE (3GPP TS 48.008 3.2.2.88) */
+struct gsm0808_vgcs_feature_flags {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t tp_ind:1, /* Talker priority supported */
+ as_ind_circuit:1, /* A-interface circuit sharing supported */
+ as_ind_link:1, /* A-interface link sharing supported */
+ bss_res:1, /* BSS supports re-establishment */
+ tcp:1, /* Talker channel parameter supported */
+ spare:3;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare:3, tcp:1, bss_res:1, as_ind_link:1, as_ind_circuit:1, tp_ind:1;
+#endif
+} __attribute__ ((packed));
+
+/* TS 48.008 3.2.2.52 */
+enum gsm0808_assignment_requirement {
+ GSM0808_ASRQ_DELAY_ALLOWED = 0x00,
+ GSM0808_ASRQ_IMMEDIATE = 0x01,
+ GSM0808_ASRQ_IMMEDIATE_ON_DEMAND = 0x02,
+};
+
+/* TS 48.008 Table 10.5.8 */
+enum gsm0808_service_flag {
+ GSM0808_SF_VBS = 0,
+ GSM0808_SF_VGCS = 1,
+};
+
+enum gsm0808_call_priority {
+ GSM0808_CALL_PRIORITY_NONE = 0x00,
+ GSM0808_CALL_PRIORITY_LEVEL_4 = 0x01,
+ GSM0808_CALL_PRIORITY_LEVEL_3 = 0x02,
+ GSM0808_CALL_PRIORITY_LEVEL_2 = 0x03,
+ GSM0808_CALL_PRIORITY_LEVEL_1 = 0x04,
+ GSM0808_CALL_PRIORITY_LEVEL_0 = 0x05,
+ GSM0808_CALL_PRIORITY_LEVEL_B = 0x06,
+ GSM0808_CALL_PRIORITY_LEVEL_A = 0x07,
+};
+
+/*! Packed representation of a Group Call Reference IE (3GPP TS 48.008 3.2.2.55) */
+struct gsm0808_group_callref {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t call_ref_hi[3];
+ uint8_t call_priority:3,
+ af:1, /* Acknowledgement flag */
+ sf:1, /* Service flag */
+ call_ref_lo:3;
+ uint8_t spare:4,
+ ciphering_info:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t call_ref_hi[3];
+ uint8_t call_ref_lo:3, sf:1, af:1, call_priority:3;
+ uint8_t ciphering_info:4, spare:4;
+#endif
+} __attribute__ ((packed));
+
+/* TS 48.008 3.2.2.26 */
+enum gsm0808_downlink_dtx_flag {
+ GSM0808_DTX_FLAG_ALLOW = 0,
+ GSM0808_DTX_FLAG_FORBID = 1,
+};
+
+/*! Parsed representation of a Cell Identifier List Segment IE (3GPP TS 48.008 3.2.2.27a) */
+struct gsm0808_cell_id_list_segment {
+ uint8_t seq_last;
+ uint8_t seq_number;
+ struct gsm0808_cell_id_list2 cil;
+};
+
+/*! Parsed representation of a Circuit Pool List IE (3GPP TS 48.008 3.2.2.26) */
+#define CIRCUIT_POOL_LIST_MAXLEN 252
+struct gsm0808_circuit_pool_list {
+ uint8_t pool[CIRCUIT_POOL_LIST_MAXLEN];
+ unsigned int list_len;
+};
+
+/* 3GPP TS 48.008 Table 3.2.2.90.1 Talker Priority */
+enum gsm0808_talker_priority {
+ GSM0808_TALKER_PRIORITY_NORMAL = 0x00,
+ GSM0808_TALKER_PRIORITY_PRIVILEGED = 0x01,
+ GSM0808_TALKER_PRIORITY_EMERGENCY = 0x02,
+};
+
+/*! Parsed representation of a Layer 3 Information IE (3GPP TS 48.008 3.2.2.24) */
+#define LAYER_3_INFORMATION_MAXLEN 252
+struct gsm0808_layer_3_information {
+ uint8_t l3[LAYER_3_INFORMATION_MAXLEN];
+ unsigned int l3_len;
+};
+
+/*! Parsed representation of a Talker Identity IE (3GPP TS 48.008 3.2.2.91) */
+#define TALKER_IDENTITY_MAXLEN 17
+struct gsm0808_talker_identity {
+ uint8_t talker_id[TALKER_IDENTITY_MAXLEN];
+ unsigned int id_bits;
+};
+
+/* 3GPP TS 48.008 3.2.2.94 VGCS/VBS Cell Status */
+enum gsm0808_vgcs_vbs_cell_status {
+ GSM0808_CSTAT_ESTABLISHED = 0x00,
+ GSM0808_CSTAT_NOT_ESTABLISHED1 = 0x01,
+ GSM0808_CSTAT_RELEASED_NO_USER = 0x02,
+ GSM0808_CSTAT_NOT_ESTABLISHED2 = 0x03,
+};
+
+/*! Parsed representation of a SMS to VGCS IE (3GPP TS 48.008 3.2.2.92) */
+#define SMS_TO_VGCS_MAXLEN 252
+struct gsm0808_sms_to_vgcs {
+ uint8_t sms[SMS_TO_VGCS_MAXLEN];
+ unsigned int sms_len;
+};
+
+/*! Parsed representation of a Application Data IE (3GPP TS 48.008 3.2.2.98) */
+#define APP_DATA_MAXLEN 9
+struct gsm0808_application_data {
+ uint8_t data[APP_DATA_MAXLEN];
+ unsigned int data_len;
+};
+
+/*! Packed representation of a Data Identity IE (GGPP TS 48.008 3.2.2.99) */
+enum gsm0808_application_idndicator {
+ GSM0808_AI_APP_DATA = 0x00,
+ GSM0808_AI_CONFIRM_APP_DATA = 0x01,
+};
+
+#define GSM0808_DP_MASK_TALKERS_LISTENERS 0x04
+#define GSM0808_DP_MASK_DISPATCHERS 0x02
+#define GSM0808_DP_MASK_NETWORK_APP 0x01
+
+struct gsm0808_data_identity {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t ai:1, /* Application Indicator */
+ di:4, /* Data identifier */
+ dp:3; /* Distribution parameter (bit mask) */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t dp:3, di:4, ai:1;
+#endif
+} __attribute__ ((packed));
+
+/*! Parsed representation of a MSISDN IE (3GPP TS 48.008 3.2.2.101) */
+#define MSISDN_MAXLEN 20
+
/*! LCLS-related parameters from 3GPP TS 48.008 */
struct osmo_lcls {
enum gsm0808_lcls_config config; /**< §3.2.2.116 Configuration */
@@ -108,12 +271,17 @@ uint8_t gsm0808_enc_lcls(struct msgb *msg, const struct osmo_lcls *lcls);
int gsm0808_dec_lcls(struct osmo_lcls *lcls, const struct tlv_parsed *tp);
uint8_t gsm0808_enc_speech_codec(struct msgb *msg,
- const struct gsm0808_speech_codec *sc);
+ const struct gsm0808_speech_codec *sc)
+ OSMO_DEPRECATED("use gsm0808_enc_speech_codec2() instead");
+int gsm0808_enc_speech_codec2(struct msgb *msg,
+ const struct gsm0808_speech_codec *sc);
int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
const uint8_t *elem, uint8_t len);
uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg,
- const struct gsm0808_speech_codec_list
- *scl);
+ const struct gsm0808_speech_codec_list *scl)
+ OSMO_DEPRECATED("use gsm0808_enc_speech_codec_list2() instead");
+int gsm0808_enc_speech_codec_list2(struct msgb *msg,
+ const struct gsm0808_speech_codec_list *scl);
int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl,
const uint8_t *elem, uint8_t len);
uint8_t gsm0808_enc_channel_type(struct msgb *msg,
@@ -124,6 +292,8 @@ uint8_t gsm0808_enc_encrypt_info(struct msgb *msg,
const struct gsm0808_encrypt_info *ei);
int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
const uint8_t *elem, uint8_t len);
+int gsm0808_enc_kc128(struct msgb *msg, const uint8_t *kc128);
+int gsm0808_dec_kc128(uint8_t *kc128, const uint8_t *elem, uint8_t len);
uint8_t gsm0808_enc_cell_id_list2(struct msgb *msg, const struct gsm0808_cell_id_list2 *cil);
uint8_t gsm0808_enc_cell_id_list(struct msgb *msg,
const struct gsm0808_cell_id_list *cil)
@@ -145,7 +315,7 @@ int gsm48_mr_cfg_from_gsm0808_sc_cfg(struct gsm48_multi_rate_conf *cfg, uint16_t
/*! \returns 3GPP TS 08.08 §3.2.2.5 Class of a given Cause */
static inline enum gsm0808_cause_class gsm0808_cause_class(enum gsm0808_cause cause)
{
- return (cause << 1) >> 4;
+ return (cause >> 4) & 0x7;
}
/*! \returns true if 3GPP TS 08.08 §3.2.2.5 Class has extended bit set */
@@ -155,7 +325,8 @@ static inline bool gsm0808_cause_ext(enum gsm0808_cause cause)
return (cause & 0x80) && !(cause & 0x0F);
}
-int gsm0808_get_cipher_reject_cause(const struct tlv_parsed *tp);
+int gsm0808_get_cipher_reject_cause(const struct tlv_parsed *tp)
+OSMO_DEPRECATED("Use gsm0808_get_cause() instead");
/*! \returns 3GPP TS 48.008 3.2.2.49 Current Channel Type 1 from enum gsm_chan_t. */
static inline uint8_t gsm0808_current_channel_type_1(enum gsm_chan_t type)
@@ -177,37 +348,34 @@ static inline uint8_t gsm0808_current_channel_type_1(enum gsm_chan_t type)
static inline enum gsm0808_permitted_speech gsm0808_permitted_speech(enum gsm_chan_t type,
enum gsm48_chan_mode mode)
{
- switch (mode) {
- case GSM48_CMODE_SPEECH_V1:
- switch (type) {
- case GSM_LCHAN_TCH_F:
- return GSM0808_PERM_FR1;
- case GSM_LCHAN_TCH_H:
- return GSM0808_PERM_HR1;
- default:
- return 0;
- }
- case GSM48_CMODE_SPEECH_EFR:
- switch (type) {
- case GSM_LCHAN_TCH_F:
- return GSM0808_PERM_FR2;
- case GSM_LCHAN_TCH_H:
- return GSM0808_PERM_HR2;
- default:
- return 0;
- }
- case GSM48_CMODE_SPEECH_AMR:
- switch (type) {
- case GSM_LCHAN_TCH_F:
- return GSM0808_PERM_FR3;
- case GSM_LCHAN_TCH_H:
- return GSM0808_PERM_HR3;
- default:
- return 0;
- }
+#define MODE_TYPE(mode, type) ((mode << 16) | type)
+
+ switch (MODE_TYPE(mode, type)) {
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V1, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR1;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V1, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR1;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_EFR, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR2;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_EFR, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR2; /* (deprecated) */
+ case MODE_TYPE(GSM48_CMODE_SPEECH_AMR, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR3;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_AMR, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR3;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V4, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR4;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V4, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR4;
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V5, GSM_LCHAN_TCH_F):
+ return GSM0808_PERM_FR5; /* FR only */
+ case MODE_TYPE(GSM48_CMODE_SPEECH_V6, GSM_LCHAN_TCH_H):
+ return GSM0808_PERM_HR6; /* HR only */
default:
return 0;
}
+
+#undef MODE_TYPE
}
/*! Return 3GPP TS 48.008 3.2.2.33 Chosen Channel. */
@@ -219,6 +387,9 @@ static inline uint8_t gsm0808_chosen_channel(enum gsm_chan_t type, enum gsm48_ch
case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_EFR:
case GSM48_CMODE_SPEECH_AMR:
+ case GSM48_CMODE_SPEECH_V4:
+ case GSM48_CMODE_SPEECH_V5:
+ case GSM48_CMODE_SPEECH_V6:
channel_mode = 0x9;
break;
case GSM48_CMODE_SIGN:
@@ -236,6 +407,33 @@ static inline uint8_t gsm0808_chosen_channel(enum gsm_chan_t type, enum gsm48_ch
case GSM48_CMODE_DATA_3k6:
channel_mode = 0xd;
break;
+ case GSM48_CMODE_DATA_29k0:
+ channel_mode = 0x1;
+ break;
+ case GSM48_CMODE_DATA_32k0:
+ channel_mode = 0x2;
+ break;
+ case GSM48_CMODE_DATA_43k5:
+ channel_mode = 0x3;
+ break;
+ case GSM48_CMODE_DATA_43k5_14k5:
+ channel_mode = 0x4;
+ break;
+ case GSM48_CMODE_DATA_29k0_14k5:
+ channel_mode = 0x5;
+ break;
+ case GSM48_CMODE_DATA_43k5_29k0:
+ channel_mode = 0x6;
+ break;
+ case GSM48_CMODE_DATA_14k5_43k5:
+ channel_mode = 0x7;
+ break;
+ case GSM48_CMODE_DATA_14k5_29k0:
+ channel_mode = 0xa;
+ break;
+ case GSM48_CMODE_DATA_29k0_43k5:
+ channel_mode = 0xf;
+ break;
default:
return 0;
}
@@ -253,6 +451,7 @@ static inline uint8_t gsm0808_chosen_channel(enum gsm_chan_t type, enum gsm48_ch
case GSM_LCHAN_TCH_H:
channel = 0x9;
break;
+ /* TODO: more than 1 TCHs? */
default:
return 0;
}
@@ -264,4 +463,23 @@ const char *gsm0808_channel_type_name(const struct gsm0808_channel_type *ct);
char *gsm0808_channel_type_name_buf(char *buf, size_t buf_len, const struct gsm0808_channel_type *ct);
char *gsm0808_channel_type_name_c(const void *ctx, const struct gsm0808_channel_type *ct);
+uint8_t gsm0808_enc_group_callref(struct msgb *msg, const struct gsm0808_group_callref *gc);
+int gsm0808_dec_group_callref(struct gsm0808_group_callref *gc, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_priority(struct msgb *msg, const struct gsm0808_priority *pri);
+int gsm0808_dec_priority(struct gsm0808_priority *pri, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_vgcs_feature_flags(struct msgb *msg, const struct gsm0808_vgcs_feature_flags *ff);
+int gsm0808_dec_vgcs_feature_flags(struct gsm0808_vgcs_feature_flags *ff, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_talker_identity(struct msgb *msg, const struct gsm0808_talker_identity *ti);
+int gsm0808_dec_talker_identity(struct gsm0808_talker_identity *ti, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_data_identity(struct msgb *msg, const struct gsm0808_data_identity *ai);
+int gsm0808_dec_data_identity(struct gsm0808_data_identity *ai, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_msisdn(struct msgb *msg, const char *msisdn);
+int gsm0808_dec_msisdn(char *msisdn, const char *elem, uint8_t len);
+uint8_t gsm0808_enc_assign_req(struct msgb *msg, const enum gsm0808_assignment_requirement ar);
+int gsm0808_dec_assign_req(enum gsm0808_assignment_requirement *ar, const uint8_t *elem, uint8_t len);
+uint8_t gsm0808_enc_cell_id_list_segment(struct msgb *msg, uint8_t ie_type,
+ const struct gsm0808_cell_id_list_segment *ci);
+int gsm0808_dec_cell_id_list_segment(struct gsm0808_cell_id_list_segment *ci, const uint8_t *elem, uint8_t len);
+int gsm0808_dec_call_id(uint32_t *ci, const uint8_t *elem, uint8_t len);
+
/*! @} */
diff --git a/include/osmocom/gsm/gsm23003.h b/include/osmocom/gsm/gsm23003.h
index 69f00f68..4070581f 100644
--- a/include/osmocom/gsm/gsm23003.h
+++ b/include/osmocom/gsm/gsm23003.h
@@ -30,6 +30,14 @@ struct osmo_cell_global_id {
uint16_t cell_identity;
};
+/* 3GPP TS 48.018:
+ * 8c.1.4.1.1 GERAN BSS identification (RIM)
+ * sec 11.3.9 Cell Identifier */
+struct osmo_cell_global_id_ps {
+ struct osmo_routing_area_id rai;
+ uint16_t cell_identity;
+};
+
/*! Bitmask of items contained in a struct osmo_cell_global_id.
* See also gsm0808_cell_id_to_cgi().
*/
@@ -37,6 +45,7 @@ enum osmo_cgi_part {
OSMO_CGI_PART_PLMN = 1,
OSMO_CGI_PART_LAC = 2,
OSMO_CGI_PART_CI = 4,
+ OSMO_CGI_PART_RAC = 8,
};
/* Actually defined in 3GPP TS 48.008 3.2.2.27 Cell Identifier List,
@@ -117,10 +126,21 @@ char *osmo_plmn_name_c(const void *ctx, const struct osmo_plmn_id *plmn);
const char *osmo_lai_name(const struct osmo_location_area_id *lai);
char *osmo_lai_name_buf(char *buf, size_t buf_len, const struct osmo_location_area_id *lai);
char *osmo_lai_name_c(const void *ctx, const struct osmo_location_area_id *lai);
+const char *osmo_rai_name2(const struct osmo_routing_area_id *rai);
+char *osmo_rai_name2_buf(char *buf, size_t buf_len, const struct osmo_routing_area_id *rai);
+char *osmo_rai_name2_c(const void *ctx, const struct osmo_routing_area_id *rai);
const char *osmo_cgi_name(const struct osmo_cell_global_id *cgi);
const char *osmo_cgi_name2(const struct osmo_cell_global_id *cgi);
char *osmo_cgi_name_buf(char *buf, size_t buf_len, const struct osmo_cell_global_id *cgi);
char *osmo_cgi_name_c(const void *ctx, const struct osmo_cell_global_id *cgi);
+const char *osmo_cgi_ps_name(const struct osmo_cell_global_id_ps *cgi_ps);
+const char *osmo_cgi_ps_name2(const struct osmo_cell_global_id_ps *cgi_ps);
+char *osmo_cgi_ps_name_buf(char *buf, size_t buf_len, const struct osmo_cell_global_id_ps *cgi_ps);
+char *osmo_cgi_ps_name_c(const void *ctx, const struct osmo_cell_global_id_ps *cgi_ps);
+const char *osmo_sai_name(const struct osmo_service_area_id *sai);
+const char *osmo_sai_name2(const struct osmo_service_area_id *sai);
+char *osmo_sai_name_buf(char *buf, size_t buf_len, const struct osmo_service_area_id *sai);
+char *osmo_sai_name_c(const void *ctx, const struct osmo_service_area_id *sai);
const char *osmo_gummei_name(const struct osmo_gummei *gummei);
char *osmo_gummei_name_buf(char *buf, size_t buf_len, const struct osmo_gummei *gummei);
char *osmo_gummei_name_c(const void *ctx, const struct osmo_gummei *gummei);
@@ -144,7 +164,9 @@ static inline int osmo_mcc_from_str(const char *mcc_str, uint16_t *mcc)
int osmo_mnc_cmp(uint16_t a_mnc, bool a_mnc_3_digits, uint16_t b_mnc, bool b_mnc_3_digits);
int osmo_plmn_cmp(const struct osmo_plmn_id *a, const struct osmo_plmn_id *b);
int osmo_lai_cmp(const struct osmo_location_area_id *a, const struct osmo_location_area_id *b);
+int osmo_rai_cmp(const struct osmo_routing_area_id *a, const struct osmo_routing_area_id *b);
int osmo_cgi_cmp(const struct osmo_cell_global_id *a, const struct osmo_cell_global_id *b);
+int osmo_cgi_ps_cmp(const struct osmo_cell_global_id_ps *a, const struct osmo_cell_global_id_ps *b);
int osmo_gen_home_network_domain(char *out, const struct osmo_plmn_id *plmn);
int osmo_parse_home_network_domain(struct osmo_plmn_id *out, const char *in);
diff --git a/include/osmocom/gsm/gsm23236.h b/include/osmocom/gsm/gsm23236.h
new file mode 100644
index 00000000..23b003a1
--- /dev/null
+++ b/include/osmocom/gsm/gsm23236.h
@@ -0,0 +1,60 @@
+/*! \file gsm23236.h
+ * API to handle Network Resource Indicator (NRI) values and ranges for MSC pooling, as in 3GPP TS 23.236.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/linuxlist.h>
+
+#define OSMO_NRI_BITLEN_MIN 1
+#define OSMO_NRI_BITLEN_MAX 15
+#define OSMO_NRI_BITLEN_DEFAULT 10
+
+/*! One range of NRI values. An NRI is a Network Resource Indicator, a number of a configured bit length (typically 10
+ * bit). In an MSC pool for load balancing, it is used to indicate which MSC has issued a TMSI: the NRI is located with
+ * its most significant bit located on the TMSI's second octet's most significant bit. See 3GPP TS 23.236. */
+struct osmo_nri_range {
+ struct llist_head entry;
+
+ /*! First value of the NRI range, i.e. inclusive. */
+ int16_t first;
+ /*! Last value of the NRI range, i.e. inclusive. */
+ int16_t last;
+};
+
+/*! A list of struct osmo_nri_range. Use osmo_nri_ranges_alloc() to create, and osmo_nri_ranges_free() (or talloc_free()
+ * on the parent context) to destroy. Always use osmo_nri_ranges_add() to insert entries, to ensure that the list
+ * remains sorted by 'first' values, which some of the osmo_nri_ranges API assumes to always be true.
+ *
+ * This struct serves as talloc context for the osmo_nri_range entries in the list, simplifying function signatures. It
+ * also makes the API future proof, to easily accomodate possible future additions.
+ */
+struct osmo_nri_ranges {
+ /* List of struct osmo_nri_range entries, talloc allocated from the parent struct osmo_nri_ranges. */
+ struct llist_head entries;
+};
+
+int osmo_nri_v_validate(int16_t nri_v, uint8_t nri_bitlen);
+bool osmo_nri_v_matches_ranges(int16_t nri_v, const struct osmo_nri_ranges *nri_ranges);
+int osmo_nri_v_limit_by_ranges(int16_t *nri_v, const struct osmo_nri_ranges *nri_ranges, uint32_t nri_bitlen);
+
+int osmo_tmsi_nri_v_get(int16_t *nri_v, uint32_t tmsi, uint8_t nri_bitlen);
+int osmo_tmsi_nri_v_set(uint32_t *tmsi, int16_t nri_v, uint8_t nri_bitlen);
+int osmo_tmsi_nri_v_limit_by_ranges(uint32_t *tmsi, const struct osmo_nri_ranges *nri_ranges, uint8_t nri_bitlen);
+
+int osmo_nri_range_validate(const struct osmo_nri_range *range, uint8_t nri_bitlen);
+bool osmo_nri_range_overlaps_ranges(const struct osmo_nri_range *range, const struct osmo_nri_ranges *nri_ranges);
+
+struct osmo_nri_ranges *osmo_nri_ranges_alloc(void *ctx);
+void osmo_nri_ranges_free(struct osmo_nri_ranges *nri_ranges);
+int osmo_nri_ranges_add(struct osmo_nri_ranges *nri_ranges, const struct osmo_nri_range *add);
+int osmo_nri_ranges_del(struct osmo_nri_ranges *nri_ranges, const struct osmo_nri_range *del);
+int osmo_nri_ranges_vty_add(const char **message, struct osmo_nri_range *added_range,
+ struct osmo_nri_ranges *nri_ranges, int argc, const char **argv, uint8_t nri_bitlen);
+int osmo_nri_ranges_vty_del(const char **message, struct osmo_nri_range *removed_range,
+ struct osmo_nri_ranges *nri_ranges, int argc, const char **argv);
+int osmo_nri_ranges_to_str_buf(char *buf, size_t buflen, const struct osmo_nri_ranges *nri_ranges);
+char *osmo_nri_ranges_to_str_c(void *ctx, const struct osmo_nri_ranges *nri_ranges);
diff --git a/include/osmocom/gsm/gsm29118.h b/include/osmocom/gsm/gsm29118.h
index e81cce4c..33c40d36 100644
--- a/include/osmocom/gsm/gsm29118.h
+++ b/include/osmocom/gsm/gsm29118.h
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
diff --git a/include/osmocom/gsm/gsm29205.h b/include/osmocom/gsm/gsm29205.h
index 95754567..6c845fb9 100644
--- a/include/osmocom/gsm/gsm29205.h
+++ b/include/osmocom/gsm/gsm29205.h
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -37,6 +33,6 @@ struct osmo_gcr_parsed {
uint8_t cr[5]; /** Call Reference ID */
};
-uint8_t osmo_enc_gcr(struct msgb *msg, const struct osmo_gcr_parsed *g) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE;
-int osmo_dec_gcr(struct osmo_gcr_parsed *gcr, const uint8_t *elem, uint8_t len) OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE;
+uint8_t osmo_enc_gcr(struct msgb *msg, const struct osmo_gcr_parsed *g);
+int osmo_dec_gcr(struct osmo_gcr_parsed *gcr, const uint8_t *elem, uint8_t len);
bool osmo_gcr_eq(const struct osmo_gcr_parsed *gcr1, const struct osmo_gcr_parsed *gcr2);
diff --git a/include/osmocom/gsm/gsm44021.h b/include/osmocom/gsm/gsm44021.h
new file mode 100644
index 00000000..8f89c56b
--- /dev/null
+++ b/include/osmocom/gsm/gsm44021.h
@@ -0,0 +1,8 @@
+#pragma once
+#include <osmocom/isdn/v110.h>
+
+int osmo_csd_12k_6k_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits);
+int osmo_csd_12k_6k_encode_frame(ubit_t *ra_bits, size_t ra_bits_size, const struct osmo_v110_decoded_frame *fr);
+int osmo_csd_3k6_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits);
+int osmo_csd_3k6_encode_frame(ubit_t *ra_bits, size_t ra_bits_size, const struct osmo_v110_decoded_frame *fr);
+void osmo_csd_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len);
diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index 7c68b1d2..00fb6f40 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -4,10 +4,12 @@
#include <stdbool.h>
+#include <osmocom/core/defs.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/gsm23003.h>
@@ -18,7 +20,9 @@
* To mark an invalid / unset MNC, this value shall be used. */
#define GSM_MCC_MNC_INVALID 0xFFFF
-/* A parsed GPRS routing area */
+/* A parsed GPRS routing area.
+ * Preferably use struct osmo_routing_area_id, it is better integrated with API like osmo_plmn_cmp().
+ */
struct gprs_ra_id {
uint16_t mcc;
uint16_t mnc;
@@ -33,6 +37,7 @@ 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 *gsm48_rr_msg_name(uint8_t msgtype);
+const char *gsm48_rr_short_pd_msg_name(uint8_t msgtype);
const char *rr_cause_name(uint8_t cause);
const char *osmo_rai_name(const struct gprs_ra_id *rai);
char *osmo_rai_name_buf(char *buf, size_t buf_len, const struct gprs_ra_id *rai);
@@ -48,23 +53,68 @@ void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
void gsm48_generate_lai2(struct gsm48_loc_area_id *lai48, const struct osmo_location_area_id *lai);
#define GSM48_MID_MAX_SIZE 11
-int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi);
-int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi);
-uint8_t gsm48_generate_mid(uint8_t *buf, const char *id, uint8_t mi_type);
+int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi)
+ OSMO_DEPRECATED_OUTSIDE("Instead use: l = msgb_tl_put(msg, GSM48_IE_MOBILE_ID);"
+ " *l = osmo_mobile_identity_encode_msgb(...)");
+int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi)
+ OSMO_DEPRECATED_OUTSIDE("Instead use: l = msgb_tl_put(msg, GSM48_IE_MOBILE_ID);"
+ " *l = osmo_mobile_identity_encode_msgb(...)");
+uint8_t gsm48_generate_mid(uint8_t *buf, const char *id, uint8_t mi_type)
+ OSMO_DEPRECATED_OUTSIDE("Instead use: l = msgb_tl_put(msg, GSM48_IE_MOBILE_ID);"
+ " *l = osmo_mobile_identity_encode_msgb(...)");
-/* Convert Mobile Identity (10.5.1.4) to string */
-int gsm48_mi_to_string(char *string, int str_len, const uint8_t *mi, int mi_len);
const char *gsm48_mi_type_name(uint8_t mi);
-const char *osmo_mi_name(const uint8_t *mi, uint8_t mi_len);
-char *osmo_mi_name_buf(char *buf, size_t buf_len, const uint8_t *mi, uint8_t mi_len);
-char *osmo_mi_name_c(const void *ctx, const uint8_t *mi, uint8_t mi_len);
+/* Convert encoded Mobile Identity (10.5.1.4) to string */
+int gsm48_mi_to_string(char *string, int str_len, const uint8_t *mi, int mi_len)
+ OSMO_DEPRECATED_OUTSIDE("Instead use osmo_mobile_identity_decode()");
+const char *osmo_mi_name(const uint8_t *mi, uint8_t mi_len)
+ OSMO_DEPRECATED_OUTSIDE("Instead use osmo_mobile_identity_to_str_c()");
+char *osmo_mi_name_buf(char *buf, size_t buf_len, const uint8_t *mi, uint8_t mi_len)
+ OSMO_DEPRECATED_OUTSIDE("Instead use osmo_mobile_identity_to_str_buf()");
+char *osmo_mi_name_c(const void *ctx, const uint8_t *mi, uint8_t mi_len)
+ OSMO_DEPRECATED_OUTSIDE("Instead use osmo_mobile_identity_to_str_c()");
+
+/*! Decoded representation of a Mobile Identity (3GPP TS 24.008 10.5.1.4).
+ * See osmo_mobile_identity_decode() and osmo_mobile_identity_from_l3(). */
+struct osmo_mobile_identity {
+ /*! A GSM_MI_TYPE_* constant (like GSM_MI_TYPE_IMSI). */
+ uint8_t type;
+ /*! Decoded Mobile Identity digits or TMSI value. IMSI, IMEI and IMEISV as digits like
+ * "12345678", and TMSI is represented as raw uint32_t. */
+ union {
+ /*! type == GSM_MI_TYPE_IMSI. */
+ char imsi[GSM23003_IMSI_MAX_DIGITS + 1];
+ /*! type == GSM_MI_TYPE_IMEI. */
+ char imei[GSM23003_IMEI_NUM_DIGITS + 1];
+ /*! type == GSM_MI_TYPE_IMEISV. */
+ char imeisv[GSM23003_IMEISV_NUM_DIGITS + 1];
+ /*! TMSI / P-TMSI / M-TMSI integer value if type == GSM_MI_TYPE_TMSI. */
+ uint32_t tmsi;
+ };
+};
+
+int osmo_mobile_identity_to_str_buf(char *buf, size_t buflen, const struct osmo_mobile_identity *mi);
+char *osmo_mobile_identity_to_str_c(void *ctx, const struct osmo_mobile_identity *mi);
+int osmo_mobile_identity_cmp(const struct osmo_mobile_identity *a, const struct osmo_mobile_identity *b);
+int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *mi_data, uint8_t mi_len,
+ bool allow_hex);
+int osmo_mobile_identity_decode_from_l3_buf(struct osmo_mobile_identity *mi, const uint8_t *l3_data, size_t l3_len,
+ bool allow_hex);
+int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex);
+int osmo_mobile_identity_encoded_len(const struct osmo_mobile_identity *mi, int *mi_digits);
+int osmo_mobile_identity_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_mobile_identity *mi, bool allow_hex);
+int osmo_mobile_identity_encode_msgb(struct msgb *msg, const struct osmo_mobile_identity *mi, bool allow_hex);
/* Parse Routeing Area Identifier */
+int osmo_routing_area_id_decode(struct osmo_routing_area_id *dst, const uint8_t *ra_data, size_t ra_data_len);
+int osmo_routing_area_id_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_routing_area_id *src);
+int osmo_routing_area_id_encode_msgb(struct msgb *msg, const struct osmo_routing_area_id *src);
void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf);
void gsm48_encode_ra(struct gsm48_ra_id *out, const struct gprs_ra_id *raid);
int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) OSMO_DEPRECATED("Use gsm48_encode_ra() instead");
+bool gsm48_ra_equal(const struct gprs_ra_id *raid1, const struct gprs_ra_id *raid2);
-int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc);
+int gsm48_number_of_paging_subchannels(const struct gsm48_control_channel_descr *chan_desc);
void gsm48_mcc_mnc_to_bcd(uint8_t *bcd_dst, uint16_t mcc, uint16_t mnc)
OSMO_DEPRECATED("Use osmo_plmn_to_bcd() instead, to not lose leading zeros in the MNC");
@@ -76,3 +126,6 @@ struct gsm48_hdr *gsm48_push_l3hdr(struct msgb *msg,
#define gsm48_push_l3hdr_tid(msg, pdisc, tid, msg_type) \
gsm48_push_l3hdr(msg, (pdisc & 0x0f) | (tid << 4), msg_type)
+
+enum gsm48_chan_mode gsm48_chan_mode_to_vamos(enum gsm48_chan_mode mode);
+enum gsm48_chan_mode gsm48_chan_mode_to_non_vamos(enum gsm48_chan_mode mode);
diff --git a/include/osmocom/gsm/gsm48_ie.h b/include/osmocom/gsm/gsm48_ie.h
index 71050df5..4768283f 100644
--- a/include/osmocom/gsm/gsm48_ie.h
+++ b/include/osmocom/gsm/gsm48_ie.h
@@ -7,6 +7,7 @@
#include <errno.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/defs.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/mncc.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
@@ -14,7 +15,7 @@
/* 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)
- OSMO_DEPRECATED("Use gsm48_decode_bcd_number2() for improved bounds checking");
+ OSMO_DEPRECATED_OUTSIDE("Use gsm48_decode_bcd_number2() for improved bounds checking");
int gsm48_decode_bcd_number2(char *output, size_t output_len,
const uint8_t *bcd_lv, size_t input_len,
size_t h_len);
@@ -116,5 +117,10 @@ struct gsm_sysinfo_freq {
};
/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
-int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
- uint8_t len, uint8_t mask, uint8_t frqt);
+int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f,
+ const uint8_t *cd, uint8_t len,
+ uint8_t mask, uint8_t frqt);
+
+/* decode "CSN.1 encoded Classmark 3" (10.5.1.7) */
+int gsm48_decode_classmark3(struct gsm48_classmark3 *classmark3_out,
+ const uint8_t *classmark3, size_t classmark3_len);
diff --git a/include/osmocom/gsm/gsm48_rest_octets.h b/include/osmocom/gsm/gsm48_rest_octets.h
index d3bb878e..cdb2e807 100644
--- a/include/osmocom/gsm/gsm48_rest_octets.h
+++ b/include/osmocom/gsm/gsm48_rest_octets.h
@@ -2,7 +2,7 @@
#include <stdbool.h>
#include <osmocom/gsm/sysinfo.h>
-#include <osmocom/gprs/protocol/gsm_04_60.h>
+#include <osmocom/gsm/protocol/gsm_44_060.h>
/* 16 is the max. number of SI2quater messages according to 3GPP TS 44.018 Table 10.5.2.33b.1:
4-bit index is used (2#1111 = 10#15) */
@@ -13,6 +13,9 @@
/* generate SI1 rest octets */
int osmo_gsm48_rest_octets_si1_encode(uint8_t *data, uint8_t *nch_pos, int is1800_net);
+int osmo_gsm48_si1ro_nch_pos_decode(uint8_t value, uint8_t *num_blocks, uint8_t *first_block);
+int osmo_gsm48_si1ro_nch_pos_encode(uint8_t num_blocks, uint8_t first_block);
+
int osmo_gsm48_rest_octets_si2quater_encode(uint8_t *data, uint8_t si2q_index, uint8_t si2q_count,
const uint16_t *uarfcn_list, size_t *u_offset,
size_t uarfcn_length, uint16_t *scramble_list,
@@ -120,8 +123,12 @@ struct osmo_gsm48_si13_info {
uint8_t prio_acc_thr;
};
-/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
+/* Parse/Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
+int osmo_gsm48_rest_octets_si13_decode(struct osmo_gsm48_si13_info *si13, const uint8_t *data);
int osmo_gsm48_rest_octets_si13_encode(uint8_t *data, const struct osmo_gsm48_si13_info *si13);
/* Parse SI3 Rest Octets */
void osmo_gsm48_rest_octets_si3_decode(struct osmo_gsm48_si_ro_info *si3, const uint8_t *data);
+
+/* Parse SI4 Rest Octets */
+void osmo_gsm48_rest_octets_si4_decode(struct osmo_gsm48_si_ro_info *si4, const uint8_t *data, int len);
diff --git a/include/osmocom/gsm/gsm_utils.h b/include/osmocom/gsm/gsm_utils.h
index de634348..b250c594 100644
--- a/include/osmocom/gsm/gsm_utils.h
+++ b/include/osmocom/gsm/gsm_utils.h
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -37,6 +33,7 @@
} while (0)
#define GSM_MAX_FN (26*51*2048)
+#define GSM_FN_UNSET 0xFFFFFFFF
/* Max length of random identifier which can be requested via osmo_get_rand_id() */
#define OSMO_MAX_RAND_ID_LEN 16
@@ -110,7 +107,10 @@ int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets_w
int gsm_7bit_encode_n_ussd(uint8_t *result, size_t n, const char *data, int *octets_written);
/* the four functions below are helper functions and here for the unit test */
-int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding);
+int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding)
+ OSMO_DEPRECATED("This function is unable to handle more than 255 septets, "
+ "use gsm_septet_pack() instead.");
+int gsm_septet_pack(uint8_t *result, const uint8_t *rdata, size_t septet_len, uint8_t padding);
int gsm_septet_encode(uint8_t *result, const char *data);
uint8_t gsm_get_octet_len(const uint8_t sept_len);
int gsm_7bit_decode_n_hdr(char *decoded, size_t n, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind);
@@ -153,6 +153,13 @@ static inline int rach_max_trans_raw2val(int raw) {
const int tbl[4] = { 1, 2, 4, 7 };
return tbl[raw & 3];
}
+static inline uint8_t rach_tx_integer_raw2val(uint8_t raw) {
+ const int tbl[6] = { 14, 16, 20, 25, 32, 50 };
+ raw &= 0x0f;
+ if (raw <= 9)
+ return raw + 3;
+ return tbl[raw - 10];
+}
#define ARFCN_PCS 0x8000
#define ARFCN_UPLINK 0x4000
@@ -174,13 +181,21 @@ void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn);
char *gsm_fn_as_gsmtime_str(uint32_t fn);
/* Convert from GSM time to frame number */
-uint32_t gsm_gsmtime2fn(struct gsm_time *time);
+uint32_t gsm_gsmtime2fn(const struct gsm_time *time);
/* Returns static buffer with string representation of a GSM Time */
char *osmo_dump_gsmtime(const struct gsm_time *tm);
char *osmo_dump_gsmtime_buf(char *buf, size_t buf_len, const struct gsm_time *tm);
char *osmo_dump_gsmtime_c(const void *ctx, const struct gsm_time *tm);
+/* Reduced Frame Number (3GPP TS 44.018 §10.5.2.38) */
+#define GSM_RFN_MODULUS 42432
+uint32_t gsm_rfn2fn(uint16_t rfn, uint32_t curr_fn);
+static inline uint16_t gsm_fn2rfn(uint32_t fn)
+{
+ return fn % GSM_RFN_MODULUS;
+}
+
/* GSM TS 03.03 Chapter 2.6 */
enum gprs_tlli_type {
TLLI_LOCAL,
@@ -210,9 +225,11 @@ enum gsm_phys_chan_config {
GSM_PCHAN_UNKNOWN,
GSM_PCHAN_CCCH_SDCCH4_CBCH,
GSM_PCHAN_SDCCH8_SACCH8C_CBCH,
- GSM_PCHAN_TCH_F_TCH_H_PDCH,
+ GSM_PCHAN_OSMO_DYN,
_GSM_PCHAN_MAX
};
+/* Backward compatibility with older naming: */
+#define GSM_PCHAN_TCH_F_TCH_H_PDCH GSM_PCHAN_OSMO_DYN
/* Osmocom internal, not part of any gsm spec */
enum gsm_chan_t {
diff --git a/include/osmocom/gsm/gsup.h b/include/osmocom/gsm/gsup.h
index 49ddb742..6ad72d28 100644
--- a/include/osmocom/gsm/gsup.h
+++ b/include/osmocom/gsm/gsup.h
@@ -37,9 +37,13 @@
*
*/
#pragma once
+#if (!EMBEDDED)
#include <stdint.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/defs.h>
+#include <osmocom/core/endian.h>
+#include <osmocom/core/socket.h>
#include <osmocom/gsm/gsup_sms.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/protocol/gsm_03_40.h>
@@ -57,8 +61,7 @@
/*! Maximum number of octets encoding MSISDN in BCD format */
#define OSMO_GSUP_MAX_MSISDN_LEN 9
#define OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN 43 /* TS 24.008 10.5.4.7 */
-
-#define OSMO_GSUP_PDP_TYPE_SIZE 2
+#define OSMO_GSUP_MAX_PCO_LEN 251
/*! Information Element Identifiers for GSUP IEs */
enum osmo_gsup_iei {
@@ -71,12 +74,14 @@ enum osmo_gsup_iei {
OSMO_GSUP_FREEZE_PTMSI_IE = 0x07,
OSMO_GSUP_MSISDN_IE = 0x08,
OSMO_GSUP_HLR_NUMBER_IE = 0x09,
- OSMO_GSUP_MESSAGE_CLASS_IE = 0x0a,
+ OSMO_GSUP_MESSAGE_CLASS_IE = 0x0a,
OSMO_GSUP_PDP_CONTEXT_ID_IE = 0x10,
- OSMO_GSUP_PDP_TYPE_IE = 0x11,
+ OSMO_GSUP_PDP_ADDRESS_IE = 0x11,
+#define OSMO_GSUP_PDP_TYPE_IE OSMO_GSUP_PDP_ADDRESS_IE /* Backward compat */
OSMO_GSUP_ACCESS_POINT_NAME_IE = 0x12,
OSMO_GSUP_PDP_QOS_IE = 0x13,
OSMO_GSUP_CHARG_CHAR_IE = 0x14,
+ OSMO_GSUP_PCO_IE = 0x15,
OSMO_GSUP_RAND_IE = 0x20,
OSMO_GSUP_SRES_IE = 0x21,
OSMO_GSUP_KC_IE = 0x22,
@@ -107,6 +112,7 @@ enum osmo_gsup_iei {
OSMO_GSUP_IMEI_IE = 0x50,
OSMO_GSUP_IMEI_RESULT_IE = 0x51,
+ OSMO_GSUP_NUM_VECTORS_REQ_IE = 0x52,
/* Inter-MSC handover related */
OSMO_GSUP_SOURCE_NAME_IE = 0x60,
@@ -196,12 +202,21 @@ enum osmo_gsup_message_type {
OSMO_GSUP_MSGT_E_CLOSE = 0b01000111,
OSMO_GSUP_MSGT_E_ABORT = 0b01001011,
- OSMO_GSUP_MSGT_E_ROUTING_ERROR = 0b01001110,
+ OSMO_GSUP_MSGT_ROUTING_ERROR = 0b01001110,
+
+ OSMO_GSUP_MSGT_EPDG_TUNNEL_REQUEST = 0b01010000,
+ OSMO_GSUP_MSGT_EPDG_TUNNEL_ERROR = 0b01010001,
+ OSMO_GSUP_MSGT_EPDG_TUNNEL_RESULT = 0b01010010,
};
+#define OSMO_GSUP_MSGT_E_ROUTING_ERROR OSMO_GSUP_MSGT_ROUTING_ERROR
+
#define OSMO_GSUP_IS_MSGT_REQUEST(msgt) (((msgt) & 0b00000011) == 0b00)
#define OSMO_GSUP_IS_MSGT_ERROR(msgt) (((msgt) & 0b00000011) == 0b01)
+#define OSMO_GSUP_IS_MSGT_RESULT(msgt) (((msgt) & 0b00000011) == 0b10)
+#define OSMO_GSUP_TO_MSGT_REQUEST(msgt) (((msgt) & 0b11111100))
#define OSMO_GSUP_TO_MSGT_ERROR(msgt) (((msgt) & 0b11111100) | 0b01)
+#define OSMO_GSUP_TO_MSGT_RESULT(msgt) (((msgt) & 0b11111100) | 0b10)
extern const struct value_string osmo_gsup_message_type_names[];
static inline const char *
@@ -250,8 +265,20 @@ osmo_gsup_session_state_name(enum osmo_gsup_session_state val)
struct osmo_gsup_pdp_info {
unsigned int context_id;
int have_info;
- /*! Type of PDP context */
- uint16_t pdp_type;
+ /*! Type of PDP context, 3GPP TS 29.060, 7.7.27 */
+ union {
+ uint16_t pdp_type OSMO_DEPRECATED("use pdp_type_org and pdp_type_nr instead");
+ struct {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t pdp_type_nr; /* enum gsm48_pdp_type_nr */
+ uint8_t pdp_type_org; /* enum gsm48_pdp_type_org */
+#elif OSMO_IS_BIG_ENDIAN
+ uint8_t pdp_type_org; /* enum gsm48_pdp_type_org */
+ uint8_t pdp_type_nr; /* enum gsm48_pdp_type_nr */
+#endif
+ };
+ };
+ struct osmo_sockaddr pdp_address[2];
/*! APN information, still in encoded form. Can be NULL if no
* APN information included */
const uint8_t *apn_enc;
@@ -275,6 +302,7 @@ enum osmo_gsup_message_class {
OSMO_GSUP_MESSAGE_CLASS_SMS = 2,
OSMO_GSUP_MESSAGE_CLASS_USSD = 3,
OSMO_GSUP_MESSAGE_CLASS_INTER_MSC = 4,
+ OSMO_GSUP_MESSAGE_CLASS_IPSEC_EPDG = 5,
/* Keep this as last entry with a value of max(enum osmo_gsup_message_class) + 1.
* This value shall serve as the size for an array to aid de-muxing all known GSUP classes. */
OSMO_GSUP_MESSAGE_CLASS_ARRAYSIZE
@@ -380,6 +408,12 @@ struct osmo_gsup_message {
enum osmo_rat_type current_rat_type;
enum osmo_rat_type supported_rat_types[8]; /*!< arbitrary choice */
size_t supported_rat_types_len;
+
+ /*! PCO protocol option 3GPP TS 24.008 10.5.6.3 / Table 10.5.136. PCO contains Octet 3-ZA */
+ const uint8_t *pco;
+ /*! Number of bytes of the PCO. */
+ size_t pco_len;
+
};
int osmo_gsup_decode(const uint8_t *data, size_t data_len,
@@ -388,4 +422,5 @@ int osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg)
int osmo_gsup_get_err_msg_type(enum osmo_gsup_message_type type_in)
OSMO_DEPRECATED("Use OSMO_GSUP_TO_MSGT_ERROR() instead");
+#endif /* (!EMBEDDED) */
/*! @} */
diff --git a/include/osmocom/gsm/i460_mux.h b/include/osmocom/gsm/i460_mux.h
new file mode 100644
index 00000000..7c2f4af3
--- /dev/null
+++ b/include/osmocom/gsm/i460_mux.h
@@ -0,0 +1,2 @@
+#pragma once
+#include <osmocom/isdn/i460_mux.h>
diff --git a/include/osmocom/gsm/ipa.h b/include/osmocom/gsm/ipa.h
index 93cb1bf1..851b58e0 100644
--- a/include/osmocom/gsm/ipa.h
+++ b/include/osmocom/gsm/ipa.h
@@ -29,7 +29,7 @@ const char *ipa_ccm_idtag_name(uint8_t tag);
int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
OSMO_DEPRECATED("Use ipa_ccm_id_{get,resp}_parse instead");
int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset)
- OSMO_DEPRECATED("Use ipa_ccm_id_{get,resp}_parse instead");
+ OSMO_DEPRECATED_OUTSIDE("Use ipa_ccm_id_{get,resp}_parse instead");
/* parse payload of IPA CCM ID GET into a osmocom TLV style representation */
int ipa_ccm_id_get_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned int len);
diff --git a/include/osmocom/gsm/iuup.h b/include/osmocom/gsm/iuup.h
new file mode 100644
index 00000000..ab6b8acf
--- /dev/null
+++ b/include/osmocom/gsm/iuup.h
@@ -0,0 +1,129 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/prim.h>
+#include <osmocom/gsm/protocol/gsm_25_415.h>
+
+/***********************************************************************
+ * Primitives towards the lower layers (typically RTP transport)
+ ***********************************************************************/
+enum osmo_iuup_tnl_prim_type {
+ OSMO_IUUP_TNL_UNITDATA,
+};
+
+struct osmo_iuup_tnl_prim {
+ struct osmo_prim_hdr oph;
+};
+
+/***********************************************************************
+ * Primitives towards the upper layers at the RNL SAP
+ ***********************************************************************/
+
+/* 3GPP TS 25.415 Section 7.2.1 */
+enum osmo_iuup_rnl_prim_type {
+ OSMO_IUUP_RNL_CONFIG,
+ OSMO_IUUP_RNL_DATA,
+ OSMO_IUUP_RNL_STATUS,
+ OSMO_IUUP_RNL_UNIT_DATA,
+};
+
+/* TS 25.413 9.2.1.3*/
+#define IUUP_MAX_SUBFLOWS 7
+#define IUUP_MAX_RFCIS 64
+
+#define IUUP_TIMER_INIT_T_DEFAULT 1000
+#define IUUP_TIMER_TA_T_DEFAULT 500
+#define IUUP_TIMER_RC_T_DEFAULT 500
+#define IUUP_TIMER_INIT_N_DEFAULT 3
+#define IUUP_TIMER_TA_N_DEFAULT 1
+#define IUUP_TIMER_RC_N_DEFAULT 1
+struct osmo_iuup_rnl_config_timer {
+ uint32_t t_ms; /* time in ms */
+ uint32_t n_max; /* max number of repetitions */
+};
+struct osmo_iuup_rfci {
+ uint8_t used:1,
+ spare1:1,
+ id:6;
+ uint8_t spare2:4,
+ IPTI:4; /* values range 0-15, 4 bits */;
+ uint16_t subflow_sizes[IUUP_MAX_SUBFLOWS];
+};
+struct osmo_iuup_rnl_config {
+ /* transparent (true) or SMpSDU (false): */
+ bool transparent;
+
+ /* should we actively transmit INIT in SmpSDU mode? */
+ bool active;
+
+ /* Currently Version 0 or 1: */
+ uint8_t data_pdu_type;
+
+ /* Supported mode versions */
+ uint16_t supported_versions_mask;
+ uint8_t num_rfci;
+ uint8_t num_subflows;
+ bool IPTIs_present;
+ struct osmo_iuup_rfci rfci[IUUP_MAX_RFCIS];
+
+ /* TODO: Indication of delivery of erroneous SDUs*/
+ struct osmo_iuup_rnl_config_timer t_init;
+ struct osmo_iuup_rnl_config_timer t_ta;
+ struct osmo_iuup_rnl_config_timer t_rc;
+};
+
+struct osmo_iuup_rnl_data {
+ uint8_t rfci;
+ uint8_t frame_nr;
+ uint8_t fqc;
+};
+
+struct osmo_iuup_rnl_status {
+ enum iuup_procedure procedure;
+ union {
+ struct {
+ enum iuup_error_cause cause;
+ enum iuup_error_distance distance;
+ } error_event;
+ struct {
+ uint16_t mode_version;
+ uint8_t data_pdu_type;
+ uint8_t num_rfci;
+ uint8_t num_subflows;
+ bool IPTIs_present;
+ struct osmo_iuup_rfci rfci[IUUP_MAX_RFCIS];
+ } initialization;
+ struct {
+ } rate_control;
+ struct {
+ } time_alignment;
+ } u;
+};
+
+/* SAP on the upper side of IuUP, towards the user */
+struct osmo_iuup_rnl_prim {
+ struct osmo_prim_hdr oph;
+ union {
+ struct osmo_iuup_rnl_config config;
+ struct osmo_iuup_rnl_data data;
+ struct osmo_iuup_rnl_status status;
+ //struct osmo_iuup_rnl_unitdata unitdata;
+ } u;
+};
+
+struct osmo_iuup_instance;
+struct osmo_iuup_instance *osmo_iuup_instance_alloc(void *ctx, const char *id);
+void osmo_iuup_instance_free(struct osmo_iuup_instance *iui);
+
+void osmo_iuup_instance_set_user_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv);
+void osmo_iuup_instance_set_transport_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv);
+int osmo_iuup_tnl_prim_up(struct osmo_iuup_instance *iui, struct osmo_iuup_tnl_prim *itp);
+int osmo_iuup_rnl_prim_down(struct osmo_iuup_instance *inst, struct osmo_iuup_rnl_prim *irp);
+
+
+int osmo_iuup_compute_header_crc(const uint8_t *iuup_pdu, unsigned int pdu_len);
+int osmo_iuup_compute_payload_crc(const uint8_t *iuup_pdu, unsigned int pdu_len);
+
+struct osmo_iuup_rnl_prim *osmo_iuup_rnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size);
+struct osmo_iuup_tnl_prim *osmo_iuup_tnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size);
diff --git a/include/osmocom/gsm/l1sap.h b/include/osmocom/gsm/l1sap.h
index 3d2ad253..0faab7e0 100644
--- a/include/osmocom/gsm/l1sap.h
+++ b/include/osmocom/gsm/l1sap.h
@@ -27,6 +27,8 @@ enum osmo_mph_info_type {
PRIM_INFO_MODIFY, /*!< Mode Modify of channel */
PRIM_INFO_ACT_CIPH, /*!< Activation of ciphering */
PRIM_INFO_DEACT_CIPH, /*!< Deactivation of ciphering */
+ PRIM_INFO_ACT_UL_ACC, /*!< Activation of uplink access detection */
+ PRIM_INFO_DEACT_UL_ACC, /*!< Deactivation of uplink access detection */
};
/*! PH-DATA presence information */
@@ -84,6 +86,7 @@ struct ph_data_param {
};
int16_t lqual_cb; /*!< Link quality in centiBel */
enum osmo_ph_pres_info_type pdch_presence_info; /*!< Info regarding presence/validity of header and data parts */
+ uint8_t is_sub:1; /*!< flags */
};
/*! for TCH.{req,ind} | TCH-RTS.ind */
@@ -94,6 +97,8 @@ struct ph_tch_param {
uint8_t marker; /*!< RTP Marker bit (speech onset indicator) */
uint16_t ber10k; /*!< BER in units of 0.01% */
int16_t lqual_cb; /*!< Link quality in centiBel */
+ int16_t ta_offs_256bits;/*!< timing advance offset (in 1/256th bits) */
+ uint8_t is_sub:1; /*!< flags */
};
/*! for PH-CONN.ind */
@@ -123,7 +128,7 @@ struct info_meas_ind_param {
/*! for {ACTIVATE,DEACTIVATE,MODIFY} MPH-INFO.req */
struct info_act_req_param {
uint8_t chan_nr; /*!< Channel Number (Like RSL) */
- uint8_t sacch_only; /*!< \breif Only deactivate SACCH */
+ uint8_t sacch_only; /*!< \brief Only deactivate SACCH */
};
/*! for {ACTIVATE,DEACTIVATE} MPH-INFO.cnf */
@@ -139,6 +144,11 @@ struct info_ciph_req_param {
uint8_t uplink; /*!< Apply to uplink */
};
+/*! for {ACT_UL_ACC,DEACT_UL_ACC} MPH-INFO.req */
+struct info_ulacc_req_param {
+ uint8_t chan_nr; /*!< Channel Number (Like RSL) */
+};
+
/*! for MPH-INFO.ind */
struct mph_info_param {
enum osmo_mph_info_type type; /*!< Info message type */
@@ -148,6 +158,7 @@ struct mph_info_param {
struct info_act_req_param act_req;
struct info_act_cnf_param act_cnf;
struct info_ciph_req_param ciph_req;
+ struct info_ulacc_req_param ulacc_req;
} u;
};
diff --git a/include/osmocom/gsm/lapd_core.h b/include/osmocom/gsm/lapd_core.h
index cfc357a7..a4dc2505 100644
--- a/include/osmocom/gsm/lapd_core.h
+++ b/include/osmocom/gsm/lapd_core.h
@@ -1,171 +1,2 @@
-/*! \file lapd_core.h
- * primitive related stuff
- */
#pragma once
-
-#include <stdint.h>
-
-#include <osmocom/core/timer.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/gsm/prim.h>
-
-/*! \defgroup lapd LAPD implementation common part
- * @{
- * \file lapd_core.h
- */
-
-/*! LAPD related primitives (L2<->L3 SAP)*/
-enum osmo_dl_prim {
- PRIM_DL_UNIT_DATA, /*!< DL-UNIT-DATA */
- PRIM_DL_DATA, /*!< DL-DATA */
- PRIM_DL_EST, /*!< DL-ESTABLISH */
- PRIM_DL_REL, /*!< DL-RLEEASE */
- PRIM_DL_SUSP, /*!< DL-SUSPEND */
- PRIM_DL_RES, /*!< DL-RESUME */
- PRIM_DL_RECON, /*!< DL-RECONNECT */
- PRIM_MDL_ERROR, /*!< MDL-ERROR */
-};
-
-/* Uses the same values as RLL, so no conversion for GSM is required. */
-#define MDL_CAUSE_T200_EXPIRED 0x01
-#define MDL_CAUSE_REEST_REQ 0x02
-#define MDL_CAUSE_UNSOL_UA_RESP 0x03
-#define MDL_CAUSE_UNSOL_DM_RESP 0x04
-#define MDL_CAUSE_UNSOL_DM_RESP_MF 0x05
-#define MDL_CAUSE_UNSOL_SPRV_RESP 0x06
-#define MDL_CAUSE_SEQ_ERR 0x07
-#define MDL_CAUSE_UFRM_INC_PARAM 0x08
-#define MDL_CAUSE_SFRM_INC_PARAM 0x09
-#define MDL_CAUSE_IFRM_INC_MBITS 0x0a
-#define MDL_CAUSE_IFRM_INC_LEN 0x0b
-#define MDL_CAUSE_FRM_UNIMPL 0x0c
-#define MDL_CAUSE_SABM_MF 0x0d
-#define MDL_CAUSE_SABM_INFO_NOTALL 0x0e
-#define MDL_CAUSE_FRMR 0x0f
-
-/*! for MDL-ERROR.ind */
-struct mdl_error_ind_param {
- uint8_t cause; /*!< generic cause value */
-};
-
-/*! for DL-REL.req */
-struct dl_rel_req_param {
- uint8_t mode; /*!< release mode */
-};
-
-/*! primitive header for LAPD DL-SAP primitives */
-struct osmo_dlsap_prim {
- struct osmo_prim_hdr oph; /*!< generic primitive header */
- union {
- struct mdl_error_ind_param error_ind;
- struct dl_rel_req_param rel_req;
- } u; /*!< request-specific data */
-};
-
-/*! LAPD mode/role */
-enum lapd_mode {
- LAPD_MODE_USER, /*!< behave like user */
- LAPD_MODE_NETWORK, /*!< behave like network */
-};
-
-/*! LAPD state (Figure B.2/Q.921)*/
-enum lapd_state {
- LAPD_STATE_NULL = 0,
- LAPD_STATE_TEI_UNASS,
- LAPD_STATE_ASS_TEI_WAIT,
- LAPD_STATE_EST_TEI_WAIT,
- LAPD_STATE_IDLE,
- LAPD_STATE_SABM_SENT,
- LAPD_STATE_DISC_SENT,
- LAPD_STATE_MF_EST,
- LAPD_STATE_TIMER_RECOV,
-};
-
-/*! LAPD message format (I / S / U) */
-enum lapd_format {
- LAPD_FORM_UKN = 0,
- LAPD_FORM_I,
- LAPD_FORM_S,
- LAPD_FORM_U,
-};
-
-/*! LAPD message context */
-struct lapd_msg_ctx {
- struct lapd_datalink *dl;
- int n201;
- /* address */
- uint8_t cr;
- uint8_t sapi;
- uint8_t tei;
- uint8_t lpd;
- /* control */
- uint8_t format;
- uint8_t p_f; /* poll / final bit */
- uint8_t n_send;
- uint8_t n_recv;
- uint8_t s_u; /* S or repectivly U function bits */
- /* length */
- int length;
- uint8_t more;
-};
-
-struct lapd_cr_ent {
- uint8_t cmd;
- uint8_t resp;
-};
-
-struct lapd_history {
- struct msgb *msg; /* message to be sent / NULL, if histoy is empty */
- int more; /* if message is fragmented */
-};
-
-/*! LAPD datalink */
-struct lapd_datalink {
- int (*send_dlsap)(struct osmo_dlsap_prim *dp,
- struct lapd_msg_ctx *lctx);
- int (*send_ph_data_req)(struct lapd_msg_ctx *lctx, struct msgb *msg);
- int (*update_pending_frames)(struct lapd_msg_ctx *lctx);
- struct {
- /*! filled-in once we set the lapd_mode above */
- struct lapd_cr_ent loc2rem;
- struct lapd_cr_ent rem2loc;
- } cr;
- enum lapd_mode mode; /*!< current mode of link */
- int use_sabme; /*!< use SABME instead of SABM */
- int reestablish; /*!< enable reestablish support */
- int n200, n200_est_rel; /*!< number of retranmissions */
- struct lapd_msg_ctx lctx; /*!< LAPD context */
- int maxf; /*!< maximum frame size (after defragmentation) */
- uint8_t k; /*!< maximum number of unacknowledged frames */
- uint8_t v_range; /*!< range of sequence numbers */
- uint8_t v_send; /*!< seq nr of next I frame to be transmitted */
- uint8_t v_ack; /*!< last frame ACKed by peer */
- uint8_t v_recv; /*!< seq nr of next I frame expected to be received */
- uint32_t state; /*!< LAPD state (\ref lapd_state) */
- int seq_err_cond; /*!< condition of sequence error */
- uint8_t own_busy; /*!< receiver busy on our side */
- uint8_t peer_busy; /*!< receiver busy on remote side */
- int t200_sec, t200_usec; /*!< retry timer (default 1 sec) */
- int t203_sec, t203_usec; /*!< retry timer (default 10 secs) */
- struct osmo_timer_list t200; /*!< T200 timer */
- struct osmo_timer_list t203; /*!< T203 timer */
- uint8_t retrans_ctr; /*!< re-transmission counter */
- struct llist_head tx_queue; /*!< frames to L1 */
- 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 */
- struct lapd_history *tx_hist; /*!< tx history structure array */
- uint8_t range_hist; /*!< range of history buffer 2..2^n */
- struct msgb *rcv_buffer; /*!< buffer to assemble the received message */
- struct msgb *cont_res; /*!< buffer to store content resolution data on network side, to detect multiple phones on same channel */
-};
-
-void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range,
- int maxf);
-void lapd_dl_exit(struct lapd_datalink *dl);
-void lapd_dl_reset(struct lapd_datalink *dl);
-int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode);
-int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx);
-int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
-
-/*! @} */
+#include <osmocom/isdn/lapd_core.h>
diff --git a/include/osmocom/gsm/lapdm.h b/include/osmocom/gsm/lapdm.h
index 931de80a..6812102e 100644
--- a/include/osmocom/gsm/lapdm.h
+++ b/include/osmocom/gsm/lapdm.h
@@ -24,6 +24,7 @@ struct lapdm_msg_ctx {
uint8_t link_id;
uint8_t ta_ind; /* TA indicated by network */
uint8_t tx_power_ind; /* MS power indicated by network */
+ uint32_t fn;
};
/*! LAPDm datalink like TS 04.06 / Section 3.5.2 */
@@ -32,6 +33,10 @@ struct lapdm_datalink {
struct lapdm_msg_ctx mctx; /*!< context of established connection */
struct lapdm_entity *entity; /*!< LAPDm entity we are part of */
+
+ struct llist_head tx_ui_queue; /*!< UI frames to L1 */
+ uint32_t t200_fn; /*!< T200 timer in frames */
+ uint32_t t200_timeout; /*!< T200 timeout frame number */
};
/*! LAPDm datalink SAPIs */
@@ -45,6 +50,8 @@ typedef int (*lapdm_cb_t)(struct msgb *msg, struct lapdm_entity *le, void *ctx);
#define LAPDM_ENT_F_EMPTY_FRAME 0x0001
#define LAPDM_ENT_F_POLLING_ONLY 0x0002
+#define LAPDM_ENT_F_DROP_2ND_REJ 0x0004
+#define LAPDM_ENT_F_RTS 0x0008
/*! a LAPDm Entity */
struct lapdm_entity {
@@ -83,13 +90,19 @@ struct lapdm_datalink *lapdm_datalink_for_sapi(struct lapdm_entity *le, uint8_t
/* initialize a LAPDm entity */
void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200)
- OSMO_DEPRECATED("Use lapdm_entity_init2() instead");
+ OSMO_DEPRECATED("Use lapdm_entity_init3() instead");
void lapdm_entity_init2(struct lapdm_entity *le, enum lapdm_mode mode,
- const int *t200_ms, int n200);
+ const int *t200_ms, int n200)
+ OSMO_DEPRECATED("Use lapdm_entity_init3() instead");
+void lapdm_entity_init3(struct lapdm_entity *le, enum lapdm_mode mode,
+ const int *t200_ms, int n200, const char *name_pfx);
void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode)
- OSMO_DEPRECATED("Use lapdm_channel_init2() instead");
+ OSMO_DEPRECATED_OUTSIDE("Use lapdm_channel_init3() instead");
int lapdm_channel_init2(struct lapdm_channel *lc, enum lapdm_mode mode,
const int *t200_ms_dcch, const int *t200_ms_acch, enum gsm_chan_t chan_t);
+int lapdm_channel_init3(struct lapdm_channel *lc, enum lapdm_mode mode,
+ const int *t200_ms_dcch, const int *t200_ms_acch, enum gsm_chan_t chan_t,
+ const char *name_pfx);
/* deinitialize a LAPDm entity */
void lapdm_entity_exit(struct lapdm_entity *le);
void lapdm_channel_exit(struct lapdm_channel *lc);
@@ -112,6 +125,11 @@ void lapdm_channel_reset(struct lapdm_channel *lc);
void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags);
void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags);
+void lapdm_entity_set_t200_fn(struct lapdm_entity *le, const uint32_t *t200_fn);
+void lapdm_channel_set_t200_fn(struct lapdm_channel *lc, const uint32_t *t200_fn_dcch, const uint32_t *t200_fn_acch);
+
int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp);
+int lapdm_phsap_dequeue_prim_fn(struct lapdm_entity *le, struct osmo_phsap_prim *pp, uint32_t fn);
+void lapdm_t200_fn(struct lapdm_entity *le, uint32_t fn);
/*! @} */
diff --git a/include/osmocom/gsm/meas_rep.h b/include/osmocom/gsm/meas_rep.h
index 79f9f06b..5628d8ec 100644
--- a/include/osmocom/gsm/meas_rep.h
+++ b/include/osmocom/gsm/meas_rep.h
@@ -11,7 +11,7 @@ struct gsm_rx_lev_qual {
uint8_t rx_qual;
};
-/* unidirectional measumrement report */
+/* unidirectional measurement report */
struct gsm_meas_rep_unidir {
struct gsm_rx_lev_qual full;
struct gsm_rx_lev_qual sub;
@@ -28,5 +28,5 @@ enum meas_rep_field {
MEAS_REP_UL_RXQUAL_SUB,
};
-size_t gsm0858_rsl_ul_meas_enc(struct gsm_meas_rep_unidir *mru, bool dtxd_used,
+size_t gsm0858_rsl_ul_meas_enc(const struct gsm_meas_rep_unidir *mru, bool dtxd_used,
uint8_t *buf);
diff --git a/include/osmocom/gsm/prim.h b/include/osmocom/gsm/prim.h
index 386b7d89..73cd7f71 100644
--- a/include/osmocom/gsm/prim.h
+++ b/include/osmocom/gsm/prim.h
@@ -14,4 +14,11 @@ enum osmo_gsm_sap {
SAP_BSSGP_LL,
SAP_BSSGP_NM,
SAP_BSSGP_PFM,
+
+ SAP_NS,
+
+ SAP_BSSGP_RIM,
+
+ SAP_IUUP_TNL,
+ SAP_IUUP_RNL,
};
diff --git a/include/osmocom/gsm/protocol/Makefile.am b/include/osmocom/gsm/protocol/Makefile.am
new file mode 100644
index 00000000..23429dcd
--- /dev/null
+++ b/include/osmocom/gsm/protocol/Makefile.am
@@ -0,0 +1,30 @@
+osmogsmproto_HEADERS = \
+ gsm_23_032.h \
+ gsm_03_40.h \
+ gsm_03_41.h \
+ gsm_04_08.h \
+ gsm_04_08_gprs.h \
+ gsm_04_11.h \
+ gsm_04_12.h \
+ gsm_04_14.h \
+ gsm_04_80.h \
+ gsm_08_08.h \
+ gsm_08_58.h \
+ gsm_09_02.h \
+ gsm_12_21.h \
+ gsm_23_003.h \
+ gsm_23_041.h \
+ gsm_25_415.h \
+ gsm_29_118.h \
+ gsm_44_004.h \
+ gsm_44_060.h \
+ gsm_44_068.h \
+ gsm_44_318.h \
+ gsm_48_049.h \
+ gsm_48_071.h \
+ gsm_49_031.h \
+ ipaccess.h \
+ smpp34_osmocom.h \
+ $(NULL)
+
+osmogsmprotodir = $(includedir)/osmocom/gsm/protocol
diff --git a/include/osmocom/gsm/protocol/gsm_03_41.h b/include/osmocom/gsm/protocol/gsm_03_41.h
index 1b399aee..f6c36cad 100644
--- a/include/osmocom/gsm/protocol/gsm_03_41.h
+++ b/include/osmocom/gsm/protocol/gsm_03_41.h
@@ -25,7 +25,7 @@ struct gsm341_ms_message {
uint8_t update:4;
uint8_t code_lo:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t gs:2, code_hi:6;
uint8_t code_lo:4, update:4;
#endif
@@ -36,7 +36,7 @@ struct gsm341_ms_message {
uint8_t language:4;
uint8_t group:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t group:4, language:4;
#endif
} dcs;
@@ -45,7 +45,7 @@ struct gsm341_ms_message {
uint8_t total:4;
uint8_t current:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t current:4, total:4;
#endif
} page;
@@ -63,7 +63,7 @@ struct gsm341_etws_message {
uint8_t update:4;
uint8_t code_lo:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t gs:2, alert:1, popup:1, code_hi:4;
uint8_t code_lo:4, update:4;
#endif
diff --git a/include/osmocom/gsm/protocol/gsm_04_08.h b/include/osmocom/gsm/protocol/gsm_04_08.h
index 8370eca1..075b6ca6 100644
--- a/include/osmocom/gsm/protocol/gsm_04_08.h
+++ b/include/osmocom/gsm/protocol/gsm_04_08.h
@@ -22,7 +22,7 @@ struct gsm48_classmark1 {
rev_lev:2,
spare:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:1, rev_lev:2, es_ind:1, a5_1:1, pwr_lev:3;
#endif
} __attribute__ ((packed));
@@ -51,13 +51,182 @@ struct gsm48_classmark2 {
spare4:1,
cm3:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:1, rev_lev:2, es_ind:1, a5_1:1, pwr_lev:3;
uint8_t spare2:1, ps_cap:1, ss_scr:2, sm_cap:1, vbs:1, vgcs:1, fc:1;
uint8_t cm3:1, spare4:1, lcsva_cap:1, spare3:1, solsa:1, cmsp:1, a5_3:1, a5_2:1;
#endif
} __attribute__ ((packed));
+/* Chapter 10.5.1.7 */
+struct gsm48_classmark3 {
+ uint8_t a5_bits;
+ uint8_t mult_band_supp;
+ uint8_t assoc_radio_cap_1;
+ uint8_t assoc_radio_cap_2;
+
+ struct {
+ bool present;
+ uint8_t r_gsm_assoc_radio_cap;
+ } r_support;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } hscsd_mult_slot_cap;
+
+ bool ucs2_treatment;
+ bool extended_meas_cap;
+
+ struct {
+ bool present;
+ uint8_t sms_value;
+ uint8_t sm_value;
+ } ms_meas_cap;
+
+ struct {
+ bool present;
+ uint8_t method;
+ } ms_pos_method_cap;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } ecsd_multislot_cap;
+
+ struct {
+ bool present;
+ bool mod_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } rf_pwr_cap_1;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } rf_pwr_cap_2;
+
+ } psk8_struct;
+
+ struct {
+ bool present;
+ uint8_t value;
+ uint8_t assoc_radio_cap;
+ } gsm_400_bands_supp;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } gsm_850_assoc_radio_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } gsm_1900_assoc_radio_cap;
+
+ bool umts_fdd_rat_cap;
+ bool umts_tdd_rat_cap;
+ bool cdma200_rat_cap;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ bool single_slot_dtm;
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } dtm_egprs_multislot_cap;
+ } dtm_gprs_multislot_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } single_band_supp;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } gsm_750_assoc_radio_cap;
+
+ bool umts_1_28_mcps_tdd_rat_cap;
+ bool geran_feature_package;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } extended_dtm_egprs_multislot_cap;
+ } extended_dtm_gprs_multislot_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } high_multislot_cap;
+
+ bool geran_feature_package_2;
+ uint8_t gmsk_multislot_power_prof;
+ uint8_t psk8_multislot_power_prof;
+
+ struct {
+ bool present;
+ uint8_t value;
+ uint8_t assoc_radio_cap;
+ } t_gsm_400_bands_supp;
+
+ uint8_t dl_advanced_rx_perf;
+ bool dtm_enhancements_cap;
+
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ bool offset_required;
+ struct {
+ bool present;
+ uint8_t mslot_class;
+ } dtm_egprs_high_multislot_cap;
+ } dtm_gprs_high_multislot_cap;
+
+ bool repeated_acch_capability;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } gsm_710_assoc_radio_cap;
+
+ struct {
+ bool present;
+ uint8_t value;
+ } t_gsm_810_assoc_radio_cap;
+
+ bool ciphering_mode_setting_cap;
+ bool add_pos_cap;
+ bool e_utra_fdd_supp;
+ bool e_utra_tdd_supp;
+ bool e_utra_meas_rep_supp;
+ bool prio_resel_supp;
+ bool utra_csg_cells_rep;
+
+ uint8_t vamos_level;
+ uint8_t tighter_capability;
+
+ bool sel_ciph_dl_sacch;
+
+ uint8_t cs_ps_srvcc_geran_utra;
+ uint8_t cs_ps_srvcc_geran_eutra;
+
+ bool geran_net_sharing;
+ bool e_utra_wb_rsrq_meas_supp;
+ bool er_band_support;
+ bool utra_mult_band_ind_supp;
+ bool e_utra_mult_band_ind_supp;
+ bool extended_tsc_set_cap_supp;
+ bool extended_earfcn_val_range;
+};
+
struct osmo_gsm48_classmark {
bool classmark1_set;
struct gsm48_classmark1 classmark1;
@@ -77,7 +246,6 @@ char *osmo_gsm48_classmark_a5_name_c(const void *ctx, const struct osmo_gsm48_cl
void osmo_gsm48_classmark_update(struct osmo_gsm48_classmark *dst, const struct osmo_gsm48_classmark *src);
int8_t osmo_gsm48_rfpowercap2powerclass(enum gsm_band band, uint8_t rf_power_cap);
/* Chapter 10.5.2.1b.3 */
-#if OSMO_IS_LITTLE_ENDIAN == 1
struct gsm48_range_1024 {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t w1_hi:2,
@@ -112,7 +280,7 @@ struct gsm48_range_1024 {
uint8_t w16:6,
w15_lo:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:5, f0:1, w1_hi:2;
uint8_t w1_lo;
uint8_t w2_hi;
@@ -131,64 +299,8 @@ struct gsm48_range_1024 {
uint8_t w15_lo:2, w16:6;
#endif
} __attribute__ ((packed));
-#else
-struct gsm48_range_1024 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t form_id:5,
- f0:1,
- w1_hi:2;
- uint8_t w1_lo;
- uint8_t w2_hi;
- uint8_t w2_lo:1,
- w3_hi:7;
- uint8_t w3_lo:2,
- w4_hi:6;
- uint8_t w4_lo:2,
- w5_hi:6;
- uint8_t w5_lo:2,
- w6_hi:6;
- uint8_t w6_lo:2,
- w7_hi:6;
- uint8_t w7_lo:2,
- w8_hi:6;
- uint8_t w8_lo:1,
- w9:7;
- uint8_t w10:7,
- w11_hi:1;
- uint8_t w11_lo:6,
- w12_hi:2;
- uint8_t w12_lo:5,
- w13_hi:3;
- uint8_t w13_lo:4,
- w14_hi:4;
- uint8_t w14_lo:3,
- w15_hi:5;
- uint8_t w15_lo:2,
- w16:6;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- 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;
-#endif
-} __attribute__ ((packed));
-#endif
/* Chapter 10.5.2.1b.4 */
-#if OSMO_IS_LITTLE_ENDIAN == 1
struct gsm48_range_512 {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t orig_arfcn_hi:1,
@@ -223,7 +335,7 @@ struct gsm48_range_512 {
uint8_t w17:5,
w16_lo:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:7, orig_arfcn_hi:1;
uint8_t orig_arfcn_mid;
uint8_t orig_arfcn_lo:1, w1_hi:7;
@@ -242,64 +354,8 @@ struct gsm48_range_512 {
uint8_t w16_lo:3, w17:5;
#endif
} __attribute__ ((packed));
-#else
-struct gsm48_range_512 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t form_id:7,
- orig_arfcn_hi:1;
- uint8_t orig_arfcn_mid;
- uint8_t orig_arfcn_lo:1,
- w1_hi:7;
- uint8_t w1_lo:2,
- w2_hi:6;
- uint8_t w2_lo:2,
- w3_hi:6;
- uint8_t w3_lo:2,
- w4_hi:6;
- uint8_t w4_lo:1,
- w5:7;
- uint8_t w6:7,
- w7_hi:1;
- uint8_t w7_lo:6,
- w8_hi:2;
- uint8_t w8_lo:4,
- w9_hi:4;
- uint8_t w9_lo:2,
- w10:6;
- uint8_t w11:6,
- w12_hi:2;
- uint8_t w12_lo:4,
- w13_hi:4;
- uint8_t w13_lo:2,
- w14:6;
- uint8_t w15:6,
- w16_hi:2;
- uint8_t w16_lo:3,
- w17:5;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- 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;
-#endif
-} __attribute__ ((packed));
-#endif
/* Chapter 10.5.2.1b.5 */
-#if OSMO_IS_LITTLE_ENDIAN == 1
struct gsm48_range_256 {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t orig_arfcn_hi:1,
@@ -340,7 +396,7 @@ struct gsm48_range_256 {
w21:4,
w20_lo:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:7, orig_arfcn_hi:1;
uint8_t orig_arfcn_mid;
uint8_t orig_arfcn_lo:1, w1_hi:7;
@@ -359,70 +415,8 @@ struct gsm48_range_256 {
uint8_t w20_lo:3, w21:4, spare:1;
#endif
} __attribute__ ((packed));
-#else
-struct gsm48_range_256 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t form_id:7,
- orig_arfcn_hi:1;
- uint8_t orig_arfcn_mid;
- uint8_t orig_arfcn_lo:1,
- w1_hi:7;
- uint8_t w1_lo:1,
- w2:7;
- uint8_t w3:7,
- w4_hi:1;
- uint8_t w4_lo:5,
- w5_hi:3;
- uint8_t w5_lo:3,
- w6_hi:5;
- uint8_t w6_lo:1,
- w7:6,
- w8_hi:1;
- uint8_t w8_lo:4,
- w9_hi:4;
- uint8_t w9_lo:1,
- w10:5,
- w11_hi:2;
- uint8_t w11_lo:3,
- w12:5;
- uint8_t w13:5,
- w14_hi:3;
- uint8_t w14_lo:2,
- w15:5,
- w16_hi:1;
- uint8_t w16_lo:3,
- w17:4,
- w18_hi:1;
- uint8_t w18_lo:3,
- w19:4,
- w20_hi:1;
- uint8_t w20_lo:3,
- w21:4,
- spare:1;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- 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;
-#endif
-} __attribute__ ((packed));
-#endif
/* Chapter 10.5.2.1b.6 */
-#if OSMO_IS_LITTLE_ENDIAN == 1
struct gsm48_range_128 {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t orig_arfcn_hi:1,
@@ -465,7 +459,7 @@ struct gsm48_range_128 {
w27:3,
w26_lo:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:7, orig_arfcn_hi:1;
uint8_t orig_arfcn_mid;
uint8_t orig_arfcn_lo:1, w1:7;
@@ -484,69 +478,6 @@ struct gsm48_range_128 {
uint8_t w26_lo:1, w27:3, w28:3, spare:1;
#endif
} __attribute__ ((packed));
-#else
-struct gsm48_range_128 {
-#if OSMO_IS_LITTLE_ENDIAN
- uint8_t form_id:7,
- orig_arfcn_hi:1;
- uint8_t orig_arfcn_mid;
- uint8_t orig_arfcn_lo:1,
- w1:7;
- uint8_t w2:6,
- w3_hi:2;
- uint8_t w3_lo:4,
- w4_hi:4;
- uint8_t w4_lo:1,
- w5:5,
- w6_hi:2;
- uint8_t w6_lo:3,
- w7:5;
- uint8_t w8:4,
- w9:4;
- uint8_t w10:4,
- w11:4;
- uint8_t w12:4,
- w13:4;
- uint8_t w14:4,
- w15:4;
- uint8_t w16:3,
- w17:3,
- w18_hi:2;
- uint8_t w18_lo:1,
- w19:3,
- w20:3,
- w21_hi:1;
- uint8_t w21_lo:2,
- w22:3,
- w23:3;
- uint8_t w24:3,
- w25:3,
- w26_hi:2;
- uint8_t w26_lo:1,
- w27:3,
- w28:3,
- spare:1;
-#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
- 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;
-#endif
-} __attribute__ ((packed));
-#endif
/* Chapter 10.5.2.1b.7 */
struct gsm48_var_bit {
@@ -558,7 +489,7 @@ struct gsm48_var_bit {
orig_arfcn_lo:1;
uint8_t rrfcn8_111[13];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t form_id:7, orig_arfcn_hi:1;
uint8_t orig_arfcn_mid;
uint8_t orig_arfcn_lo:1, rrfcn1_7:7;
@@ -578,7 +509,7 @@ struct gsm48_chan_desc {
uint8_t hsn:6,
maio_low:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t tsc:3, h:1, maio_high:4;
uint8_t maio_low:2, hsn:6;
#endif
@@ -591,7 +522,7 @@ struct gsm48_chan_desc {
tsc:3;
uint8_t arfcn_low;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t tsc:3, h:1, spare:2, arfcn_high:2;
uint8_t arfcn_low;
#endif
@@ -642,7 +573,7 @@ struct gsm48_meas_res {
uint8_t bsic_nc6:6,
bcch_f_nc6_lo:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t ba_used:1, dtx_used:1, rxlev_full:6;
uint8_t spare:1, meas_valid:1, rxlev_sub:6;
uint8_t spare2:1, rxqual_full:3, rxqual_sub:3, no_nc_n_hi:1;
@@ -662,6 +593,12 @@ struct gsm48_meas_res {
#endif
} __attribute__ ((packed));
+/*! Check if the given mr contains valid measurement results */
+static inline bool gsm48_meas_res_is_valid(const struct gsm48_meas_res *mr)
+{
+ return (mr->meas_valid == 0); /* 0 means valid */
+}
+
/* Chapter 10.5.2.21aa */
struct gsm48_multi_rate_conf {
#if OSMO_IS_LITTLE_ENDIAN
@@ -679,7 +616,7 @@ struct gsm48_multi_rate_conf {
m10_2 : 1,
m12_2 : 1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t ver:3, nscb:1, icmi:1, spare:1, smod:2;
uint8_t m12_2:1, m10_2:1, m7_95:1, m7_40:1, m6_70:1, m5_90:1, m5_15:1, m4_75:1;
#endif
@@ -692,7 +629,7 @@ struct gsm48_power_cmd {
spare:2,
atc:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t atc:1, spare:2, power_level:5;
#endif
} __attribute__((packed));
@@ -707,7 +644,7 @@ struct gsm48_rach_control {
uint8_t t2; /* ACC 8-15 barred flags */
uint8_t t3; /* ACC 0-7 barred flags */
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t max_trans:2, tx_integer:4, cell_bar:1, re:1;
uint8_t t2;
uint8_t t3;
@@ -765,7 +702,7 @@ struct gsm48_req_ref {
uint8_t t2:5,
t3_low:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t ra;
uint8_t t1:5, t3_high:3;
uint8_t t3_low:3, t2:5;
@@ -780,7 +717,7 @@ struct gsm48_start_time {
uint8_t t2:5,
t3_low:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t t1:5, t3_high:3;
uint8_t t3_low:3, t2:5;
#endif
@@ -794,7 +731,7 @@ struct gsm48_sync_ind {
nci:1,
sync_ie:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t sync_ie:4, nci:1, rot:1, si:2;
#endif
} __attribute__((packed));
@@ -809,15 +746,59 @@ struct gsm48_chan_mode_modify {
uint8_t mode;
} __attribute__ ((packed));
+/*! 10.5.2.6 Channel Mode value */
enum gsm48_chan_mode {
+ /*! Signalling only (TCH/F or TCH/H) */
GSM48_CMODE_SIGN = 0x00,
+ /*! Speech: FR (TCH/FS) or HR (TCH/HS) */
GSM48_CMODE_SPEECH_V1 = 0x01,
- GSM48_CMODE_SPEECH_EFR = 0x21,
- GSM48_CMODE_SPEECH_AMR = 0x41,
+ /*! Speech: EFR (TCH/EFS) */
+ GSM48_CMODE_SPEECH_EFR = 0x21, /*!< a.k.a. V2 */
+ /*! Speech: AMR (TCH/AFS or TCH/AHS) */
+ GSM48_CMODE_SPEECH_AMR = 0x41, /*!< a.k.a. V3 */
+ /*! Speech: OFR AMR-WB (O-TCH/WFS) or OHR AMR-WB (O-TCH/WHS) */
+ GSM48_CMODE_SPEECH_V4 = 0x81,
+ /*! Speech: FR AMR-WB (TCH/WFS) */
+ GSM48_CMODE_SPEECH_V5 = 0x82,
+ /*! Speech: OHR AMR (O-TCH/AHS) */
+ GSM48_CMODE_SPEECH_V6 = 0x83,
+
+ /* ECSD: 43.5 kbit/s (DL) + 14.5 kbit/s (UL) */
+ GSM48_CMODE_DATA_43k5_14k5 = 0x61,
+ /* ECSD: 29.0 kbit/s (DL) + 14.5 kbit/s (UL) */
+ GSM48_CMODE_DATA_29k0_14k5 = 0x62,
+ /* ECSD: 43.5 kbit/s (DL) + 29.0 kbit/s (UL) */
+ GSM48_CMODE_DATA_43k5_29k0 = 0x64,
+ /* ECSD: 14.5 kbit/s (DL) + 43.5 kbit/s (UL) */
+ GSM48_CMODE_DATA_14k5_43k5 = 0x67,
+ /* ECSD: 14.5 kbit/s (DL) + 29.0 kbit/s (UL) */
+ GSM48_CMODE_DATA_14k5_29k0 = 0x65,
+ /* ECSD: 29.0 kbit/s (DL) + 43.5 kbit/s (UL) */
+ GSM48_CMODE_DATA_29k0_43k5 = 0x66,
+
+ /*! ECSD: 43.5 kbit/s radio interface rate, 43.2 kbit/s services (E-TCH/F43.2) */
+ GSM48_CMODE_DATA_43k5 = 0x27,
+ /*! ECSD: 32.0 kbit/s radio interface rate, 32.0 kbit/s services (E-TCH/F32.0) */
+ GSM48_CMODE_DATA_32k0 = 0x63,
+ /*! ECSD: 29.0 kbit/s radio interface rate, 28.8 kbit/s services (E-TCH/F28.8) */
+ GSM48_CMODE_DATA_29k0 = 0x43,
+ /*! CSD: 14.5 kbit/s radio interface rate, 14.4 kbit/s services (TCH/F14.4) */
GSM48_CMODE_DATA_14k5 = 0x0f,
+ /*! CSD: 12.0 kbit/s radio interface rate, 9.6 kbit/s services (TCH/F9.6) */
GSM48_CMODE_DATA_12k0 = 0x03,
+ /*! CSD: 6.0 kbit/s radio interface rate, 4.8 kbit/s services (TCH/{F,H}4.8) */
GSM48_CMODE_DATA_6k0 = 0x0b,
+ /*! CSD: 3.6 kbit/s radio interface rate, 2.4 kbit/s and less services (TCH/{F,H}2.4) */
GSM48_CMODE_DATA_3k6 = 0x13,
+
+ /*! Same as GSM48_CMODE_SPEECH_V1, in VAMOS mode */
+ GSM48_CMODE_SPEECH_V1_VAMOS = 0xc1,
+ /*! Same as GSM48_CMODE_SPEECH_EFR, in VAMOS mode */
+ GSM48_CMODE_SPEECH_V2_VAMOS = 0xc2,
+ /*! Same as GSM48_CMODE_SPEECH_AMR, in VAMOS mode */
+ GSM48_CMODE_SPEECH_V3_VAMOS = 0xc3,
+ /*! Speech: GSM48_CMODE_SPEECH_V5, in VAMOS mode */
+ GSM48_CMODE_SPEECH_V5_VAMOS = 0xc5,
};
extern const struct value_string gsm48_chan_mode_names[];
@@ -856,7 +837,7 @@ struct gsm48_cell_desc {
arfcn_hi:2;
uint8_t arfcn_lo;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t arfcn_hi:2, ncc:3, bcc:3;
uint8_t arfcn_lo;
#endif
@@ -899,10 +880,10 @@ struct gsm48_pag_resp {
uint8_t mi_len;
uint8_t mi[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t key_seq:4, spare:4;
union {
- uint32_t classmark2; /* Backward compatibility */
+ uint32_t classmark2;
struct {
uint8_t cm2_len;
struct gsm48_classmark2 cm2;
@@ -926,7 +907,7 @@ struct gsm48_auth_req {
spare:4;
uint8_t rand[16];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:4, key_seq:4;
uint8_t rand[16];
#endif
@@ -947,7 +928,7 @@ struct gsm48_loc_upd_req {
uint8_t mi_len;
uint8_t mi[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t key_seq:4, type:4;
struct gsm48_loc_area_id lai;
struct gsm48_classmark1 classmark1;
@@ -963,6 +944,20 @@ struct gsm48_hdr {
uint8_t data[0];
} __attribute__ ((packed));
+/* Short header */
+struct gsm48_hdr_sh {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t l2_header:2, /* < short layer 2 header : bit(2) > See 3GPP TS 44.006 §6.4a */
+ msg_type:5, /* < message type : bit(5) > See 3GPP TS 44.018 Table 10.4.2 */
+ rr_short_pd:1; /* < RR short PD : bit > See 3GPP TS 24.007 §11.3.2 */
+ uint8_t data[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t rr_short_pd:1, msg_type:5, l2_header:2;
+ uint8_t data[0];
+#endif
+} __attribute__ ((packed));
+
/* Section 9.1.3x System information Type header */
struct gsm48_system_information_type_header {
#if OSMO_IS_LITTLE_ENDIAN
@@ -971,7 +966,7 @@ struct gsm48_system_information_type_header {
skip_indicator:4;
uint8_t system_information;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t l2_plen;
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
@@ -987,7 +982,7 @@ struct gsm48_cell_sel_par {
neci:1,
acs:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t cell_resel_hyst:3, ms_txpwr_max_ccch:5;
uint8_t acs:1, neci:1, rxlev_acc_min:6;
#endif
@@ -1006,7 +1001,7 @@ struct gsm48_control_channel_descr {
spare_2 :1;
uint8_t t3212;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t mscr:1, att:1, bs_ag_blks_res:3, ccch_conf:3;
uint8_t spare_2:1, cbq3:2, spare_1:2, bs_pa_mfrms:3;
uint8_t t3212;
@@ -1029,7 +1024,7 @@ struct gsm48_cell_options {
/* either DN-IND or top bit of DTX IND */
d:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t d:1, pwrc:1, dtx:2, radio_link_timeout:4;
#endif
} __attribute__ ((packed));
@@ -1050,10 +1045,10 @@ struct gsm48_service_request {
uint8_t mi[0];
/* optional priority level */
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t cipher_key_seq:4, cm_service_type:4;
union {
- uint32_t classmark; /* Backward compatibility */
+ uint32_t classmark;
struct {
uint8_t cm2_len;
struct gsm48_classmark2 classmark2;
@@ -1133,7 +1128,7 @@ struct gsm48_system_information_type_5 {
uint8_t system_information;
uint8_t bcch_frequency_list[16];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
uint8_t bcch_frequency_list[16];
@@ -1148,7 +1143,7 @@ struct gsm48_system_information_type_5bis {
uint8_t system_information;
uint8_t bcch_frequency_list[16];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
uint8_t bcch_frequency_list[16];
@@ -1163,7 +1158,7 @@ struct gsm48_system_information_type_5ter {
uint8_t system_information;
uint8_t bcch_frequency_list[16];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
uint8_t bcch_frequency_list[16];
@@ -1182,7 +1177,7 @@ struct gsm48_system_information_type_6 {
uint8_t ncc_permitted;
uint8_t rest_octets[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t skip_indicator:4, rr_protocol_discriminator:4;
uint8_t system_information;
uint16_t cell_identity;
@@ -1193,6 +1188,27 @@ struct gsm48_system_information_type_6 {
#endif
} __attribute__ ((packed));
+/* Section 9.1.50 System Information type 10 (ASCI) */
+struct gsm48_system_information_type_10 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t l2_header:2, /* < short layer 2 header : bit(2) > See 3GPP TS 44.006 §6.4a */
+ msg_type:5, /* < message type : bit(5) > See 3GPP TS 44.018 Table 10.4.2 */
+ rr_short_pd:1; /* < RR short PD : bit > See 3GPP TS 24.007 §11.3.2 */
+ uint8_t rest_octets[0]; /* < SI10 Rest Octets : bit(160) > See 3GPP TS 44.018 §10.5.2.44 */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t rr_short_pd:1, msg_type:5, l2_header:2;
+ uint8_t rest_octets[0];
+#endif
+} __attribute__ ((packed));
+
+/* TS 44.018 Section 9.1.49 */
+struct gsm0408_vgcs_ul_grant {
+ struct gsm48_hdr hdr;
+ struct gsm48_req_ref req_ref;
+ uint8_t ta;
+} __attribute__ ((packed));
+
/* Section 9.1.43a System Information type 13 */
struct gsm48_system_information_type_13 {
struct gsm48_system_information_type_header header;
@@ -1248,7 +1264,7 @@ struct gsm48_cip_mode_cmd {
cr:1,
spare:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:3, cr:1, alg_id:3, sc:1;
#endif
} __attribute__((packed));
@@ -1293,6 +1309,14 @@ struct gsm48_imm_ass_rej {
uint8_t rest[0];
} __attribute__ ((packed));
+/* Section 9.1.21b */
+struct gsm48_notification_nch {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t data[0];
+} __attribute__((packed));
+
/* Section 9.1.22 */
struct gsm48_paging1 {
#if OSMO_IS_LITTLE_ENDIAN
@@ -1305,7 +1329,7 @@ struct gsm48_paging1 {
cneed2:2;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t l2_plen;
uint8_t proto_discr;
uint8_t msg_type;
@@ -1328,7 +1352,7 @@ struct gsm48_paging2 {
uint32_t tmsi2;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t l2_plen;
uint8_t proto_discr;
uint8_t msg_type;
@@ -1358,7 +1382,7 @@ struct gsm48_paging3 {
spare2:4;
uint8_t rest[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t l2_plen;
uint8_t proto_discr;
uint8_t msg_type;
@@ -1381,7 +1405,7 @@ struct gsm48_pag_rsp {
struct gsm48_classmark2 cm2;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:5, key_seq:3;
uint8_t cm2_len;
struct gsm48_classmark2 cm2;
@@ -1394,6 +1418,18 @@ struct gsm48_rr_status {
uint8_t rr_cause;
} __attribute__((packed));
+/* Section 9.1.44 */
+struct gsm48_talker_indication {
+ uint8_t cm2_len;
+ struct gsm48_classmark2 cm2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.48 */
+struct gsm48_uplink_release {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
/* Section 10.2 + GSM 04.07 12.2.3.1.1 + 3GPP TS 24.007 11.2.3.1.1 */
#define GSM48_PDISC_GROUP_CC 0x00
#define GSM48_PDISC_BCAST_CC 0x01
@@ -1412,6 +1448,9 @@ struct gsm48_rr_status {
#define GSM48_PDISC_TEST 0x0f /* as per 11.10, 04.14 */
#define GSM48_PDISC_MASK 0x0f
+/* Section 11.3.2.1 3GPP TS 24.007: Short PDISC */
+#define GSM48_PDISC_SH_RR 0
+
extern const struct value_string gsm48_pdisc_names[];
static inline const char *gsm48_pdisc_name(uint8_t val)
{ return get_value_string(gsm48_pdisc_names, val); }
@@ -1599,6 +1638,21 @@ void gsm48_set_dtx(struct gsm48_cell_options *op, enum gsm48_dtx_mode full,
#define GSM48_MT_RR_APP_INFO 0x38
+/* 3GPP TS 44.018 Table 10.4.2 */
+#define GSM48_MT_RR_SH_SI10 0x0
+#define GSM48_MT_RR_SH_FACCH 0x1
+#define GSM48_MT_RR_SH_UL_FREE 0x2
+#define GSM48_MT_RR_SH_MEAS_REP 0x4
+#define GSM48_MT_RR_SH_MEAS_INFO 0x5
+#define GSM48_MT_RR_SH_VGCS_RECON 0x6
+#define GSM48_MT_RR_SH_VGCS_RECON2 0x7
+#define GSM48_MT_RR_SH_VGCS_INFO 0x8
+#define GSM48_MT_RR_SH_VGCS_SMS 0x9
+#define GSM48_MT_RR_SH_SI10bis 0xA
+#define GSM48_MT_RR_SH_SI10ter 0xB
+#define GSM48_MT_RR_SH_VGCS_NEIGH 0xC
+#define GSM48_MT_RR_SH_APP_DATA 0xD
+
/* Table 10.2/3GPP TS 04.08 */
#define GSM48_MT_MM_IMSI_DETACH_IND 0x01
#define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02
@@ -1764,6 +1818,10 @@ static inline const char *osmo_lu_type_name(uint8_t lu_type)
#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_GROUP_CIP_SEQ_HO 0x08 /* HO = Half Octet Tag */
+#define GSM48_IE_CIP_MODE_SET_HO 0x09 /* HO = Half Octet Tag */
+#define GSM48_IE_GPRS_RESUMPT_HO 0xc0 /* HO = Half Octet Tag */
+#define GSM48_IE_SYNC_IND_HO 0x0d /* HO = Half Octet Tag */
#define GSM48_IE_MSLOT_DESC 0x10
#define GSM48_IE_CHANMODE_2 0x11
#define GSM48_IE_FRQSHORT_BEFORE 0x12
@@ -1794,6 +1852,7 @@ static inline const char *osmo_lu_type_name(uint8_t lu_type)
#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_EXTENDED_TSC_SET 0x6d
#define GSM48_IE_MA_AFTER 0x72
#define GSM48_IE_BA_RANGE 0x73
#define GSM48_IE_GROUP_CHDES 0x74
@@ -1804,20 +1863,21 @@ static inline const char *osmo_lu_type_name(uint8_t lu_type)
#define GSM48_IE_START_TIME 0x7c
#define GSM48_IE_INDIVIDUAL_PRIORITIES 0x7c /* 44.018 Section 9.1.7 */
#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
+#define GSM48_IE_GROUP_CIP_SEQ 0x80 /* DEPRECATED, use GSM48_IE_GROUP_CIP_SEQ_HO instead */
+#define GSM48_IE_CIP_MODE_SET 0x90 /* DEPRECATED, use GSM48_IE_CIP_MODE_SET_HO instead */
+#define GSM48_IE_GPRS_RESUMPT 0xc0 /* DEPRECATED, use GSM48_IE_GPRS_RESUMPT_HO instead */
+#define GSM48_IE_SYNC_IND 0xd0 /* DEPRECATED, use GSM48_IE_SYNC_IND_HO instead */
/* 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_PRIORITY_LEV_HO 0x08 /* HO = Half Octet Tag */
#define GSM48_IE_LOCATION_AREA 0x13
#define GSM48_IE_AUTN 0x20
#define GSM48_IE_AUTH_RES_EXT 0x21
#define GSM48_IE_AUTS 0x22
-#define GSM48_IE_PRIORITY_LEV 0x80
+#define GSM48_IE_PRIORITY_LEV 0x80 /* DEPRECATED, use GSM48_IE_PRIORITY_LEV_HO instead */
#define GSM48_IE_FOLLOW_ON_PROC 0xa1
#define GSM48_IE_CTS_PERMISSION 0xa2
@@ -1876,9 +1936,12 @@ enum gsm48_rr_cause {
GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03,
GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04,
GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05,
+ GSM48_RR_CAUSE_UTRAN_CFG_UNK = 0x06,
GSM48_RR_CAUSE_HNDOVER_IMP = 0x08,
GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x09,
GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x0a,
+ GSM48_RR_CAUSE_LEAVE_GROUP_CA = 0x0b,
+ GSM48_RR_CAUSE_LOW_LEVEL_FAIL = 0x0c,
GSM48_RR_CAUSE_CALL_CLEARED = 0x41,
GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f,
GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60,
@@ -2147,8 +2210,31 @@ enum gsm48_bcap_modem_type {
GSM48_BCAP_MT_AUTO_1 = 8,
};
+/*! Table 10.5.112/3GPP TS 24.008: Other modem type (octet 6d) */
+enum gsm48_bcap_other_modem_type {
+ GSM48_BCAP_OTHER_MT_NONE = 0, /*!< No other modem type specified */
+ GSM48_BCAP_OTHER_MT_V34 = 2, /*!< According to ITU-T Rec. V.34 */
+};
+
+/*! Table 10.5.112/3GPP TS 24.008: Fixed network user rate (octet 6d) */
+enum gsm48_bcap_fixed_net_user_rate {
+ GSM48_BCAP_FNUR_NONE = 0, /*!< FNUR not applicable / No meaning associated */
+ GSM48_BCAP_FNUR_X1_V110_9600 = 1, /*!< 9.6 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_14400 = 2, /*!< 14.4 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_19200 = 3, /*!< 19.2 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_28800 = 4, /*!< 28.8 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_38400 = 5, /*!< 38.4 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_48000 = 6, /*!< 48.0 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_X1_V110_56000 = 7, /*!< 56.0 kbit/s (according to ITU-T Rec. X.1 and V.110) */
+ GSM48_BCAP_FNUR_BT_64000 = 8, /*!< 64.0 kbit/s bit transparent */
+ GSM48_BCAP_FNUR_BT_33600 = 9, /*!< 33.6 kbit/s bit transparent */
+ GSM48_BCAP_FNUR_I460_32000 = 10, /*!< 32.0 kbit/s (according to ITU-T Rec. I.460) */
+ GSM48_BCAP_FNUR_V34_31200 = 11, /*!< 31.2 kbit/s (according to ITU-T Rec. V.34) */
+};
+
/*! GSM 04.08 Bearer Capability: Speech Version Indication
- * (See also 3GPP TS 24.008, Table 10.5.103) */
+ * (See also 3GPP TS 24.008, Table 10.5.103
+ * and 3GPP TS 26.103, Table 4.1 "Support of Codec Types in Radio Access Technologies") */
enum gsm48_bcap_speech_ver {
GSM48_BCAP_SV_FR = 0, /*!< GSM FR V1 (GSM FR) */
GSM48_BCAP_SV_HR = 1, /*!< GSM HR V1 (GSM HR) */
diff --git a/include/osmocom/gsm/protocol/gsm_04_08_gprs.h b/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
index 86c5b016..2671aafb 100644
--- a/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
+++ b/include/osmocom/gsm/protocol/gsm_04_08_gprs.h
@@ -1,7 +1,6 @@
/*! \file gsm_04_08_gprs.h */
-#ifndef _GSM48_GPRS_H
-#define _GSM48_GPRS_H
+#pragma once
#include <stdint.h>
#include <stdbool.h>
@@ -83,6 +82,8 @@ extern const struct value_string *gprs_upd_t_strs;
enum gsm48_gprs_ie_mm {
GSM48_IE_GMM_CIPH_CKSN = 0x08, /* 10.5.1.2 */
+ GSM48_IE_GMM_PTMSI_TYPE = 0x0e, /* 10.5.5.29 */
+ GSM48_IE_GMM_TMSI_BASED_NRI_C = 0x10, /* 10.5.5.31 */
GSM48_IE_GMM_TIMER_READY = 0x17, /* 10.5.7.3 */
GSM48_IE_GMM_ALLOC_PTMSI = 0x18, /* 10.5.1.4 */
GSM48_IE_GMM_PTMSI_SIG = 0x19, /* 10.5.5.8 */
@@ -90,26 +91,39 @@ enum gsm48_gprs_ie_mm {
GSM48_IE_GMM_AUTH_SRES = 0x22, /* 10.5.3.2 */
GSM48_IE_GMM_IMEISV = 0x23, /* 10.5.1.4 */
GSM48_IE_GMM_CAUSE = 0x25, /* 10.5.5.14 */
+ GSM48_IE_GMM_RX_NPDU_NUM_LIST = 0x26, /* 10.5.5.11 */
GSM48_IE_GMM_DRX_PARAM = 0x27, /* 10.5.5.6 */
GSM48_IE_GMM_AUTN = 0x28, /* 10.5.3.1.1 */
GSM48_IE_GMM_AUTH_RES_EXT = 0x29, /* 10.5.3.2.1 */
+ GSM48_IE_GMM_TIMER_T3302 = 0x2A, /* 10.5.7.4 */
GSM48_IE_GMM_AUTH_FAIL_PAR = 0x30, /* 10.5.3.2.2 */
GSM48_IE_GMM_MS_NET_CAPA = 0x31, /* 10.5.5.12 */
GSM48_IE_GMM_PDP_CTX_STATUS = 0x32, /* 10.5.7.1 */
GSM48_IE_GMM_PS_LCS_CAPA = 0x33, /* 10.5.5.22 */
GSM48_IE_GMM_GMM_MBMS_CTX_ST = 0x35, /* 10.5.7.6 */
+ GSM48_IE_GMM_TIMER_T3346 = 0x3A, /* 10.5.7.4 */
+ GSM48_IE_GMM_NET_FEAT_SUPPORT = 0xB0, /* 10.5.5.23 */
};
enum gsm48_gprs_ie_sm {
+ GSM48_IE_GSM_RADIO_PRIO = 0x08, /* 10.5.7.2 */
+ GSM48_IE_GSM_DEV_PROP = 0x0C, /* 10.5.7.8 */
GSM48_IE_GSM_APN = 0x28, /* 10.5.6.1 */
GSM48_IE_GSM_PROTO_CONF_OPT = 0x27, /* 10.5.6.3 */
GSM48_IE_GSM_PDP_ADDR = 0x2b, /* 10.5.6.4 */
GSM48_IE_GSM_AA_TMR = 0x29, /* 10.5.7.3 */
+ GSM48_IE_GSM_QOS = 0x30, /* 10.5.6.5 */
+ GSM48_IE_GSM_TFT = 0x31, /* 10.5.6.12 */
+ GSM48_IE_GSM_LLC_SAPI = 0x32, /* 10.5.6.9 */
+ GSM48_IE_GSM_MBIFOM_CONT = 0x33, /* 10.5.6.21 */
+ GSM48_IE_GSM_PFI = 0x34, /* 10.5.6.11 */
GSM48_IE_GSM_NAME_FULL = 0x43, /* 10.5.3.5a */
GSM48_IE_GSM_NAME_SHORT = 0x45, /* 10.5.3.5a */
GSM48_IE_GSM_TIMEZONE = 0x46, /* 10.5.3.8 */
GSM48_IE_GSM_UTC_AND_TZ = 0x47, /* 10.5.3.9 */
GSM48_IE_GSM_LSA_ID = 0x48, /* 10.5.3.11 */
+ GSM48_IE_GSM_EXT_QOS = 0x5C, /* 10.5.6.5B */
+ GSM48_IE_GSM_EXT_PROTO_CONF_OPT = 0x7B, /* 10.5.6.3a */
/* Fake IEs that are not present on the Layer3 air interface,
* but which we use to simplify internal APIs */
@@ -128,7 +142,7 @@ struct gsm48_ra_upd_ack {
struct gsm48_ra_id ra_id; /* 10.5.5.15 */
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t upd_result:4, force_stby:4;
uint8_t ra_upd_timer;
struct gsm48_ra_id ra_id;
@@ -157,7 +171,7 @@ struct gsm48_attach_ack {
struct gsm48_ra_id ra_id; /* 10.5.5.15 */
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t force_stby:4, att_result:4;
uint8_t ra_upd_timer;
uint8_t radio_prio;
@@ -175,7 +189,7 @@ struct gsm48_auth_ciph_req {
ac_ref_nr:4; /* 10.5.5.19 */
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t imeisv_req:4, ciph_alg:4;
uint8_t ac_ref_nr:4, force_stby:4;
uint8_t data[0];
@@ -189,7 +203,7 @@ struct gsm48_auth_ciph_resp {
spare:4;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:4, ac_ref_nr:4;
uint8_t data[0];
#endif
@@ -280,17 +294,52 @@ enum gsm48_pdp_state {
PDP_S_MODIFY_PENDING,
};
-/* Table 10.5.155/3GPP TS 24.008 */
+/* TS 24.008 Table 10.5.155/3GPP */
enum gsm48_pdp_type_org {
PDP_TYPE_ORG_ETSI = 0x00,
PDP_TYPE_ORG_IETF = 0x01,
+ PDP_TYPE_ORG_EMPTY = 0x0f,
};
enum gsm48_pdp_type_nr {
PDP_TYPE_N_ETSI_RESERVED = 0x00,
PDP_TYPE_N_ETSI_PPP = 0x01,
PDP_TYPE_N_IETF_IPv4 = 0x21,
PDP_TYPE_N_IETF_IPv6 = 0x57,
+ PDP_TYPE_N_IETF_IPv4v6 = 0x8D,
};
+/* TS 24.008 10.5.6.4 "Packet data protocol address" value
+ * Note: This can be reused for 3GPP TS 29.060 7.7.27 "End User Address"
+ * with minor changes in the values, like spare being 1111 instead.
+*/
+struct gsm48_pdp_address {
+#if OSMO_IS_LITTLE_ENDIAN
+uint8_t organization:4, /* enum gsm48_pdp_type_org */
+ spare:4; /* 0000 */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+uint8_t spare:4, organization:4;
+#endif
+uint8_t type; /* enum gsm48_pdp_type_nr */
+ union {
+ /* PDP_TYPE_ORG_ETSI */
+ union {
+ } etsi;
+ /* PDP_TYPE_ORG_IETF */
+ union {
+ /* PDP_TYPE_N_IETF_IPv4 */
+ uint32_t v4;
+
+ /* PDP_TYPE_N_IETF_IPv6 */
+ uint8_t v6[16];
+
+ /* PDP_TYPE_N_IETF_IPv4v6 */
+ struct {
+ uint32_t v4;
+ uint8_t v6[16];
+ } __attribute__ ((packed)) v4v6;
+ } ietf;
+ };
+} __attribute__ ((packed));
/* Figure 10.5.138/24.008 / Chapter 10.5.6.5 */
enum gsm48_qos_reliab_class {
@@ -463,6 +512,3 @@ struct gsm48_qos {
/* octet 16 */
uint8_t guar_bitrate_down_ext;
};
-
-
-#endif /* _GSM48_GPRS_H */
diff --git a/include/osmocom/gsm/protocol/gsm_04_11.h b/include/osmocom/gsm/protocol/gsm_04_11.h
index 31f25acb..90543020 100644
--- a/include/osmocom/gsm/protocol/gsm_04_11.h
+++ b/include/osmocom/gsm/protocol/gsm_04_11.h
@@ -62,6 +62,16 @@ enum gsm411_rp_ie {
GSM411_IE_RP_CAUSE = 0x42, /* 8.2.5.4 */
};
+/* Sections 8.2.5.1 and 8.2.5.2 set limits on the length of an SMSC-address.
+ * The spec states these limits in terms of min and max values of the length
+ * octet in type 4 IEs SM-RP-OA and SM-RP-DA; these IE length limits translate
+ * into a minimum of 1 digit and a maximum of 20 digits.
+ */
+#define GSM411_SMSC_ADDR_MIN_OCTETS 2
+#define GSM411_SMSC_ADDR_MAX_OCTETS 11
+#define GSM411_SMSC_ADDR_MIN_DIGITS 1
+#define GSM411_SMSC_ADDR_MAX_DIGITS 20
+
/* Chapter 8.2.5.4 Table 8.4 */
enum gsm411_rp_cause {
/* valid only for MO */
diff --git a/include/osmocom/gsm/protocol/gsm_04_12.h b/include/osmocom/gsm/protocol/gsm_04_12.h
index 3f34ee7f..17ac6454 100644
--- a/include/osmocom/gsm/protocol/gsm_04_12.h
+++ b/include/osmocom/gsm/protocol/gsm_04_12.h
@@ -23,7 +23,7 @@ struct gsm412_block_type {
lpd : 2,
spare : 1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:1, lpd:2, lb:1, seq_nr:4;
#endif
} __attribute__((packed));
@@ -37,7 +37,7 @@ struct gsm412_sched_msg {
uint8_t cbsms_msg_map[6];
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t type:2, beg_slot_nr:6;
uint8_t spare2:1, spare1:1, end_slot_nr:6;
uint8_t cbsms_msg_map[6];
diff --git a/include/osmocom/gsm/protocol/gsm_04_14.h b/include/osmocom/gsm/protocol/gsm_04_14.h
index deb474ec..dddec519 100644
--- a/include/osmocom/gsm/protocol/gsm_04_14.h
+++ b/include/osmocom/gsm/protocol/gsm_04_14.h
@@ -30,7 +30,7 @@ struct gsm414_close_mslot_loop_cmd {
loop_mech:3,
tn:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t tn:3, loop_mech:3, chc:2;
#endif
} __attribute__((packed));
@@ -43,7 +43,7 @@ struct gsm414_close_mslot_loop_ack {
chc:2,
spare:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t spare:2, chc:2, loop_mech:3, err_ind:1;
#endif
} __attribute__((packed));
@@ -70,7 +70,7 @@ struct gsm414_gprs_test_mode_cmd {
dl_tx_offset:3,
_spare:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint16_t d:12,
spare:3,
l:1;
@@ -86,7 +86,7 @@ struct gsm414_egprs_st_sb_loop_cmd {
dl_tx_offset:3,
m:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t m:1, dl_tx_offset:3, _spare:4;
#endif
} __attribute__((packed));
diff --git a/include/osmocom/gsm/protocol/gsm_08_08.h b/include/osmocom/gsm/protocol/gsm_08_08.h
index a5406095..1e211dc9 100644
--- a/include/osmocom/gsm/protocol/gsm_08_08.h
+++ b/include/osmocom/gsm/protocol/gsm_08_08.h
@@ -7,6 +7,7 @@
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/endian.h>
/*
* this is from GSM 03.03 CGI but is copied in GSM 08.08
@@ -24,6 +25,10 @@ enum CELL_IDENT {
CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8,
CELL_IDENT_UTRAN_RNC = 9,
CELL_IDENT_UTRAN_LAC_RNC = 10,
+ CELL_IDENT_SAI = 11,
+
+ /* Not in 03.03 nor 08.08. Place them > 0x0f (discr_id is 4 bits) */
+ CELL_IDENT_WHOLE_GLOBAL_PS = 128, /* CGI + RAC, TS 48.018 8c.1.4.1.1 */
};
/* Keep this misnamed CELL_IDENT for API backwards compatibility (see OS#3124). */
#define CELL_IDENT_LAI_AND_LAC CELL_IDENT_LAI
@@ -41,10 +46,30 @@ struct bssmap_header {
struct dtap_header {
uint8_t type;
- uint8_t link_id;
+ union {
+ uint8_t link_id; /* Backward compatibility */
+ struct {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t dlci_sapi:3, /* enum gsm0406_dlci_sapi */
+ dlci_spare:3,
+ dlci_cc:2;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t dlci_cc:2, dlci_spare:3, dlci_sapi:3;
+#endif
+ };
+ };
uint8_t length;
} __attribute__((packed));
+/* Data Link Control SAPI, GSM 08.06 § 6.3.2, GSM 04.06 § 3.3.3 */
+enum gsm0406_dlci_sapi {
+ DLCI_SAPI_RR_MM_CC = 0x0,
+ DLCI_SAPI_SMS = 0x3,
+};
+extern const struct value_string gsm0406_dlci_sapi_names[];
+static inline const char *gsm0406_dlci_sapi_name(enum gsm0406_dlci_sapi val)
+{ return get_value_string(gsm0406_dlci_sapi_names, val); }
enum BSS_MAP_MSG_TYPE {
BSS_MAP_MSG_RESERVED_0 = 0,
@@ -143,12 +168,15 @@ enum BSS_MAP_MSG_TYPE {
BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30,
BSS_MAP_MSG_UPLINK_RQST = 31,
BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39,
+ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_STATUS = 59,
+ BSS_MAP_MSG_VGCS_VBS_AREA_CELL_INFO = 60,
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,
BSS_MAP_MSG_VGCS_ADDL_INFO = 0x60,
+ BSS_MAP_MSG_VGCS_SMS = 0x61,
BSS_MAP_MSG_NOTIFICATION_DATA = 0x62,
BSS_MAP_MSG_UPLINK_APP_DATA = 0x63,
@@ -241,7 +269,7 @@ enum GSM0808_IE_CODING {
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_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000 = 82,
GSM0808_IE_RESERVED_5 = 65,
GSM0808_IE_RESERVED_6 = 66,
GSM0808_IE_GERAN_CLASSMARK = 0x53,
@@ -309,7 +337,8 @@ enum GSM0808_IE_CODING {
GSM0808_IE_OSMO_OSMUX_CID = 0xf1,
};
-/* 3GPP TS 48.008 3.2.3 Signalling Field Element Coding */
+/* 3GPP TS 48.008 3.2.3 Signalling Field Element Coding.
+ See also extra fields in 3.2.2.58 and 3.2.2.80 */
enum GSM0808_SIGNALLING_FIELD_ELEMENT_CODING {
GSM0808_FE_IE_EXTRA_INFORMATION = 0x01, /*< 3.2.3.1 */
GSM0808_FE_IE_CURRENT_CHANNEL_TYPE_2 = 0x02, /*< 3.2.3.2 */
@@ -329,6 +358,8 @@ enum GSM0808_SIGNALLING_FIELD_ELEMENT_CODING {
GSM0808_FE_IE_IRAT_MEASUREMENT_CONFIGURATION = 0x0f, /*< 3.2.3.16 */
GSM0808_FE_IE_SOURCE_CELL_ID = 0x10, /*< 3.2.3.17 */
GSM0808_FE_IE_IRAT_MEASUREMENT_CONFIGURATION_EXTENDED_E_ARFCNS = 0x11, /*< 3.2.3.18 */
+ GSM0808_FE_IE_VGCS_TALKER_MODE = 0x6f, /*< 3.2.2.93 */
+ GSM0808_FE_IE_LAST_USED_EUTRAN_PLMN_ID = 0x95, /*< 3.2.2.127 */
};
/* 3GPP TS 48.008 3.2.2.5 Cause */
@@ -415,13 +446,15 @@ enum gsm0808_chan_indicator {
GSM0808_CHAN_SPEECH = 1,
GSM0808_CHAN_DATA = 2,
GSM0808_CHAN_SIGN = 3,
+ GSM0808_CHAN_SPEECH_CTM_TEXT_TELEPHONY = 4,
};
/* GSM 08.08 3.2.2.11 Channel Type */
+#define GSM0808_DATA_FULL_RPREF GSM0808_DATA_FULL_PREF
enum gsm0808_chan_rate_type_data {
GSM0808_DATA_FULL_BM = 0x8,
GSM0808_DATA_HALF_LM = 0x9,
- GSM0808_DATA_FULL_RPREF = 0xa,
+ GSM0808_DATA_FULL_PREF = 0xa,
GSM0808_DATA_HALF_PREF = 0xb,
GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a,
GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b,
@@ -470,6 +503,42 @@ enum gsm0808_permitted_speech {
GSM0808_PERM_HR6 = 0x45, /*!< OHR AMR */
};
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Transparent: Data Rate */
+enum gsm0808_data_rate_transp {
+ GSM0808_DATA_RATE_TRANSP_32k0 = 0x3a,
+ GSM0808_DATA_RATE_TRANSP_28k8 = 0x39,
+ GSM0808_DATA_RATE_TRANSP_14k4 = 0x18,
+ GSM0808_DATA_RATE_TRANSP_9k6 = 0x10,
+ GSM0808_DATA_RATE_TRANSP_4k8 = 0x11,
+ GSM0808_DATA_RATE_TRANSP_2k4 = 0x12,
+ GSM0808_DATA_RATE_TRANSP_1k2 = 0x13,
+ GSM0808_DATA_RATE_TRANSP_600 = 0x14,
+ GSM0808_DATA_RATE_TRANSP_1200_75 = 0x15,
+};
+
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Non-Transparent: Radio Interface Data Rate (preferred) */
+enum gsm0808_data_rate_non_transp {
+ GSM0808_DATA_RATE_NON_TRANSP_12000_6000 = 0x00,
+ GSM0808_DATA_RATE_NON_TRANSP_43k5 = 0x34,
+ GSM0808_DATA_RATE_NON_TRANSP_29k0 = 0x31,
+ GSM0808_DATA_RATE_NON_TRANSP_14k5 = 0x14,
+ GSM0808_DATA_RATE_NON_TRANSP_12k0 = 0x10,
+ GSM0808_DATA_RATE_NON_TRANSP_6k0 = 0x11,
+};
+
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Non-Transparent: Allowed Radio Interface Data Rate (all possible allowed) */
+enum gsm0808_data_rate_allowed_r_if {
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_43k5 = 0x40,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_32k0 = 0x20,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_29k0 = 0x10,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_14k5 = 0x08,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_12k0 = 0x02,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_6k0 = 0x01,
+};
+
extern const struct value_string gsm0808_permitted_speech_names[];
static inline const char *gsm0808_permitted_speech_name(enum gsm0808_permitted_speech val)
{ return get_value_string(gsm0808_permitted_speech_names, val); }
@@ -488,6 +557,12 @@ enum gsm0808_speech_codec_type {
GSM0808_SCT_CSD = 0xfd, /*!< CSData (see also TS 26.103) */
};
+/* Codec Extension (the real Codec Type follows in the next octet).
+ * This value is intentionally not included in gsm0808_speech_codec_type,
+ * because {enc,dec}_speech_codec() functions take care of the extended
+ * encoding internally. It shall not be used in struct gsm0808_speech_codec. */
+#define GSM0808_SCT_EXT 0x0f
+
extern const struct value_string gsm0808_speech_codec_type_names[];
static inline const char *gsm0808_speech_codec_type_name(enum gsm0808_speech_codec_type val)
{ return get_value_string(gsm0808_speech_codec_type_names, val); }
@@ -512,11 +587,16 @@ enum gsm0808_paging_info {
GSM0808_PAGINF_FOR_USSD = 0x02,
};
-/*! 3GPP TS 48.008 3.2.2.104 Speech Codec */
+/*! 3GPP TS 48.008 3.2.2.104 Speech Codec.
+ * Valid if (fi || pi || pt) == true, otherwise ignore. */
struct gsm0808_speech_codec {
+ /*! Full IP: AoIP with compressed speech via RTP/UDP/IP. */
bool fi;
+ /*! PCMoIP: PCM over A-Interface via RTP/UPD/IP. */
bool pi;
+ /*! PCMoTDM: PCM over A-Interface with TDM as transport. */
bool pt;
+ /*! TFO (Inband Tandem Free Operation). Only valid if (pi || pt) == true. */
bool tf;
/*! See enum gsm0808_speech_codec_type. */
uint8_t type;
@@ -535,7 +615,44 @@ struct gsm0808_speech_codec {
*
* Default values for FR_AMR_WB, OFR_AMR_WB and OHR_AMR_WB:
* See also: 3GPP TS 26.103, Table 5.7-1: Allowed Configurations
- * for the Adaptive Multi-Rate - Wideband Codec Types */
+ * for the Adaptive Multi-Rate - Wideband Codec Types
+ *
+ * This is a copy of 3GPP TS 28.062, Table 7.11.3.1.3-2:
+ *
+ * S0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * 12,20 (x) x x x
+ * 10,20 x x x
+ * 7,95 x x x
+ * 7,40 x x x x
+ * 6,70 x x x x x x
+ * 5,90 x x x x x x x x x x
+ * 5,15
+ * 4,75 x x x x x x x x x x
+ *
+ * OM F F F F F F F F F F F A F A F A
+ *
+ * HR Y Y Y Y Y Y Y Y Y
+ * FR Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y
+ *
+ * Each bit allows one Codec Configuration.
+ * E.g. when bit S3 is set, look at column labeled "3", and see that only 6,7k is active in this configuration; it is
+ * "F" forbidden to change in Optimisation Mode, "Y" HR AMR supports this mode, and "Y" FR AMR can also do it.
+ *
+ * This means that whichever configuration is chosen from S0 thru S15, there are never more than four rates active.
+ *
+ * The spec praises S1 as the most desired configuration: "because it leads in all call cases to TFO/TrFO compatible
+ * connections with optimal voice quality." (Since HR AMR supports up to 7.95k, it seems that S14 would be more optimal
+ * voice quality, but it is not marked as supported by HR AMR.)
+ *
+ * For FR_AMR below, the default of 0x57ff means:
+ * 0x57ff = 0101 0111 1111 1111
+ * ^14 ^10 ^0
+ * allow config 0 thru 10, and configs 12 and 14.
+ *
+ * For HR_AMR, drop all those where there is no "Y" in the HR row:
+ * 0x073f = 0000 0111 0011 1111
+ * ^15 ^11 ^6 ^0
+ */
enum gsm0808_speech_codec_defaults {
GSM0808_SC_CFG_DEFAULT_FR_AMR = 0x57ff,
GSM0808_SC_CFG_DEFAULT_HR_AMR = 0x073f,
@@ -545,9 +662,12 @@ enum gsm0808_speech_codec_defaults {
GSM0808_SC_CFG_DEFAULT_OHR_AMR_WB = 0x01,
};
-/*! Default speech codec configurations broken down by reate.
+/*! Default speech codec configurations broken down by rate.
* See also: 3GPP TS 28.062, Table 7.11.3.1.3-2: Preferred Configurations for
- * the Adaptive Multi-Rate Codec Types. */
+ * the Adaptive Multi-Rate Codec Types.
+ *
+ * Set all Sn bits that have this rate listed as active.
+ */
enum gsm0808_speech_codec_rate_defaults {
GSM0808_SC_CFG_DEFAULT_AMR_4_75 = 0xff03,
GSM0808_SC_CFG_DEFAULT_AMR_5_15 = 0x0000,
@@ -559,9 +679,12 @@ enum gsm0808_speech_codec_rate_defaults {
GSM0808_SC_CFG_DEFAULT_AMR_12_2 = 0xc082
};
-/*! Single speech codec configurations broken down by reate.
+/*! Single speech codec configurations broken down by rate.
* See also: 3GPP TS 28.062, Table 7.11.3.1.3-2: Preferred Configurations for
- * the Adaptive Multi-Rate Codec Types. */
+ * the Adaptive Multi-Rate Codec Types.
+ *
+ * Set bit Sn (S0 = 0x01), where Sn is identified by a descriptive name.
+ */
enum gsm0808_speech_codec_rate {
GSM0808_SC_CFG_AMR_4_75 = 0x0001,
GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20 = 0x0002,
@@ -573,6 +696,29 @@ enum gsm0808_speech_codec_rate {
GSM0808_SC_CFG_AMR_12_2 = 0x0080,
};
+/* Bit index of a mode as returned by gsm0808_amr_modes_from_cfg[].
+ * Example:
+ * if (gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][9] & GSM0808_AMR_MODE_4_75)
+ * printf("S9 supports 4k75");
+ */
+enum gsm0808_amr_mode {
+ GSM0808_AMR_MODE_4_75 = 0,
+ GSM0808_AMR_MODE_5_15,
+ GSM0808_AMR_MODE_5_90,
+ GSM0808_AMR_MODE_6_70,
+ GSM0808_AMR_MODE_7_40,
+ GSM0808_AMR_MODE_7_95,
+ GSM0808_AMR_MODE_10_2,
+ GSM0808_AMR_MODE_12_2,
+};
+extern const struct value_string gsm0808_amr_mode_names[];
+static inline const char *gsm0808_amr_mode_name(enum gsm0808_amr_mode val)
+{
+ return get_value_string(gsm0808_amr_mode_names, val);
+}
+
+extern const uint8_t gsm0808_amr_modes_from_cfg[2][16];
+
/* 3GPP TS 48.008 3.2.2.103 Speech Codec List */
#define SPEECH_CODEC_MAXLEN 255
struct gsm0808_speech_codec_list {
@@ -580,13 +726,32 @@ struct gsm0808_speech_codec_list {
uint8_t len;
};
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Asymmetry Preference (used for data, non-transparent service) */
+enum gsm0808_channel_type_asym_pref {
+ GSM0808_CT_ASYM_PREF_NOT_APPLICABLE = 0,
+ GSM0808_CT_ASYM_PREF_UL = 1,
+ GSM0808_CT_ASYM_PREF_DL = 2,
+ GSM0808_CT_ASYM_PREF_SPARE = 3,
+};
+
/* 3GPP TS 48.008 3.2.2.11 Channel Type */
#define CH_TYPE_PERM_SPCH_MAXLEN 9
struct gsm0808_channel_type {
uint8_t ch_indctr;
uint8_t ch_rate_type;
+
+ /* Speech only */
uint8_t perm_spch[CH_TYPE_PERM_SPCH_MAXLEN];
unsigned int perm_spch_len;
+
+ /* Data only */
+ bool data_transparent;
+ uint8_t data_rate;
+ bool data_rate_allowed_is_set;
+ uint8_t data_rate_allowed;
+ bool data_asym_pref_is_set;
+ enum gsm0808_channel_type_asym_pref data_asym_pref;
};
/* 3GPP TS 48.008 3.2.2.10 Encryption Information */
@@ -642,3 +807,16 @@ enum gsm0808_lcls_status {
GSM0808_LCLS_STS_LOCALLY_SWITCHED = 0x04,
GSM0808_LCLS_STS_NA = 0xFF
};
+
+/* 3GPP TS 48.008 3.2.2.32 Diagnostics */
+struct gsm0808_diagnostics {
+ uint8_t error_pointer_octet;
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t error_pointer_bit_spare:4,
+ error_pointer_bit:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t error_pointer_bit:4, error_pointer_bit_spare:4;
+#endif
+ uint8_t msg[0]; /*! received message which provoked the error */
+} __attribute__((packed));
diff --git a/include/osmocom/gsm/protocol/gsm_08_58.h b/include/osmocom/gsm/protocol/gsm_08_58.h
index da55a8d9..e8758df0 100644
--- a/include/osmocom/gsm/protocol/gsm_08_58.h
+++ b/include/osmocom/gsm/protocol/gsm_08_58.h
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -33,6 +29,7 @@
/* Channel Number 9.3.1 */
union abis_rsl_chan_nr {
+ struct {
#if OSMO_IS_BIG_ENDIAN
uint8_t cbits:5,
tn:3;
@@ -40,7 +37,8 @@ union abis_rsl_chan_nr {
uint8_t tn:3,
cbits:5;
#endif
- uint8_t chan_nr;
+ } __attribute__ ((packed));
+ uint8_t chan_nr;
} __attribute__ ((packed));
#define ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs 0x01
#define ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(ss) (0x02 + (ss))
@@ -53,8 +51,13 @@ union abis_rsl_chan_nr {
#define ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH4 0x19 /*< non-standard, for CBCH/SDCCH4 */
#define ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8 0x1a /*< non-standard, for CBCH/SDCCH8 */
+/* non-standard, Osmocom specific Bm/Lm equivalents for VAMOS */
+#define ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Bm_ACCHs 0x1d /*< VAMOS TCH/F */
+#define ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Lm_ACCHs(ss) (0x1e + (ss)) /*< VAMOS TCH/H */
+
/* Link Identifier 9.3.2 */
union abis_rsl_link_id {
+ struct {
#if OSMO_IS_BIG_ENDIAN
uint8_t cbits:2,
na:1,
@@ -66,7 +69,8 @@ union abis_rsl_link_id {
na:1,
cbits:2;
#endif
- uint8_t link_id;
+ } __attribute__ ((packed));
+ uint8_t link_id;
} __attribute__ ((packed));
#define ABIS_RSL_LINK_ID_CBITS_FACCH_SDCCH 0x00
#define ABIS_RSL_LINK_ID_CBITS_SACCH 0x01
@@ -116,6 +120,33 @@ struct abis_rsl_cchan_hdr {
uint8_t data[0]; /*!< message payload data */
} __attribute__ ((packed));
+/* Osmocom specific IE to negotiate repeated ACCH capabilities */
+struct abis_rsl_osmo_rep_acch_cap {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t dl_facch_cmd:1,
+ dl_facch_all:1,
+ dl_sacch:1,
+ ul_sacch:1,
+ rxqual:3,
+ reserved:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved:1, rxqual:3, ul_sacch:1, dl_sacch:1, dl_facch_all:1, dl_facch_cmd:1;
+#endif
+} __attribute__ ((packed));
+
+/* Osmocom specific IE to negotiate temporary overpower of ACCH channels */
+struct abis_rsl_osmo_temp_ovp_acch_cap {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t overpower_db:3,
+ rxqual:3,
+ facch_enable:1,
+ sacch_enable:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t sacch_enable:1, facch_enable:1, rxqual:3, overpower_db:3;
+#endif
+} __attribute__ ((packed));
/* Chapter 9.1 */
/* RSL Message Discriminator: RLL */
@@ -337,6 +368,12 @@ enum abis_rsl_ie {
RSL_IE_SIEMENS_HIGHEST_RATE = 0x4e,
RSL_IE_SIEMENS_SUGGESTED_RATE = 0x4f,
+ /* Osmocom specific */
+ RSL_IE_OSMO_REP_ACCH_CAP = 0x60,
+ RSL_IE_OSMO_TRAINING_SEQUENCE = 0x61,
+ RSL_IE_OSMO_TEMP_OVP_ACCH_CAP = 0x62,
+ RSL_IE_OSMO_OSMUX_CID = 0x63,
+
/* ip.access */
RSL_IE_IPAC_SRTP_CONFIG = 0xe0,
RSL_IE_IPAC_PROXY_UDP = 0xe1,
@@ -353,9 +390,9 @@ enum abis_rsl_ie {
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_PAYLOAD2 = 0xfc,
RSL_IE_IPAC_RTP_MPLEX = 0xfd,
- RSL_IE_IPAC_RTP_MPLEX_ID= 0xfe,
+ RSL_IE_IPAC_RTP_MPLEX_ID = 0xfe,
};
/* Ericsson specific IEs, clash with above partially, so they're not
@@ -385,10 +422,12 @@ enum abis_rsl_ie {
enum {
IPAC_UNWEIGHTED_AVE = 0,
IPAC_WEIGHTED_AVE,
- IPAC_MEDIAN_AVE
+ IPAC_MEDIAN_AVE,
+ /* EWMA is an Osmocom specific extension */
+ IPAC_OSMO_EWMA_AVE,
};
-/* IPAC MEAS_PREPROC AVERAGING PARAMID */
+/* IPAC MEAS_PREPROC AVERAGING PARAM ID */
enum {
IPAC_RXLEV_AVE = 0,
IPAC_RXQUAL_AVE,
@@ -431,6 +470,11 @@ enum {
#define RSL_CHAN_OSMO_CBCH4 0xc8 /*< non-standard, for CBCH/SDCCH4 */
#define RSL_CHAN_OSMO_CBCH8 0xd0 /*< non-standard, for CBCH/SDCCH8 */
+/* non-standard, Osmocom specific Bm/Lm equivalents for VAMOS */
+#define RSL_CHAN_OSMO_VAMOS_Bm_ACCHs 0xe8 /* VAMOS TCH/F */
+#define RSL_CHAN_OSMO_VAMOS_Lm_ACCHs 0xf0 /* VAMOS TCH/H */
+#define RSL_CHAN_OSMO_VAMOS_MASK 0xe0 /* VAMOS TCH/{F,H} */
+
/* Chapter 9.3.3 */
#define RSL_ACT_TYPE_INITIAL 0x00
#define RSL_ACT_TYPE_REACT 0x80
@@ -456,34 +500,66 @@ enum rsl_cmod_spd {
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
-/* non-transparent data */
-#define RSL_CMOD_CSD_NT_43k5 0x74
-#define RSL_CMOD_CSD_NT_28k8 0x71
-#define RSL_CMOD_CSD_NT_14k5 0x58
-#define RSL_CMOD_CSD_NT_12k0 0x50
-#define RSL_CMOD_CSD_NT_6k0 0x51
+/*! Channel rate and type */
+enum rsl_cmod_crt {
+ RSL_CMOD_CRT_SDCCH = 0x01,
+ RSL_CMOD_CRT_TCH_Bm = 0x08, /* full-rate */
+ RSL_CMOD_CRT_TCH_Lm = 0x09, /* half-rate */
+ RSL_CMOD_CRT_TCH_BI_Bm = 0x0a, /* full-rate: bi-directional (multislot) */
+ RSL_CMOD_CRT_TCH_UNI_Bm = 0x1a, /* full-rate: uni-directional (multislot) */
+ RSL_CMOD_CRT_TCH_GROUP_Bm = 0x18, /* full-rate: group call channel */
+ RSL_CMOD_CRT_TCH_GROUP_Lm = 0x19, /* half-rate: group call channel */
+ RSL_CMOD_CRT_TCH_BCAST_Bm = 0x28, /* full-rate: broadcast call channel */
+ RSL_CMOD_CRT_TCH_BCAST_Lm = 0x29, /* half-rate: broadcast call channel */
+ RSL_CMOD_CRT_OSMO_TCH_VAMOS_Bm = 0x88, /* full-rate in VAMOS mode */
+ RSL_CMOD_CRT_OSMO_TCH_VAMOS_Lm = 0x89, /* half-rate in VAMOS mode */
+};
+/*! Speech */
+enum rsl_cmod_sp {
+ RSL_CMOD_SP_GSM1 = 0x01,
+ RSL_CMOD_SP_GSM2 = 0x11,
+ RSL_CMOD_SP_GSM3 = 0x21,
+ RSL_CMOD_SP_GSM4 = 0x31,
+ RSL_CMOD_SP_GSM5 = 0x09,
+ RSL_CMOD_SP_GSM6 = 0x0d,
+};
+/*! Non-transparent data */
+enum rsl_cmod_csd_nt {
+ RSL_CMOD_CSD_NTA_43k5_14k5 = 0x61, /* asymmetric 43.5 kbit/s (DL) + 14.5 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_29k0_14k5 = 0x62, /* asymmetric 29.0 kbit/s (DL) + 14.5 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_43k5_29k0 = 0x63, /* asymmetric 43.5 kbit/s (DL) + 29.0 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_14k5_43k5 = 0x69, /* asymmetric 14.5 kbit/s (DL) + 43.5 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_14k5_29k0 = 0x6a, /* asymmetric 14.5 kbit/s (DL) + 29.0 kbit/s (UL) */
+ RSL_CMOD_CSD_NTA_29k0_43k5 = 0x6b, /* asymmetric 29.0 kbit/s (DL) + 43.5 kbit/s (UL) */
+ RSL_CMOD_CSD_NT_43k5 = 0x74,
+ RSL_CMOD_CSD_NT_28k8 = 0x71,
+ RSL_CMOD_CSD_NT_14k5 = 0x58,
+ RSL_CMOD_CSD_NT_12k0 = 0x50,
+ RSL_CMOD_CSD_NT_6k0 = 0x51,
+};
/* legacy #defines with wrong name */
#define RSL_CMOD_SP_NT_14k5 RSL_CMOD_CSD_NT_14k5
#define RSL_CMOD_SP_NT_12k0 RSL_CMOD_CSD_NT_12k0
#define RSL_CMOD_SP_NT_6k0 RSL_CMOD_CSD_NT_6k0
-/* transparent data */
-#define RSL_CMOD_CSD_T_32000 0x38
-#define RSL_CMOD_CSD_T_29000 0x39
-#define RSL_CMOD_CSD_T_14400 0x18
-#define RSL_CMOD_CSD_T_9600 0x10
-#define RSL_CMOD_CSD_T_4800 0x11
-#define RSL_CMOD_CSD_T_2400 0x12
-#define RSL_CMOD_CSD_T_1200 0x13
-#define RSL_CMOD_CSD_T_600 0x14
-#define RSL_CMOD_CSD_T_1200_75 0x15
+#define RSL_CMOD_CSD_T_32000 RSL_CMOD_CSD_T_32k0
+#define RSL_CMOD_CSD_T_29000 RSL_CMOD_CSD_T_29k0
+#define RSL_CMOD_CSD_T_14400 RSL_CMOD_CSD_T_14k4
+#define RSL_CMOD_CSD_T_9600 RSL_CMOD_CSD_T_9k6
+#define RSL_CMOD_CSD_T_4800 RSL_CMOD_CSD_T_4k8
+#define RSL_CMOD_CSD_T_2400 RSL_CMOD_CSD_T_2k4
+#define RSL_CMOD_CSD_T_1200 RSL_CMOD_CSD_T_1k2
+/*! Transparent data */
+enum rsl_cmod_csd_t {
+ RSL_CMOD_CSD_T_32k0 = 0x38,
+ RSL_CMOD_CSD_T_29k0 = 0x39,
+ RSL_CMOD_CSD_T_14k4 = 0x18,
+ RSL_CMOD_CSD_T_9k6 = 0x10,
+ RSL_CMOD_CSD_T_4k8 = 0x11,
+ RSL_CMOD_CSD_T_2k4 = 0x12,
+ RSL_CMOD_CSD_T_1k2 = 0x13,
+ RSL_CMOD_CSD_T_600 = 0x14,
+ RSL_CMOD_CSD_T_1200_75 = 0x15,
+};
/*! RSL Channel Identification IE (Chapter 9.3.5) */
struct rsl_ie_chan_ident {
@@ -542,6 +618,9 @@ struct rsl_ie_chan_ident {
#define RSL_ERR_CCCH_OVERLOAD 0x23
#define RSL_ERR_ACCH_OVERLOAD 0x24
#define RSL_ERR_PROCESSOR_OVERLOAD 0x25
+#define RSL_ERR_BTS_NOT_EQUIPPED 0x27
+#define RSL_ERR_REMOTE_TRANSC_FAIL 0x28
+#define RSL_ERR_NOTIFICATION_OVERFL 0x29
#define RSL_ERR_RES_UNAVAIL 0x2f
/* service or option not available */
#define RSL_ERR_TRANSC_UNAVAIL 0x30
@@ -598,7 +677,7 @@ struct rsl_ie_chan_ident {
#define RSL_CHANNEED_TCH_F 0x02
#define RSL_CHANNEED_TCH_ForH 0x03
-/*! RSL Cell Broadcast Command (Chapter 9.3.45) */
+/*! RSL Cell Broadcast Command (Chapter 9.3.41) */
struct rsl_ie_cb_cmd_type {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t last_block:2;
@@ -606,7 +685,7 @@ struct rsl_ie_cb_cmd_type {
uint8_t def_bcast:1;
uint8_t command:4;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t command:4, def_bcast:1, spare:1, last_block:2;
#endif
} __attribute__ ((packed));
@@ -624,6 +703,23 @@ struct rsl_ie_cb_cmd_type {
#define RSL_CB_CMD_LASTBLOCK_2 2
#define RSL_CB_CMD_LASTBLOCK_3 3
+/*! NCH DRX Information (Chapter 9.3.47) */
+struct rsl_ie_nch_drx_info {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t nln:2;
+ uint8_t emlpp_priority:3;
+ uint8_t nln_status:1;
+ uint8_t spare:2;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare:2, nln_status:1, emlpp_priority:3, nln:2;
+#endif
+} __attribute__ ((packed));
+
+/*! Command Indicator (Chapter 9.3.48) */
+#define RSL_CMD_INDICATOR_START 0x00
+#define RSL_CMD_INDICATOR_STOP 0x01
+
/* Chapter 3.3.2.3 Brocast control channel */
/* CCCH-CONF, NC is not combined */
#define RSL_BCCH_CCCH_CONF_1_NC 0x00
@@ -669,10 +765,10 @@ enum rsl_ipac_speech_mode_m {
/* 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_IPAC_RTP_CSD_EXT_TRAU = 0, /*!< TRAU-like RTP format, without leading zero-bits */
+ RSL_IPAC_RTP_CSD_NON_TRAU = 1, /*!< packed 16k (252/288 bit) / 8k (126 bit) in RTP */
+ RSL_IPAC_RTP_CSD_TRAU_BTS = 2, /*!< TRAU in BTS; V.110 in RTP/CLEARMODE */
+ RSL_IPAC_RTP_CSD_IWF_FREE = 3, /*!< unknown proprietary IWF-free BTS-BTS data */
};
/* RSL_IE_IPAC_RTP_CSD_FMT, upper four bits */
enum rsl_ipac_rtp_csd_format_ir {
@@ -740,8 +836,14 @@ enum rsl_ipac_embedded_ie {
RSL_IPAC_EIE_SDCCH_CTL_PARAM = 0x1a,
RSL_IPAC_EIE_AMR_CONV_THRESH = 0x1b,
+ /* Osmocom specific extensions: */
+ RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG = 0xf0,
+ RSL_IPAC_EIE_OSMO_MS_PWR_CTL = 0xf1,
+ RSL_IPAC_EIE_OSMO_PC_THRESH_COMP = 0xf2,
+
};
+/* Value of TLV IE RSL_IPAC_EIE_MEAS_AVG_CFG */
struct ipac_preproc_ave_cfg {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t h_reqave:5,
@@ -749,13 +851,67 @@ struct ipac_preproc_ave_cfg {
reserved:1;
uint8_t h_reqt:5,
ave_method:3;
+ uint8_t params[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved:1, param_id:2, h_reqave:5;
uint8_t ave_method:3, h_reqt:5;
+ uint8_t params[0];
#endif
}__attribute__ ((packed));
+
+struct osmo_preproc_ave_cfg_field {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t h_reqave:5,
+ ave_enabled:1,
+ reserved:2;
+ uint8_t h_reqt:5,
+ ave_method:3;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved:2, ave_enabled:1, h_reqave:5;
+ uint8_t ave_method:3, h_reqt:5;
+#endif
+}__attribute__ ((packed));
+/* Value of TLV IE RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG: */
+struct osmo_preproc_ave_cfg {
+ struct osmo_preproc_ave_cfg_field ci_fr;
+ struct osmo_preproc_ave_cfg_field ci_hr;
+ struct osmo_preproc_ave_cfg_field ci_amr_fr;
+ struct osmo_preproc_ave_cfg_field ci_amr_hr;
+ struct osmo_preproc_ave_cfg_field ci_sdcch;
+ struct osmo_preproc_ave_cfg_field ci_gprs;
+ uint8_t params[0]; /* Contains params for each above, appended one after the other */
+}__attribute__ ((packed));
+
+/*! MS/BS Power Control Thresholds (RSL_IPAC_EIE_MS_PWR_CTL) */
+struct ipac_preproc_pc_thresh {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t l_rxlev:6, reserved_l_rxlev:2;
+ uint8_t u_rxlev:6, reserved_u_rxlev:2;
+ uint8_t u_rxqual:3, reserved_u_rxqual:1,
+ l_rxqual:3, reserved_l_rxqual:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved_l_rxlev:2, l_rxlev:6;
+ uint8_t reserved_u_rxlev:2, u_rxlev:6;
+ uint8_t reserved_l_rxqual:1, l_rxqual:3, reserved_u_rxqual:1, u_rxqual:3;
+#endif
+}__attribute__ ((packed));
+
+/*! Osmocom extension for: MS/BS Power Control Thresholds (RSL_IPAC_EIE_OSMO_MS_PWR_CTL) */
+struct osmo_preproc_pc_thresh {
+ /* Carrier-to-Interference (C/I), in dB: */
+ int8_t l_ci_fr; int8_t u_ci_fr; /* FR/EFR */
+ int8_t l_ci_hr; int8_t u_ci_hr; /* HR */
+ int8_t l_ci_amr_fr; int8_t u_ci_amr_fr; /* AMR FR */
+ int8_t l_ci_amr_hr; int8_t u_ci_amr_hr; /* AMR HR */
+ int8_t l_ci_sdcch; int8_t u_ci_sdcch; /* SDCCH */
+ int8_t l_ci_gprs; int8_t u_ci_gprs; /* GPRS */
+}__attribute__ ((packed));
+
+/*! Handover Thresholds */
struct ipac_preproc_ho_thresh {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t l_rxlev_ul_h:6,
@@ -773,7 +929,7 @@ struct ipac_preproc_ho_thresh {
uint8_t ms_range_max:6,
reserved_ms_range:2;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved_l_rxlev_ul:2, l_rxlev_ul_h:6;
uint8_t reserved_l_rxlev_dl:2, l_rxlev_dl_h:6;
uint8_t reserved_rxlev_ul:2, rxlev_ul_ih:6;
@@ -783,6 +939,60 @@ struct ipac_preproc_ho_thresh {
#endif
}__attribute__ ((packed));
+/*! PC Threshold Comparators (RSL_IPAC_EIE_PC_THRESH_COMP) */
+struct ipac_preproc_pc_comp {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t p1:5, reserved_p1:3;
+ uint8_t n1:5, reserved_n1:3;
+ uint8_t p2:5, reserved_p2:3;
+ uint8_t n2:5, reserved_n2:3;
+ uint8_t p3:5, reserved_p3:3;
+ uint8_t n3:5, reserved_n3:3;
+ uint8_t p4:5, reserved_p4:3;
+ uint8_t n4:5, reserved_n4:3;
+ uint8_t pc_interval:5, reserved_pc:3;
+ uint8_t red_step_size:4, inc_step_size:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved_p1:3, p1:5;
+ uint8_t reserved_n1:3, n1:5;
+ uint8_t reserved_p2:3, p2:5;
+ uint8_t reserved_n2:3, n2:5;
+ uint8_t reserved_p3:3, p3:5;
+ uint8_t reserved_n3:3, n3:5;
+ uint8_t reserved_p4:3, p4:5;
+ uint8_t reserved_n4:3, n4:5;
+ uint8_t reserved_pc:3, pc_interval:5;
+ uint8_t inc_step_size:4, red_step_size:4;
+#endif
+}__attribute__ ((packed));
+
+/*! Osmocom extension for: PC Threshold Comparators (RSL_IPAC_EIE_OSMO_PC_THRESH_COMP) */
+struct ipac_preproc_pc_comp_field {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t lower_p:5, reserved_lower_p:3;
+ uint8_t lower_n:5, reserved_lower_n:3;
+ uint8_t upper_p:5, reserved_upper_p:3;
+ uint8_t upper_n:5, reserved_upper_n:3;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved_lower_p:3, lower_p:5;
+ uint8_t reserved_lower_n:3, lower_n:5;
+ uint8_t reserved_upper_p:3, upper_p:5;
+ uint8_t reserved_upper_n:3, upper_n:5;
+#endif
+}__attribute__ ((packed));
+struct osmo_preproc_pc_comp {
+ /* Used for Carrier-to-Interference (C/I), in dB: */
+ struct ipac_preproc_pc_comp_field ci_fr;
+ struct ipac_preproc_pc_comp_field ci_hr;
+ struct ipac_preproc_pc_comp_field ci_amr_fr;
+ struct ipac_preproc_pc_comp_field ci_amr_hr;
+ struct ipac_preproc_pc_comp_field ci_sdcch;
+ struct ipac_preproc_pc_comp_field ci_gprs;
+}__attribute__ ((packed));
+
+/*! HO Threshold Comparators */
struct ipac_preproc_ho_comp {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t p5:5,
@@ -806,7 +1016,7 @@ struct ipac_preproc_ho_comp {
uint8_t reserved;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved_p5:3, p5:5;
uint8_t reserved_n5:3, n5:5;
uint8_t reserved_p6:3, p6:5;
@@ -829,7 +1039,7 @@ struct ipac_preproc_ho_candidates {
s:1,
reserved1:1;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved0:2, bsic:6;
uint8_t reserved1:1, s:1, ba_used:1, bcch_freq:5;
#endif
@@ -844,7 +1054,7 @@ struct ipac_preproc_ncell_dflts {
uint8_t ms_txpwr_max_def:5,
reserved_ms_txpwr_max_def:3;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved_rxlev_min_def:2, rxlev_min_def:6;
uint8_t reserved_ho_margin_def:3, ho_margin_def:5;
uint8_t reserved_ms_txpwr_max_def:3, ms_txpwr_max_def:5;
@@ -857,7 +1067,7 @@ struct ipac_preproc_ho_ctl_param {
sdcch_ho_umts:1,
reserved:6;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t reserved:6, sdcch_ho_umts:1, sdcch_ho_gsm:1;
#endif
}__attribute__ ((packed));
@@ -873,4 +1083,18 @@ struct ipac_preproc_cfg {
struct ipac_preproc_ho_ctl_param ho_ctl_param;
};
+struct rsl_l1_info {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t reserved:1,
+ srr_sro:1,
+ fpc_epc:1,
+ ms_pwr:5;
+ uint8_t ta;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t ms_pwr:5, fpc_epc:1, srr_sro:1, reserved:1;
+ uint8_t ta;
+#endif
+} __attribute__ ((packed));
+
/*! @} */
diff --git a/include/osmocom/gsm/protocol/gsm_09_02.h b/include/osmocom/gsm/protocol/gsm_09_02.h
index 0b54fb74..4d5ff13c 100644
--- a/include/osmocom/gsm/protocol/gsm_09_02.h
+++ b/include/osmocom/gsm/protocol/gsm_09_02.h
@@ -1,8 +1,7 @@
/*! \file gsm_09_02.h
* GSM TS 09.02 definitions (MAP). */
-#ifndef PROTO_GSM_09_02_H
-#define PROTO_GSM_09_02_H
+#pragma once
/* Section 17.7.4 */
/* SS-Status */
@@ -134,5 +133,3 @@
#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_C 0xDC
#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_D 0xDD
#define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_E 0xDE
-
-#endif /* PROTO_GSM_09_02_H */
diff --git a/include/osmocom/gsm/protocol/gsm_12_21.h b/include/osmocom/gsm/protocol/gsm_12_21.h
index 86d12ea7..39b1d45c 100644
--- a/include/osmocom/gsm/protocol/gsm_12_21.h
+++ b/include/osmocom/gsm/protocol/gsm_12_21.h
@@ -1,3 +1,4 @@
+/* 3GPP TS 12.21, nowadays 3GPP TS 52.021 */
/*
* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
@@ -12,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
@@ -445,7 +442,7 @@ enum abis_nm_attr {
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_RLC_CFG = 0xa3,
NM_ATT_IPACC_ALM_THRESH_LIST = 0xa4,
NM_ATT_IPACC_MONIT_VAL_LIST = 0xa5,
NM_ATT_IPACC_TIB_CONTROL = 0xa6,
@@ -462,7 +459,7 @@ enum abis_nm_attr {
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,
@@ -511,6 +508,7 @@ enum abis_nm_attr {
/* osmocom (osmo-bts) specific attributes, used in combination
* with the "org.osmocom" manufacturer identification */
+ NM_ATT_OSMO_NS_LINK_CFG = 0xfd, /* osmocom version supports IPv4 & IPv6 in difference to IPACC */
NM_ATT_OSMO_REDUCEPOWER = 0xfe, /* TLV_TYPE_TV */
};
#define NM_ATT_BS11_FILE_DATA NM_ATT_EVENT_TYPE
@@ -525,9 +523,11 @@ enum abis_nm_adm_state {
/*! OML Availability State (Section 9.4.7) */
enum abis_nm_avail_state {
- NM_AVSTATE_IN_TEST = 1,
+ NM_AVSTATE_IN_TEST = 0,
+ NM_AVSTATE_FAILED = 1,
NM_AVSTATE_POWER_OFF = 2,
NM_AVSTATE_OFF_LINE = 3,
+ /* <not used> = 4, */
NM_AVSTATE_DEPENDENCY = 5,
NM_AVSTATE_DEGRADED = 6,
NM_AVSTATE_NOT_INSTALLED= 7,
@@ -563,8 +563,10 @@ enum abis_nm_chan_comb {
NM_CHANC_IPAC_TCHFull_PDCH = 0x80,
NM_CHANC_IPAC_TCHFull_TCHHalf = 0x81,
/* osmocom */
- NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH = 0x90,
+ NM_CHANC_OSMO_DYN = 0x90,
};
+/* Backward compatibility with older naming: */
+#define NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH NM_CHANC_OSMO_DYN
/*! Event Type (Section 9.4.16) */
enum abis_nm_event_type {
@@ -773,6 +775,98 @@ enum ipac_eie {
NM_IPAC_EIE_BTS_ID = 0x25,
};
+/*! ip.access support flags for NM_IPAC_EIE_FREQ_BANDS */
+#define NM_IPAC_F_FREQ_BAND_PGSM (1 << 0)
+#define NM_IPAC_F_FREQ_BAND_EGSM (1 << 1)
+#define NM_IPAC_F_FREQ_BAND_RGSM (1 << 2)
+#define NM_IPAC_F_FREQ_BAND_DCS (1 << 3)
+#define NM_IPAC_F_FREQ_BAND_PCS (1 << 4)
+#define NM_IPAC_F_FREQ_BAND_850 (1 << 5)
+#define NM_IPAC_F_FREQ_BAND_480 (1 << 6)
+#define NM_IPAC_F_FREQ_BAND_450 (1 << 7)
+
+/*! ip.access support flags for NM_IPAC_EIE_CIPH_ALGOS */
+#define NM_IPAC_F_CIPH_ALGO_A51 (1 << 0)
+#define NM_IPAC_F_CIPH_ALGO_A52 (1 << 1)
+#define NM_IPAC_F_CIPH_ALGO_A53 (1 << 2)
+#define NM_IPAC_F_CIPH_ALGO_A54 (1 << 3)
+#define NM_IPAC_F_CIPH_ALGO_A55 (1 << 4)
+#define NM_IPAC_F_CIPH_ALGO_A56 (1 << 5)
+#define NM_IPAC_F_CIPH_ALGO_A57 (1 << 6)
+#define NM_IPAC_F_CIPH_ALGO_A58 (1 << 7)
+
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_TYPES (1st octet) */
+#define NM_IPAC_F_CHANT_TCHF (1 << 0)
+#define NM_IPAC_F_CHANT_TCHH (1 << 1)
+#define NM_IPAC_F_CHANT_SDCCH8 (1 << 2)
+#define NM_IPAC_F_CHANT_BCCH (1 << 3)
+#define NM_IPAC_F_CHANT_BCCH_SDCCH4 (1 << 4)
+#define NM_IPAC_F_CHANT_BCH (1 << 5)
+#define NM_IPAC_F_CHANT_BCCH_SDCCH4_CBCH (1 << 6)
+#define NM_IPAC_F_CHANT_SDCCH8_CBCH (1 << 7)
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_TYPES (2nd octet) */
+#define NM_IPAC_F_CHANT_PDCHF (1 << 8)
+#define NM_IPAC_F_CHANT_TCHF_PDCHF (1 << 9)
+#define NM_IPAC_F_CHANT_TCHH_PDCHH (1 << 10)
+#define NM_IPAC_F_CHANT_TCHF_TCHH (1 << 11)
+
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_MODES (speech codecs) */
+#define NM_IPAC_F_CHANM_SPEECH_FS (1 << 0)
+#define NM_IPAC_F_CHANM_SPEECH_EFS (1 << 1)
+#define NM_IPAC_F_CHANM_SPEECH_AFS (1 << 2)
+#define NM_IPAC_F_CHANM_SPEECH_HS (1 << 3)
+#define NM_IPAC_F_CHANM_SPEECH_AHS (1 << 4)
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_MODES (CSD non-transparent) */
+#define NM_IPAC_F_CHANM_CSD_NT_4k8 (1 << 8)
+#define NM_IPAC_F_CHANM_CSD_NT_9k6 (1 << 9)
+#define NM_IPAC_F_CHANM_CSD_NT_14k4 (1 << 10)
+/*! ip.access support flags for NM_IPAC_EIE_CHAN_MODES (CSD transparent) */
+#define NM_IPAC_F_CHANM_CSD_T_1200_75 (1 << 16)
+#define NM_IPAC_F_CHANM_CSD_T_600 (1 << 17)
+#define NM_IPAC_F_CHANM_CSD_T_1k2 (1 << 18)
+#define NM_IPAC_F_CHANM_CSD_T_2k4 (1 << 19)
+#define NM_IPAC_F_CHANM_CSD_T_4k8 (1 << 20)
+#define NM_IPAC_F_CHANM_CSD_T_9k6 (1 << 21)
+#define NM_IPAC_F_CHANM_CSD_T_14k4 (1 << 22)
+
+/*! ip.access support flags for NM_IPAC_EIE_GPRS_CODING (GPRS) */
+#define NM_IPAC_F_GPRS_CODING_CS1 (1 << 0)
+#define NM_IPAC_F_GPRS_CODING_CS2 (1 << 1)
+#define NM_IPAC_F_GPRS_CODING_CS3 (1 << 2)
+#define NM_IPAC_F_GPRS_CODING_CS4 (1 << 3)
+/*! ip.access support flags for NM_IPAC_EIE_GPRS_CODING (EGPRS) */
+#define NM_IPAC_F_GPRS_CODING_MCS1 (1 << 7)
+#define NM_IPAC_F_GPRS_CODING_MCS2 (1 << 8)
+#define NM_IPAC_F_GPRS_CODING_MCS3 (1 << 9)
+#define NM_IPAC_F_GPRS_CODING_MCS4 (1 << 10)
+#define NM_IPAC_F_GPRS_CODING_MCS5 (1 << 11)
+#define NM_IPAC_F_GPRS_CODING_MCS6 (1 << 12)
+#define NM_IPAC_F_GPRS_CODING_MCS7 (1 << 13)
+#define NM_IPAC_F_GPRS_CODING_MCS8 (1 << 14)
+#define NM_IPAC_F_GPRS_CODING_MCS9 (1 << 15)
+
+/*! ip.access support flags for NM_IPAC_EIE_RTP_FEATURES */
+#define NM_IPAC_F_RTP_FEAT_COMPR_CONTROL (1 << 0) /* RTP Compression Control */
+#define NM_IPAC_F_RTP_FEAT_IR_8k (1 << 1) /* IR 8 kbit/s */
+#define NM_IPAC_F_RTP_FEAT_IR_16k (1 << 2) /* IR 16 kbit/s */
+#define NM_IPAC_F_RTP_FEAT_IR_32k (1 << 3) /* IR 32 kbit/s */
+#define NM_IPAC_F_RTP_FEAT_IR_64k (1 << 4) /* IR 64 kbit/s */
+#define NM_IPAC_F_RTP_FEAT_MULTIPLEX_RTP (1 << 6) /* RTP Multiplexing */
+#define NM_IPAC_F_RTP_FEAT_MULTIPLEX_SRTP (1 << 7) /* SRTP Multiplexing */
+
+/*! ip.access support flags for NM_IPAC_EIE_RSL_FEATURES */
+#define NM_IPAC_F_RSL_FEAT_PHYSICAL_CONTEXT (1 << 0)
+#define NM_IPAC_F_RSL_FEAT_DYN_PDCH_ACT (1 << 1)
+#define NM_IPAC_F_RSL_FEAT_RTP_PT2 (1 << 2)
+
+extern const struct value_string abis_nm_ipacc_freq_band_desc[];
+extern const struct value_string abis_nm_ipacc_ciph_algo_desc[];
+extern const struct value_string abis_nm_ipacc_chant_desc[];
+extern const struct value_string abis_nm_ipacc_chanm_desc[];
+extern const struct value_string abis_nm_ipacc_gprs_coding_desc[];
+extern const struct value_string abis_nm_ipacc_rtp_feat_desc[];
+extern const struct value_string abis_nm_ipacc_rsl_feat_desc[];
+
/*! ip.access NWL BCCH information type */
enum ipac_bcch_info_type {
IPAC_BINF_RXLEV = (1 << 8),
@@ -788,6 +882,69 @@ enum ipac_bcch_info_type {
IPAC_BINF_CELL_ALLOC = (1 << 2),
};
+/*! ip.access NM_ATT_IPACC_NS_CFG value */
+struct abis_nm_ipacc_att_ns_cfg {
+ uint8_t un_blocking_timer; /* (un)blocking Timer (Tns-block) timeout */
+ uint8_t un_blocking_retries; /* (un)blocking Timer (Tns-block) number of retries */
+ uint8_t reset_timer; /* Reset Timer (Tns-reset) timeout */
+ uint8_t reset_retries; /* Reset Timer (Tns-reset) number of retries */
+ uint8_t test_timer; /* Test Timer (Tns-test) timeout */
+ uint8_t alive_timer; /* Alive Timer (Tns-alive) timeout */
+ uint8_t alive_retries; /* Alive Timer (Tns-alive) number of retries */
+} __attribute__((packed));
+
+/*! ip.access NM_ATT_IPACC_BSSGP_CFG value */
+struct abis_nm_ipacc_att_bssgp_cfg {
+ uint8_t t1_s; /* blocking timer (T1) */
+ uint8_t t1_blocking_retries; /* blocking retries */
+ uint8_t t1_unblocking_retries; /* unblocking retries */
+ uint8_t t2_s; /* reset timer (T2) */
+ uint8_t t2_retries; /* reset retries */
+ uint8_t t3_100ms; /* suspend timer (T3) in 100ms */
+ uint8_t t3_retries; /* suspend retries */
+ uint8_t t4_100ms; /* resume timer (T4) in 100ms */
+ uint8_t t4_retries; /* resume retries */
+ uint8_t t5_s; /* capability update timer (T5) */
+ uint8_t t5_retries; /* capability update retries */
+} __attribute__((packed));
+
+/*! ip.access NM_ATT_IPACC_RLC_CFG value */
+struct abis_nm_ipacc_att_rlc_cfg {
+ uint8_t t3142;
+ uint8_t t3169;
+ uint8_t t3191;
+ uint8_t t3193_10ms;
+ uint8_t t3195;
+ uint8_t n3101;
+ uint8_t n3103;
+ uint8_t n3105;
+ uint8_t rlc_cv_countdown;
+} __attribute__((packed));
+
+/*! ip.access NM_ATT_IPACC_RLC_CFG_2 value */
+struct abis_nm_ipacc_att_rlc_cfg_2 {
+ /* T downlink TBF extension (0..500, network order) */
+ uint16_t t_dl_tbf_ext_10ms;
+ /* T uplink TBF extension (0..500, network order) */
+ uint16_t t_ul_tbf_ext_10ms;
+ /* Initial CS to use: CS1 -> 1, CS2 -> 2, CS3 -> 3, CS4 -> 4 */
+ uint8_t initial_cs;
+} __attribute__((packed));
+
+/*! ip.access NM_ATT_IPACC_RLC_CFG_3 value */
+struct abis_nm_ipacc_att_rlc_cfg_3 {
+ /* Initial MCS to use when EGPRS is used:
+ * MCS1 -> 1, MCS2 -> 2, ..., MCS9 -> 9 */
+ uint8_t initial_mcs;
+} __attribute__((packed));
+
+/*! Osmocom NSVC address type for NM_ATT_OSMO_NS_LINK_CFG */
+enum osmo_oml_nsvc_address_type {
+ OSMO_NSVC_ADDR_UNSPEC = 0x00,
+ OSMO_NSVC_ADDR_IPV4 = 0x04,
+ OSMO_NSVC_ADDR_IPV6 = 0x29,
+};
+
/*! 3GPP TS 52.021 §9.4.62 SW Description */
struct abis_nm_sw_desc {
uint8_t file_id[UINT8_MAX];
diff --git a/include/osmocom/gsm/protocol/gsm_23_032.h b/include/osmocom/gsm/protocol/gsm_23_032.h
new file mode 100644
index 00000000..6eb65ca2
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_23_032.h
@@ -0,0 +1,252 @@
+/*! \defgroup gad 3GPP TS 23.032 GAD: Universal Geographical Area Description.
+ * @{
+ * \file gsm_23_032.h
+ */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/endian.h>
+
+enum gad_type {
+ /*! Ellipsoid point */
+ GAD_TYPE_ELL_POINT = 0,
+ /*! Ellipsoid point with uncertainty circle. */
+ GAD_TYPE_ELL_POINT_UNC_CIRCLE = 1,
+ /*! Ellipsoid point with uncertainty ellipse. */
+ GAD_TYPE_ELL_POINT_UNC_ELLIPSE = 3,
+ GAD_TYPE_POLYGON = 5,
+ /*! Ellipsoid point with altitude. */
+ GAD_TYPE_ELL_POINT_ALT = 8,
+ /*! Ellipsoid point with altitude and uncertainty ellipsoid. */
+ GAD_TYPE_ELL_POINT_ALT_UNC_ELL = 9,
+ /*! Ellipsoid arc */
+ GAD_TYPE_ELL_ARC = 10,
+ /*! High accuracy ellipsoid point with uncertainty ellipse. */
+ GAD_TYPE_HA_ELL_POINT_UNC_ELLIPSE = 11,
+ /*! High accuracy ellipsoid point with altitude and uncertainty ellipsoid. */
+ GAD_TYPE_HA_ELL_POINT_ALT_UNC_ELL = 12,
+};
+
+struct gad_raw_head {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t spare:4,
+ type:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t type:4, spare:4;
+#endif
+} __attribute__ ((packed));
+
+struct gad_raw_ell_point {
+ struct gad_raw_head h; /*!< type = GAD_TYPE_ELL_POINT */
+ uint8_t lat[3];
+ uint8_t lon[3];
+} __attribute__ ((packed));
+
+struct gad_raw_ell_point_unc_circle {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct gad_raw_head h; /*!< type = GAD_TYPE_ELL_POINT_UNC_CIRCLE */
+ uint8_t lat[3];
+ uint8_t lon[3];
+ uint8_t unc:7,
+ spare2:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct gad_raw_head h;
+ uint8_t lat[3];
+ uint8_t lon[3];
+ uint8_t spare2:1, unc:7;
+#endif
+} __attribute__ ((packed));
+
+struct gad_raw_ell_point_unc_ellipse {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct gad_raw_head h; /*!< type = GAD_TYPE_ELL_POINT_UNC_ELLIPSE */
+ uint8_t lat[3];
+ uint8_t lon[3];
+ uint8_t unc_semi_major:7,
+ spare1:1;
+ uint8_t unc_semi_minor:7,
+ spare2:1;
+ uint8_t major_ori;
+ uint8_t confidence:7,
+ spare3:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct gad_raw_head h;
+ uint8_t lat[3];
+ uint8_t lon[3];
+ uint8_t spare1:1, unc_semi_major:7;
+ uint8_t spare2:1, unc_semi_minor:7;
+ uint8_t major_ori;
+ uint8_t spare3:1, confidence:7;
+#endif
+} __attribute__ ((packed));
+
+struct gad_raw_polygon {
+ struct {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t num_points:4;
+ uint8_t type:4; /*!< type = GAD_TYPE_POLYGON */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t type:4, num_points:4;
+#endif
+ } h;
+ struct {
+ uint8_t lat[3];
+ uint8_t lon[3];
+ } point[15];
+} __attribute__ ((packed));
+
+struct gad_raw_ell_point_alt {
+ struct gad_raw_head h; /*!< type = GAD_TYPE_ELL_POINT_ALT */
+ uint8_t lat[3];
+ uint8_t lon[3];
+ uint8_t alt[2];
+} __attribute__ ((packed));
+
+struct gad_raw_ell_point_alt_unc_ell {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct gad_raw_head h; /*!< type = GAD_TYPE_ELL_POINT_ALT_UNC_ELL */
+ uint8_t lat[3];
+ uint8_t lon[3];
+ uint8_t alt[2];
+ uint8_t unc_semi_major:7,
+ spare1:1;
+ uint8_t unc_semi_minor:7,
+ spare2:1;
+ uint8_t major_ori;
+ uint8_t unc_alt:7,
+ spare3:1;
+ uint8_t confidence:7,
+ spare4:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct gad_raw_head h;
+ uint8_t lat[3];
+ uint8_t lon[3];
+ uint8_t alt[2];
+ uint8_t spare1:1, unc_semi_major:7;
+ uint8_t spare2:1, unc_semi_minor:7;
+ uint8_t major_ori;
+ uint8_t spare3:1, unc_alt:7;
+ uint8_t spare4:1, confidence:7;
+#endif
+} __attribute__ ((packed));
+
+struct gad_raw_ell_arc {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct gad_raw_head h; /*!< type = GAD_TYPE_ELL_ARC */
+ uint8_t lat[3];
+ uint8_t lon[3];
+ uint8_t inner_r[2];
+ uint8_t unc_r:7,
+ spare1:1;
+ uint8_t ofs_angle;
+ uint8_t incl_angle;
+ uint8_t confidence:7,
+ spare2:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct gad_raw_head h;
+ uint8_t lat[3];
+ uint8_t lon[3];
+ uint8_t inner_r[2];
+ uint8_t spare1:1, unc_r:7;
+ uint8_t ofs_angle;
+ uint8_t incl_angle;
+ uint8_t spare2:1, confidence:7;
+#endif
+} __attribute__ ((packed));
+
+struct gad_raw_ha_ell_point_unc_ell {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct gad_raw_head h; /*!< type = GAD_TYPE_HA_ELL_POINT_UNC_ELLIPSE */
+ uint8_t lat[4];
+ uint8_t lon[4];
+ uint8_t alt[3];
+ uint8_t unc_semi_major;
+ uint8_t unc_semi_minor;
+ uint8_t major_ori;
+ uint8_t confidence:7,
+ spare1:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct gad_raw_head h;
+ uint8_t lat[4];
+ uint8_t lon[4];
+ uint8_t alt[3];
+ uint8_t unc_semi_major;
+ uint8_t unc_semi_minor;
+ uint8_t major_ori;
+ uint8_t spare1:1, confidence:7;
+#endif
+} __attribute__ ((packed));
+
+struct gad_raw_ha_ell_point_alt_unc_ell {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct gad_raw_head h; /*!< type = GAD_TYPE_HA_ELL_POINT_ALT_UNC_ELL */
+ uint8_t lat[4];
+ uint8_t lon[4];
+ uint8_t alt[3];
+ uint8_t unc_semi_major;
+ uint8_t unc_semi_minor;
+ uint8_t major_ori;
+ uint8_t h_confidence:7,
+ spare1:1;
+ uint8_t unc_alt;
+ uint8_t v_confidence:7,
+ spare2:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct gad_raw_head h;
+ uint8_t lat[4];
+ uint8_t lon[4];
+ uint8_t alt[3];
+ uint8_t unc_semi_major;
+ uint8_t unc_semi_minor;
+ uint8_t major_ori;
+ uint8_t spare1:1, h_confidence:7;
+ uint8_t unc_alt;
+ uint8_t spare2:1, v_confidence:7;
+#endif
+} __attribute__ ((packed));
+
+/*! GAD PDU in network-byte-order according to 3GPP TS 23.032 GAD: Universal Geographical Area Description. */
+union gad_raw {
+ struct gad_raw_head h;
+ struct gad_raw_ell_point ell_point;
+ struct gad_raw_ell_point_unc_circle ell_point_unc_circle;
+ struct gad_raw_ell_point_unc_ellipse ell_point_unc_ellipse;
+ struct gad_raw_polygon polygon;
+ struct gad_raw_ell_point_alt ell_point_alt;
+ struct gad_raw_ell_point_alt_unc_ell ell_point_alt_unc_ell;
+ struct gad_raw_ell_arc ell_arc;
+ struct gad_raw_ha_ell_point_unc_ell ha_ell_point_unc_ell;
+ struct gad_raw_ha_ell_point_alt_unc_ell ha_ell_point_alt_unc_ell;
+} __attribute__ ((packed));
+
+/*! @} */
diff --git a/include/osmocom/gsm/protocol/gsm_23_041.h b/include/osmocom/gsm/protocol/gsm_23_041.h
index c75c0883..2a2b006f 100644
--- a/include/osmocom/gsm/protocol/gsm_23_041.h
+++ b/include/osmocom/gsm/protocol/gsm_23_041.h
@@ -1,5 +1,17 @@
#pragma once
+#include <osmocom/core/endian.h>
+
+/* Section 9.3.24: Warning-Type */
+enum gsm23041_warning_type_value {
+ CBS_ETWS_WARN_TYPE_EARTHQUAKE = 0,
+ CBS_ETWS_WARN_TYPE_TSUNAMI = 1,
+ CBS_ETWS_WARN_TYPE_EARTHQUAKE_AND_TSUNAMI = 2,
+ CBS_ETWS_WARN_TYPE_TEST = 3,
+ CBS_ETWS_WARN_TYPE_OTHER = 4,
+ /* 0000101-1111111 Reserved for future use */
+};
+
/* Section 9.4.1.2: GSM Message Format */
struct gsm23041_msg_param_gsm {
uint16_t serial_nr;
@@ -9,9 +21,9 @@ struct gsm23041_msg_param_gsm {
#if OSMO_IS_LITTLE_ENDIAN
uint8_t num_pages:4,
page_nr:4;
-#else
- uint8_t page_nr:4,
- num_pages:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t page_nr:4, num_pages:4;
#endif
} page_param;
uint8_t content[0];
diff --git a/include/osmocom/gsm/protocol/gsm_25_415.h b/include/osmocom/gsm/protocol/gsm_25_415.h
new file mode 100644
index 00000000..5c4dd2bb
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_25_415.h
@@ -0,0 +1,222 @@
+#pragma once
+/* Iu User Plane (IuUP) Definitions as per 3GPP TS 25.415 */
+/* (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <stdint.h>
+#include <osmocom/core/endian.h>
+
+/* 3GPP TS 25.415 Section 6.6.2.1 */
+struct iuup_pdutype0_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ /* control part */
+ uint8_t frame_nr:4,
+ pdu_type:4;
+ uint8_t rfci:6,
+ fqc:2;
+ /* checksum part */
+ uint8_t payload_crc_hi:2, header_crc:6;
+ uint8_t payload_crc_lo;
+
+ /* payload part */
+ uint8_t payload[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t pdu_type:4, frame_nr:4;
+ uint8_t fqc:2, rfci:6;
+ uint8_t header_crc:6, payload_crc_hi:2;
+ uint8_t payload_crc_lo;
+ uint8_t payload[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.2 */
+struct iuup_pdutype1_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ /* control part */
+ uint8_t frame_nr:4,
+ pdu_type:4;
+ uint8_t rfci:6,
+ fqc:2;
+ /* checksum part */
+ uint8_t spare:2,
+ header_crc:6;
+ /* payload part */
+ uint8_t payload[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t pdu_type:4, frame_nr:4;
+ uint8_t fqc:2, rfci:6;
+ uint8_t header_crc:6, spare:2;
+ uint8_t payload[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3 */
+struct iuup_pdutype14_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ /* control part */
+ uint8_t frame_nr:2,
+ ack_nack:2,
+ pdu_type:4;
+ uint8_t proc_ind:4,
+ mode_version:4;
+ /* checksum part */
+ uint8_t payload_crc_hi:2, header_crc:6;
+ uint8_t payload_crc_lo;
+ /* payload part */
+ uint8_t payload[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t pdu_type:4, ack_nack:2, frame_nr:2;
+ uint8_t mode_version:4, proc_ind:4;
+ uint8_t header_crc:6, payload_crc_hi:2;
+ uint8_t payload_crc_lo;
+ uint8_t payload[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3.4.1 */
+struct iuup_ctrl_init_rfci_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t rfci:6,
+ li:1,
+ lri:1;
+ uint8_t subflow_length[0]; /* 1 or 2 bytes depending on li */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t lri:1, li:1, rfci:6;
+ uint8_t subflow_length[0];
+#endif
+} __attribute__((packed));
+struct iuup_ctrl_init_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t chain_ind:1,
+ num_subflows_per_rfci:3,
+ ti:1,
+ spare:3;
+ uint8_t rfci_data[0]; /* struct iuup_ctrl_init_rfci_hdr* */
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare:3, ti:1, num_subflows_per_rfci:3, chain_ind:1;
+ uint8_t rfci_data[0]; /* struct iuup_ctrl_init_rfci_hdr* */
+;
+#endif
+} __attribute__((packed));
+struct iuup_ctrl_init_tail {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint16_t versions_supported;
+ uint8_t spare:4,
+ data_pdu_type:4;
+ uint8_t spare_extension[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint16_t versions_supported;
+ uint8_t data_pdu_type:4, spare:4;
+ uint8_t spare_extension[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3.4.4 */
+struct iuup_ctrl_error_event {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t error_cause:6,
+ error_distance:2;
+ uint8_t spare_extension[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t error_distance:2, error_cause:6;
+ uint8_t spare_extension[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3.2 */
+struct iuup_ctrl_ack {
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t spare_extension[0];
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2.3.3 */
+struct iuup_ctrl_nack {
+#if OSMO_IS_LITTLE_ENDIAN
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t spare:2,
+ error_cause:6;
+ uint8_t spare_extension[0];
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ struct iuup_pdutype14_hdr hdr;
+ uint8_t error_cause:6, spare:2;
+ uint8_t spare_extension[0];
+#endif
+} __attribute__((packed));
+
+/* 3GPP TS 25.415 Section 6.6.2 + 6.6.3.1 */
+enum iuup_pdu_type {
+ IUUP_PDU_T_DATA_CRC = 0,
+ IUUP_PDU_T_DATA_NOCRC = 1,
+ IUUP_PDU_T_CONTROL = 14,
+};
+
+/* 3GPP TS 25.415 Section 6.6.3.2 */
+enum iuup_ack_nack {
+ IUUP_AN_PROCEDURE = 0,
+ IUUP_AN_ACK = 1,
+ IUUP_AN_NACK = 2,
+};
+
+/* 3GPP TS 25.415 Section 6.6.3.5 */
+enum iuup_fqc {
+ IUUP_FQC_FRAME_GOOD = 0,
+ IUUP_FQC_FRAME_BAD = 1,
+ IUUP_FQC_FRAME_BAD_RADIO = 2,
+};
+
+/* 3GPP TS 25.415 Section 6.6.3.7 */
+enum iuup_procedure {
+ IUUP_PROC_INIT = 0,
+ IUUP_PROC_RATE_CTRL = 1,
+ IUUP_PROC_TIME_ALIGN = 2,
+ IUUP_PROC_ERR_EVENT = 3,
+};
+
+
+/* 3GPP TS 25.415 Section 6.6.3.15 */
+enum iuup_error_distance {
+ IUUP_ERR_DIST_LOCAL = 0,
+ IUUP_ERR_DIST_FIRST_FWD = 1,
+ IUUP_ERR_DIST_SECOND_FWD = 2,
+ IUUP_ERR_DIST_RESERVED = 3,
+};
+
+
+/* 3GPP TS 25.415 Section 6.6.3.16 */
+enum iuup_error_cause {
+ IUUP_ERR_CAUSE_CRC_ERR_HDR = 0,
+ IUUP_ERR_CAUSE_CRC_ERR_DATA = 1,
+ IUUP_ERR_CAUSE_UNEXPECTED_FN = 2,
+ IUUP_ERR_CAUSE_FRAME_LOSS = 3,
+ IUUP_ERR_CAUSE_UNKNOWN_PDUTYPE = 4,
+ IUUP_ERR_CAUSE_UNKNOWN_PROC = 5,
+ IUUP_ERR_CAUSE_UNKNNOWN_RES_VAL = 6,
+ IUUP_ERR_CAUSE_UNKNNOWN_FIELD = 7,
+ IUUP_ERR_CAUSE_FRAME_TOO_SHORT = 8,
+ IUUP_ERR_CAUSE_MISSING_FIELDS = 9,
+ IUUP_ERR_CAUSE_UNEXPECTED_PDU_T = 16,
+ IUUP_ERR_CAUSE_UNEXPECTED_PROC = 18,
+ IUUP_ERR_CAUSE_UNEXPECTED_RFCI = 19,
+ IUUP_ERR_CAUSE_UNEXPECTED_VALUE = 20,
+ IUUP_ERR_CAUSE_INIT_FAILURE = 42,
+ IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR = 43,
+ IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK = 44,
+ IUUP_ERR_CAUSE_RATE_CTRL_FAILURE = 45,
+ IUUP_ERR_CAUSE_ERR_EVENT_FAIL = 46,
+ IUUP_ERR_CAUSE_TIME_ALIGN_NOTSUPP = 47,
+ IUUP_ERR_CAUSE_REQ_TIME_ALIGN_NOTPOSS = 48,
+ IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED = 49,
+};
diff --git a/include/osmocom/gsm/protocol/gsm_29_118.h b/include/osmocom/gsm/protocol/gsm_29_118.h
index 9adb90f5..15835888 100644
--- a/include/osmocom/gsm/protocol/gsm_29_118.h
+++ b/include/osmocom/gsm/protocol/gsm_29_118.h
@@ -181,4 +181,4 @@ static inline const char *sgsap_ue_emm_mode_name(enum sgsap_ue_emm_mode mode) {
* See also: RFC1123 Section 2.1 Host Names and Numbers */
#define SGS_VLR_NAME_MAXLEN 255
-const struct tlv_definition sgsap_ie_tlvdef;
+extern const struct tlv_definition sgsap_ie_tlvdef;
diff --git a/include/osmocom/gsm/protocol/gsm_44_004.h b/include/osmocom/gsm/protocol/gsm_44_004.h
new file mode 100644
index 00000000..c30ba0c9
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_44_004.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <osmocom/core/endian.h>
+
+/* TS 44.004 Section 7.1 */
+
+struct gsm_sacch_l1_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t ms_pwr:5,
+ fpc_epc:1,
+ srr_sro:1,
+ reserved:1;
+ uint8_t ta;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t reserved:1, srr_sro:1, fpc_epc:1, ms_pwr:5;
+ uint8_t ta;
+#endif
+} __attribute__ ((packed));
diff --git a/include/osmocom/gsm/protocol/gsm_44_060.h b/include/osmocom/gsm/protocol/gsm_44_060.h
new file mode 100644
index 00000000..1df2f800
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_44_060.h
@@ -0,0 +1,252 @@
+/*! \file gsm_44_060.h
+ * General Packet Radio Service (GPRS).
+ * Radio Link Control / Medium Access Control (RLC/MAC) protocol
+ * 3GPP TS 44.060
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/endian.h>
+
+/* TS 44.060 10.3a.4.1.1 */
+struct gprs_rlc_ul_header_egprs_1 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t r:1,
+ si:1,
+ cv:4,
+ tfi_hi:2;
+ uint8_t tfi_lo:3,
+ bsn1_hi:5;
+ uint8_t bsn1_lo:6,
+ bsn2_hi:2;
+ uint8_t bsn2_lo:8;
+ uint8_t cps:5,
+ rsb:1,
+ pi:1,
+ spare_hi:1;
+ uint8_t spare_lo:6,
+ dummy:2;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:2, cv:4, si:1, r:1;
+ uint8_t bsn1_hi:5, tfi_lo:3;
+ uint8_t bsn2_hi:2, bsn1_lo:6;
+ uint8_t bsn2_lo:8;
+ uint8_t spare_hi:1, pi:1, rsb:1, cps:5;
+ uint8_t dummy:2, spare_lo:6;
+#endif
+} __attribute__ ((packed));
+
+/* TS 44.060 10.3a.4.2.1 */
+struct gprs_rlc_ul_header_egprs_2 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t r:1,
+ si:1,
+ cv:4,
+ tfi_hi:2;
+ uint8_t tfi_lo:3,
+ bsn1_hi:5;
+ uint8_t bsn1_lo:6,
+ cps_hi:2;
+ uint8_t cps_lo:1,
+ rsb:1,
+ pi:1,
+ spare_hi:5;
+ uint8_t spare_lo:5,
+ dummy:3;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:2, cv:4, si:1, r:1;
+ uint8_t bsn1_hi:5, tfi_lo:3;
+ uint8_t cps_hi:2, bsn1_lo:6;
+ uint8_t spare_hi:5, pi:1, rsb:1, cps_lo:1;
+ uint8_t dummy:3, spare_lo:5;
+#endif
+} __attribute__ ((packed));
+
+/* TS 44.060 10.3a.4.3.1 */
+struct gprs_rlc_ul_header_egprs_3 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t r:1,
+ si:1,
+ cv:4,
+ tfi_hi:2;
+ uint8_t tfi_lo:3,
+ bsn1_hi:5;
+ uint8_t bsn1_lo:6,
+ cps_hi:2;
+ uint8_t cps_lo:2,
+ spb:2,
+ rsb:1,
+ pi:1,
+ spare:1,
+ dummy:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:2, cv:4, si:1, r:1;
+ uint8_t bsn1_hi:5, tfi_lo:3;
+ uint8_t cps_hi:2, bsn1_lo:6;
+ uint8_t dummy:1, spare:1, pi:1, rsb:1, spb:2, cps_lo:2;
+#endif
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_1 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t usf:3,
+ es_p:2,
+ rrbp:2,
+ tfi_hi:1;
+ uint8_t tfi_lo:4,
+ pr:2,
+ bsn1_hi:2;
+ uint8_t bsn1_mid:8;
+ uint8_t bsn1_lo:1,
+ bsn2_hi:7;
+ uint8_t bsn2_lo:3,
+ cps:5;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
+ uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
+ uint8_t bsn1_mid:8;
+ uint8_t bsn2_hi:7, bsn1_lo:1;
+ uint8_t cps:5, bsn2_lo:3;
+#endif
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_2 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t usf:3,
+ es_p:2,
+ rrbp:2,
+ tfi_hi:1;
+ uint8_t tfi_lo:4,
+ pr:2,
+ bsn1_hi:2;
+ uint8_t bsn1_mid:8;
+ uint8_t bsn1_lo:1,
+ cps:3,
+ dummy:4;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
+ uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
+ uint8_t bsn1_mid:8;
+ uint8_t dummy:4, cps:3, bsn1_lo:1;
+#endif
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_3 {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t usf:3,
+ es_p:2,
+ rrbp:2,
+ tfi_hi:1;
+ uint8_t tfi_lo:4,
+ pr:2,
+ bsn1_hi:2;
+ uint8_t bsn1_mid:8;
+ uint8_t bsn1_lo:1,
+ cps:4,
+ spb:2,
+ dummy:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
+ uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
+ uint8_t bsn1_mid:8;
+ uint8_t dummy:1, spb:2, cps:4, bsn1_lo:1;
+#endif
+} __attribute__ ((packed));
+
+/* TS 44.060 Table 12.24.2
+* Meaning of values documented in TS 23.060 Chapter 6.3.3.1: Network Mode of Operation */
+enum osmo_gprs_nmo {
+ GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */
+ GPRS_NMO_II = 1, /* all paging on CCCH */
+ GPRS_NMO_III = 2, /* no paging coordination */
+};
+
+/* TS 44.060 12.24 */
+struct osmo_gprs_cell_options {
+ enum osmo_gprs_nmo nmo;
+ /* T3168: wait for packet uplink assignment message */
+ uint32_t t3168; /* in milliseconds */
+ /* T3192: wait for release of the TBF after reception of the final block */
+ uint32_t t3192; /* in milliseconds */
+ uint32_t drx_timer_max;/* in seconds */
+ uint32_t bs_cv_max;
+ uint8_t supports_egprs_11bit_rach;
+ bool ctrl_ack_type_use_block; /* use PACKET CONTROL ACKNOWLEDGMENT */
+
+ uint8_t ext_info_present;
+ struct {
+ uint8_t egprs_supported;
+ uint8_t use_egprs_p_ch_req;
+ uint8_t bep_period;
+ uint8_t pfc_supported;
+ uint8_t dtm_supported;
+ uint8_t bss_paging_coordination;
+ bool ccn_active;
+ } ext_info;
+};
+
+/* TS 44.060 Table 12.9.2 */
+struct osmo_gprs_power_ctrl_pars {
+ uint8_t alpha;
+ uint8_t t_avg_w;
+ uint8_t t_avg_t;
+ uint8_t pc_meas_chan;
+ uint8_t n_avg_i;
+};
+
+
+/*! Structure for CPS coding and puncturing scheme (TS 44.060 10.4.8a) */
+struct egprs_cps {
+ uint8_t bits;
+ uint8_t mcs;
+ uint8_t p[2];
+};
+
+/*! CPS puncturing table selection (TS 44.060 10.4.8a) */
+enum egprs_cps_punc {
+ EGPRS_CPS_P1,
+ EGPRS_CPS_P2,
+ EGPRS_CPS_P3,
+ EGPRS_CPS_NONE = -1,
+};
+
+/*! EGPRS header types (TS 44.060 10.0a.2) */
+enum egprs_hdr_type {
+ EGPRS_HDR_TYPE1,
+ EGPRS_HDR_TYPE2,
+ EGPRS_HDR_TYPE3,
+};
+
+enum osmo_gprs_cs {
+ OSMO_GPRS_CS_NONE,
+ OSMO_GPRS_CS1,
+ OSMO_GPRS_CS2,
+ OSMO_GPRS_CS3,
+ OSMO_GPRS_CS4,
+ OSMO_GPRS_MCS1,
+ OSMO_GPRS_MCS2,
+ OSMO_GPRS_MCS3,
+ OSMO_GPRS_MCS4,
+ OSMO_GPRS_MCS5,
+ OSMO_GPRS_MCS6,
+ OSMO_GPRS_MCS7,
+ OSMO_GPRS_MCS8,
+ OSMO_GPRS_MCS9,
+ _NUM_OSMO_GPRS_CS
+};
+
+int egprs_get_cps(struct egprs_cps *cps, uint8_t type, uint8_t bits);
+
+int osmo_gprs_ul_block_size_bits(enum osmo_gprs_cs cs);
+int osmo_gprs_dl_block_size_bits(enum osmo_gprs_cs cs);
+int osmo_gprs_ul_block_size_bytes(enum osmo_gprs_cs cs);
+int osmo_gprs_dl_block_size_bytes(enum osmo_gprs_cs cs);
+enum osmo_gprs_cs osmo_gprs_ul_cs_by_block_bytes(uint8_t block_size);
+enum osmo_gprs_cs osmo_gprs_dl_cs_by_block_bytes(uint8_t block_size);
diff --git a/include/osmocom/gsm/protocol/gsm_44_068.h b/include/osmocom/gsm/protocol/gsm_44_068.h
new file mode 100644
index 00000000..3a33c163
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_44_068.h
@@ -0,0 +1,136 @@
+#pragma once
+#include <stdint.h>
+#include <osmocom/core/utils.h>
+
+/* Group Call Control (GCC) is an ETSI/3GPP standard protocol used between
+ * MS (Mobile Station) and MSC (Mobile Switchting Center) in 2G/GSM-R network.
+ * It is specified in 3GPP TS 44.068.
+ *
+ * (C) 2023 by Sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Andreas Eversberg
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/* 9 Information Element Identifiers */
+enum osmo_gsm44068_iei {
+ OSMO_GSM44068_IEI_MOBILE_IDENTITY = 0x17,
+ OSMO_GSM44068_IEI_USER_USER = 0x7E,
+ OSMO_GSM44068_IEI_CALL_STATE = 0xA0,
+ OSMO_GSM44068_IEI_STATE_ATTRIBUTES = 0xB0,
+ OSMO_GSM44068_IEI_TALKER_PRIORITY = 0xC0,
+ OSMO_GSM44068_IEI_SMS_INDICATIONS = 0xD0,
+};
+
+/* 9.3 Message Type */
+enum osmo_gsm44068_msg_type {
+ OSMO_GSM44068_MSGT_IMMEDIATE_SETUP = 0x31,
+ OSMO_GSM44068_MSGT_SETUP = 0x32,
+ OSMO_GSM44068_MSGT_CONNECT = 0x33,
+ OSMO_GSM44068_MSGT_TERMINATION = 0x34,
+ OSMO_GSM44068_MSGT_TERMINATION_REQUEST = 0x35,
+ OSMO_GSM44068_MSGT_TERMINATION_REJECT = 0x36,
+ OSMO_GSM44068_MSGT_STATUS = 0x38,
+ OSMO_GSM44068_MSGT_GET_STATUS = 0x39,
+ OSMO_GSM44068_MSGT_SET_PARAMETER = 0x3a,
+ OSMO_GSM44068_MSGT_IMMEDIATE_SETUP_2 = 0x3b,
+};
+
+/* Table 9.2 priority */
+enum osmo_gsm44068_priority_level {
+ OSMO_GSM44068_PRIO_LEVEL_4 = 0x1,
+ OSMO_GSM44068_PRIO_LEVEL_3 = 0x2,
+ OSMO_GSM44068_PRIO_LEVEL_2 = 0x3,
+ OSMO_GSM44068_PRIO_LEVEL_1 = 0x4,
+ OSMO_GSM44068_PRIO_LEVEL_0 = 0x5,
+ OSMO_GSM44068_PRIO_LEVEL_B = 0x6,
+ OSMO_GSM44068_PRIO_LEVEL_A = 0x7,
+};
+
+/* 9.4.2 Call State */
+enum osmo_gsm44068_call_state {
+ OSMO_GSM44068_CSTATE_U0 = 0x0,
+ OSMO_GSM44068_CSTATE_U1 = 0x1,
+ OSMO_GSM44068_CSTATE_U2sl_U2 = 0x2,
+ OSMO_GSM44068_CSTATE_U3 = 0x3,
+ OSMO_GSM44068_CSTATE_U4 = 0x4,
+ OSMO_GSM44068_CSTATE_U5 = 0x5,
+ OSMO_GSM44068_CSTATE_U0p = 0x6,
+ OSMO_GSM44068_CSTATE_U2wr_U6 = 0x7,
+ OSMO_GSM44068_CSTATE_U2r = 0x8,
+ OSMO_GSM44068_CSTATE_U2ws = 0x9,
+ OSMO_GSM44068_CSTATE_U2sr = 0xa,
+ OSMO_GSM44068_CSTATE_U2nc = 0xb,
+};
+
+/* 9.4.3 Cause */
+enum osmo_gsm44068_cause {
+ OSMO_GSM44068_CAUSE_ILLEGAL_MS = 0x03,
+ OSMO_GSM44068_CAUSE_IMEI_NOT_ACCEPTED = 0x05,
+ OSMO_GSM44068_CAUSE_ILLEGAL_ME = 0x06,
+ OSMO_GSM44068_CAUSE_SERVICE_NOT_AUTHORIZED = 0x08,
+ OSMO_GSM44068_CAUSE_APP_NOT_SUPPORTED_ON_PROTO = 0x09,
+ OSMO_GSM44068_CAUSE_RR_CONNECTION_ABORTED = 0x0a,
+ OSMO_GSM44068_CAUSE_NORMAL_CALL_CLEARING = 0x10,
+ OSMO_GSM44068_CAUSE_NETWORK_FAILURE = 0x11,
+ OSMO_GSM44068_CAUSE_BUSY = 0x14,
+ OSMO_GSM44068_CAUSE_CONGESTION = 0x16,
+ OSMO_GSM44068_CAUSE_USER_NOT_ORIGINATOR = 0x17,
+ OSMO_GSM44068_CAUSE_NET_WANTS_TO_MAINTAIN_CALL = 0x18,
+ OSMO_GSM44068_CAUSE_RESPONSE_TO_GET_STATUS = 0x1e,
+ OSMO_GSM44068_CAUSE_SERVICE_OPTION_NOT_SUBSCR = 0x20,
+ OSMO_GSM44068_CAUSE_REQUESTED_SERVICE_NOT_SUB = 0x21,
+ OSMO_GSM44068_CAUSE_SERVICE_OPTION_OOO = 0x22,
+ OSMO_GSM44068_CAUSE_CALL_CANNOT_BE_IDENTIFIED = 0x26,
+ OSMO_GSM44068_CAUSE_RETRY_UPON_ENTRY_NEW_CALL = 0x30, /* up to 0x3f */
+ OSMO_GSM44068_CAUSE_INVALID_TRANSACTION_ID = 0x51,
+ OSMO_GSM44068_CAUSE_SEMANTICALLY_INCORRECT_MSG = 0x5f,
+ OSMO_GSM44068_CAUSE_INVALID_MANDATORY_INFO = 0x60,
+ OSMO_GSM44068_CAUSE_MESSAGE_TYPE_NON_EXISTENT = 0x61,
+ OSMO_GSM44068_CAUSE_MESSAGE_TYPE_NOT_COMPAT = 0x62,
+ OSMO_GSM44068_CAUSE_IE_NON_EXISTENT = 0x63,
+ OSMO_GSM44068_CAUSE_IE_NOT_COMPAT = 0x64,
+ OSMO_GSM44068_CAUSE_PROTOCOL_ERROR = 0x70,
+};
+
+/* 9.4.4 Originator Indication */
+#define OSMO_GSM44068_OI_MS_IS_ORIGINATOR 0x01
+
+/* 9.4.7 State Attributes */
+#define OSMO_GSM44068_DA_DOWNLINK_ATTACHED 0x08
+#define OSMO_GSM44068_UA_UPLINK_ATTACHED 0x04
+#define OSMO_GSM44068_COMM_T 0x02
+
+/* 9.4.9 Talker Priority */
+enum osmo_gsm44068_talker_priority {
+ OSMO_GSM44068_PRIO_NORMAL = 0x0,
+ OSMO_GSM44068_PRIO_PRIVILEGED = 0x1,
+ OSMO_GSM44068_PRIO_EMERGENCY = 0x2,
+};
+
+/* 9.4.10 SMS Indications */
+#define OSMO_GSM44068_DC_DATA_CONFIDENTALLY_RQD 0x02
+#define OSMO_GSM44068_GP_GUARANTEED_PRIVACY_RQD 0x01
+
+extern const struct value_string osmo_gsm44068_msg_type_names[];
+extern const struct value_string osmo_gsm44068_priority_level_names[];
+extern const struct value_string osmo_gsm44068_cause_names[];
+extern const struct value_string osmo_gsm44068_call_state_names[];
+extern const struct value_string osmo_gsm44068_talker_priority_names[];
+
+extern const struct tlv_definition osmo_gsm44068_att_tlvdef;
diff --git a/include/osmocom/gsm/protocol/gsm_44_318.h b/include/osmocom/gsm/protocol/gsm_44_318.h
index b3942be2..f31a80ae 100644
--- a/include/osmocom/gsm/protocol/gsm_44_318.h
+++ b/include/osmocom/gsm/protocol/gsm_44_318.h
@@ -162,7 +162,7 @@ struct gan_rc_csr_hdr {
uint8_t msg_type;
uint8_t data[0];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint16_t len;
uint8_t skip_ind:4, pdisc:4;
uint8_t msg_type;
@@ -190,7 +190,7 @@ struct gan_cch_desc_ie {
spare2:2;
uint8_t access_class[2];
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t mscr:1, att:1, dtm:1, gprs:1, nmo:2, ecmc:1, spare:1;
uint8_t t3212;
uint8_t rac;
diff --git a/include/osmocom/gsm/protocol/gsm_48_071.h b/include/osmocom/gsm/protocol/gsm_48_071.h
new file mode 100644
index 00000000..961211b3
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_48_071.h
@@ -0,0 +1,118 @@
+/*! \defgroup bsslap 3GPP TS 48.071 BSS LCS Assistance Protocol (BSSLAP).
+ * @{
+ * \file gsm_48_071.h
+ */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#pragma once
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+enum bsslap_msgt {
+ BSSLAP_MSGT_TA_REQUEST = 0x1,
+ BSSLAP_MSGT_TA_RESPONSE = 0x2,
+ BSSLAP_MSGT_REJECT = 0xa,
+ BSSLAP_MSGT_RESET = 0xb,
+ BSSLAP_MSGT_ABORT = 0xc,
+ BSSLAP_MSGT_TA_LAYER3 = 0xd,
+ BSSLAP_MSGT_MS_POS_CMD = 0xf,
+ BSSLAP_MSGT_MS_POS_RESP = 0x10,
+ BSSLAP_MSGT_UTDOA_REQ = 0x11,
+ BSSLAP_MSGT_UTDOA_RESP = 0x12,
+};
+
+enum bsslap_cause {
+ BSSLAP_CAUSE_CONGESTION = 0x0,
+ BSSLAP_CAUSE_CHAN_MODE_NOT_SUPP = 0x1,
+ BSSLAP_CAUSE_POS_PROC_NOT_SUPP = 0x2,
+ BSSLAP_CAUSE_OTHER_RADIO_EVT_FAIL = 0x3,
+ BSSLAP_CAUSE_INTRA_BSS_HO = 0x4,
+ BSSLAP_CAUSE_SUPERV_TIMER_EXPIRED = 0x5,
+ BSSLAP_CAUSE_INTER_BSS_HO = 0x6,
+ BSSLAP_CAUSE_LOSS_SIG_CONN_MS = 0x7,
+ BSSLAP_CAUSE_INCORR_SERV_CELL_ID = 0x8,
+ BSSLAP_CAUSE_BSSAP_LE_SEGMENT_ERR = 0x9,
+ BSSLAP_CAUSE_CONCUR_POS_PROC_NOT_EN = 0xa,
+};
+
+enum bsslap_iei {
+ BSSLAP_IEI_TA = 0x1,
+ BSSLAP_IEI_CELL_ID = 0x9,
+ BSSLAP_IEI_CHAN_DESC = 0x10,
+ BSSLAP_IEI_MEAS_REP = 0x14,
+ BSSLAP_IEI_CAUSE = 0x18,
+ BSSLAP_IEI_RRLP_FLAG = 0x19,
+ BSSLAP_IEI_RRLP = 0x1b,
+ BSSLAP_IEI_CELL_ID_LIST = 0x1c,
+ BSSLAP_IEI_ENH_MEAS_REP = 0x1d,
+ BSSLAP_IEI_LAC = 0x1e,
+ BSSLAP_IEI_FREQ_LIST = 0x21,
+ BSSLAP_IEI_MS_POWER = 0x22,
+ BSSLAP_IEI_DELTA_TIMER = 0x23,
+ BSSLAP_IEI_SERVING_CELL_ID = 0x24,
+ BSSLAP_IEI_ENCR_KEY = 0x25,
+ BSSLAP_IEI_CIPH_MODE_SET = 0x26,
+ BSSLAP_IEI_CHAN_MODE = 0x27,
+ BSSLAP_IEI_MR_CONFIG = 0x28,
+ BSSLAP_IEI_POLLING_REPETITION = 0x29,
+ BSSLAP_IEI_PACKET_CHAN_DESC = 0x2a,
+ BSSLAP_IEI_TLLI = 0x2b,
+ BSSLAP_IEI_TFI = 0x2c,
+ BSSLAP_IEI_TBF_START_TIME = 0x2d,
+ BSSLAP_IEI_PWRUP_START_TIME = 0x2e,
+ BSSLAP_IEI_LONG_ENCR_KEY = 0x2f,
+ BSSLAP_IEI_CONCUR_POS_PROC_F = 0x30,
+};
+
+struct bsslap_ta_response {
+ uint16_t cell_id;
+ uint8_t ta;
+
+ bool more_items; /*!< always set this to false */
+};
+
+struct bsslap_ta_layer3 {
+ uint8_t ta;
+
+ bool more_items; /*!< always set this to false */
+};
+
+struct bsslap_reset {
+ uint16_t cell_id;
+ uint8_t ta;
+ struct gsm48_chan_desc chan_desc;
+ enum bsslap_cause cause;
+
+ bool more_items; /*!< always set this to false */
+};
+
+struct bsslap_pdu {
+ enum bsslap_msgt msg_type;
+ union {
+ /* ta_request: a TA Request message consists only of the message type. */
+ struct bsslap_ta_response ta_response;
+ enum bsslap_cause reject;
+ struct bsslap_reset reset;
+ enum bsslap_cause abort;
+ struct bsslap_ta_layer3 ta_layer3;
+ };
+};
+
+/*! @} */
diff --git a/include/osmocom/gsm/protocol/gsm_49_031.h b/include/osmocom/gsm/protocol/gsm_49_031.h
new file mode 100644
index 00000000..463fabf5
--- /dev/null
+++ b/include/osmocom/gsm/protocol/gsm_49_031.h
@@ -0,0 +1,234 @@
+/*! \defgroup bssmap_le 3GPP TS 49.031 BSSMAP-LE.
+ * @{
+ * \file gsm_49_031.h
+ */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/endian.h>
+#include <osmocom/gsm/protocol/gsm_48_071.h>
+#include <osmocom/gsm/protocol/gsm_23_032.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+#include <osmocom/gsm/gsm48.h>
+
+/*! 3GPP TS 49.031 10.13 LCS Cause, also in 3GPP TS 48.008 3.2.2.66, which simply refers to the former. */
+enum lcs_cause {
+ LCS_CAUSE_UNSPECIFIED = 0,
+ LCS_CAUSE_SYSTEM_FAILURE = 1,
+ LCS_CAUSE_PROTOCOL_ERROR = 2,
+ LCS_CAUSE_DATA_MISSING_IN_REQ = 3,
+ LCS_CAUSE_UNEXP_DATA_IN_REQ = 4,
+ LCS_CAUSE_POS_METH_FAILURE = 5,
+ LCS_CAUSE_TGT_MS_UNREACHABLE = 6,
+ LCS_CAUSE_REQUEST_ABORTED = 7,
+ LCS_CAUSE_FACILITY_NOTSUPP = 8,
+ LCS_CAUSE_INTER_BSC_HO = 9,
+ LCS_CAUSE_INTRA_BSC_HO = 10,
+ LCS_CAUSE_CONGESTION = 11,
+ LCS_CAUSE_INTER_NSE_CHG = 12,
+ LCS_CAUSE_RA_UPDAT = 13,
+ LCS_CAUSE_PTMSI_REALLOC = 14,
+ LCS_CAUSE_GPRS_SUSPENSION = 15,
+};
+
+/*! 3GPP TS 49.031 10.13 LCS Cause, also in 3GPP TS 48.008 3.2.2.66, which simply refers to the former. */
+struct lcs_cause_ie {
+ bool present;
+ enum lcs_cause cause_val;
+ bool diag_val_present;
+ uint8_t diag_val;
+};
+
+/* 3GPP TS 49.031 10.16 LCS QoS IE */
+struct osmo_bssmap_le_lcs_qos {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t vert:1, vel:1, spare1:6;
+ uint8_t ha_val:7, ha_ind:1;
+ uint8_t va_val:7, va_ind:1;
+ uint8_t spare3:6, rt:2;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t spare1:6, vel:1, vert:1;
+ uint8_t ha_ind:1, ha_val:7;
+ uint8_t va_ind:1, va_val:7;
+ uint8_t rt:2, spare3:6;
+#endif
+} __attribute__ ((packed));
+
+enum bssap_le_msg_discr {
+ BSSAP_LE_MSG_DISCR_BSSMAP_LE = 0,
+};
+
+enum bssmap_le_msgt {
+ BSSMAP_LE_MSGT_PERFORM_LOC_REQ = 0x2b,
+ BSSMAP_LE_MSGT_PERFORM_LOC_RESP = 0x2d,
+ BSSMAP_LE_MSGT_PERFORM_LOC_ABORT = 0x2e,
+ BSSMAP_LE_MSGT_PERFORM_LOC_INFO = 0x2f,
+ BSSMAP_LE_MSGT_ASSIST_INFO_REQ = 0x20,
+ BSSMAP_LE_MSGT_ASSIST_INFO_RESP = 0x21,
+ BSSMAP_LE_MSGT_CONN_ORIENTED_INFO = 0x2a,
+ BSSMAP_LE_MSGT_CONN_LESS_INFO = 0x3a,
+ BSSMAP_LE_MSGT_RESET = 0x30,
+ BSSMAP_LE_MSGT_RESET_ACK = 0x31,
+};
+
+enum bssmap_le_iei {
+ BSSMAP_LE_IEI_LCS_QoS = 0x3e,
+ BSSMAP_LE_IEI_LCS_PRIORITY = 0x43,
+ BSSMAP_LE_IEI_LOCATION_TYPE = 0x44,
+ BSSMAP_LE_IEI_GANSS_LOCATION_TYPE = 0x82,
+ BSSMAP_LE_IEI_GEO_LOCATION = 0x45,
+ BSSMAP_LE_IEI_POSITIONING_DATA = 0x46,
+ BSSMAP_LE_IEI_GANSS_POS_DATA = 0x83,
+ BSSMAP_LE_IEI_VELOCITY_DATA = 0x55,
+ BSSMAP_LE_IEI_LCS_CAUSE = 0x47,
+ BSSMAP_LE_IEI_LCS_CLIENT_TYPE = 0x48,
+ BSSMAP_LE_IEI_APDU = 0x49,
+ BSSMAP_LE_IEI_NET_ELEM_ID = 0x4a,
+ BSSMAP_LE_IEI_REQ_GPS_ASS_D = 0x4b,
+ BSSMAP_LE_IEI_REQ_GANSS_ASS_D = 0x41,
+ BSSMAP_LE_IEI_DECIPH_KEYS = 0x4c,
+ BSSMAP_LE_IEI_RET_ERR_REQ = 0x4d,
+ BSSMAP_LE_IEI_RET_ERR_CAUSE = 0x4e,
+ BSSMAP_LE_IEI_SEGMENTATION = 0x4f,
+ BSSMAP_LE_IEI_CLASSMARK3_INFO = 0x13,
+ BSSMAP_LE_IEI_CAUSE = 0x4,
+ BSSMAP_LE_IEI_CELL_ID = 0x5,
+ BSSMAP_LE_IEI_CHOSEN_CHAN = 0x21,
+ BSSMAP_LE_IEI_IMSI = 0x0,
+ BSSMAP_LE_IEI_LCS_CAPABILITY = 0x50,
+ BSSMAP_LE_IEI_PKT_MEAS_REP = 0x51,
+ BSSMAP_LE_IEI_CELL_ID_LIST = 0x52,
+ BSSMAP_LE_IEI_IMEI = 0x80,
+ BSSMAP_LE_IEI_BSS_MLAT_CAP = 0x84,
+ BSSMAP_LE_IEI_CELL_INFO_LIST = 0x85,
+ BSSMAP_LE_IEI_BTS_RX_ACC_LVL = 0x86,
+ BSSMAP_LE_IEI_MLAT_METHOD = 0x87,
+ BSSMAP_LE_IEI_MLAT_TA = 0x88,
+ BSSMAP_LE_IEI_MS_SYNC_ACC = 0x89,
+ BSSMAP_LE_IEI_SHORT_ID_SET = 0x8a,
+ BSSMAP_LE_IEI_RANDOM_ID_SET = 0x8b,
+ BSSMAP_LE_IEI_SHORT_BSS_ID = 0x8c,
+ BSSMAP_LE_IEI_RANDOM_ID = 0x8d,
+ BSSMAP_LE_IEI_SHORT_ID = 0x8e,
+ BSSMAP_LE_IEI_COVERAGE_CLASS = 0x8f,
+ BSSMAP_LE_IEI_MTA_ACC_SEC_RQD = 0x90,
+};
+
+enum bssmap_le_apdu_proto {
+ BSSMAP_LE_APDU_PROT_RESERVED = 0,
+ BSSMAP_LE_APDU_PROT_BSSLAP = 1,
+ BSSMAP_LE_APDU_PROT_LLP = 2,
+ BSSMAP_LE_APDU_PROT_SMLCPP = 3,
+};
+
+enum bssmap_le_location_information {
+ BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC = 0x0,
+ BSSMAP_LE_LOC_INFO_ASSIST_TARGET_MS = 0x1,
+ BSSMAP_LE_LOC_INFO_BC_DECIPHER_KEYS = 0x2,
+};
+
+enum bssmap_le_positioning_method {
+ BSSMAP_LE_POS_METHOD_OMITTED = 0x0,
+ BSSMAP_LE_POS_METHOD_MOBILE_ASSISTED_E_OTD = 0x1,
+ BSSMAP_LE_POS_METHOD_MOBILE_BASED_E_OTD = 0x2,
+ BSSMAP_LE_POS_METHOD_ASSISTED_GPS = 0x3,
+};
+
+struct bssmap_le_location_type {
+ enum bssmap_le_location_information location_information;
+ enum bssmap_le_positioning_method positioning_method;
+};
+
+enum bssmap_le_lcs_client_type {
+ BSSMAP_LE_LCS_CTYPE_VALUE_ADDED_UNSPECIFIED = 0x0,
+ BSSMAP_LE_LCS_CTYPE_PLMN_OPER_UNSPECIFIED = 0x20,
+ BSSMAP_LE_LCS_CTYPE_PLMN_OPER_BCAST_SERVICE = 0x21,
+ BSSMAP_LE_LCS_CTYPE_PLMN_OPER_OAM = 0x22,
+ BSSMAP_LE_LCS_CTYPE_PLMN_OPER_ANON_STATS = 0x23,
+ BSSMAP_LE_LCS_CTYPE_PLMN_OPER_TGT_MS_SVC = 0x24,
+ BSSMAP_LE_LCS_CTYPE_EMERG_SVC_UNSPECIFIED = 0x30,
+ BSSMAP_LE_LCS_CTYPE_LI_UNSPECIFIED = 0x40,
+};
+
+struct bssmap_le_perform_loc_req {
+ struct bssmap_le_location_type location_type;
+ struct gsm0808_cell_id cell_id;
+
+ bool lcs_client_type_present;
+ enum bssmap_le_lcs_client_type lcs_client_type;
+
+ struct osmo_mobile_identity imsi;
+ struct osmo_mobile_identity imei;
+
+ bool apdu_present;
+ struct bsslap_pdu apdu;
+
+ bool more_items; /*!< set this to true iff any fields below are used */
+
+ bool lcs_priority_present;
+ uint8_t lcs_priority; /*!< see in 3GPP TS 29.002 */
+
+ bool lcs_qos_present;
+ struct osmo_bssmap_le_lcs_qos lcs_qos;
+
+ bool more_items2; /*!< always set this to false */
+};
+
+struct bssmap_le_perform_loc_resp {
+ bool location_estimate_present;
+ union gad_raw location_estimate;
+
+ struct lcs_cause_ie lcs_cause;
+
+ bool more_items; /*!< always set this to false */
+};
+
+struct bssmap_le_conn_oriented_info {
+ struct bsslap_pdu apdu;
+
+ bool more_items; /*!< always set this to false */
+};
+
+struct bssmap_le_pdu {
+ enum bssmap_le_msgt msg_type;
+ union {
+ enum gsm0808_cause reset;
+ /* reset_ack consists only of the message type */
+ struct bssmap_le_perform_loc_req perform_loc_req;
+ struct bssmap_le_perform_loc_resp perform_loc_resp;
+ struct lcs_cause_ie perform_loc_abort;
+ struct bssmap_le_conn_oriented_info conn_oriented_info;
+ };
+};
+
+struct bssap_le_pdu {
+ enum bssap_le_msg_discr discr;
+ union {
+ struct bssmap_le_pdu bssmap_le;
+ /* future: add DTAP PDU, currently not implemented */
+ };
+};
+
+/*! @} */
diff --git a/include/osmocom/gsm/protocol/ipaccess.h b/include/osmocom/gsm/protocol/ipaccess.h
index 80413d10..51827607 100644
--- a/include/osmocom/gsm/protocol/ipaccess.h
+++ b/include/osmocom/gsm/protocol/ipaccess.h
@@ -39,29 +39,39 @@ enum ipaccess_proto_ext {
IPAC_PROTO_EXT_GSUP = 0x05, /* GSUP GPRS extension */
IPAC_PROTO_EXT_OAP = 0x06, /* Osmocom Authn Protocol */
IPAC_PROTO_EXT_RSPRO = 0x07, /* Remote SIM protocol */
+ IPAC_PROTO_EXT_PCU = 0x08, /* BSC<->BTS<->PCU communication */
};
enum ipaccess_msgtype {
- IPAC_MSGT_PING = 0x00,
- IPAC_MSGT_PONG = 0x01,
- IPAC_MSGT_ID_GET = 0x04,
- IPAC_MSGT_ID_RESP = 0x05,
- IPAC_MSGT_ID_ACK = 0x06,
+ IPAC_MSGT_PING = 0x00, /* Heartbeet */
+ IPAC_MSGT_PONG = 0x01, /* Heartbeat Ack */
+ IPAC_MSGT_ID_GET = 0x04, /* Identity Request */
+ IPAC_MSGT_ID_RESP = 0x05, /* Identity */
+ IPAC_MSGT_ID_ACK = 0x06, /* Identity Ack */
+ IPAC_MSGT_ID_NACK = 0x07, /* Identity Nack */
+ IPAC_MSGT_PROXY = 0x08, /* Proxy */
+ IPAC_MSGT_PROXY_ACK = 0x09, /* Proxy Ack */
+ IPAC_MSGT_PROXY_NACK = 0x0a, /* Proxy Nack */
+ IPAC_MSGT_SSL_INFO = 0x0b, /* SSL Info */
/* OpenBSC extension */
IPAC_MSGT_SCCP_OLD = 0xff,
};
enum ipaccess_id_tags {
- IPAC_IDTAG_SERNR = 0x00,
- IPAC_IDTAG_UNITNAME = 0x01,
- IPAC_IDTAG_LOCATION1 = 0x02,
- IPAC_IDTAG_LOCATION2 = 0x03,
- IPAC_IDTAG_EQUIPVERS = 0x04,
- IPAC_IDTAG_SWVERSION = 0x05,
- IPAC_IDTAG_IPADDR = 0x06,
- IPAC_IDTAG_MACADDR = 0x07,
- IPAC_IDTAG_UNIT = 0x08,
+ IPAC_IDTAG_SERNR = 0x00, /* Unit Serial Number */
+ IPAC_IDTAG_UNITNAME = 0x01, /* Unit Name */
+ IPAC_IDTAG_LOCATION1 = 0x02, /* Unit Location */
+ IPAC_IDTAG_LOCATION2 = 0x03, /* Unit Type */
+ IPAC_IDTAG_EQUIPVERS = 0x04, /* Hardware Version */
+ IPAC_IDTAG_SWVERSION = 0x05, /* Software Version */
+ IPAC_IDTAG_IPADDR = 0x06, /* IP Address */
+ IPAC_IDTAG_MACADDR = 0x07, /* Ethernet Address */
+ IPAC_IDTAG_UNIT = 0x08, /* Unit ID */
+ IPAC_IDTAG_USERNAME = 0x09, /* User Name */
+ IPAC_IDTAG_PASSWORD = 0x0a, /* Password */
+ IPAC_IDTAG_ACCESS_CLASS = 0x0b, /* Access Class */
+ IPAC_IDTG_APP_PROTO_VER = 0x0c, /* Application Protocol Version */
};
/*
diff --git a/include/osmocom/gsm/rlp.h b/include/osmocom/gsm/rlp.h
new file mode 100644
index 00000000..47b0a6c2
--- /dev/null
+++ b/include/osmocom/gsm/rlp.h
@@ -0,0 +1,81 @@
+/*
+ * GSM RLP (Radio Link Protocol) as used in CSD (3GPP TS 44.022)
+ *
+ * Copyright (C) 2022-2023 Harald Welte <laforge@osmocom.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.
+ */
+
+
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include <osmocom/core/utils.h>
+
+/*! \defgroup rlp GSM RLP (Radio Link Protocol) as used in CSD (3GPP TS 24.022)
+ * @{
+ * \file rlp.h */
+
+/*! RLP frame type as per 3GPP TS 24.022 Section 5.2.1 */
+enum osmo_rlp_ftype {
+ OSMO_RLP_FT_U,
+ OSMO_RLP_FT_S,
+ OSMO_RLP_FT_IS,
+};
+extern const struct value_string osmo_rlp_ftype_vals[];
+
+/*! RLP U-Frame Type as per 3GPP TS 24.022 Section 5.2.1 */
+enum osmo_rlp_u_ftype {
+ OSMO_RLP_U_FT_SABM = 0x07,
+ OSMO_RLP_U_FT_UA = 0x0c,
+ OSMO_RLP_U_FT_DISC = 0x08,
+ OSMO_RLP_U_FT_DM = 0x03,
+ OSMO_RLP_U_FT_NULL = 0x0f,
+ OSMO_RLP_U_FT_UI = 0x00,
+ OSMO_RLP_U_FT_XID = 0x17,
+ OSMO_RLP_U_FT_TEST = 0x1c,
+ OSMO_RLP_U_FT_REMAP = 0x11,
+};
+extern const struct value_string osmo_rlp_ftype_u_vals[];
+
+/*! RLP S-Frame type as per 3GPP TS 24.022 Section 5.2.1 */
+enum osmo_rlp_s_ftype {
+ OSMO_RLP_S_FT_RR = 0,
+ OSMO_RLP_S_FT_REJ = 2,
+ OSMO_RLP_S_FT_RNR = 1,
+ OSMO_RLP_S_FT_SREJ = 3,
+};
+extern const struct value_string osmo_rlp_ftype_s_vals[];
+
+/*! Data structure representing one decoded RLP frame */
+struct osmo_rlp_frame_decoded {
+ uint8_t version;
+ enum osmo_rlp_ftype ftype;
+ enum osmo_rlp_u_ftype u_ftype;
+ enum osmo_rlp_s_ftype s_ftype;
+ bool c_r;
+ bool p_f;
+ uint8_t s_bits;
+ uint16_t n_s;
+ uint16_t n_r;
+ uint32_t fcs;
+ uint8_t info[536/8];
+ uint16_t info_len;
+};
+
+int osmo_rlp_decode(struct osmo_rlp_frame_decoded *out, uint8_t version, const uint8_t *data, size_t data_len);
+int osmo_rlp_encode(uint8_t *out, size_t out_size, const struct osmo_rlp_frame_decoded *in);
+uint32_t osmo_rlp_fcs_compute(const uint8_t *in, size_t in_len);
+
+/*! @} */
diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h
index bb0e8fc9..28e897d7 100644
--- a/include/osmocom/gsm/tlv.h
+++ b/include/osmocom/gsm/tlv.h
@@ -4,6 +4,7 @@
#include <string.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/byteswap.h>
#include <osmocom/core/bit16gen.h>
#include <osmocom/core/bit32gen.h>
@@ -40,6 +41,16 @@
/*! maximum length of TLV of one byte length */
#define TVLV_MAX_ONEBYTE 0x7f
+/*! error return codes of various TLV parser functions */
+enum osmo_tlv_parser_error {
+ OSMO_TLVP_ERR_OFS_BEYOND_BUFFER = -1,
+ OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER = -2,
+ OSMO_TLVP_ERR_UNKNOWN_TLV_TYPE = -3,
+
+ OSMO_TLVP_ERR_MAND_IE_MISSING = -50,
+ OSMO_TLVP_ERR_IE_TOO_SHORT = -51,
+};
+
/*! gross length of a TVLV type field */
static inline uint16_t TVLV_GROSS_LEN(uint16_t len)
{
@@ -111,6 +122,14 @@ static inline uint8_t *tlv_put(uint8_t *buf, uint8_t tag, uint8_t len,
return buf + len;
}
+/*! put (append) a TL field (a TLV field but omitting the value part). */
+static inline uint8_t *tl_put(uint8_t *buf, uint8_t tag, uint8_t len)
+{
+ *buf++ = tag;
+ *buf++ = len;
+ return buf;
+}
+
/*! put (append) a TLV16 field */
static inline uint8_t *tlv16_put(uint8_t *buf, uint8_t tag, uint8_t len,
const uint16_t *val)
@@ -129,7 +148,16 @@ static inline uint8_t *tl16v_put(uint8_t *buf, uint8_t tag, uint16_t len,
*buf++ = len >> 8;
*buf++ = len & 0xff;
memcpy(buf, val, len);
- return buf + len*2;
+ return buf + len;
+}
+
+/*! put (append) a TL16 field. */
+static inline uint8_t *tl16_put(uint8_t *buf, uint8_t tag, uint16_t len)
+{
+ *buf++ = tag;
+ *buf++ = len >> 8;
+ *buf++ = len & 0xff;
+ return buf;
}
/*! put (append) a TL16V field */
@@ -140,7 +168,7 @@ static inline uint8_t *t16lv_put(uint8_t *buf, uint16_t tag, uint8_t len,
*buf++ = tag & 0xff;
*buf++ = len;
memcpy(buf, val, len);
- return buf + len + 2;
+ return buf + len;
}
/*! put (append) a TvLV field */
@@ -158,6 +186,23 @@ static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len,
return ret;
}
+/*! put (append) a TvL field (a TvLV with variable-size length, where the value part's length is already known, but will
+ * be put() later).
+ * \returns pointer to the value's start position.
+ */
+static inline uint8_t *tvl_put(uint8_t *buf, uint8_t tag, uint16_t len)
+{
+ uint8_t *ret;
+
+ if (len <= TVLV_MAX_ONEBYTE) {
+ ret = tl_put(buf, tag, len);
+ buf[1] |= 0x80;
+ } else
+ ret = tl16_put(buf, tag, len);
+
+ return ret;
+}
+
/*! put (append) a variable-length tag or variable-length length * */
static inline uint8_t *vt_gan_put(uint8_t *buf, uint16_t tag)
{
@@ -215,6 +260,17 @@ static inline uint8_t *msgb_t16lv_put(struct msgb *msg, uint16_t tag, uint8_t le
return t16lv_put(buf, tag, len, val);
}
+/*! put (append) a TvL field to \ref msgb, i.e. a TvLV with variable-size length, where the value's length is already
+ * known, but will be put() later. The value section is not yet reserved, only tag and variable-length are put in the
+ * msgb.
+ * \returns pointer to the value's start position and end of the msgb.
+ */
+static inline uint8_t *msgb_tvl_put(struct msgb *msg, uint8_t tag, uint16_t len)
+{
+ uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));
+ return tvl_put(buf, tag, len);
+}
+
/*! put (append) a TvLV field to \ref msgb */
static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len,
const uint8_t *val)
@@ -223,6 +279,20 @@ static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len
return tvlv_put(buf, tag, len, val);
}
+/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */
+static inline uint8_t *msgb_tvlv_put_16be(struct msgb *msg, uint8_t tag, uint16_t val)
+{
+ uint16_t val_be = osmo_htons(val);
+ return msgb_tvlv_put(msg, tag, 2, (const uint8_t *)&val_be);
+}
+
+/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */
+static inline uint8_t *msgb_tvlv_put_32be(struct msgb *msg, uint8_t tag, uint32_t val)
+{
+ uint32_t val_be = osmo_htonl(val);
+ return msgb_tvlv_put(msg, tag, 4, (const uint8_t *)&val_be);
+}
+
/*! put (append) a vTvLV field to \ref msgb */
static inline uint8_t *msgb_vtvlv_gan_put(struct msgb *msg, uint16_t tag,
uint16_t len, const uint8_t *val)
@@ -252,7 +322,7 @@ static inline uint8_t *v_put(uint8_t *buf, uint8_t val)
}
/*! put (append) a TV field */
-static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag,
+static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag,
uint8_t val)
{
*buf++ = tag;
@@ -274,7 +344,7 @@ static inline uint8_t *tv_fixed_put(uint8_t *buf, uint8_t tag,
* \param[in] tag Tag value
* \param[in] val Value (in host byte order!)
*/
-static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag,
+static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag,
uint16_t val)
{
*buf++ = tag;
@@ -336,7 +406,7 @@ static inline uint8_t *msgb_tl_put(struct msgb *msg, uint8_t tag)
return len;
}
-/*! put (append) a TV16 field to a \ref msgb
+/*! put (append) a TV16 field (network order) to the given msgb
* \returns pointer to first byte after newly-put information */
static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val)
{
@@ -344,6 +414,16 @@ static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val
return tv16_put(buf, tag, val);
}
+/*! put (append) a TV32 field (network order) to the given msgb
+ * \returns pointer to first byte after newly-put information */
+static inline uint8_t *msgb_tv32_put(struct msgb *msg, uint8_t tag, uint32_t val)
+{
+ uint8_t *buf = msgb_put(msg, 1 + 4);
+ *buf++ = tag;
+ osmo_store32be(val, buf);
+ return msg->tail;
+}
+
/*! push (prepend) a TLV field to a \ref msgb
* \returns pointer to first byte of newly-pushed information */
static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)
@@ -377,6 +457,16 @@ static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t va
return buf;
}
+/*! push (prepend) a TV32 field to a \ref msgb
+ * \returns pointer to first byte of newly-pushed information */
+static inline uint8_t *msgb_tv32_push(struct msgb *msg, uint8_t tag, uint32_t val)
+{
+ uint8_t *buf = msgb_push(msg, 5);
+ *buf++ = tag;
+ osmo_store32be(val, buf);
+ return buf;
+}
+
/*! push (prepend) a TvLV field to a \ref msgb
* \returns pointer to first byte of newly-pushed information */
static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len,
@@ -463,7 +553,7 @@ int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct
int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,
const uint8_t *tag_order, unsigned int tag_order_len);
-#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
+#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
@@ -575,4 +665,54 @@ int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
int osmo_shift_lv(uint8_t **data, size_t *data_len,
uint8_t **value, size_t *value_len);
+#define MSG_DEF(name, mand_ies, flags) { name, mand_ies, ARRAY_SIZE(mand_ies), flags }
+
+struct osmo_tlv_prot_msg_def {
+ /*! human-readable name of message type (optional) */
+ const char *name;
+ /*! array of mandatory IEs */
+ const uint8_t *mand_ies;
+ /*! number of entries in 'mand_ies' above */
+ uint8_t mand_count;
+ /*! user-defined flags (like uplink/downlink/...) */
+ uint32_t flags;
+};
+struct osmo_tlv_prot_ie_def {
+ /*! minimum length of IE value part, in octets */
+ uint16_t min_len;
+ /*! huamn-readable name (optional) */
+ const char *name;
+};
+
+/*! Osmocom TLV protocol definition */
+struct osmo_tlv_prot_def {
+ /*! human-readable name of protocol */
+ const char *name;
+ /*! TLV parser definition (optional) */
+ const struct tlv_definition *tlv_def;
+ /*! definition of each message (8-bit message type) */
+ struct osmo_tlv_prot_msg_def msg_def[256];
+ /*! definition of IE for each 8-bit tag */
+ struct osmo_tlv_prot_ie_def ie_def[256];
+ /*! value_string array of message type names (legacy, if not populated in msg_def) */
+ const struct value_string *msgt_names;
+};
+
+const char *osmo_tlv_prot_msg_name(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type);
+const char *osmo_tlv_prot_ie_name(const struct osmo_tlv_prot_def *pdef, uint8_t iei);
+
+int osmo_tlv_prot_validate_tp(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type,
+ const struct tlv_parsed *tp, int log_subsys, const char *log_pfx);
+
+int osmo_tlv_prot_parse(const struct osmo_tlv_prot_def *pdef,
+ struct tlv_parsed *dec, unsigned int dec_multiples, uint8_t msg_type,
+ const uint8_t *buf, unsigned int buf_len, uint8_t lv_tag, uint8_t lv_tag2,
+ int log_subsys, const char *log_pfx);
+
+static inline uint32_t osmo_tlv_prot_msgt_flags(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type)
+{
+ return pdef->msg_def[msg_type].flags;
+}
+
+
/*! @} */
diff --git a/include/osmocom/isdn/Makefile.am b/include/osmocom/isdn/Makefile.am
new file mode 100644
index 00000000..fbc92d8b
--- /dev/null
+++ b/include/osmocom/isdn/Makefile.am
@@ -0,0 +1,8 @@
+osmoisdn_HEADERS = \
+ i460_mux.h \
+ lapd_core.h \
+ v110.h \
+ v110_ta.h \
+ $(NULL)
+
+osmoisdndir = $(includedir)/osmocom/isdn
diff --git a/include/osmocom/isdn/i460_mux.h b/include/osmocom/isdn/i460_mux.h
new file mode 100644
index 00000000..537e3257
--- /dev/null
+++ b/include/osmocom/isdn/i460_mux.h
@@ -0,0 +1,120 @@
+/*! \file i460_mux.h
+ * ITU-T I.460 sub-channel multiplexer + demultiplexer */
+/*
+ * (C) 2020 by Harald Welte <laforge@gnumonks.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+
+#define OSMO_I460_NUM_SUBCHAN 8
+
+/* I.460 sub-slot rate */
+enum osmo_i460_rate {
+ OSMO_I460_RATE_NONE, /* disabled */
+ OSMO_I460_RATE_64k,
+ OSMO_I460_RATE_32k,
+ OSMO_I460_RATE_16k,
+ OSMO_I460_RATE_8k,
+};
+
+struct osmo_i460_subchan;
+
+typedef void (*out_cb_bits_t)(struct osmo_i460_subchan *schan, void *user_data,
+ const ubit_t *bits, unsigned int num_bits);
+typedef void (*out_cb_bytes_t)(struct osmo_i460_subchan *schan, void *user_data,
+ const uint8_t *bytes, unsigned int num_bytes);
+
+struct osmo_i460_subchan_demux {
+ /*! bit-buffer for output bits */
+ uint8_t *out_bitbuf;
+ /*! size of out_bitbuf in bytes */
+ unsigned int out_bitbuf_size;
+ /*! offset of next bit to be written in out_bitbuf */
+ unsigned int out_idx;
+ /*! callback to be called once we have received out_bitbuf_size bits */
+ out_cb_bits_t out_cb_bits;
+ out_cb_bytes_t out_cb_bytes;
+ void *user_data;
+};
+
+typedef void (*in_cb_queue_empty_t)(struct osmo_i460_subchan *schan, void *user_data);
+
+struct osmo_i460_subchan_mux {
+ /*! list of to-be-transmitted message buffers */
+ struct llist_head tx_queue;
+ in_cb_queue_empty_t in_cb_queue_empty;
+ void *user_data;
+};
+
+struct osmo_i460_subchan {
+ struct osmo_i460_timeslot *ts; /* back-pointer */
+ enum osmo_i460_rate rate; /* 8/16/32/64k */
+ uint8_t bit_offset; /* bit offset inside each byte of the B-channel */
+ struct osmo_i460_subchan_demux demux;
+ struct osmo_i460_subchan_mux mux;
+};
+
+struct osmo_i460_timeslot {
+ struct osmo_i460_subchan schan[OSMO_I460_NUM_SUBCHAN];
+};
+
+/*! description of a sub-channel; passed by caller */
+struct osmo_i460_schan_desc {
+ enum osmo_i460_rate rate;
+ uint8_t bit_offset;
+ struct {
+ /* size (in bits) of the internal buffer; determines granularity */
+ size_t num_bits;
+ /*! call-back function called whenever we received num_bits */
+ out_cb_bits_t out_cb_bits;
+ /*! out_cb_bytes call-back function called whenever we received num_bits.
+ * The user is usually expected to provide either out_cb_bits or out_cb_bytes. If only
+ * out_cb_bits is provided, output data will always be provided as unpacked bits; if only
+ * out_cb_bytes is provided, output data will always be provided as packet bits (bytes). If
+ * both are provided, it is up to the I.460 multiplex to decide if it calls either of the two,
+ * depending on what can be provided without extra conversion. */
+ out_cb_bytes_t out_cb_bytes;
+ /* opaque user data pointer to pass to out_cb */
+ void *user_data;
+ } demux;
+
+ struct {
+ /* call-back function whenever the muxer requires more input data from the sub-channels,
+ * but has nothing enqueued yet. A typical function would then call osmo_i460_mux_enqueue() */
+ in_cb_queue_empty_t in_cb_queue_empty;
+ /* opaque user data pointer to pass to in_cb */
+ void *user_data;
+ } mux;
+};
+
+void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len);
+
+void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg);
+int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len);
+
+void osmo_i460_ts_init(struct osmo_i460_timeslot *ts);
+
+struct osmo_i460_subchan *
+osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd);
+
+void osmo_i460_subchan_del(struct osmo_i460_subchan *schan);
+
+int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts);
+
+/*! @} */
diff --git a/include/osmocom/isdn/lapd_core.h b/include/osmocom/isdn/lapd_core.h
new file mode 100644
index 00000000..1e010afe
--- /dev/null
+++ b/include/osmocom/isdn/lapd_core.h
@@ -0,0 +1,192 @@
+/*! \file lapd_core.h
+ * primitive related stuff
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/prim.h>
+
+/*! \defgroup lapd LAPD implementation common part
+ * @{
+ * \file lapd_core.h
+ */
+
+#define LOGDL(dl, level, fmt, args...) \
+ LOGP(DLLAPD, level, "(%s) " fmt, (dl)->name, ## args)
+
+/*! LAPD related primitives (L2<->L3 SAP)*/
+enum osmo_dl_prim {
+ PRIM_DL_UNIT_DATA, /*!< DL-UNIT-DATA */
+ PRIM_DL_DATA, /*!< DL-DATA */
+ PRIM_DL_EST, /*!< DL-ESTABLISH */
+ PRIM_DL_REL, /*!< DL-RLEEASE */
+ PRIM_DL_SUSP, /*!< DL-SUSPEND */
+ PRIM_DL_RES, /*!< DL-RESUME */
+ PRIM_DL_RECON, /*!< DL-RECONNECT */
+ PRIM_MDL_ERROR, /*!< MDL-ERROR */
+};
+
+/* Uses the same values as RLL, so no conversion for GSM is required. */
+#define MDL_CAUSE_T200_EXPIRED 0x01
+#define MDL_CAUSE_REEST_REQ 0x02
+#define MDL_CAUSE_UNSOL_UA_RESP 0x03
+#define MDL_CAUSE_UNSOL_DM_RESP 0x04
+#define MDL_CAUSE_UNSOL_DM_RESP_MF 0x05
+#define MDL_CAUSE_UNSOL_SPRV_RESP 0x06
+#define MDL_CAUSE_SEQ_ERR 0x07
+#define MDL_CAUSE_UFRM_INC_PARAM 0x08
+#define MDL_CAUSE_SFRM_INC_PARAM 0x09
+#define MDL_CAUSE_IFRM_INC_MBITS 0x0a
+#define MDL_CAUSE_IFRM_INC_LEN 0x0b
+#define MDL_CAUSE_FRM_UNIMPL 0x0c
+#define MDL_CAUSE_SABM_MF 0x0d
+#define MDL_CAUSE_SABM_INFO_NOTALL 0x0e
+#define MDL_CAUSE_FRMR 0x0f
+
+/*! for MDL-ERROR.ind */
+struct mdl_error_ind_param {
+ uint8_t cause; /*!< generic cause value */
+};
+
+/*! for DL-REL.req */
+struct dl_rel_req_param {
+ uint8_t mode; /*!< release mode */
+};
+
+/*! primitive header for LAPD DL-SAP primitives */
+struct osmo_dlsap_prim {
+ struct osmo_prim_hdr oph; /*!< generic primitive header */
+ union {
+ struct mdl_error_ind_param error_ind;
+ struct dl_rel_req_param rel_req;
+ } u; /*!< request-specific data */
+};
+
+/*! LAPD mode/role */
+enum lapd_mode {
+ LAPD_MODE_USER, /*!< behave like user */
+ LAPD_MODE_NETWORK, /*!< behave like network */
+};
+
+/*! LAPD state (Figure B.2/Q.921)*/
+enum lapd_state {
+ LAPD_STATE_NULL = 0,
+ LAPD_STATE_TEI_UNASS,
+ LAPD_STATE_ASS_TEI_WAIT,
+ LAPD_STATE_EST_TEI_WAIT,
+ LAPD_STATE_IDLE,
+ LAPD_STATE_SABM_SENT,
+ LAPD_STATE_DISC_SENT,
+ LAPD_STATE_MF_EST,
+ LAPD_STATE_TIMER_RECOV,
+};
+
+/*! lapd_flags */
+#define LAPD_F_RTS 0x0001
+#define LAPD_F_DROP_2ND_REJ 0x0002
+
+/*! LAPD T200 state in RTS mode */
+enum lapd_t200_rts {
+ LAPD_T200_RTS_OFF = 0,
+ LAPD_T200_RTS_PENDING,
+ LAPD_T200_RTS_RUNNING,
+};
+
+/*! LAPD message format (I / S / U) */
+enum lapd_format {
+ LAPD_FORM_UKN = 0,
+ LAPD_FORM_I,
+ LAPD_FORM_S,
+ LAPD_FORM_U,
+};
+
+/*! LAPD message context */
+struct lapd_msg_ctx {
+ struct lapd_datalink *dl;
+ int n201;
+ /* address */
+ uint8_t cr;
+ uint8_t sapi;
+ uint8_t tei;
+ uint8_t lpd;
+ /* control */
+ uint8_t format;
+ uint8_t p_f; /* poll / final bit */
+ uint8_t n_send;
+ uint8_t n_recv;
+ uint8_t s_u; /* S or repectivly U function bits */
+ /* length */
+ int length;
+ uint8_t more;
+};
+
+struct lapd_cr_ent {
+ uint8_t cmd;
+ uint8_t resp;
+};
+
+struct lapd_history {
+ struct msgb *msg; /* message to be sent / NULL, if histoy is empty */
+ int more; /* if message is fragmented */
+};
+
+/*! LAPD datalink */
+struct lapd_datalink {
+ int (*send_dlsap)(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
+ int (*send_ph_data_req)(struct lapd_msg_ctx *lctx, struct msgb *msg);
+ int (*update_pending_frames)(struct lapd_msg_ctx *lctx);
+ struct {
+ /*! filled-in once we set the lapd_mode above */
+ struct lapd_cr_ent loc2rem;
+ struct lapd_cr_ent rem2loc;
+ } cr;
+ enum lapd_mode mode; /*!< current mode of link */
+ unsigned int lapd_flags; /*!< \ref lapd_flags to change processing */
+ int use_sabme; /*!< use SABME instead of SABM */
+ int reestablish; /*!< enable reestablish support */
+ int n200, n200_est_rel; /*!< number of retranmissions */
+ struct lapd_msg_ctx lctx; /*!< LAPD context */
+ int maxf; /*!< maximum frame size (after defragmentation) */
+ uint8_t k; /*!< maximum number of unacknowledged frames */
+ uint8_t v_range; /*!< range of sequence numbers */
+ uint8_t v_send; /*!< seq nr of next I frame to be transmitted */
+ uint8_t v_ack; /*!< last frame ACKed by peer */
+ uint8_t v_recv; /*!< seq nr of next I frame expected to be received */
+ uint32_t state; /*!< LAPD state (\ref lapd_state) */
+ int seq_err_cond; /*!< condition of sequence error */
+ uint8_t own_busy; /*!< receiver busy on our side */
+ uint8_t peer_busy; /*!< receiver busy on remote side */
+ int t200_sec, t200_usec; /*!< retry timer (default 1 sec) */
+ int t203_sec, t203_usec; /*!< retry timer (default 10 secs) */
+ enum lapd_t200_rts t200_rts; /*!< state of T200 in RTS mode */
+ struct osmo_timer_list t200; /*!< T200 timer */
+ struct osmo_timer_list t203; /*!< T203 timer */
+ uint8_t retrans_ctr; /*!< re-transmission counter */
+ struct llist_head tx_queue; /*!< frames to L1 */
+ 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 */
+ struct lapd_history *tx_hist; /*!< tx history structure array */
+ uint8_t range_hist; /*!< range of history buffer 2..2^n */
+ struct msgb *rcv_buffer; /*!< buffer to assemble the received message */
+ struct msgb *cont_res; /*!< buffer to store content resolution data on network side, to detect multiple phones on same channel */
+ char *name; /*!< user-provided name */
+};
+
+void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf)
+ OSMO_DEPRECATED("Use lapd_dl_init2() instead");
+void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf, const char *name);
+void lapd_dl_set_name(struct lapd_datalink *dl, const char *name);
+void lapd_dl_exit(struct lapd_datalink *dl);
+void lapd_dl_reset(struct lapd_datalink *dl);
+int lapd_dl_set_flags(struct lapd_datalink *dl, unsigned int flags);
+int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode);
+int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx);
+int lapd_ph_rts_ind(struct lapd_msg_ctx *lctx);
+int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
+int lapd_t200_timeout(struct lapd_datalink *dl);
+
+/*! @} */
diff --git a/include/osmocom/isdn/v110.h b/include/osmocom/isdn/v110.h
new file mode 100644
index 00000000..9555932e
--- /dev/null
+++ b/include/osmocom/isdn/v110.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <osmocom/core/bits.h>
+
+/* See Section 5.1.2.1 of ITU-T V.110 */
+#define MAX_D_BITS 48
+#define MAX_E_BITS 7
+#define MAX_S_BITS 9
+#define MAX_X_BITS 2
+
+/*! a 'decoded' representation of a single V.110 frame. contains unpacket D, E, S and X bits */
+struct osmo_v110_decoded_frame {
+ ubit_t d_bits[MAX_D_BITS];
+ ubit_t e_bits[MAX_E_BITS];
+ ubit_t s_bits[MAX_S_BITS];
+ ubit_t x_bits[MAX_X_BITS];
+};
+
+int osmo_v110_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits);
+int osmo_v110_encode_frame(ubit_t *ra_bits, size_t n_bits, const struct osmo_v110_decoded_frame *fr);
+
+void osmo_v110_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len);
+
+
+/*! enum for each supported V.110 synchronous RA1 function (one for each user bitrate) */
+enum osmo_v100_sync_ra1_rate {
+ OSMO_V110_SYNC_RA1_600,
+ OSMO_V110_SYNC_RA1_1200,
+ OSMO_V110_SYNC_RA1_2400,
+ OSMO_V110_SYNC_RA1_4800,
+ OSMO_V110_SYNC_RA1_7200,
+ OSMO_V110_SYNC_RA1_9600,
+ OSMO_V110_SYNC_RA1_12000,
+ OSMO_V110_SYNC_RA1_14400,
+ OSMO_V110_SYNC_RA1_19200,
+ OSMO_V110_SYNC_RA1_24000,
+ OSMO_V110_SYNC_RA1_28800,
+ OSMO_V110_SYNC_RA1_38400,
+ _NUM_OSMO_V110_SYNC_RA1
+};
+
+extern const ubit_t osmo_v110_e1e2e3[_NUM_OSMO_V110_SYNC_RA1][3];
+
+#define osmo_v110_e1e2e3_set(e_bits, rate) \
+ memcpy(e_bits, osmo_v110_e1e2e3[rate], 3)
+#define osmo_v110_e1e2e3_cmp(e_bits, rate) \
+ memcmp(e_bits, osmo_v110_e1e2e3[rate], 3)
+
+int osmo_v110_sync_ra1_get_user_data_chunk_bitlen(enum osmo_v100_sync_ra1_rate rate);
+int osmo_v110_sync_ra1_get_user_data_rate(enum osmo_v100_sync_ra1_rate rate);
+int osmo_v110_sync_ra1_get_intermediate_rate(enum osmo_v100_sync_ra1_rate rate);
+
+int osmo_v110_sync_ra1_user_to_ir(enum osmo_v100_sync_ra1_rate rate, struct osmo_v110_decoded_frame *fr,
+ const ubit_t *d_in, size_t in_len);
+
+int osmo_v110_sync_ra1_ir_to_user(enum osmo_v100_sync_ra1_rate rate, ubit_t *d_out, size_t out_len,
+ const struct osmo_v110_decoded_frame *fr);
diff --git a/include/osmocom/isdn/v110_ta.h b/include/osmocom/isdn/v110_ta.h
new file mode 100644
index 00000000..b6bf7b52
--- /dev/null
+++ b/include/osmocom/isdn/v110_ta.h
@@ -0,0 +1,113 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+
+/* Definition of this struct is [intentionally] kept private */
+struct osmo_v110_ta;
+
+/*! V.110 5.4.1 Local flow control (DTE-DCE or TE-TA) mode */
+enum osmo_v110_local_flow_ctrl_mode {
+ OSMO_V110_LOCAL_FLOW_CTRL_NONE, /*!< No local flow control */
+ OSMO_V110_LOCAL_FLOW_CTRL_133_106, /*!< 5.4.1.1 133/106 operation */
+ OSMO_V110_LOCAL_FLOW_CTRL_105_106, /*!< 5.4.1.2 105/106 operation */
+ OSMO_V110_LOCAL_FLOW_CTRL_XON_XOFF, /*!< 5.4.1.3 XON/XOFF operation */
+};
+
+/*! Configuration for a V.110 TA instance */
+struct osmo_v110_ta_cfg {
+ /*! Configuration flags (behavior switches and quirks) */
+ unsigned int flags;
+ /*! Synchronous user rate */
+ enum osmo_v100_sync_ra1_rate rate;
+
+ /*! Flow control configuration */
+ struct {
+ /*! Local TA-TE (DTE-DCE) flow control mode */
+ enum osmo_v110_local_flow_ctrl_mode local;
+ /*! End-to-end (TA-to-TA) flow control state */
+ bool end_to_end;
+ } flow_ctrl;
+
+ /*! Opaque application-private data; passed to call-backs. */
+ void *priv;
+
+ /*! Receive call-back of the application.
+ * \param[in] priv opaque application-private data.
+ * \param[in] buf output buffer for writing to be transmitted data.
+ * \param[in] buf_size size of the output buffer. */
+ void (*rx_cb)(void *priv, const ubit_t *buf, size_t buf_size);
+
+ /*! Transmit call-back of the application.
+ * \param[in] priv opaque application-private data.
+ * \param[out] buf output buffer for writing to be transmitted data.
+ * \param[in] buf_size size of the output buffer. */
+ void (*tx_cb)(void *priv, ubit_t *buf, size_t buf_size);
+
+ /*! Modem status line update call-back (optional).
+ * \param[in] priv opaque application-private data.
+ * \param[in] status updated status; bit-mask of OSMO_V110_TA_C_*. */
+ void (*status_update_cb)(void *priv, unsigned int status);
+};
+
+struct osmo_v110_ta *osmo_v110_ta_alloc(void *ctx, const char *name,
+ const struct osmo_v110_ta_cfg *cfg);
+void osmo_v110_ta_free(struct osmo_v110_ta *ta);
+
+/*! Various timers for a V.110 TA instance */
+enum osmo_v110_ta_timer {
+ /*! 7.1.5 Loss of frame synchronization: sync recovery timer.
+ * T-number is not assigned in V.110, so we call it X1. */
+ OSMO_V110_TA_TIMER_X1 = -1,
+ /*! 7.1.2 Connect TA to line: sync establishment timer */
+ OSMO_V110_TA_TIMER_T1 = 1,
+ /*! 7.1.4 Disconnect mode: disconnect confirmation timer */
+ OSMO_V110_TA_TIMER_T2 = 2,
+};
+
+int osmo_v110_ta_set_timer_val_ms(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_timer timer,
+ unsigned long val_ms);
+
+int osmo_v110_ta_frame_in(struct osmo_v110_ta *ta, const struct osmo_v110_decoded_frame *in);
+int osmo_v110_ta_frame_out(struct osmo_v110_ta *ta, struct osmo_v110_decoded_frame *out);
+
+int osmo_v110_ta_sync_ind(struct osmo_v110_ta *ta);
+int osmo_v110_ta_desync_ind(struct osmo_v110_ta *ta);
+
+/*! ITU-T Table 9 "Interchange circuit" (see also ITU-T V.24 Chapter 3).
+ * XXX: Not all circuits are present here, only those which we actually use.
+ * TODO: add human-friendly abbreviated circuit names. */
+enum osmo_v110_ta_circuit {
+ OSMO_V110_TA_C_105, /*!< DTE->DCE | RTS (Request to Send) */
+ OSMO_V110_TA_C_106, /*!< DTE<-DCE | CTS (Clear to Send) */
+ OSMO_V110_TA_C_107, /*!< DTE<-DCE | DSR (Data Set Ready) */
+ OSMO_V110_TA_C_108, /*!< DTE->DCE | DTR (Data Terminal Ready) */
+ OSMO_V110_TA_C_109, /*!< DTE<-DCE | DCD (Data Carrier Detect) */
+ OSMO_V110_TA_C_133, /*!< DTE->DCE | Ready for receiving */
+};
+
+extern const struct value_string osmo_v110_ta_circuit_names[];
+extern const struct value_string osmo_v110_ta_circuit_descs[];
+
+/*! Get a short name of the given TA's circuit (format: NNN[/ABBR]). */
+static inline const char *osmo_v110_ta_circuit_name(enum osmo_v110_ta_circuit circuit)
+{
+ return get_value_string(osmo_v110_ta_circuit_names, circuit);
+}
+
+/*! Get a brief description of the given TA's circuit. */
+static inline const char *osmo_v110_ta_circuit_desc(enum osmo_v110_ta_circuit circuit)
+{
+ return get_value_string(osmo_v110_ta_circuit_descs, circuit);
+}
+
+unsigned int osmo_v110_ta_get_status(const struct osmo_v110_ta *ta);
+bool osmo_v110_ta_get_circuit(const struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit);
+int osmo_v110_ta_set_circuit(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit, bool active);
diff --git a/include/osmocom/sim/Makefile.am b/include/osmocom/sim/Makefile.am
new file mode 100644
index 00000000..bcc9459f
--- /dev/null
+++ b/include/osmocom/sim/Makefile.am
@@ -0,0 +1,6 @@
+osmosim_HEADERS = \
+ class_tables.h \
+ sim.h
+ $(NULL)
+
+osmosimdir = $(includedir)/osmocom/sim
diff --git a/include/osmocom/sim/class_tables.h b/include/osmocom/sim/class_tables.h
index d5be39dd..ec9ec490 100644
--- a/include/osmocom/sim/class_tables.h
+++ b/include/osmocom/sim/class_tables.h
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
diff --git a/include/osmocom/sim/sim.h b/include/osmocom/sim/sim.h
index 2bc47153..639682a0 100644
--- a/include/osmocom/sim/sim.h
+++ b/include/osmocom/sim/sim.h
@@ -2,13 +2,20 @@
* Routines for helping with SIM (ISO/IEC 7816-4 more generally) communication.
*/
-#ifndef _OSMOCOM_SIM_H
-#define _OSMOCOM_SIM_H
+#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#define APDU_HDR_LEN 5
+#define MAX_AID_LEN 16 /* Table 13.2 of TS 102 221 */
+
+
+/*! Maximum Answer-To-Reset (ATR) size in bytes
+ * @note defined in ISO/IEC 7816-3:2006(E) section 8.2.1 as 32, on top the initial character TS of section 8.1
+ * @remark technical there is no size limitation since Yi present in T0,TDi will indicate if more interface bytes are present, including TDi+i
+ */
+#define OSIM_MAX_ATR_LEN 33
/*! command-response pairs cases
*
@@ -66,6 +73,8 @@ struct osim_msgb_cb {
#define msgb_apdu_dc(__x) ((__x)->l2h + sizeof(struct osim_apdu_cmd_hdr))
#define msgb_apdu_de(__x) ((__x)->l2h + sizeof(struct osim_apdu_cmd_hdr) + msgb_apdu_lc(__x))
+int osim_init(void *ctx);
+
/* FILES */
struct osim_file;
@@ -135,6 +144,7 @@ enum osim_file_type {
TYPE_ADF, /*!< Application Dedicated File */
TYPE_EF, /*!< Entry File */
TYPE_EF_INT, /*!< Internal Entry File */
+ TYPE_MF, /*!< Master File */
};
enum osim_ef_type {
@@ -242,6 +252,9 @@ struct osim_file_desc *
osim_file_desc_find_name(struct osim_file_desc *parent, const char *name);
struct osim_file_desc *
+osim_file_desc_find_aid(struct osim_file_desc *parent, const uint8_t *aid, uint8_t aid_len);
+
+struct osim_file_desc *
osim_file_desc_find_fid(struct osim_file_desc *parent, uint16_t fid);
struct osim_file_desc *
@@ -276,11 +289,34 @@ struct osim_card_sw {
} u;
};
-#define OSIM_CARD_SW_LAST (const struct osim_card_sw) { \
+#define OSIM_CARD_SW_LAST { \
.code = 0, .mask = 0, .type = SW_TYPE_NONE, \
.class = SW_CLS_NONE, .u.str = NULL \
}
+/*! A card application (e.g. USIM, ISIM, HPSIM) */
+struct osim_card_app_profile {
+ /*! entry in the global list of card application profiles */
+ struct llist_head list;
+ /*! human-readable name */
+ const char *name;
+ /*! AID of this application, as used in EF.DIR */
+ uint8_t aid[MAX_AID_LEN];
+ uint8_t aid_len;
+ /*! file system description */
+ struct osim_file_desc *adf;
+ /*! Status words defined by application */
+ const struct osim_card_sw *sw;
+};
+
+const struct osim_card_app_profile *
+osim_app_profile_find_by_name(const char *name);
+
+const struct osim_card_app_profile *
+osim_app_profile_find_by_aid(const uint8_t *aid, uint8_t aid_len);
+
+const struct osim_card_sw *osim_app_profile_find_sw(const struct osim_card_app_profile *ap, uint16_t sw_in);
+
/*! A card profile (e.g. SIM card */
struct osim_card_profile {
const char *name;
@@ -290,15 +326,13 @@ struct osim_card_profile {
const struct osim_card_sw **sws;
};
-const struct osim_card_sw *osim_find_sw(const struct osim_card_profile *cp,
- uint16_t sw);
-enum osim_card_sw_class osim_sw_class(const struct osim_card_profile *cp,
- uint16_t sw_in);
+const struct osim_card_sw *osim_cprof_find_sw(const struct osim_card_profile *cp, uint16_t sw_in);
-struct osim_card_hdl;
-char *osim_print_sw_buf(char *buf, size_t buf_len, const struct osim_card_hdl *ch, uint16_t sw_in);
-char *osim_print_sw(const struct osim_card_hdl *ch, uint16_t sw_in);
-char *osim_print_sw_c(const void *ctx, const struct osim_card_hdl *ch, uint16_t sw_in);
+struct osim_chan_hdl;
+enum osim_card_sw_class osim_sw_class(const struct osim_chan_hdl *ch, uint16_t sw_in);
+char *osim_print_sw_buf(char *buf, size_t buf_len, const struct osim_chan_hdl *ch, uint16_t sw_in);
+char *osim_print_sw(const struct osim_chan_hdl *ch, uint16_t sw_in);
+char *osim_print_sw_c(const void *ctx, const struct osim_chan_hdl *ch, uint16_t sw_in);
extern const struct tlv_definition ts102221_fcp_tlv_def;
extern const struct value_string ts102221_fcp_vals[14];
@@ -340,6 +374,8 @@ struct osim_reader_ops {
const char *name;
struct osim_reader_hdl *(*reader_open)(int idx, const char *name, void *ctx);
struct osim_card_hdl *(*card_open)(struct osim_reader_hdl *rh, enum osim_proto proto);
+ int (*card_reset)(struct osim_card_hdl *card, bool cold_reset);
+ int (*card_close)(struct osim_card_hdl *card);
int (*transceive)(struct osim_reader_hdl *rh, struct msgb *msg);
};
@@ -353,6 +389,19 @@ struct osim_reader_hdl {
struct osim_card_hdl *card;
};
+/*! descriptor for a given application present on a card */
+struct osim_card_app_hdl {
+ /*! member in card list of applications */
+ struct llist_head list;
+ /*! AID of the application */
+ uint8_t aid[MAX_AID_LEN];
+ uint8_t aid_len;
+ /*! application label from EF_DIR */
+ char *label;
+ /*! application profile (if any known) */
+ const struct osim_card_app_profile *prof;
+};
+
struct osim_card_hdl {
/*! member in global list of cards */
struct llist_head list;
@@ -365,6 +414,13 @@ struct osim_card_hdl {
/*! list of channels for this card */
struct llist_head channels;
+
+ /*! list of applications found on card */
+ struct llist_head apps;
+
+ /*! ATR (Answer To Reset) of the card */
+ uint8_t atr[OSIM_MAX_ATR_LEN];
+ unsigned int atr_len;
};
struct osim_chan_hdl {
@@ -372,12 +428,19 @@ struct osim_chan_hdl {
struct llist_head list;
/*! card to which this channel belongs */
struct osim_card_hdl *card;
+ /*! current working directory */
const struct osim_file_desc *cwd;
+ /*! currently selected application (if any) */
+ struct osim_card_app_hdl *cur_app;
};
+int osim_card_hdl_add_app(struct osim_card_hdl *ch, const uint8_t *aid, uint8_t aid_len,
+ const char *label);
+
/* reader.c */
int osim_transceive_apdu(struct osim_chan_hdl *st, struct msgb *amsg);
struct osim_reader_hdl *osim_reader_open(enum osim_reader_driver drv, int idx,
const char *name, void *ctx);
struct osim_card_hdl *osim_card_open(struct osim_reader_hdl *rh, enum osim_proto proto);
-#endif /* _OSMOCOM_SIM_H */
+int osim_card_reset(struct osim_card_hdl *card, bool cold_reset);
+int osim_card_close(struct osim_card_hdl *card);
diff --git a/include/osmocom/usb/Makefile.am b/include/osmocom/usb/Makefile.am
new file mode 100644
index 00000000..3b2b8b4d
--- /dev/null
+++ b/include/osmocom/usb/Makefile.am
@@ -0,0 +1,7 @@
+if ENABLE_LIBUSB
+osmousb_HEADERS = \
+ libusb.h \
+ $(NULL)
+endif
+
+osmousbdir = $(includedir)/osmocom/usb
diff --git a/include/osmocom/usb/libusb.h b/include/osmocom/usb/libusb.h
new file mode 100644
index 00000000..33caa862
--- /dev/null
+++ b/include/osmocom/usb/libusb.h
@@ -0,0 +1,115 @@
+#pragma once
+/* libusb utilities
+ *
+ * (C) 2010-2019 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <libusb.h>
+
+#define USB_MAX_PATH_LEN 20
+
+struct dev_id {
+ uint16_t vendor_id;
+ uint16_t product_id;
+};
+
+/* structure describing a single matching interface found */
+struct usb_interface_match {
+ /* libusb device E*/
+ libusb_device *usb_dev;
+ /* Vendor ID of the device running matching interface */
+ uint16_t vendor;
+ /* Product ID of the device running matching interface */
+ uint16_t product;
+ /* USB Bus Address */
+ uint8_t addr;
+ /* physical path */
+ char path[USB_MAX_PATH_LEN];
+ /* configuration of matching interface */
+ uint8_t configuration;
+ /* interface number of matching interface */
+ uint8_t interface;
+ /* altsetting of matching interface */
+ uint8_t altsetting;
+ /* bInterfaceClass of matching interface */
+ uint8_t class;
+ /* bInterfaceSubClass of matching interface */
+ uint8_t sub_class;
+ /* bInterfaceProtocol of matching interface */
+ uint8_t protocol;
+ /* index of string descriptor of matching interface */
+ uint8_t string_idx;
+};
+
+/*! Description of the USB device+interface we're looking for */
+struct osmo_usb_matchspec {
+ /*! specify the USB device */
+ struct {
+ int vendor_id; /*!< typically -1 for compile time defaults */
+ int product_id; /*!< typically -1 for compile time defaults */
+ char *path; /*!< used for disambiguation when multiple matches; can be NULL */
+ } dev;
+
+ /*! specify the USB configuration */
+ int config_id; /*!< typically -1 unless user selects specific configuration */
+
+ /*! specify the USB interface */
+ struct {
+ /* typically those three are set to application defaults */
+ int class; /*!< -1 or a user-specified class */
+ int subclass; /*!< -1 or a user-specified subclass */
+ int proto; /*!< -1 or a user-specified protocol */
+
+ /* typically those two are -1; but user can override them */
+ int num;
+ int altsetting;
+ } intf;
+};
+
+
+char *osmo_libusb_dev_get_path_buf(char *buf, size_t bufsize, libusb_device *dev);
+char *osmo_libusb_dev_get_path_c(void *ctx, libusb_device *dev);
+
+libusb_device **osmo_libusb_find_matching_usb_devs(void *ctx, struct libusb_context *luctx,
+ const struct dev_id *dev_ids);
+
+libusb_device *osmo_libusb_find_matching_dev_path(struct libusb_context *luctx,
+ const struct dev_id *dev_ids,
+ const char *path);
+
+libusb_device *osmo_libusb_find_matching_dev_serial(struct libusb_context *luctx,
+ const struct dev_id *dev_ids,
+ const char *serial);
+
+int osmo_libusb_dev_find_matching_interfaces(libusb_device *dev, int class, int sub_class,
+ int protocol, struct usb_interface_match *out,
+ unsigned int out_len);
+
+int osmo_libusb_find_matching_interfaces(libusb_context *luctx, const struct dev_id *dev_ids,
+ int class, int sub_class, int protocol,
+ struct usb_interface_match *out, unsigned int out_len);
+
+libusb_device_handle *osmo_libusb_open_claim_interface(void *ctx, libusb_context *luctx,
+ const struct usb_interface_match *ifm);
+
+void osmo_libusb_match_init(struct osmo_usb_matchspec *cfg, int if_class, int if_subclass, int if_proto);
+
+libusb_device_handle *osmo_libusb_find_open_claim(const struct osmo_usb_matchspec *cfg,
+ const struct dev_id *default_dev_ids);
+
+int osmo_libusb_get_ep_addrs(libusb_device_handle *devh, unsigned int if_num,
+ uint8_t *out, uint8_t *in, uint8_t *irq);
+
+
+int osmo_libusb_init(libusb_context **luctx);
+void osmo_libusb_exit(libusb_context *luctx);
diff --git a/include/osmocom/vty/Makefile.am b/include/osmocom/vty/Makefile.am
new file mode 100644
index 00000000..adb05dcb
--- /dev/null
+++ b/include/osmocom/vty/Makefile.am
@@ -0,0 +1,17 @@
+if ENABLE_VTY
+osmovty_HEADERS = \
+ buffer.h \
+ command.h \
+ logging.h \
+ stats.h \
+ misc.h \
+ telnet_interface.h \
+ vector.h \
+ vty.h \
+ ports.h \
+ cpu_sched_vty.h \
+ tdef_vty.h \
+ $(NULL)
+endif
+
+osmovtydir = $(includedir)/osmocom/vty
diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h
index d63dbdef..61b58815 100644
--- a/include/osmocom/vty/command.h
+++ b/include/osmocom/vty/command.h
@@ -24,10 +24,12 @@
#pragma once
#include <stdio.h>
+#include <stdbool.h>
#include <sys/types.h>
#include "vector.h"
#include <osmocom/core/defs.h>
+#include <osmocom/core/utils.h>
/*! \defgroup command VTY Command
* @{
@@ -97,11 +99,21 @@ enum node_type {
L_CS7_SCCPADDR_NODE, /*!< SS7 SCCP Address */
L_CS7_SCCPADDR_GT_NODE, /*!< SS7 SCCP Global Title */
+ L_CPU_SCHED_NODE, /*!< CPU Sched related options node */
+ L_NS_BIND_NODE, /*!< NS bind node */
+ L_NS_NSE_NODE, /*!< NS NSE node */
/*
* When adding new nodes to the libosmocore project, these nodes can be
* used to avoid ABI changes for unrelated projects.
*/
+ RESERVED1_NODE, /*!< Reserved for later extensions */
+ RESERVED2_NODE, /*!< Reserved for later extensions */
RESERVED3_NODE, /*!< Reserved for later extensions */
+ RESERVED4_NODE, /*!< Reserved for later extensions */
+ RESERVED5_NODE, /*!< Reserved for later extensions */
+ RESERVED6_NODE, /*!< Reserved for later extensions */
+ RESERVED7_NODE, /*!< Reserved for later extensions */
+ RESERVED8_NODE, /*!< Reserved for later extensions */
_LAST_OSMOVTY_NODE
};
@@ -136,6 +148,27 @@ struct cmd_node {
enum {
CMD_ATTR_DEPRECATED = (1 << 0),
CMD_ATTR_HIDDEN = (1 << 1),
+ CMD_ATTR_IMMEDIATE = (1 << 2),
+ CMD_ATTR_NODE_EXIT = (1 << 3),
+ CMD_ATTR_LIB_COMMAND = (1 << 4),
+};
+
+/*! Attributes shared between libraries (up to 32 entries). */
+enum {
+ /* The entries of this enum shall conform the following requirements:
+ * 1. Naming format: 'OSMO_' + <LIBNAME> + '_LIB_ATTR_' + <ATTRNAME>,
+ * where LIBNAME is a short name of the library, e.g. 'ABIS', 'MGCP',
+ * and ATTRNAME is a brief name of the attribute, e.g. RTP_CONN_EST;
+ * for example: 'OSMO_ABIS_LIB_ATTR_RSL_LINK_UP'.
+ * 2. Brevity: shortenings and abbreviations are welcome!
+ * 3. Values are not flags but indexes, unlike CMD_ATTR_*.
+ * 4. Ordering: new entries added before _OSMO_CORE_LIB_ATTR_COUNT. */
+ OSMO_SCCP_LIB_ATTR_RSTRT_ASP,
+ OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK,
+ OSMO_ABIS_LIB_ATTR_LINE_UPD,
+
+ /* Keep this floating entry last, it's needed for count check. */
+ _OSMO_CORE_LIB_ATTR_COUNT
};
/*! Structure of a command element */
@@ -148,7 +181,8 @@ struct cmd_element {
unsigned int cmdsize; /*!< Command index count. */
char *config; /*!< Configuration string */
vector subconfig; /*!< Sub configuration string */
- unsigned char attr; /*!< Command attributes */
+ unsigned char attr; /*!< Command attributes (global) */
+ unsigned int usrattr; /*!< Command attributes (program specific) */
};
/*! Command description structure. */
@@ -199,6 +233,16 @@ struct desc {
.daemon = dnum, \
};
+#define DEFUN_CMD_ELEMENT_ATTR_USRATTR(funcname, cmdname, cmdstr, helpstr, attrs, usrattrs) \
+ static struct cmd_element cmdname = \
+ { \
+ .string = cmdstr, \
+ .func = funcname, \
+ .doc = helpstr, \
+ .attr = attrs, \
+ .usrattr = usrattrs, \
+ };
+
#define DEFUN_CMD_FUNC_DECL(funcname) \
static int funcname (struct cmd_element *, struct vty *, int, const char *[]); \
@@ -237,7 +281,23 @@ struct desc {
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_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
+
+/*! Macro for defining a VTY node and function with global & program specific attributes.
+ * \param[in] funcname Name of the function implementing the node.
+ * \param[in] cmdname Name of the command node.
+ * \param[in] attr Global attributes (see CMD_ATTR_*).
+ * \param[in] usrattr Program specific attributes.
+ * \param[in] cmdstr String with syntax of node.
+ * \param[in] helpstr String with help message of node.
+ */
+#define DEFUN_ATTR_USRATTR(funcname, cmdname, attr, usrattr, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT_ATTR_USRATTR(funcname, cmdname, cmdstr, helpstr, attr, usrattr) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_USRATTR(funcname, cmdname, usrattr, cmdstr, helpstr) \
+ DEFUN_ATTR_USRATTR(funcname, cmdname, 0, usrattr, cmdstr, helpstr)
/* DEFUN_NOSH for commands that vtysh should ignore */
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
@@ -304,6 +364,10 @@ struct desc {
#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
+#define VTY_IPV4_CMD "A.B.C.D"
+#define VTY_IPV6_CMD "X:X::X:X"
+#define VTY_IPV46_CMD "(" VTY_IPV4_CMD "|" VTY_IPV6_CMD ")"
+
/* Common descriptions. */
#define SHOW_STR "Show running system information\n"
#define IP_STR "IP information\n"
@@ -363,7 +427,9 @@ struct desc {
void install_node(struct cmd_node *, int (*)(struct vty *));
void install_default(int node_type) OSMO_DEPRECATED("Now happens implicitly with install_node()");
void install_element(int node_type, struct cmd_element *);
+void install_lib_element(int node_type, struct cmd_element *);
void install_element_ve(struct cmd_element *cmd);
+void install_lib_element_ve(struct cmd_element *cmd);
void sort_node(void);
void vty_install_default(int node_type) OSMO_DEPRECATED("Now happens implicitly with install_node()");
@@ -376,8 +442,8 @@ char *argv_concat(const char **argv, int argc, int shift);
vector cmd_make_strvec(const char *);
int cmd_make_strvec2(const char *string, char **indent, vector *strvec_p);
void cmd_free_strvec(vector);
-vector cmd_describe_command();
-char **cmd_complete_command();
+vector cmd_describe_command(vector vline, struct vty *vty, int *status);
+char **cmd_complete_command(vector vline, struct vty *vty, int *status);
const char *cmd_prompt(enum node_type);
int config_from_file(struct vty *, FILE *);
enum node_type node_parent(enum node_type);
@@ -391,7 +457,7 @@ extern struct cmd_element config_exit_cmd;
extern struct cmd_element config_help_cmd;
extern struct cmd_element config_list_cmd;
extern struct cmd_element config_end_cmd;
-char *host_config_file();
+const char *host_config_file(void);
void host_config_set(const char *);
char *osmo_asciidoc_escape(const char *inp);
@@ -401,4 +467,22 @@ void print_version(int print_copyright);
extern void *tall_vty_cmd_ctx;
+/*! VTY reference generation mode. */
+enum vty_ref_gen_mode {
+ /*! Default mode: all commands except deprecated and hidden. */
+ VTY_REF_GEN_MODE_DEFAULT = 0,
+ /*! Expert mode: all commands including hidden, excluding deprecated. */
+ VTY_REF_GEN_MODE_EXPERT,
+ /*! "Inverse" mode: only hidden commands. */
+ VTY_REF_GEN_MODE_HIDDEN,
+};
+
+extern const struct value_string vty_ref_gen_mode_names[];
+extern const struct value_string vty_ref_gen_mode_desc[];
+
+int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode);
+int vty_dump_xml_ref(FILE *stream) OSMO_DEPRECATED("Use vty_dump_xml_ref_mode() instead");
+
+int vty_cmd_range_match(const char *range, const char *str);
+
/*! @} */
diff --git a/include/osmocom/vty/cpu_sched_vty.h b/include/osmocom/vty/cpu_sched_vty.h
new file mode 100644
index 00000000..171f1687
--- /dev/null
+++ b/include/osmocom/vty/cpu_sched_vty.h
@@ -0,0 +1,37 @@
+/*! \file cpu_sched_vty.h
+ * API to CPU / Threading / Scheduler properties from VTY configuration.
+ */
+/* (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#pragma once
+
+#include <osmocom/vty/command.h>
+
+/*! \defgroup cpu_sched_VTY Configuration
+ * @{
+ * \file cpu_sched_vty.h
+ */
+
+void osmo_cpu_sched_vty_init(void *tall_ctx);
+int osmo_cpu_sched_vty_apply_localthread(void);
+
+/*! @} */
diff --git a/include/osmocom/vty/logging.h b/include/osmocom/vty/logging.h
index 90c8fa17..b3ce92c7 100644
--- a/include/osmocom/vty/logging.h
+++ b/include/osmocom/vty/logging.h
@@ -6,7 +6,13 @@
#define FILTER_STR "Filter log messages\n"
struct log_info;
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+/* note this undefined argument declaration is intentional. There used
+ * to be an argument until 2017 which we no longer need .*/
void logging_vty_add_cmds();
+#pragma GCC diagnostic pop
void logging_vty_add_deprecated_subsys(void *ctx, const char *name);
struct vty;
struct log_target *osmo_log_vty2tgt(struct vty *vty);
diff --git a/include/osmocom/vty/misc.h b/include/osmocom/vty/misc.h
index 2ad96508..f031c4ab 100644
--- a/include/osmocom/vty/misc.h
+++ b/include/osmocom/vty/misc.h
@@ -14,21 +14,31 @@ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals,
void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
struct rate_ctr_group *ctrg);
+void vty_out_rate_ctr_group2(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg, bool skip_zero);
void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt,
struct rate_ctr_group *ctrg);
+void vty_out_rate_ctr_group_fmt2(struct vty *vty, const char *fmt,
+ struct rate_ctr_group *ctrg, bool skip_zero);
+
void vty_out_stat_item_group(struct vty *vty, const char *prefix,
struct osmo_stat_item_group *statg);
+void vty_out_stat_item_group2(struct vty *vty, const char *prefix,
+ struct osmo_stat_item_group *statg, bool skip_zero);
void vty_out_statistics_full(struct vty *vty, const char *prefix);
-void vty_out_statistics_partial(struct vty *vty, const char *prefix,
- int max_level);
+void vty_out_statistics_full2(struct vty *vty, const char *prefix, bool skip_zero);
+void vty_out_statistics_partial(struct vty *vty, const char *prefix, int max_level);
+void vty_out_statistics_partial2(struct vty *vty, const char *prefix, int max_level, bool skip_zero);
struct osmo_fsm;
struct osmo_fsm_inst;
void vty_out_fsm(struct vty *vty, struct osmo_fsm *fsm);
+void vty_out_fsm2(struct vty *vty, const char *prefix, struct osmo_fsm *fsm);
void vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi);
+void vty_out_fsm_inst2(struct vty *vty, const char *prefix, struct osmo_fsm_inst *fsmi);
void osmo_fsm_vty_add_cmds(void);
void osmo_talloc_vty_add_cmds(void);
diff --git a/include/osmocom/vty/ports.h b/include/osmocom/vty/ports.h
index 201e1157..bc001282 100644
--- a/include/osmocom/vty/ports.h
+++ b/include/osmocom/vty/ports.h
@@ -8,30 +8,43 @@
#pragma once
+#define OSMO_VTY_PORT_PCAP_CLIENT 4227
+#define OSMO_VTY_PORT_PCAP_SERVER 4228
/* 4236 used by control interface */
-#define OSMO_VTY_PORT_TRX 4237
+#define OSMO_VTY_PORT_TRX 4237
/* 4238 used by osmo-bts control interface */
-#define OSMO_VTY_PORT_STP 4239
-#define OSMO_VTY_PORT_PCU 4240 /* also: osmo_pcap_client */
-#define OSMO_VTY_PORT_BTS 4241 /* also: osmo_pcap_server */
-#define OSMO_VTY_PORT_NITB_BSC 4242
-#define OSMO_VTY_PORT_BSC_MGCP 4243
-#define OSMO_VTY_PORT_MGW OSMO_VTY_PORT_BSC_MGCP
-#define OSMO_VTY_PORT_BSC_NAT 4244
-#define OSMO_VTY_PORT_SGSN 4245
-#define OSMO_VTY_PORT_GBPROXY 4246
-#define OSMO_VTY_PORT_BB 4247
+#define OSMO_VTY_PORT_STP 4239
+#define OSMO_VTY_PORT_PCU 4240
+#define OSMO_VTY_PORT_BTS 4241
+#define OSMO_VTY_PORT_NITB_BSC 4242
+#define OSMO_VTY_PORT_BSC_MGCP 4243
+#define OSMO_VTY_PORT_MGW OSMO_VTY_PORT_BSC_MGCP
+#define OSMO_VTY_PORT_BSC_NAT 4244
+#define OSMO_VTY_PORT_SGSN 4245
+#define OSMO_VTY_PORT_GBPROXY 4246
+#define OSMO_VTY_PORT_BB 4247
/* 4249-4251 used by control interface */
-#define OSMO_VTY_PORT_BTSMGR 4252
-#define OSMO_VTY_PORT_GTPHUB 4253
-#define OSMO_VTY_PORT_MSC 4254
+#define OSMO_VTY_PORT_BTSMGR 4252
+#define OSMO_VTY_PORT_GTPHUB 4253
+#define OSMO_VTY_PORT_MSC 4254
/* 4255 used by control interface */
-#define OSMO_VTY_PORT_MNCC_SIP 4256
+#define OSMO_VTY_PORT_MNCC_SIP 4256
/* 4257 used by control interface */
-#define OSMO_VTY_PORT_HLR 4258
+#define OSMO_VTY_PORT_HLR 4258
/* 4259 used by control interface */
-#define OSMO_VTY_PORT_GGSN 4260
-#define OSMO_VTY_PORT_HNBGW 4261
+#define OSMO_VTY_PORT_GGSN 4260
+#define OSMO_VTY_PORT_HNBGW 4261
/* 4262-4263 used by control interface */
-#define OSMO_VTY_PORT_CBC 4264
+#define OSMO_VTY_PORT_CBC 4264
+#define OSMO_VTY_PORT_UECUPS 4268
+#define OSMO_VTY_PORT_E1D 4269
+#define OSMO_VTY_PORT_ISDNTAP 4270
+#define OSMO_VTY_PORT_SMLC 4271
+/* 4272 used by control interface */
+#define OSMO_VTY_PORT_HNODEB 4273
+/* 4274 used by control interface */
+#define OSMO_VTY_PORT_UPF 4275
+/* 4276: OSMO_CTRL_PORT_UPF */
+#define OSMO_VTY_PORT_PFCP_TOOL 4277
+/* 4278: OSMO_CTRL_PORT_UPF */
/* When adding/changing port numbers, keep docs and wiki in sync. See above. */
diff --git a/include/osmocom/vty/tdef_vty.h b/include/osmocom/vty/tdef_vty.h
index 6334d5ba..800af7d7 100644
--- a/include/osmocom/vty/tdef_vty.h
+++ b/include/osmocom/vty/tdef_vty.h
@@ -68,7 +68,7 @@ void osmo_tdef_vty_out_all_va(struct vty *vty, struct osmo_tdef *tdefs, const ch
struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *tdefs, const char *osmo_tdef_str);
unsigned long osmo_tdef_vty_parse_val_arg(const char *val_arg, unsigned long default_val);
-void osmo_tdef_vty_groups_init(enum node_type parent_node, struct osmo_tdef_group *groups);
+void osmo_tdef_vty_groups_init(unsigned int parent_cfg_node, struct osmo_tdef_group *groups);
void osmo_tdef_vty_groups_write(struct vty *vty, const char *indent);
/*! @} */
diff --git a/include/osmocom/vty/telnet_interface.h b/include/osmocom/vty/telnet_interface.h
index da7cf839..73b79df1 100644
--- a/include/osmocom/vty/telnet_interface.h
+++ b/include/osmocom/vty/telnet_interface.h
@@ -14,14 +14,11 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#pragma once
+#include <osmocom/core/defs.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/select.h>
@@ -45,10 +42,13 @@ struct telnet_connection {
struct log_target *dbg;
};
-int telnet_init(void *tall_ctx, void *priv, int port);
-int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port);
int telnet_init_default(void *tall_ctx, void *priv, int default_port);
+int telnet_init(void *tall_ctx, void *priv, int port)
+ OSMO_DEPRECATED("This function ignores dynamic port configuration. Use telnet_init_default() instead");
+int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
+ OSMO_DEPRECATED("This function ignores dynamic port configuration. Use telnet_init_default() instead");
+
void telnet_exit(void);
/*! @} */
diff --git a/include/osmocom/vty/vector.h b/include/osmocom/vty/vector.h
index 0a639ad9..ac524bc3 100644
--- a/include/osmocom/vty/vector.h
+++ b/include/osmocom/vty/vector.h
@@ -14,11 +14,6 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Zebra; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
*/
#pragma once
diff --git a/include/osmocom/vty/vty.h b/include/osmocom/vty/vty.h
index 03a29248..3a2ec6f6 100644
--- a/include/osmocom/vty/vty.h
+++ b/include/osmocom/vty/vty.h
@@ -3,8 +3,10 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <time.h>
#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/defs.h>
/*! \defgroup vty VTY (Virtual TTY) interface
* @{
@@ -27,6 +29,12 @@
#define VTY_BUFSIZ 512
#define VTY_MAXHIST 20
+/* Number of application / library specific VTY attributes */
+#define VTY_CMD_USR_ATTR_NUM 32
+/* Flag characters reserved for global VTY attributes */
+#define VTY_CMD_ATTR_FLAGS_RESERVED \
+ { '.', '!', '@', '^' }
+
/*! VTY events */
enum event {
VTY_SERV,
@@ -48,20 +56,6 @@ enum vty_type {
VTY_SHELL_SERV
};
-struct vty_parent_node {
- struct llist_head entry;
-
- /*! private data, specified by creator */
- void *priv;
-
- /*! Node status of this vty */
- int node;
-
- /*! When reading from a config file, these are the indenting characters expected for children of
- * this VTY node. */
- char *indent;
-};
-
/*! Internal representation of a single VTY */
struct vty {
/*! underlying file (if any) */
@@ -134,7 +128,7 @@ struct vty {
#define TELNET_NAWS_SB_LEN 5
/*! sub-negotiation buffer */
unsigned char sb_buf[TELNET_NAWS_SB_LEN];
- /*! How many subnegotiation characters have we received?
+ /*! How many subnegotiation characters have we received?
*
* We just drop those that do not fit in the buffer. */
size_t sb_len;
@@ -158,6 +152,9 @@ struct vty {
/*! When reading from a config file, these are the indenting characters expected for children of
* the current VTY node. */
char *indent;
+
+ /*! Whether the expert mode is enabled. */
+ bool expert_mode;
};
/* Small macro to determine newline is newline only or linefeed needed. */
@@ -178,17 +175,29 @@ struct vty_app_info {
const char *copyright;
/*! \ref talloc context */
void *tall_ctx;
- /*! call-back for returning to parent n ode */
+ /*! Call-back for taking actions upon exiting a node.
+ * The return value is ignored, and changes to vty->node and vty->index made in this callback are ignored.
+ * Implicit parent node tracking always sets the correct parent node and vty->index after this callback exits,
+ * so this callback can handle only those nodes that should take specific actions upon node exit, or can be left
+ * NULL entirely. */
int (*go_parent_cb)(struct vty *vty);
- /*! call-back to determine if node is config node */
- int (*is_config_node)(struct vty *vty, int node);
+ /*! OBSOLETED: Implicit parent node tracking has replaced the use of this callback. This callback is no longer
+ * called, ever, and can be left NULL. */
+ int (*is_config_node)(struct vty *vty, int node)
+ OSMO_DEPRECATED("Implicit parent node tracking has replaced the use of this callback. This callback is"
+ " no longer called, ever, and can be left NULL.");
/*! Check if the config is consistent before write */
int (*config_is_consistent)(struct vty *vty);
+ /*! Description of the application specific VTY attributes (optional). */
+ const char * usr_attr_desc[VTY_CMD_USR_ATTR_NUM];
+ /*! Flag letters of the application specific VTY attributes (optional). */
+ char usr_attr_letters[VTY_CMD_USR_ATTR_NUM];
};
/* Prototypes. */
void vty_init(struct vty_app_info *app_info);
int vty_read_config_file(const char *file_name, void *priv);
+int vty_read_config_filep(FILE *confp, void *priv);
void vty_init_vtysh (void);
void vty_reset (void);
struct vty *vty_new (void);
@@ -197,9 +206,11 @@ bool vty_is_active(struct vty *vty);
int vty_out (struct vty *, const char *, ...) VTY_PRINTF_ATTRIBUTE(2, 3);
int vty_out_va(struct vty *vty, const char *format, va_list ap);
int vty_out_newline(struct vty *);
+int vty_out_uptime(struct vty *vty, const struct timespec *starttime);
int vty_read(struct vty *vty);
//void vty_time_print (struct vty *, int);
void vty_close (struct vty *);
+void vty_flush(struct vty *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 *);
diff --git a/libosmocodec.pc.in b/libosmocodec.pc.in
index 3030230b..0c4de8d5 100644
--- a/libosmocodec.pc.in
+++ b/libosmocodec.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom Codec related utilities Library
Description: C Utility Library
Version: @VERSION@
+Requires: talloc, libosmocore
Libs: -L${libdir} -losmocodec
Cflags: -I${includedir}/
-
diff --git a/libosmocoding.pc.in b/libosmocoding.pc.in
index 580b1702..5b91a606 100644
--- a/libosmocoding.pc.in
+++ b/libosmocoding.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom L1 transcoding Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} -losmocoding -losmocodec -losmogsm -losmocore
+Requires: talloc, libosmocore, libosmogsm, libosmocodec
+Libs: -L${libdir} -losmocoding
Cflags: -I${includedir}/
-
diff --git a/libosmocore.pc.in b/libosmocore.pc.in
index 80f17c8b..6479221e 100644
--- a/libosmocore.pc.in
+++ b/libosmocore.pc.in
@@ -6,6 +6,8 @@ includedir=@includedir@
Name: Osmocom Core Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmocore
+Requires: talloc @LIBMNL_PC@
+Requires.private: @LIBSCTP_PC@
+Libs: -L${libdir} -losmocore
Libs.private: @PTHREAD_LIBS@ @LIBSCTP_LIBS@
-Cflags: -I${includedir}/ @TALLOC_CFLAGS@ @PTHREAD_CFLAGS@
+Cflags: -I${includedir}/ @PTHREAD_CFLAGS@
diff --git a/libosmoctrl.pc.in b/libosmoctrl.pc.in
index 4676b31d..e9947dce 100644
--- a/libosmoctrl.pc.in
+++ b/libosmoctrl.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom Control Interface Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmoctrl -losmogsm -losmocore
+Requires: talloc, libosmocore, libosmogsm
+Libs: -L${libdir} -losmoctrl
Cflags: -I${includedir}/
-
diff --git a/libosmogb.pc.in b/libosmogb.pc.in
index a163cc42..5573bd98 100644
--- a/libosmogb.pc.in
+++ b/libosmogb.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom GPRS Gb Library
Description: Osmocom GPRS Gb Interface (NS/BSSGP) Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmogb -losmovty -losmocore
+Requires: talloc, libosmocore, libosmovty, libosmogsm
+Libs: -L${libdir} -losmogb
Cflags: -I${includedir}/ -fno-strict-aliasing
-
diff --git a/libosmogsm.pc.in b/libosmogsm.pc.in
index 0160be8d..17b9212a 100644
--- a/libosmogsm.pc.in
+++ b/libosmogsm.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom GSM Core Library
Description: GSM Core Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmogsm -losmocore
+Requires: talloc, libosmocore, libosmoisdn
+Libs: -L${libdir} -losmogsm
Cflags: -I${includedir}/
-
diff --git a/libosmoisdn.pc.in b/libosmoisdn.pc.in
new file mode 100644
index 00000000..fedbb694
--- /dev/null
+++ b/libosmoisdn.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom ISDN Library
+Description: ISDN Library
+Version: @VERSION@
+Requires: talloc, libosmocore
+Libs: -L${libdir} -losmoisdn
+Cflags: -I${includedir}/
diff --git a/libosmosim.pc.in b/libosmosim.pc.in
index 83777c31..a8f50d6f 100644
--- a/libosmosim.pc.in
+++ b/libosmosim.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom SIM card related utilities Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmosim -losmocore
+Requires: talloc, libosmocore
+Libs: -L${libdir} -losmosim
Cflags: -I${includedir}/
-
diff --git a/libosmousb.pc.in b/libosmousb.pc.in
new file mode 100644
index 00000000..dbedb0e5
--- /dev/null
+++ b/libosmousb.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Osmocom libusb (USB) integration
+Description: C Utility Library
+Version: @VERSION@
+Requires: talloc, libusb-1.0, libosmocore
+Libs: -L${libdir} -losmousb
+Cflags: -I${includedir}/
diff --git a/libosmovty.pc.in b/libosmovty.pc.in
index 6204e455..5d9286a2 100644
--- a/libosmovty.pc.in
+++ b/libosmovty.pc.in
@@ -6,6 +6,6 @@ includedir=@includedir@
Name: Osmocom VTY Interface Library
Description: C Utility Library
Version: @VERSION@
-Libs: -L${libdir} @TALLOC_LIBS@ -losmovty -losmocore
+Requires: talloc, libosmocore
+Libs: -L${libdir} -losmovty
Cflags: -I${includedir}/
-
diff --git a/m4/osmo_ac_code_coverage.m4 b/m4/osmo_ac_code_coverage.m4
new file mode 100644
index 00000000..7921db59
--- /dev/null
+++ b/m4/osmo_ac_code_coverage.m4
@@ -0,0 +1,51 @@
+AC_DEFUN([OSMO_AC_CODE_COVERAGE],[
+ dnl Check for --enable-code-coverage
+ AC_REQUIRE([OSMO_AX_CODE_COVERAGE])
+ AC_REQUIRE([AX_CHECK_COMPILE_FLAG])
+
+ AS_IF([ test "x$enable_code_coverage" = "xyes" ], [
+ # Check whether --coverage flags is supported and add it to CFLAGS
+ # When it is not supported add CODE_COVERAGE_CFLAGS to CFLAGS instead
+ AX_CHECK_COMPILE_FLAG([--coverage],
+ [CFLAGS="$CFLAGS -O0 -g --coverage"],
+ [CFLAGS="$CFLAGS $CODE_COVERAGE_CFLAGS"])
+
+ # Add both the absolute source and build directories to the coverage directories.
+ CODE_COVERAGE_DIRECTORY='$(abspath $(abs_top_srcdir)) $(abspath $(abs_top_builddir))'
+ AC_SUBST(CODE_COVERAGE_DIRECTORY)
+
+ # Enable branch coverage by default
+ CODE_COVERAGE_BRANCH_COVERAGE='1'
+ AC_SUBST(CODE_COVERAGE_BRANCH_COVERAGE)
+
+ # Exclude external files by default
+ CODE_COVERAGE_LCOV_OPTIONS='$(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) --no-external'
+ AC_SUBST(CODE_COVERAGE_LCOV_OPTIONS)
+
+ # Exclude tests sources from the coverage report
+ CODE_COVERAGE_IGNORE_PATTERN='"$(abspath $(abs_top_srcdir))/tests/*"'
+ AC_SUBST(CODE_COVERAGE_IGNORE_PATTERN)
+
+ # lcov_cobertura is needed only when you want to export the coverage report in
+ # the Cobertura's XML format supported by Jenkin's Cobertura plugin
+ AC_CHECK_PROG([LCOV_COBERTURA], [lcov_cobertura], [lcov_cobertura])
+ AS_IF([test "x$LCOV_COBERTURA" != "xno"], [m4_pattern_allow([AM_V_GEN]) CODE_COVERAGE_RULES+='
+coverage-cobertura.xml: $(CODE_COVERAGE_OUTPUT_FILE)
+ $(AM_V_GEN)$(LCOV_COBERTURA) -b $(top_srcdir) -o $$@@ $(CODE_COVERAGE_OUTPUT_FILE)
+
+.PHONY: code-coverage-cobertura
+code-coverage-cobertura: code-coverage-capture coverage-cobertura.xml
+'
+ ], [CODE_COVERAGE_RULES+='
+.PHONY: code-coverage-cobertura
+code-coverage-cobertura:
+ @echo "Need to install lcov_cobertura"
+'
+ ])
+ ], [CODE_COVERAGE_RULES+='
+.PHONY: code-coverage-cobertura
+code-coverage-cobertura:
+ @echo "Need to and reconfigure with --enable-code-coverage"
+'
+ ])
+])
diff --git a/m4/osmo_ax_code_coverage.m4 b/m4/osmo_ax_code_coverage.m4
new file mode 100644
index 00000000..23cebb04
--- /dev/null
+++ b/m4/osmo_ax_code_coverage.m4
@@ -0,0 +1,267 @@
+#
+# Renamed version of AX_CODE_COVERAGE macro from autoconf-archive v2018.03.13
+#
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# OSMO_AX_CODE_COVERAGE()
+#
+# DESCRIPTION
+#
+# Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS,
+# CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included
+# in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every
+# build target (program or library) which should be built with code
+# coverage support. Also defines CODE_COVERAGE_RULES which should be
+# substituted in your Makefile; and $enable_code_coverage which can be
+# used in subsequent configure output. CODE_COVERAGE_ENABLED is defined
+# and substituted, and corresponds to the value of the
+# --enable-code-coverage option, which defaults to being disabled.
+#
+# Test also for gcov program and create GCOV variable that could be
+# substituted.
+#
+# Note that all optimization flags in CFLAGS must be disabled when code
+# coverage is enabled.
+#
+# Usage example:
+#
+# configure.ac:
+#
+# OSMO_AX_CODE_COVERAGE
+#
+# Makefile.am:
+#
+# @CODE_COVERAGE_RULES@
+# my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ...
+# my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ...
+# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ...
+# my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ...
+#
+# This results in a "check-code-coverage" rule being added to any
+# Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module
+# has been configured with --enable-code-coverage). Running `make
+# check-code-coverage` in that directory will run the module's test suite
+# (`make check`) and build a code coverage report detailing the code which
+# was touched, then print the URI for the report.
+#
+# In earlier versions of this macro, CODE_COVERAGE_LDFLAGS was defined
+# instead of CODE_COVERAGE_LIBS. They are both still defined, but use of
+# CODE_COVERAGE_LIBS is preferred for clarity; CODE_COVERAGE_LDFLAGS is
+# deprecated. They have the same value.
+#
+# This code was derived from Makefile.decl in GLib, originally licenced
+# under LGPLv2.1+.
+#
+# LICENSE
+#
+# Copyright (c) 2012, 2016 Philip Withnall
+# Copyright (c) 2012 Xan Lopez
+# Copyright (c) 2012 Christian Persch
+# Copyright (c) 2012 Paolo Borelli
+# Copyright (c) 2012 Dan Winship
+# Copyright (c) 2015 Bastien ROUCARIES
+#
+# 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 2.1 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 program. If not, see <https://www.gnu.org/licenses/>.
+
+#serial 25
+
+AC_DEFUN([OSMO_AX_CODE_COVERAGE],[
+ dnl Check for --enable-code-coverage
+ AC_REQUIRE([AC_PROG_SED])
+
+ # allow to override gcov location
+ AC_ARG_WITH([gcov],
+ [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])],
+ [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov],
+ [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov])
+
+ AC_MSG_CHECKING([whether to build with code coverage support])
+ AC_ARG_ENABLE([code-coverage],
+ AS_HELP_STRING([--enable-code-coverage],
+ [Whether to enable code coverage support]),,
+ enable_code_coverage=no)
+
+ AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes])
+ AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage])
+ AC_MSG_RESULT($enable_code_coverage)
+
+ AS_IF([ test "$enable_code_coverage" = "yes" ], [
+ # check for gcov
+ AC_CHECK_TOOL([GCOV],
+ [$_AX_CODE_COVERAGE_GCOV_PROG_WITH],
+ [:])
+ AS_IF([test "X$GCOV" = "X:"],
+ [AC_MSG_ERROR([gcov is needed to do coverage])])
+ AC_SUBST([GCOV])
+
+ dnl Check if gcc is being used
+ AS_IF([ test "$GCC" = "no" ], [
+ AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage])
+ ])
+
+ AC_CHECK_PROG([LCOV], [lcov], [lcov])
+ AC_CHECK_PROG([GENHTML], [genhtml], [genhtml])
+
+ AS_IF([ test -z "$LCOV" ], [
+ AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed])
+ ])
+
+ AS_IF([ test -z "$GENHTML" ], [
+ AC_MSG_ERROR([Could not find genhtml from the lcov package])
+ ])
+
+ dnl Build the code coverage flags
+ dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility
+ CODE_COVERAGE_CPPFLAGS="-DNDEBUG"
+ CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
+ CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
+ CODE_COVERAGE_LIBS="-lgcov"
+ CODE_COVERAGE_LDFLAGS="$CODE_COVERAGE_LIBS"
+
+ AC_SUBST([CODE_COVERAGE_CPPFLAGS])
+ AC_SUBST([CODE_COVERAGE_CFLAGS])
+ AC_SUBST([CODE_COVERAGE_CXXFLAGS])
+ AC_SUBST([CODE_COVERAGE_LIBS])
+ AC_SUBST([CODE_COVERAGE_LDFLAGS])
+
+ [CODE_COVERAGE_RULES_CHECK='
+ -$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check
+ $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
+']
+ [CODE_COVERAGE_RULES_CAPTURE='
+ $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS)
+ $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS)
+ -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp
+ $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS)
+ @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html"
+']
+ [CODE_COVERAGE_RULES_CLEAN='
+clean: code-coverage-clean
+distclean: code-coverage-clean
+code-coverage-clean:
+ -$(LCOV) --directory $(top_builddir) -z
+ -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY)
+ -find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete
+']
+ ], [
+ [CODE_COVERAGE_RULES_CHECK='
+ @echo "Need to reconfigure with --enable-code-coverage"
+']
+ CODE_COVERAGE_RULES_CAPTURE="$CODE_COVERAGE_RULES_CHECK"
+ CODE_COVERAGE_RULES_CLEAN=''
+ ])
+
+[CODE_COVERAGE_RULES='
+# Code coverage
+#
+# Optional:
+# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting.
+# Multiple directories may be specified, separated by whitespace.
+# (Default: $(top_builddir))
+# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated
+# by lcov for code coverage. (Default:
+# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info)
+# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage
+# reports to be created. (Default:
+# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage)
+# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage,
+# set to 0 to disable it and leave empty to stay with the default.
+# (Default: empty)
+# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov
+# instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
+# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov
+# instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
+# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov
+# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the
+# collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
+# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov
+# instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
+# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering
+# lcov instance. (Default: empty)
+# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov
+# instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
+# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the
+# genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
+# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml
+# instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
+# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore
+#
+# The generated report will be titled using the $(PACKAGE_NAME) and
+# $(PACKAGE_VERSION). In order to add the current git hash to the title,
+# use the git-version-gen script, available online.
+
+# Optional variables
+CODE_COVERAGE_DIRECTORY ?= $(top_builddir)
+CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info
+CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage
+CODE_COVERAGE_BRANCH_COVERAGE ?=
+CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
+--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
+CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
+CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)"
+CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
+CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
+CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?=
+CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
+CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\
+$(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
+--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
+CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
+CODE_COVERAGE_IGNORE_PATTERN ?=
+
+GITIGNOREFILES ?=
+GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
+
+code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V))
+code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY))
+code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\
+ $(CODE_COVERAGE_OUTPUT_FILE);
+code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V))
+code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY))
+code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\
+ $(CODE_COVERAGE_IGNORE_PATTERN);
+code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V))
+code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY))
+code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY);
+code_coverage_quiet = $(code_coverage_quiet_$(V))
+code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY))
+code_coverage_quiet_0 = --quiet
+
+# sanitizes the test-name: replaces with underscores: dashes and dots
+code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1)))
+
+# Use recursive makes in order to ignore errors during check
+check-code-coverage:'"$CODE_COVERAGE_RULES_CHECK"'
+
+# Capture code coverage data
+code-coverage-capture: code-coverage-capture-hook'"$CODE_COVERAGE_RULES_CAPTURE"'
+
+# Hook rule executed before code-coverage-capture, overridable by the user
+code-coverage-capture-hook:
+
+'"$CODE_COVERAGE_RULES_CLEAN"'
+
+A''M_DISTCHECK_CONFIGURE_FLAGS ?=
+A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
+
+.PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean
+']
+
+ AC_SUBST([CODE_COVERAGE_RULES])
+ m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])])
+])
diff --git a/osmo-release.sh b/osmo-release.sh
index 44504360..e947fe43 100755
--- a/osmo-release.sh
+++ b/osmo-release.sh
@@ -9,10 +9,11 @@ fi
ALLOW_NO_LIBVERSION_CHANGE="${ALLOW_NO_LIBVERSION_CHANGE:-0}"
ALLOW_NO_LIBVERSION_DEB_MATCH="${ALLOW_NO_LIBVERSION_DEB_MATCH:-0}"
+ALLOW_NO_LIBVERSION_RPM_MATCH="${ALLOW_NO_LIBVERSION_RPM_MATCH:-0}"
# Test stuff but don't modify stuff:
DRY_RUN="${DRY_RUN:-0}"
-libversion_to_deb_major() {
+libversion_to_lib_major() {
libversion="$1"
current="$(echo "$libversion" | cut -d ":" -f 1)"
#revision="$(echo "$libversion" | cut -d ":" -f 2)"
@@ -21,16 +22,19 @@ libversion_to_deb_major() {
echo "$major"
}
-# Make sure that depedency requirement versions match in configure.ac vs debian/control.
-#eg: "PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.1.0)" vs "libosmocore-dev (>= 1.0.0),"
-check_configureac_debctrl_deps_match() {
+get_configureac_pkg_check_modules_list() {
if [ -f "${GIT_TOPDIR}/openbsc/configure.ac" ]; then
configureac_file="openbsc/configure.ac"
else
configureac_file="configure.ac"
fi
- configureac_list=$(grep -e "PKG_CHECK_MODULES" "${GIT_TOPDIR}/${configureac_file}" | cut -d "," -f 2 | tr -d ")" | tr -d " " | sed "s/>=/ /g")
- echo "$configureac_list" | \
+ grep -e "PKG_CHECK_MODULES(" "${GIT_TOPDIR}/${configureac_file}" | cut -d "," -f 2 | tr -d ")" | tr -d "[" | tr -d "]" | tr -d " " | sed "s/>=/ /g"
+}
+
+# Make sure that depedency requirement versions match in configure.ac vs debian/control.
+#eg: "PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.1.0)" vs "libosmocore-dev (>= 1.0.0),"
+check_configureac_debctrl_deps_match() {
+ get_configureac_pkg_check_modules_list | \
{ return_error=0
while read -r dep ver; do
@@ -41,7 +45,7 @@ check_configureac_debctrl_deps_match() {
if [ "z$debctrl_match_count" != "z1" ]; then
echo "WARN: configure.ac <$dep, $ver> matches debian/control $debctrl_match_count times, manual check required!"
else # 1 match:
- parsed_match=$(echo "$debctrl_match" | tr -d "(" | tr -d ")" | tr -d "," | tr -d " " | sed "s/>=/ /g")
+ parsed_match=$(echo "$debctrl_match" | tr -d "(" | tr -d ")" | tr -d "," | tr -d " " | tr -d "\t" | sed "s/>=/ /g")
debctrl_dep=$(echo "$parsed_match" | cut -d " " -f 1 | sed "s/-dev//g")
debctrl_ver=$(echo "$parsed_match" | cut -d " " -f 2)
if [ "z$dep" != "z$debctrl_dep" ] || [ "z$ver" != "z$debctrl_ver" ]; then
@@ -66,6 +70,51 @@ check_configureac_debctrl_deps_match() {
echo "OK: dependency specific versions in configure.ac and debian/control match"
}
+# Make sure that depedency requirement versions match in configure.ac vs contrib/*.spec.in.
+#eg: "PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.1.0)" vs "pkgconfig(libosmocore-dev) >= 1.0.0,"
+check_configureac_rpmspecin_deps_match() {
+ # Some projects don't have rpm spec files:
+ if [ "z$(find "${GIT_TOPDIR}/contrib" -name "*.spec.in" | wc -l)" = "z0" ]; then
+ echo "INFO: Project has no 'contrib/*.spec.in' files, skipping RPM specific configure.ac dependency version checks"
+ return
+ fi
+
+ get_configureac_pkg_check_modules_list | \
+ { return_error=0
+ while read -r dep ver; do
+
+ rpmspecin_match="$(grep -e "pkgconfig(${dep})" ${GIT_TOPDIR}/contrib/*.spec.in | grep BuildRequires | grep pkgconfig | grep ">=")"
+ rpmspecin_match_count="$(echo "$rpmspecin_match" | grep -c ">=")"
+ if [ "z$rpmspecin_match_count" != "z0" ]; then
+ #echo "Dependency <$dep, $ver> from configure.ac matched in contrib/*.spec.in! ($rpmspecin_match_count)"
+ if [ "z$rpmspecin_match_count" != "z1" ]; then
+ echo "WARN: configure.ac <$dep, $ver> matches contrib/*.spec.in $rpmspecin_match_count times, manual check required!"
+ else # 1 match:
+ parsed_match=$(echo "$rpmspecin_match" | tr -d "(" | tr -d ")" | tr -d ":" | tr -d " " | tr -d "\t" | sed "s/BuildRequires//g" | sed "s/pkgconfig//g" |sed "s/>=/ /g")
+ rpmspecin_dep=$(echo "$parsed_match" | cut -d " " -f 1)
+ rpmspecin_ver=$(echo "$parsed_match" | cut -d " " -f 2)
+ if [ "z$dep" != "z$rpmspecin_dep" ] || [ "z$ver" != "z$rpmspecin_ver" ]; then
+ echo "ERROR: configure.ac <$dep, $ver> does NOT match contrib/*.spec.in <$rpmspecin_dep, $rpmspecin_ver>!"
+ return_error=1
+ #else
+ # echo "OK: configure.ac <$dep, $ver> matches contrib/*.spec.in <$debctrl_dep, $debctrl_ver>"
+ fi
+ fi
+ fi
+ done
+ if [ $return_error -ne 0 ]; then
+ exit 1
+ fi
+ }
+
+ # catch and forward exit from pipe subshell "while read":
+ if [ $? -ne 0 ]; then
+ echo "ERROR: exiting due to previous errors"
+ exit 1
+ fi
+ echo "OK: dependency specific versions in configure.ac and contrib/*.spec.in match"
+}
+
# Make sure that patches under debian/patches/ apply:
check_debian_patch_apply() {
if [ ! -d "${GIT_TOPDIR}/debian/patches" ]; then
@@ -82,79 +131,122 @@ check_debian_patch_apply() {
done
}
-BUMPVER=`command -v bumpversion`
-GIT_TOPDIR="$(git rev-parse --show-toplevel)"
-NEW_VER=`bumpversion --list --current-version $VERSION $REL --allow-dirty | awk -F '=' '{ print $2 }'`
-LIBVERS=`git grep -n LIBVERSION | grep '=' | grep am | grep -v LDFLAGS`
-MAKEMOD=`git diff --cached -GLIBVERSION --stat | grep Makefile.am`
-ISODATE=`date -I`
+libversion_deb_match() {
+ echo "$LIBVERS" | while read -r line; do
+ libversion=$(echo "$line" | cut -d "=" -f 2 | tr -d "[:space:]")
+ major="$(libversion_to_lib_major "$libversion")"
+ file_matches="$(find "${GIT_TOPDIR}/debian" -name "lib*${major}.install" | wc -l)"
+ if [ "z$file_matches" = "z0" ]; then
+ echo "ERROR: Found no matching debian/lib*$major.install file for LIBVERSION=$libversion"
+ exit 1
+ elif [ "z$file_matches" = "z1" ]; then
+ echo "OK: Found matching debian/lib*$major.install for LIBVERSION=$libversion"
+ else
+ echo "WARN: Found $file_matches files matching debian/lib*$major.install for LIBVERSION=$libversion, manual check required!"
+ fi
+ control_matches="$(grep -e "Package" "${GIT_TOPDIR}/debian/control" | grep "lib" | grep "$major$" | wc -l)"
+ if [ "z$control_matches" = "z0" ]; then
+ echo "ERROR: Found no matching Package lib*$major in debian/control for LIBVERSION=$libversion"
+ exit 1
+ elif [ "z$control_matches" = "z1" ]; then
+ echo "OK: Found 'Package: lib*$major' in debian/control for LIBVERSION=$libversion"
+ else
+ echo "WARN: Found $file_matches files matching 'Package: lib*$major' in debian/control for LIBVERSION=$libversion, manual check required!"
+ fi
+
+ dhstrip_lib_total="$(grep -e "dh_strip" "${GIT_TOPDIR}/debian/rules" | grep "\-plib" | wc -l)"
+ dhstrip_lib_matches="$(grep -e "dh_strip" "${GIT_TOPDIR}/debian/rules" | grep "\-plib" | grep "$major" | wc -l)"
+ if [ "z$dhstrip_lib_total" != "z0" ]; then
+ if [ "z$dhstrip_lib_matches" = "z0" ] ; then
+ echo "ERROR: Found no matching 'dh_strip -plib*$major' line in debian/rules for LIBVERSION=$libversion"
+ exit 1
+ elif [ "z$dhstrip_lib_total" = "z1" ]; then
+ echo "OK: Found 'dh_strip -plib*$major' in debian/rules for LIBVERSION=$libversion"
+ else
+ echo "WARN: Found $dhstrip_lib_matches/$dhstrip_lib_total dh_strip matches 'dh_strip -plib*$major' in debian/rules for LIBVERSION=$libversion, manual check required!"
+ fi
+ fi
+ done
+ # catch and forward exit from pipe subshell "while read":
+ if [ $? -ne 0 ]; then
+ exit 1
+ fi
+}
+
+libversion_rpmspecin_match() {
+ # Some projects don't have rpm spec files:
+ if [ "z$(find "${GIT_TOPDIR}/contrib" -name "*.spec.in" | wc -l)" = "z0" ]; then
+ echo "INFO: Project has no 'contrib/*.spec.in' files, skipping RPM specific LIBVERSION checks"
+ return
+ fi
+
+ echo "$LIBVERS" | while read -r line; do
+ libversion=$(echo "$line" | cut -d "=" -f 2 | tr -d "[:space:]")
+ major="$(libversion_to_lib_major "$libversion")"
+
+ control_matches="$(grep -e "%files" "${GIT_TOPDIR}/contrib/"*.spec.in | grep "lib" | grep "$major$" | wc -l)"
+ if [ "z$control_matches" = "z0" ]; then
+ echo "ERROR: Found no matching '%files -n lib*$major' in contrib/*.spec.in for LIBVERSION=$libversion"
+ exit 1
+ elif [ "z$control_matches" = "z1" ]; then
+ echo "OK: Found '%files -n lib*$major' in contrib/*.spec.in for LIBVERSION=$libversion"
+ else
+ echo "WARN: Found $file_matches files matching '%files -n lib*$major' in contrib/*.spec.in for LIBVERSION=$libversion, manual check required!"
+ fi
+
+ control_matches="$(grep -e "_libdir" "${GIT_TOPDIR}/contrib/"*.spec.in | grep "/lib" | grep "so.$major" | wc -l)"
+ if [ "z$control_matches" = "z0" ]; then
+ echo "ERROR: Found no matching '%_libdir/lib*.so.$major*' in contrib/*.spec.in for LIBVERSION=$libversion"
+ exit 1
+ elif [ "z$control_matches" = "z1" ]; then
+ echo "OK: Found '%_libdir/lib*.so.$major*' in contrib/*.spec.in for LIBVERSION=$libversion"
+ else
+ echo "WARN: Found $file_matches files matching '%_libdir/lib*.so.$major*' in contrib/*.spec.in for LIBVERSION=$libversion, manual check required!"
+ fi
+ done
+ # catch and forward exit from pipe subshell "while read":
+ if [ $? -ne 0 ]; then
+ exit 1
+ fi
+}
+
+
+BUMPVER=`command -v bumpversion`
if [ "z$BUMPVER" = "z" ]; then
echo Unable to find 'bumpversion' command.
exit 1
fi
-
+NEW_VER=`$BUMPVER --list --current-version $VERSION $REL --allow-dirty | awk -F '=' '{ print $2 }'`
if [ "z$NEW_VER" = "z" ]; then
echo "Please fix versioning to match http://semver.org/ spec (current is $VERSION) before proceeding."
exit 1
fi
+GIT_TOPDIR="$(git rev-parse --show-toplevel)"
+LIBVERS=`git grep -n LIBVERSION | grep '=' | grep am | grep -v LDFLAGS | grep -v osmo-release.sh`
+MAKEMOD=`git diff --cached -GLIBVERSION --stat | grep Makefile.am`
+ISODATE=`date -I`
echo "Releasing $VERSION -> $NEW_VER..."
check_configureac_debctrl_deps_match
+check_configureac_rpmspecin_deps_match
check_debian_patch_apply
if [ "z$LIBVERS" != "z" ]; then
if [ "z$MAKEMOD" = "z" ] && [ "z$ALLOW_NO_LIBVERSION_CHANGE" = "z0" ]; then
- echo "ERROR: Before releasing, please modify some of the libversions: $LIBVERS"
+ echo "ERROR: Before releasing, please modify some of the libversions:"
+ for l in $LIBVERS; do echo " $l"; done
+ echo "After making changes, add modified file(s) to the index using git-add."
echo "You should NOT be doing this unless you've read and understood following article:"
echo "https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info"
exit 1
fi
if [ "z$ALLOW_NO_LIBVERSION_DEB_MATCH" = "z0" ]; then
- echo "$LIBVERS" | while read -r line; do
- libversion=$(echo "$line" | cut -d "=" -f 2 | tr -d "[:space:]")
- major="$(libversion_to_deb_major "$libversion")"
- file_matches="$(find "${GIT_TOPDIR}/debian" -name "lib*${major}.install" | wc -l)"
- if [ "z$file_matches" = "z0" ]; then
- echo "ERROR: Found no matching debian/lib*$major.install file for LIBVERSION=$libversion"
- exit 1
- elif [ "z$file_matches" = "z1" ]; then
- echo "OK: Found matching debian/lib*$major.install for LIBVERSION=$libversion"
- else
- echo "WARN: Found $file_matches files matching debian/lib*$major.install for LIBVERSION=$libversion, manual check required!"
- fi
-
- control_matches="$(grep -e "Package" "${GIT_TOPDIR}/debian/control" | grep "lib" | grep "$major$" | wc -l)"
- if [ "z$control_matches" = "z0" ]; then
- echo "ERROR: Found no matching Package lib*$major in debian/control for LIBVERSION=$libversion"
- exit 1
- elif [ "z$control_matches" = "z1" ]; then
- echo "OK: Found 'Package: lib*$major' in debian/control for LIBVERSION=$libversion"
- else
- echo "WARN: Found $file_matches files matching 'Package: lib*$major' in debian/control for LIBVERSION=$libversion, manual check required!"
- fi
-
- dhstrip_lib_total="$(grep -e "dh_strip" "${GIT_TOPDIR}/debian/rules" | grep "\-plib" | wc -l)"
- dhstrip_lib_matches="$(grep -e "dh_strip" "${GIT_TOPDIR}/debian/rules" | grep "\-plib" | grep "$major" | wc -l)"
- if [ "z$dhstrip_lib_total" != "z0" ]; then
- if [ "z$dhstrip_lib_matches" = "z0" ] ; then
- echo "ERROR: Found no matching 'dh_strip -plib*$major' line in debian/rules for LIBVERSION=$libversion"
- exit 1
- elif [ "z$dhstrip_lib_total" = "z1" ]; then
- echo "OK: Found 'dh_strip -plib*$major' in debian/rules for LIBVERSION=$libversion"
- else
- echo "WARN: Found $dhstrip_lib_matches/$dhstrip_lib_total dh_strip matches 'dh_strip -plib*$major' in debian/rules for LIBVERSION=$libversion, manual check required!"
- fi
- fi
- done
- # catch and forward exit from pipe subshell "while read":
- if [ $? -ne 0 ]; then
- exit 1
- fi
+ libversion_deb_match
fi
- if [ "z$DRY_RUN" != "z0" ]; then
- exit 0
+ if [ "z$ALLOW_NO_LIBVERSION_RPM_MATCH" = "z0" ]; then
+ libversion_rpmspecin_match
fi
fi
@@ -162,16 +254,34 @@ if [ "z$DRY_RUN" != "z0" ]; then
exit 0
fi
+set -e
if [ -f "TODO-RELEASE" ]; then
- grep '#' TODO-RELEASE > TODO-RELEASE.clean
+ grep '#' TODO-RELEASE > TODO-RELEASE.clean || true
mv TODO-RELEASE.clean TODO-RELEASE
git add TODO-RELEASE
fi
-gbp dch --debian-tag='%(version)s' --auto --meta --git-author --multimaint-merge --ignore-branch --new-version="$NEW_VER"
+# Add missing epoch (OS#5046)
+DEB_VER=$(head -1 debian/changelog | cut -d ' ' -f 2 | sed 's,(,,' | sed 's,),,')
+NEW_VER_WITH_EPOCH="$NEW_VER"
+case "$DEB_VER" in
+*:*)
+ epoch="$(echo "$DEB_VER" | cut -d: -f1)"
+ NEW_VER_WITH_EPOCH="$epoch:$NEW_VER"
+ ;;
+esac
+
+gbp dch \
+ --debian-tag='%(version)s' \
+ --auto \
+ --meta \
+ --git-author \
+ --multimaint-merge \
+ --ignore-branch \
+ --new-version="$NEW_VER_WITH_EPOCH"
dch -r -m --distribution "unstable" ""
git add ${GIT_TOPDIR}/debian/changelog
-bumpversion --current-version $VERSION $REL --tag --commit --tag-name $NEW_VER --allow-dirty
+$BUMPVER --current-version $VERSION $REL --tag --commit --tag-name $NEW_VER --allow-dirty
git commit --amend # let the user add extra information to the release commit.
git tag -s $NEW_VER -f -m "Release v$NEW_VER on $ISODATE."
echo "Release $NEW_VER prepared, tagged and signed."
diff --git a/src/Makefile.am b/src/Makefile.am
index 99432811..86066466 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,69 +1,13 @@
-# This is _NOT_ the library release version, it's an API version.
-# Please read chapter "Library interface versions" of the libtool documentation
-# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
-LIBVERSION=14:0:2
-
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS)
-
-if ENABLE_PSEUDOTALLOC
-AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
-endif
-
-lib_LTLIBRARIES = libosmocore.la
-
-libosmocore_la_LIBADD = $(BACKTRACE_LIB) $(TALLOC_LIBS) $(LIBRARY_RT) $(PTHREAD_LIBS) $(LIBSCTP_LIBS)
-libosmocore_la_SOURCES = context.c timer.c timer_gettimeofday.c timer_clockgettime.c \
- select.c signal.c msgb.c bits.c \
- bitvec.c bitcomp.c counter.c fsm.c \
- write_queue.c utils.c socket.c \
- logging.c logging_syslog.c logging_gsmtap.c rate_ctr.c \
- gsmtap_util.c crc16.c panic.c backtrace.c \
- conv.c application.c rbtree.c strrb.c \
- loggingrb.c crc8gen.c crc16gen.c crc32gen.c crc64gen.c \
- macaddr.c stat_item.c stats.c stats_statsd.c prim.c \
- conv_acc.c conv_acc_generic.c sercomm.c prbs.c \
- isdnhdlc.c \
- tdef.c \
- sockaddr_str.c \
- use_count.c \
- $(NULL)
-
-if HAVE_SSSE3
-libosmocore_la_SOURCES += conv_acc_sse.c
-if HAVE_SSE4_1
-conv_acc_sse.lo : AM_CFLAGS += -mssse3 -msse4.1
-else
-conv_acc_sse.lo : AM_CFLAGS += -mssse3
-endif
-
-if HAVE_AVX2
-libosmocore_la_SOURCES += conv_acc_sse_avx.c
-if HAVE_SSE4_1
-conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2 -msse4.1
-else
-conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2
-endif
-endif
-endif
-
-BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c
-EXTRA_DIST = conv_acc_sse_impl.h
-
-libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
-
-if ENABLE_PLUGIN
-libosmocore_la_SOURCES += plugin.c
-libosmocore_la_LIBADD += $(LIBRARY_DLOPEN)
-endif
-
-if ENABLE_MSGFILE
-libosmocore_la_SOURCES += msgfile.c
-endif
-
-if ENABLE_SERIAL
-libosmocore_la_SOURCES += serial.c
-endif
-
-crc%gen.c: crcXXgen.c.tpl
- $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
+SUBDIRS = \
+ core \
+ vty \
+ isdn \
+ codec \
+ gsm \
+ coding \
+ gb \
+ ctrl \
+ pseudotalloc \
+ sim \
+ usb \
+ $(NULL) \ No newline at end of file
diff --git a/src/codec/Makefile.am b/src/codec/Makefile.am
index c9d7a228..bb01b9d3 100644
--- a/src/codec/Makefile.am
+++ b/src/codec/Makefile.am
@@ -1,10 +1,10 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
-LIBVERSION=1:1:1
+LIBVERSION=4:0:0
-AM_CPPFLAGS = -I$(top_srcdir)/include $(TALLOC_CFLAGS)
-AM_CFLAGS = -Wall
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
if ENABLE_PSEUDOTALLOC
AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
@@ -13,6 +13,14 @@ endif
lib_LTLIBRARIES = libosmocodec.la
-libosmocodec_la_SOURCES = gsm610.c gsm620.c gsm660.c gsm690.c ecu.c ecu_fr.c
+libosmocodec_la_SOURCES = \
+ gsm610.c \
+ gsm620.c \
+ gsm660.c \
+ gsm690.c \
+ ecu.c \
+ ecu_fr.c \
+ ecu_fr_old.c \
+ $(NULL)
libosmocodec_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
-libosmocodec_la_LIBADD = $(top_builddir)/src/libosmocore.la
+libosmocodec_la_LIBADD = $(top_builddir)/src/core/libosmocore.la
diff --git a/src/codec/ecu.c b/src/codec/ecu.c
index db7148ce..cdb3e62e 100644
--- a/src/codec/ecu.c
+++ b/src/codec/ecu.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/* As the developer and copyright holder of the related code, I hereby
@@ -48,7 +44,7 @@ static const struct osmo_ecu_ops *g_ecu_ops[_NUM_OSMO_ECU_CODECS];
/*! initialize an ECU instance for given codec.
* \param[in] ctx talloc context from which to allocate
- * \parma[in] codec codec for which to initialize/create ECU */
+ * \param[in] codec codec for which to initialize/create ECU */
struct osmo_ecu_state *osmo_ecu_init(void *ctx, enum osmo_ecu_codec codec)
{
if (codec >= ARRAY_SIZE(g_ecu_ops))
@@ -100,6 +96,20 @@ int osmo_ecu_frame_out(struct osmo_ecu_state *st, uint8_t *frame_out)
return g_ecu_ops[st->codec]->frame_out(st, frame_out);
}
+/*! check if the current state of this ECU is a DTX pause.
+ * \param[in] st ECU state/instance on which to operate
+ * \return true if DTX pause, false otherwise */
+bool osmo_ecu_is_dtx_pause(struct osmo_ecu_state *st)
+{
+ if (st->codec >= ARRAY_SIZE(g_ecu_ops))
+ return false;
+ if (!g_ecu_ops[st->codec])
+ return false;
+ if (!g_ecu_ops[st->codec]->is_dtx_pause)
+ return false;
+ return g_ecu_ops[st->codec]->is_dtx_pause(st);
+}
+
/***********************************************************************
* low-level API for ECU implementations
***********************************************************************/
diff --git a/src/codec/ecu_fr.c b/src/codec/ecu_fr.c
index 4545172a..45ba0cc8 100644
--- a/src/codec/ecu_fr.c
+++ b/src/codec/ecu_fr.c
@@ -1,9 +1,15 @@
/*
* (C) 2017 by sysmocom - s.f.m.c. GmbH
* (C) 2017 by Philipp Maier <pmaier@sysmocom.de>
- *
* All Rights Reserved
*
+ * Significantly reworked in 2023 by Mother
+ * Mychaela N. Falconia <falcon@freecalypso.org> - however,
+ * Mother Mychaela's contributions are NOT subject to copyright.
+ * No rights reserved, all rights relinquished.
+ * Portions of this code are based on Themyscira libgsmfrp,
+ * a public domain library by the same 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
@@ -14,10 +20,46 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
+ * The present ECU implementation for GSM-FR is closely based on the
+ * TS 46.011 spec from 3GPP; more specifically, it is based on the
+ * Example solution presented in Chapter 6 of that spec, adapted for
+ * libosmocodec ECU architecture, and comes as close to fulfilling
+ * the spec's officially stated requirements (Chapter 5) as is
+ * possible within this Osmocom-imposed architecture. Please note
+ * the following areas where the present implementation fails to
+ * fulfill the original intent of GSM spec authors:
+ *
+ * - The "lost SID" criterion, defined in GSM 06.31, is based on the
+ * TAF bit from the Radio Subsystem. However, libosmocodec ECU API
+ * does not include this flag, thus spec requirements related to
+ * lost SID conditions cannot be implemented in a strictly compliant
+ * manner. The present implementation improvises its own "lost SID"
+ * detector (not strictly spec-compliant) by counting frame_out()
+ * calls in between good traffic frame inputs via frame_in().
+ *
+ * - In the architecture envisioned and assumed in the GSM specs,
+ * the ECU function of GSM 06.11 was never intended to be a fully
+ * modular component with its own bona fide I/O interfaces - this
+ * approach appears to be an Osmocom invention - instead this ECU
+ * function was intended to be subsumed in the Rx DTX handler
+ * component of GSM 06.31, also incorporating the comfort noise
+ * generator of GSM 06.12 - and unlike the narrower-scope ECU,
+ * this slightly-larger-scope Rx DTX handler is a modular component
+ * with well-defined I/O interfaces. In the case of BFI conditions
+ * following a SID, GSM 06.11 spec was written with the assumption
+ * that the ECU controls the comfort noise generator via internal
+ * signals, as opposed to emitting "corrected" SID frames on a
+ * modular interface going to a CN generator located somewhere else.
+ * Thus the "correct" behavior for a fully modularized ECU is unclear,
+ * and an argument can be made that the very existence of such a
+ * fully modularized ECU is incorrect in itself. The present
+ * implementation re-emits a "rejuvenated" form of the last saved
+ * SID frame during BFI conditions following a SID within the
+ * permitted window of 48 frames, then starts emitting muted SIDs
+ * with Xmaxc decreasing by 4 on each frame, and finally switches
+ * to emitting non-SID silence frames (Table 1 of TS 46.011)
+ * once Xmaxc reaches 0.
*/
#include <stdbool.h>
@@ -25,144 +67,216 @@
#include <stdint.h>
#include <errno.h>
-#include <osmocom/core/bitvec.h>
+#include <osmocom/core/prbs.h>
-#include <osmocom/codec/gsm610_bits.h>
#include <osmocom/codec/codec.h>
#include <osmocom/codec/ecu.h>
+#include <osmocom/core/linuxlist.h>
+
+/* See TS 46.011, Chapter 6 Example solution */
+#define GSM611_XMAXC_REDUCE 4
+
+/* The first 5 bytes of RTP encoding neatly contain the magic nibble
+ * and LARc parameters, which also happens to be the part of SID frames
+ * that needs to be passed through as-is. */
+#define SID_PREFIX_LEN 5
+
+enum ecu_principal_state {
+ STATE_NO_DATA,
+ STATE_SPEECH,
+ STATE_SP_MUTING,
+ STATE_SID,
+ STATE_SID_MUTING,
+};
-/* See also GSM 06.11, chapter 6 Example solution */
-#define GSM610_XMAXC_REDUCE 4
-#define GSM610_XMAXC_LEN 6
+struct fr_ecu_state {
+ struct osmo_ecu_state ecu_state;
+ enum ecu_principal_state pr_state;
+ uint8_t speech_frame[GSM_FR_BYTES];
+ uint8_t sid_prefix[SID_PREFIX_LEN];
+ uint8_t sid_xmaxc;
+ uint8_t sid_reemit_count;
+ struct osmo_prbs_state prng;
+ bool last_input_was_sid;
+};
-/**
- * Reduce the XMAXC field. When the XMAXC field reaches
- * zero the function will return true.
+/* This function is the frame input to the ECU - all inputs to this
+ * function have been received by the Radio Subsystem as good traffic
+ * frames in the GSM 06.31 definition.
*/
-static bool reduce_xmaxcr(struct bitvec *frame_bitvec,
- const unsigned int index)
+static void fr_ecu_input(struct fr_ecu_state *fr, const uint8_t *frame)
{
- unsigned int field_index;
- uint64_t field;
-
- field_index = index;
- field = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
- if (field > GSM610_XMAXC_REDUCE)
- field -= GSM610_XMAXC_REDUCE;
- else
- field = 0;
-
- field_index = index;
- bitvec_write_field(frame_bitvec, &field_index, field, GSM610_XMAXC_LEN);
-
- return field == 0;
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_fr_sid_classify(frame);
+ switch (sidc) {
+ case OSMO_GSM631_SID_CLASS_SPEECH:
+ memcpy(fr->speech_frame, frame, GSM_FR_BYTES);
+ fr->pr_state = STATE_SPEECH;
+ fr->last_input_was_sid = false;
+ return;
+ case OSMO_GSM631_SID_CLASS_INVALID:
+ /* GSM 06.31 section 6.1.2 says: "an invalid SID frame
+ * shall be substituted by the last valid SID frame
+ * and the procedure for valid SID frames be applied."
+ * However, libosmocodec ECU architecture prevents us
+ * from doing what the spec says: the frame_in() method
+ * gets a const frame that can't be modified, and
+ * frame_out() will never get called when BFI=0, even
+ * when the "good traffic frame" (in the BFI=0 sense)
+ * is an invalid SID by the bit-counting rule.
+ * Thus there is no place where we can re-emit a cached
+ * copy of the last valid SID upon receiving an invalid SID.
+ *
+ * In the standard GSM architecture this problem never
+ * arises because the ECU is not a separate component
+ * but is coupled with the CN generator, thus the output
+ * from the Rx DTX handler block will be a CN frame,
+ * for both valid-SID and invalid-SID inputs to the block.
+ * But what can we do within the constraints of libosmocodec
+ * ECU framework? We treat the invalid SID almost like a
+ * BFI, doing almost nothing in the frame_in() method,
+ * but we reset sid_reemit_count because by the rules of
+ * GSM 06.31 an invalid SID is still an accepted SID frame
+ * for the purpose of "lost SID" logic. */
+ fr->sid_reemit_count = 0;
+ fr->last_input_was_sid = true;
+ return;
+ case OSMO_GSM631_SID_CLASS_VALID:
+ /* save LARc part */
+ memcpy(fr->sid_prefix, frame, SID_PREFIX_LEN);
+ /* save Xmaxc from the last subframe */
+ fr->sid_xmaxc = ((frame[27] & 0x1F) << 1) | (frame[28] >> 7);
+ fr->pr_state = STATE_SID;
+ fr->sid_reemit_count = 0;
+ fr->last_input_was_sid = true;
+ return;
+ default:
+ /* There are only 3 possible SID classifications per GSM 06.31
+ * section 6.1.1, thus any other return value is a grave error
+ * in the code. */
+ OSMO_ASSERT(0);
+ }
}
-/**
- * Reduce all XMAXC fields in the frame. When all XMAXC fields
- * reach zero, then the function will return true.
+/* Reduce all 4 Xmaxc fields in the frame. When all 4 Xmaxc fields
+ * reach 0, the function will return true for "mute".
*/
-static bool reduce_xmaxcr_all(struct bitvec *frame_bitvec)
+static bool reduce_xmaxc(uint8_t *frame)
{
- bool silent = true;
-
- silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC00);
- silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC10);
- silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC20);
- silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC30);
-
- return silent;
+ bool mute_flag = true;
+ uint8_t sub, xmaxc;
+
+ for (sub = 0; sub < 4; sub++) {
+ xmaxc = ((frame[sub*7+6] & 0x1F) << 1) | (frame[sub*7+7] >> 7);
+ if (xmaxc > GSM611_XMAXC_REDUCE) {
+ xmaxc -= GSM611_XMAXC_REDUCE;
+ mute_flag = false;
+ } else
+ xmaxc = 0;
+ frame[sub*7+6] &= 0xE0;
+ frame[sub*7+6] |= xmaxc >> 1;
+ frame[sub*7+7] &= 0x7F;
+ frame[sub*7+7] |= (xmaxc & 1) << 7;
+ }
+ return mute_flag;
}
-/* Use certain modifications to conceal the errors in a full rate frame */
-static int conceal_frame(uint8_t *frame)
+/* TS 46.011 chapter 6, paragraph 4, last sentence: "The grid position
+ * parameters are chosen randomly between 0 and 3 during this time."
+ * (The "during this time" qualifier refers to the speech muting state.)
+ * This sentence in the spec must have been overlooked by previous ECU
+ * implementors, as this aspect of the muting logic was missing.
+ */
+static void random_grid_pos(struct fr_ecu_state *fr, uint8_t *frame)
{
- struct bitvec *frame_bitvec;
- unsigned int len;
- bool silent;
- int rc = 0;
-
- /* In case we already deal with a silent frame,
- * there is nothing to, we just abort immediately */
- if (osmo_fr_check_sid(frame, GSM_FR_BYTES))
- return 0;
-
- /* Attempt to allocate memory for bitvec */
- frame_bitvec = bitvec_alloc(GSM_FR_BYTES, NULL);
- if (!frame_bitvec)
- return -ENOMEM;
+ uint8_t sub;
- /* Convert a frame to bitvec */
- len = bitvec_unpack(frame_bitvec, frame);
- if (len != GSM_FR_BYTES) {
- rc = -EIO;
- goto leave;
- }
-
- /* Fudge frame parameters */
- silent = reduce_xmaxcr_all(frame_bitvec);
-
- /* If we reached silence level, mute the frame
- * completely, this also means that we can
- * save the bitvec_pack operation */
- if (silent) {
- memset(frame, 0x00, GSM_FR_BYTES);
- frame[0] = 0xd0;
- goto leave;
+ for (sub = 0; sub < 4; sub++) {
+ frame[sub*7+6] &= 0x9F;
+ frame[sub*7+6] |= osmo_prbs_get_ubit(&fr->prng) << 6;
+ frame[sub*7+6] |= osmo_prbs_get_ubit(&fr->prng) << 5;
}
+}
- /* Convert back to packed byte form */
- len = bitvec_pack(frame_bitvec, frame);
- if (len != GSM_FR_BYTES) {
- rc = -EIO;
- goto leave;
+/* Like reduce_xmaxc() above, but for comfort noise rather than speech. */
+static bool reduce_xmaxc_sid(struct fr_ecu_state *fr)
+{
+ if (fr->sid_xmaxc > GSM611_XMAXC_REDUCE) {
+ fr->sid_xmaxc -= GSM611_XMAXC_REDUCE;
+ return false;
}
-
-leave:
- bitvec_free(frame_bitvec);
- return rc;
+ fr->sid_xmaxc = 0;
+ return true;
}
-/*!
- * To be called when a good frame is received.
- * This function will then create a backup of the frame
- * and reset the internal state.
- * \param[in] state The state object for the ECU
- * \param[out] frame The valid frame (GSM_FR_BYTES bytes in RTP payload format)
+/* This function implements the part which is peculiar to the present
+ * "standalone" packaging of GSM-FR ECU, without a directly coupled
+ * comfort noise generator - it re-emits synthetic SID frames during
+ * DTX pauses, initially unchanged from the saved SID and later muted.
*/
-void osmo_ecu_fr_reset(struct osmo_ecu_fr_state *state, const uint8_t *frame)
+static void reemit_sid(struct fr_ecu_state *fr, uint8_t *frame)
{
- state->subsequent_lost_frame = false;
- memcpy(state->frame_backup, frame, GSM_FR_BYTES);
+ uint8_t *p, sub;
+
+ memcpy(frame, fr->sid_prefix, SID_PREFIX_LEN);
+ p = frame + SID_PREFIX_LEN;
+ for (sub = 0; sub < 4; sub++) {
+ *p++ = 0;
+ *p++ = fr->sid_xmaxc >> 1;
+ *p++ = (fr->sid_xmaxc & 1) << 7;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ }
}
-/*!
- * To be called when a bad frame is received.
- * This function will then generate a replacement frame
- * that can be used to conceal the dropout.
- * \param[in] state The state object for the ECU
- * \param[out] frame The buffer to fill with GSM_FR_BYTES of replacement frame
- * \returns 0 if the frame was sucessfully filled
+/* This function is responsible for generating the ECU's output
+ * in the event that the Radio Subsystem does not have a good
+ * traffic frame - conditions corresponding to BFI=1 in the specs.
*/
-int osmo_ecu_fr_conceal(struct osmo_ecu_fr_state *state, uint8_t *frame)
+static void fr_ecu_output(struct fr_ecu_state *fr, uint8_t *frame)
{
- int rc;
-
- /* For subsequent frames we run the error concealment
- * functions on the backed up frame before we restore
- * the backup */
- if (state->subsequent_lost_frame) {
- rc = conceal_frame(state->frame_backup);
- if (rc)
- return rc;
+ bool mute;
+
+ switch (fr->pr_state) {
+ case STATE_NO_DATA:
+ memcpy(frame, osmo_gsm611_silence_frame, GSM_FR_BYTES);
+ return;
+ case STATE_SPEECH:
+ /* TS 46.011 chapter 6: "The first lost speech frame is
+ * replaced at the speech decoder input by the previous
+ * good speech frame." */
+ memcpy(frame, fr->speech_frame, GSM_FR_BYTES);
+ fr->pr_state = STATE_SP_MUTING;
+ return;
+ case STATE_SP_MUTING:
+ mute = reduce_xmaxc(fr->speech_frame);
+ memcpy(frame, fr->speech_frame, GSM_FR_BYTES);
+ random_grid_pos(fr, frame);
+ if (mute)
+ fr->pr_state = STATE_NO_DATA;
+ return;
+ case STATE_SID:
+ fr->sid_reemit_count++;
+ if (fr->sid_reemit_count >= 48) {
+ fr->pr_state = STATE_SID_MUTING;
+ reduce_xmaxc_sid(fr);
+ }
+ reemit_sid(fr, frame);
+ return;
+ case STATE_SID_MUTING:
+ if (reduce_xmaxc_sid(fr)) {
+ fr->pr_state = STATE_NO_DATA;
+ memcpy(frame, osmo_gsm611_silence_frame, GSM_FR_BYTES);
+ } else
+ reemit_sid(fr, frame);
+ return;
+ default:
+ /* a severe bug in the state machine! */
+ OSMO_ASSERT(0);
}
-
- /* Restore the backed up frame and set flag in case
- * we receive even more bad frames */
- memcpy(frame, state->frame_backup, GSM_FR_BYTES);
- state->subsequent_lost_frame = true;
-
- return 0;
}
/***********************************************************************
@@ -171,44 +285,57 @@ int osmo_ecu_fr_conceal(struct osmo_ecu_fr_state *state, uint8_t *frame)
static struct osmo_ecu_state *ecu_fr_init(void *ctx, enum osmo_ecu_codec codec)
{
- struct osmo_ecu_state *st;
- size_t size = sizeof(*st) + sizeof(struct osmo_ecu_fr_state);
+ struct fr_ecu_state *fr;
- st = talloc_named_const(ctx, size, "ecu_state_FR");
- if (!st)
- return NULL;
+ fr = talloc_zero(ctx, struct fr_ecu_state);
+ fr->ecu_state.codec = codec;
+ fr->pr_state = STATE_NO_DATA;
+ osmo_prbs_state_init(&fr->prng, &osmo_prbs15);
- memset(st, 0, size);
- st->codec = codec;
+ return (struct osmo_ecu_state *) fr;
+}
- return st;
+static inline struct fr_ecu_state *_osmo_ecu_state_get_fr(struct osmo_ecu_state *st)
+{
+ return (struct fr_ecu_state *)container_of(st, struct fr_ecu_state, ecu_state);
}
static int ecu_fr_frame_in(struct osmo_ecu_state *st, bool bfi, const uint8_t *frame,
unsigned int frame_bytes)
{
- struct osmo_ecu_fr_state *fr = (struct osmo_ecu_fr_state *) &st->data;
+ struct fr_ecu_state *fr = _osmo_ecu_state_get_fr(st);
+
if (bfi)
return 0;
+ if (frame_bytes != GSM_FR_BYTES)
+ return 0;
+ if ((frame[0] & 0xF0) != 0xD0)
+ return 0;
- osmo_ecu_fr_reset(fr, frame);
+ fr_ecu_input(fr, frame);
return 0;
}
static int ecu_fr_frame_out(struct osmo_ecu_state *st, uint8_t *frame_out)
{
- struct osmo_ecu_fr_state *fr = (struct osmo_ecu_fr_state *) &st->data;
+ struct fr_ecu_state *fr = _osmo_ecu_state_get_fr(st);
+
+ fr_ecu_output(fr, frame_out);
+ return GSM_FR_BYTES;
+}
+
+static bool ecu_fr_is_dtx_pause(struct osmo_ecu_state *st)
+{
+ struct fr_ecu_state *fr = _osmo_ecu_state_get_fr(st);
- if (osmo_ecu_fr_conceal(fr, frame_out) == 0)
- return GSM_FR_BYTES;
- else
- return -1;
+ return fr->last_input_was_sid;
}
static const struct osmo_ecu_ops osmo_ecu_ops_fr = {
.init = ecu_fr_init,
.frame_in = ecu_fr_frame_in,
.frame_out = ecu_fr_frame_out,
+ .is_dtx_pause = ecu_fr_is_dtx_pause,
};
static __attribute__((constructor)) void on_dso_load_ecu_fr(void)
diff --git a/src/codec/ecu_fr_old.c b/src/codec/ecu_fr_old.c
new file mode 100644
index 00000000..cfefce14
--- /dev/null
+++ b/src/codec/ecu_fr_old.c
@@ -0,0 +1,166 @@
+/*
+ * (C) 2017 by sysmocom - s.f.m.c. GmbH
+ * (C) 2017 by Philipp Maier <pmaier@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * This module implements legacy, deprecated osmo_ecu_fr_reset() and
+ * osmo_ecu_fr_conceal() functions only - see ecu_fr.c for the new
+ * GSM-FR ECU implementation.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bitvec.h>
+
+#include <osmocom/codec/gsm610_bits.h>
+#include <osmocom/codec/codec.h>
+#include <osmocom/codec/ecu.h>
+
+/* See also GSM 06.11, chapter 6 Example solution */
+#define GSM610_XMAXC_REDUCE 4
+#define GSM610_XMAXC_LEN 6
+
+/**
+ * Reduce the XMAXC field. When the XMAXC field reaches
+ * zero the function will return true.
+ */
+static bool reduce_xmaxcr(struct bitvec *frame_bitvec,
+ const unsigned int index)
+{
+ unsigned int field_index;
+ uint64_t field;
+
+ field_index = index;
+ field = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
+ if (field > GSM610_XMAXC_REDUCE)
+ field -= GSM610_XMAXC_REDUCE;
+ else
+ field = 0;
+
+ field_index = index;
+ bitvec_write_field(frame_bitvec, &field_index, field, GSM610_XMAXC_LEN);
+
+ return field == 0;
+}
+
+/**
+ * Reduce all XMAXC fields in the frame. When all XMAXC fields
+ * reach zero, then the function will return true.
+ */
+static bool reduce_xmaxcr_all(struct bitvec *frame_bitvec)
+{
+ bool silent = true;
+
+ silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC00);
+ silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC10);
+ silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC20);
+ silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC30);
+
+ return silent;
+}
+
+/* Use certain modifications to conceal the errors in a full rate frame */
+static int conceal_frame(uint8_t *frame)
+{
+ struct bitvec *frame_bitvec;
+ unsigned int len;
+ bool silent;
+ int rc = 0;
+
+ /* In case we already deal with a silent frame,
+ * there is nothing to, we just abort immediately */
+ if (osmo_fr_check_sid(frame, GSM_FR_BYTES))
+ return 0;
+
+ /* Attempt to allocate memory for bitvec */
+ frame_bitvec = bitvec_alloc(GSM_FR_BYTES, NULL);
+ if (!frame_bitvec)
+ return -ENOMEM;
+
+ /* Convert a frame to bitvec */
+ len = bitvec_unpack(frame_bitvec, frame);
+ if (len != GSM_FR_BYTES) {
+ rc = -EIO;
+ goto leave;
+ }
+
+ /* Fudge frame parameters */
+ silent = reduce_xmaxcr_all(frame_bitvec);
+
+ /* If we reached silence level, mute the frame
+ * completely, this also means that we can
+ * save the bitvec_pack operation */
+ if (silent) {
+ memset(frame, 0x00, GSM_FR_BYTES);
+ frame[0] = 0xd0;
+ goto leave;
+ }
+
+ /* Convert back to packed byte form */
+ len = bitvec_pack(frame_bitvec, frame);
+ if (len != GSM_FR_BYTES) {
+ rc = -EIO;
+ goto leave;
+ }
+
+leave:
+ bitvec_free(frame_bitvec);
+ return rc;
+}
+
+/*!
+ * To be called when a good frame is received.
+ * This function will then create a backup of the frame
+ * and reset the internal state.
+ * \param[in] state The state object for the ECU
+ * \param[out] frame The valid frame (GSM_FR_BYTES bytes in RTP payload format)
+ */
+void osmo_ecu_fr_reset(struct osmo_ecu_fr_state *state, const uint8_t *frame)
+{
+ state->subsequent_lost_frame = false;
+ memcpy(state->frame_backup, frame, GSM_FR_BYTES);
+}
+
+/*!
+ * To be called when a bad frame is received.
+ * This function will then generate a replacement frame
+ * that can be used to conceal the dropout.
+ * \param[in] state The state object for the ECU
+ * \param[out] frame The buffer to fill with GSM_FR_BYTES of replacement frame
+ * \returns 0 if the frame was successfully filled
+ */
+int osmo_ecu_fr_conceal(struct osmo_ecu_fr_state *state, uint8_t *frame)
+{
+ int rc;
+
+ /* For subsequent frames we run the error concealment
+ * functions on the backed up frame before we restore
+ * the backup */
+ if (state->subsequent_lost_frame) {
+ rc = conceal_frame(state->frame_backup);
+ if (rc)
+ return rc;
+ }
+
+ /* Restore the backed up frame and set flag in case
+ * we receive even more bad frames */
+ memcpy(frame, state->frame_backup, GSM_FR_BYTES);
+ state->subsequent_lost_frame = true;
+
+ return 0;
+}
diff --git a/src/codec/gsm610.c b/src/codec/gsm610.c
index a05eabab..5cc4f148 100644
--- a/src/codec/gsm610.c
+++ b/src/codec/gsm610.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -300,6 +296,35 @@ const uint16_t gsm610_bitorder[260] = {
29, /* LARc5:0 */
};
+/*
+ * Table 1 in section 6 of 3GPP TS 46.011 (substitution and muting of lost
+ * frames) specifies a silence frame in the form of GSM 06.10 parameters;
+ * the following const datum is this GSM 06.11 silence frame in GSM-FR
+ * RTP encoding.
+ */
+const uint8_t osmo_gsm611_silence_frame[GSM_FR_BYTES] = {
+ 0xDA, 0xA7, 0xAA, 0xA5, 0x1A,
+ 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+ 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+ 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+ 0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
+};
+
+static const uint16_t sid_code_word_bits[95] = {
+ /* bit numbers are relative to the RTP frame beginning,
+ * with signature bits included in the count. */
+ 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73,
+ 75, 76, 78, 79, 81, 82, 84, 85, 87, 88, 90, 91,
+ 93, 94, 113, 114, 116, 117, 119, 120, 122, 123,
+ 125, 126, 128, 129, 131, 132, 134, 135, 137,
+ 138, 140, 141, 143, 144, 146, 147, 149, 150,
+ 169, 170, 172, 173, 175, 176, 178, 179, 181,
+ 182, 184, 185, 187, 188, 190, 191, 193, 194,
+ 196, 197, 199, 200, 202, 203, 205, 206, 225,
+ 226, 228, 229, 231, 232, 234, 235, 237, 240,
+ 243, 246, 249, 252, 255, 258, 261
+};
+
/*! Check whether RTP frame contains FR SID code word according to
* TS 101 318 §5.1.2
* \param[in] rtp_payload Buffer with RTP payload
@@ -309,16 +334,7 @@ const uint16_t gsm610_bitorder[260] = {
bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
{
struct bitvec bv;
- uint16_t i, z_bits[] = { 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73,
- 75, 76, 78, 79, 81, 82, 84, 85, 87, 88, 90, 91,
- 93, 94, 113, 114, 116, 117, 119, 120, 122, 123,
- 125, 126, 128, 129, 131, 132, 134, 135, 137,
- 138, 140, 141, 143, 144, 146, 147, 149, 150,
- 169, 170, 172, 173, 175, 176, 178, 179, 181,
- 182, 184, 185, 187, 188, 190, 191, 193, 194,
- 196, 197, 199, 200, 202, 203, 205, 206, 225,
- 226, 228, 229, 231, 232, 234, 235, 237, 240,
- 243, 246, 249, 252, 255, 258, 261 };
+ uint16_t i;
/* signature does not match Full Rate SID */
if ((rtp_payload[0] >> 4) != 0xD)
@@ -327,10 +343,120 @@ bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
bv.data = (uint8_t *) rtp_payload;
bv.data_len = payload_len;
- /* code word is all 0 at given bits, numbered from 1 */
- for (i = 0; i < ARRAY_SIZE(z_bits); i++)
- if (bitvec_get_bit_pos(&bv, z_bits[i]) != ZERO)
+ /* code word is all 0 at given bits */
+ for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
+ if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]) != ZERO)
return false;
+ }
return true;
}
+
+/*! Classify potentially-SID FR codec frame in RTP format according
+ * to the rules of GSM 06.31 §6.1.1
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \returns enum osmo_gsm631_sid_class, with symbolic values
+ * OSMO_GSM631_SID_CLASS_SPEECH, OSMO_GSM631_SID_CLASS_INVALID or
+ * OSMO_GSM631_SID_CLASS_VALID corresponding to the 3 possible bit-counting
+ * classifications prescribed by the spec.
+ *
+ * Differences between the more familiar osmo_fr_check_sid() and the present
+ * function are:
+ *
+ * 1. osmo_fr_check_sid() returns true only if the SID frame is absolutely
+ * perfect, with all 95 bits of the SID code word zeroed. However, the
+ * rules of GSM 06.31 §6.1.1 allow up to one bit to be in error,
+ * and the frame is still accepted as valid SID.
+ *
+ * 2. The third possible state of invalid SID is not handled at all by the
+ * simpler osmo_fr_check_sid() function.
+ *
+ * 3. osmo_fr_check_sid() includes a check for 0xD RTP signature, and returns
+ * false if that signature nibble is wrong. That check is not included
+ * in the present version because there is no place for it in the
+ * ETSI-prescribed classification, it is neither speech nor SID. The
+ * assumption is that this function is used to classify the bit content
+ * of received codec frames, not their RTP encoding - the latter needs
+ * to be validated beforehand.
+ *
+ * Which function should one use? The answer depends on the specific
+ * circumstances, and needs to be addressed on a case-by-case basis.
+ */
+enum osmo_gsm631_sid_class osmo_fr_sid_classify(const uint8_t *rtp_payload)
+{
+ struct bitvec bv;
+ uint16_t i, n;
+
+ bv.data = (uint8_t *) rtp_payload;
+ bv.data_len = GSM_FR_BYTES;
+
+ /* count not-SID-matching bits per the spec */
+ n = 0;
+ for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
+ if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]) != ZERO)
+ n++;
+ if (n >= 16)
+ return OSMO_GSM631_SID_CLASS_SPEECH;
+ }
+ if (n >= 2)
+ return OSMO_GSM631_SID_CLASS_INVALID;
+ else
+ return OSMO_GSM631_SID_CLASS_VALID;
+}
+
+/*! Reset the SID field and the unused bits of a potentially corrupted,
+ * but still valid GSM-FR SID frame in RTP encoding to their pristine state.
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ *
+ * Per GSM 06.12 section 5.2, a freshly minted SID frame carries 60 bits
+ * of comfort noise parameters (LARc and 4 times Xmaxc), while the remaining
+ * 200 bits are all zeros; the latter 200 all-0 bits further break down into
+ * 95 bits of SID field (checked by receivers to detect SID) and 105 unused
+ * bits which receivers are told to ignore. Network elements that receive
+ * SID frames from call leg A uplink and need to retransmit them on leg B
+ * downlink should "rejuvenate" received SID frames prior to retransmission;
+ * this function does the job.
+ */
+void osmo_fr_sid_reset(uint8_t *rtp_payload)
+{
+ uint8_t *p, sub;
+
+ p = rtp_payload + 5; /* skip magic+LARc */
+ for (sub = 0; sub < 4; sub++) {
+ *p++ = 0;
+ *p++ &= 0x1F; /* upper 5 bits of Xmaxc field */
+ *p++ &= 0x80; /* and the lsb spilling into the next byte */
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ }
+}
+
+/*! Preen potentially-SID FR codec frame in RTP format, ensuring that it is
+ * either a speech frame or a valid SID, and if the latter, making it a
+ * perfect, error-free SID frame.
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ * \returns true if the frame is good, false otherwise
+ */
+bool osmo_fr_sid_preen(uint8_t *rtp_payload)
+{
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_fr_sid_classify(rtp_payload);
+ switch (sidc) {
+ case OSMO_GSM631_SID_CLASS_SPEECH:
+ return true;
+ case OSMO_GSM631_SID_CLASS_INVALID:
+ return false;
+ case OSMO_GSM631_SID_CLASS_VALID:
+ /* "Rejuvenate" this SID frame, correcting any errors */
+ osmo_fr_sid_reset(rtp_payload);
+ return true;
+ default:
+ /* There are only 3 possible SID classifications per GSM 06.31
+ * section 6.1.1, thus any other return value is a grave error
+ * in the code. */
+ OSMO_ASSERT(0);
+ }
+}
diff --git a/src/codec/gsm620.c b/src/codec/gsm620.c
index 282781fd..ef1d3b9b 100644
--- a/src/codec/gsm620.c
+++ b/src/codec/gsm620.c
@@ -17,14 +17,11 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License 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 <stdbool.h>
+#include <string.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/core/utils.h>
@@ -268,12 +265,6 @@ const uint16_t gsm620_voiced_bitorder[112] = {
81, /* Code 3:7 */
};
-static inline uint16_t mask(const uint8_t msb)
-{
- const uint16_t m = (uint16_t)1 << (msb - 1);
- return (m - 1) ^ m;
-}
-
/*! Check whether RTP frame contains HR SID code word according to
* TS 101 318 §5.2.2
* \param[in] rtp_payload Buffer with RTP payload
@@ -282,16 +273,44 @@ static inline uint16_t mask(const uint8_t msb)
*/
bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
{
- uint8_t i, bits[] = { 1, 2, 8, 9, 5, 4, 9, 5, 4, 9, 5, 4, 9, 5 };
- struct bitvec bv;
- bv.data = (uint8_t *) rtp_payload;
- bv.data_len = payload_len;
- bv.cur_bit = 33;
+ struct bitvec bv = {
+ .data = (uint8_t *)rtp_payload,
+ .data_len = payload_len,
+ };
- /* code word is all 1 at given bits, numbered from 1, MODE is always 3 */
- for (i = 0; i < ARRAY_SIZE(bits); i++)
- if (bitvec_get_uint(&bv, bits[i]) != mask(bits[i]))
+ /* A SID frame is identified by a SID codeword consisting of 79 bits which are all 1,
+ * so we basically check if all bits in range r34..r112 (inclusive) are 1. */
+ for (bv.cur_bit = 33; bv.cur_bit < bv.data_len * 8; bv.cur_bit++)
+ if (bitvec_get_bit_pos(&bv, bv.cur_bit) != ONE)
return false;
return true;
}
+
+/*! Reset the SID field of a potentially corrupted, but still valid GSM-HR
+ * SID frame in TS 101 318 format to its pristine state (full SID codeword).
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ *
+ * Per GSM 06.22 section 5.3, a freshly minted SID frame consists of 33 bits
+ * of comfort noise parameters and 79 bits of SID codeword (all 1s). Network
+ * elements that receive SID frames from call leg A uplink and need to
+ * retransmit them on leg B downlink should "rejuvenate" received SID frames
+ * prior to retransmission by resetting the SID field to its pristine state
+ * of all 1s; this function does the job.
+ *
+ * Important note: because of HR-specific quirks (lack of exact bit counting
+ * rules in GSM 06.41 spec compared to 06.31 & 06.81, plus the fact that such
+ * bit counting can only be done efficiently in the GSM 05.03 channel decoder
+ * prior to bit reordering based on voiced or unvoiced mode), a generic
+ * (usable from any network element) SID classification function similar to
+ * osmo_{fr,efr}_sid_classify() unfortunately cannot exist for HR. Therefore,
+ * the triggering condition for invoking this SID rejuvenation/reset function
+ * can only be an out-of-band SID indication, as in GSM 08.61 TRAU frames
+ * or RFC 5993 ToC octet.
+ */
+void osmo_hr_sid_reset(uint8_t *rtp_payload)
+{
+ /* set all 79 SID codeword bits to 1 */
+ rtp_payload[4] |= 0x7F;
+ memset(rtp_payload + 5, 0xFF, 9);
+}
diff --git a/src/codec/gsm660.c b/src/codec/gsm660.c
index 4f7bb099..b15bdf37 100644
--- a/src/codec/gsm660.c
+++ b/src/codec/gsm660.c
@@ -17,13 +17,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/utils.h>
#include <osmocom/codec/codec.h>
/* GSM EFR - subjective importance bit ordering */
@@ -257,3 +257,161 @@ const uint16_t gsm660_bitorder[260] = {
243, /* 258 -> PULSE 4_9: b0 */
246, /* 259 -> PULSE 4_10: b0 */
};
+
+static const uint8_t sid_code_word_bits[95] = {
+ /* bit numbers are relative to "pure" EFR frame beginning,
+ * not counting the signature bits. */
+ 45, 46, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 94, 95, 96, 98, 99, 100, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
+ 171, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 212, 213, 214, 215, 216,
+ 217, 218, 219, 220, 221
+};
+
+/*! Check whether RTP frame contains EFR SID code word according to
+ * TS 101 318 §5.3.2
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \param[in] payload_len Length of payload
+ * \returns true if code word is found, false otherwise
+ */
+bool osmo_efr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
+{
+ struct bitvec bv;
+ uint16_t i;
+
+ /* signature does not match Enhanced Full Rate SID */
+ if ((rtp_payload[0] >> 4) != 0xC)
+ return false;
+
+ bv.data = (uint8_t *) rtp_payload;
+ bv.data_len = payload_len;
+
+ /* code word is all 1 at given bits */
+ for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
+ if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]+4) != ONE)
+ return false;
+ }
+
+ return true;
+}
+
+/*! Classify potentially-SID EFR codec frame in RTP format according
+ * to the rules of GSM 06.81 §6.1.1
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \returns enum osmo_gsm631_sid_class, with symbolic values
+ * OSMO_GSM631_SID_CLASS_SPEECH, OSMO_GSM631_SID_CLASS_INVALID or
+ * OSMO_GSM631_SID_CLASS_VALID corresponding to the 3 possible bit-counting
+ * classifications prescribed by the spec.
+ *
+ * Differences between the more familiar osmo_efr_check_sid() and the present
+ * function are:
+ *
+ * 1. osmo_efr_check_sid() returns true only if the SID frame is absolutely
+ * perfect, with all 95 bits of the SID code word set. However, the
+ * rules of GSM 06.81 §6.1.1 allow up to one bit to be in error,
+ * and the frame is still accepted as valid SID.
+ *
+ * 2. The third possible state of invalid SID is not handled at all by the
+ * simpler osmo_efr_check_sid() function.
+ *
+ * 3. osmo_efr_check_sid() includes a check for 0xC RTP signature, and returns
+ * false if that signature nibble is wrong. That check is not included
+ * in the present version because there is no place for it in the
+ * ETSI-prescribed classification, it is neither speech nor SID. The
+ * assumption is that this function is used to classify the bit content
+ * of received codec frames, not their RTP encoding - the latter needs
+ * to be validated beforehand.
+ *
+ * Which function should one use? The answer depends on the specific
+ * circumstances, and needs to be addressed on a case-by-case basis.
+ */
+enum osmo_gsm631_sid_class osmo_efr_sid_classify(const uint8_t *rtp_payload)
+{
+ struct bitvec bv;
+ uint16_t i, n;
+
+ bv.data = (uint8_t *) rtp_payload;
+ bv.data_len = GSM_EFR_BYTES;
+
+ /* count not-SID-matching bits per the spec */
+ n = 0;
+ for (i = 0; i < ARRAY_SIZE(sid_code_word_bits); i++) {
+ if (bitvec_get_bit_pos(&bv, sid_code_word_bits[i]+4) != ONE)
+ n++;
+ if (n >= 16)
+ return OSMO_GSM631_SID_CLASS_SPEECH;
+ }
+ if (n >= 2)
+ return OSMO_GSM631_SID_CLASS_INVALID;
+ else
+ return OSMO_GSM631_SID_CLASS_VALID;
+}
+
+/*! Reset the SID field of a potentially corrupted, but still valid GSM-EFR
+ * SID frame in RTP encoding to its pristine state (full SID code word).
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ *
+ * Per GSM 06.62 section 5.3, a freshly minted SID frame consists of 58 bits
+ * of comfort noise parameters (LSF and 4 times fixed codebook gain), 95 bits
+ * of SID code word (all 1s) and 91 unused bits (all 0s). Network elements
+ * that receive SID frames from call leg A uplink and need to retransmit them
+ * on leg B downlink should "rejuvenate" received SID frames prior to
+ * retransmission by resetting the SID field to its pristine state of all 1s;
+ * this function does the job.
+ *
+ * Potential TODO: it would be nice to also zero out the remaining 91 bits
+ * which the spec leaves as reserved, clearing out leg A radio bit errors -
+ * but do we really need to?
+ */
+void osmo_efr_sid_reset(uint8_t *rtp_payload)
+{
+ /* set all 95 SID code word bits to 1 */
+ rtp_payload[6] |= 0x6F;
+ rtp_payload[7] = 0xFF;
+ rtp_payload[8] = 0xFF;
+ rtp_payload[9] |= 0x80;
+ rtp_payload[12] |= 0x3B;
+ rtp_payload[13] = 0xFF;
+ rtp_payload[14] = 0xFF;
+ rtp_payload[15] |= 0xE0;
+ rtp_payload[19] = 0xFF;
+ rtp_payload[20] = 0xFF;
+ rtp_payload[21] = 0xFF;
+ rtp_payload[25] = 0xFF;
+ rtp_payload[26] |= 0xFC;
+ rtp_payload[27] = 0xFF;
+ rtp_payload[28] |= 0xC0;
+}
+
+/*! Preen potentially-SID EFR codec frame in RTP format, ensuring that it is
+ * either a speech frame or a valid SID, and if the latter, making it a
+ * perfect, error-free SID frame.
+ * \param[in] rtp_payload Buffer with RTP payload - must be writable!
+ * \returns true if the frame is good, false otherwise
+ */
+bool osmo_efr_sid_preen(uint8_t *rtp_payload)
+{
+ enum osmo_gsm631_sid_class sidc;
+
+ sidc = osmo_efr_sid_classify(rtp_payload);
+ switch (sidc) {
+ case OSMO_GSM631_SID_CLASS_SPEECH:
+ return true;
+ case OSMO_GSM631_SID_CLASS_INVALID:
+ return false;
+ case OSMO_GSM631_SID_CLASS_VALID:
+ /* "Rejuvenate" this SID frame, correcting any errors */
+ osmo_efr_sid_reset(rtp_payload);
+ return true;
+ default:
+ /* There are only 3 possible SID classifications per GSM 06.81
+ * section 6.1.1, thus any other return value is a grave error
+ * in the code. */
+ OSMO_ASSERT(0);
+ }
+}
diff --git a/src/codec/gsm690.c b/src/codec/gsm690.c
index 19557164..3b2e6694 100644
--- a/src/codec/gsm690.c
+++ b/src/codec/gsm690.c
@@ -2,6 +2,7 @@
* GSM 06.90 - GSM AMR Codec. */
/*
* (C) 2010 Sylvain Munaut <tnt@246tNt.com>
+ * (C) 2020 Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -17,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -29,6 +26,7 @@
#include <stdlib.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
#include <osmocom/codec/codec.h>
/*
* These table map between the raw encoder parameter output and
@@ -216,8 +214,117 @@ const uint16_t gsm690_4_75_bitorder[95] = {
92, 31, 52, 65, 86,
};
+/*! These constants refer to the length of one "AMR core frame" as per
+ * TS 26.101 Section 4.2.2 / Table 2. */
+const uint8_t gsm690_bitlength[AMR_NO_DATA+1] = {
+ [AMR_4_75] = 95,
+ [AMR_5_15] = 103,
+ [AMR_5_90] = 118,
+ [AMR_6_70] = 134,
+ [AMR_7_40] = 148,
+ [AMR_7_95] = 159,
+ [AMR_10_2] = 204,
+ [AMR_12_2] = 244,
+ [AMR_SID] = 39,
+};
+
+struct ts26101_reorder_table {
+ /*! Table as per TS 26.101 Annex B to compute d-bits from s-bits */
+ const uint16_t *s_to_d;
+ /*! size of table */
+ uint8_t len;
+};
+
+static const struct ts26101_reorder_table ts26101_reorder_tables[8] = {
+ [AMR_4_75] = {
+ .s_to_d = gsm690_4_75_bitorder,
+ .len = ARRAY_SIZE(gsm690_4_75_bitorder),
+ },
+ [AMR_5_15] = {
+ .s_to_d = gsm690_5_15_bitorder,
+ .len = ARRAY_SIZE(gsm690_5_15_bitorder),
+ },
+ [AMR_5_90] = {
+ .s_to_d = gsm690_5_9_bitorder,
+ .len = ARRAY_SIZE(gsm690_5_9_bitorder),
+ },
+ [AMR_6_70] = {
+ .s_to_d = gsm690_6_7_bitorder,
+ .len = ARRAY_SIZE(gsm690_6_7_bitorder),
+ },
+ [AMR_7_40] = {
+ .s_to_d = gsm690_7_4_bitorder,
+ .len = ARRAY_SIZE(gsm690_7_4_bitorder),
+ },
+ [AMR_7_95] = {
+ .s_to_d = gsm690_7_95_bitorder,
+ .len = ARRAY_SIZE(gsm690_7_95_bitorder),
+ },
+ [AMR_10_2] = {
+ .s_to_d = gsm690_10_2_bitorder,
+ .len = ARRAY_SIZE(gsm690_10_2_bitorder),
+ },
+ [AMR_12_2] = {
+ .s_to_d = gsm690_12_2_bitorder,
+ .len = ARRAY_SIZE(gsm690_12_2_bitorder),
+ },
+};
+
+/*! Convert from S-bits (codec output) to d-bits.
+ * \param[out] out user-provided output buffer for generated unpacked d-bits
+ * \param[in] in input buffer for unpacked s-bits
+ * \param[in] n_bits number of bits (in both in and out)
+ * \param[in] AMR mode (0..7) */
+int osmo_amr_s_to_d(ubit_t *out, const ubit_t *in, uint16_t n_bits, enum osmo_amr_type amr_mode)
+{
+ const struct ts26101_reorder_table *tbl;
+ int i;
+
+ if (amr_mode >= ARRAY_SIZE(ts26101_reorder_tables))
+ return -ENODEV;
+
+ tbl = &ts26101_reorder_tables[amr_mode];
+
+ if (n_bits > tbl->len)
+ return -EINVAL;
+
+ for (i = 0; i < n_bits; i++) {
+ uint16_t n = tbl->s_to_d[i];
+ out[i] = in[n];
+ }
+
+ return n_bits;
+}
+
+/*! Convert from d-bits to s-bits (codec input).
+ * \param[out] out user-provided output buffer for generated unpacked s-bits
+ * \param[in] in input buffer for unpacked d-bits
+ * \param[in] n_bits number of bits (in both in and out)
+ * \param[in] AMR mode (0..7) */
+int osmo_amr_d_to_s(ubit_t *out, const ubit_t *in, uint16_t n_bits, enum osmo_amr_type amr_mode)
+{
+ const struct ts26101_reorder_table *tbl;
+ int i;
+
+ if (amr_mode >= ARRAY_SIZE(ts26101_reorder_tables))
+ return -ENODEV;
+
+ tbl = &ts26101_reorder_tables[amr_mode];
+
+ if (n_bits > tbl->len)
+ return -EINVAL;
+
+ for (i = 0; i < n_bits; i++) {
+ uint16_t n = tbl->s_to_d[i];
+ out[n] = in[i];
+ }
+
+ return n_bits;
+}
+
+/* See also RFC 4867 §3.6, Table 1, Column "Total speech bits" */
static const uint8_t amr_len_by_ft[16] = {
- 12, 13, 15, 17, 19, 20, 26, 31, 7, 0, 0, 0, 0, 0, 0, 0
+ 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0
};
const struct value_string osmo_amr_type_names[] = {
diff --git a/src/coding/Makefile.am b/src/coding/Makefile.am
index f47fe457..0cab66ff 100644
--- a/src/coding/Makefile.am
+++ b/src/coding/Makefile.am
@@ -1,13 +1,14 @@
# This is _NOT_ the library release version, it's an API version.
# Please read Chapter 6 "Library interface versions" of the libtool
# documentation before making any modification
-LIBVERSION=1:1:1
+LIBVERSION=3:0:3
AM_CPPFLAGS = \
-I"$(top_srcdir)/include" \
-I"$(top_builddir)/include" \
- $(TALLOC_CFLAGS)
-AM_CFLAGS = -Wall
+ -I"$(top_builddir)" \
+ $(NULL)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
if ENABLE_PSEUDOTALLOC
AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
@@ -20,16 +21,19 @@ libosmocoding_la_SOURCES = \
gsm0503_mapping.c \
gsm0503_tables.c \
gsm0503_parity.c \
- gsm0503_coding.c
+ gsm0503_coding.c \
+ gsm0503_amr_dtx.c
libosmocoding_la_LDFLAGS = \
$(LTLDFLAGS_OSMOCODING) \
- -version-info \
- $(LIBVERSION) \
+ -version-info $(LIBVERSION) \
-no-undefined \
- $(TALLOC_LIBS)
+ $(NULL)
+
libosmocoding_la_LIBADD = \
- ../libosmocore.la \
- ../gsm/libosmogsm.la \
- ../codec/libosmocodec.la
+ $(top_builddir)/src/core/libosmocore.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(top_builddir)/src/codec/libosmocodec.la \
+ $(NULL)
EXTRA_DIST = libosmocoding.map
+EXTRA_libosmocoding_la_DEPENDENCIES = libosmocoding.map
diff --git a/src/coding/gsm0503_amr_dtx.c b/src/coding/gsm0503_amr_dtx.c
new file mode 100644
index 00000000..7de3b281
--- /dev/null
+++ b/src/coding/gsm0503_amr_dtx.c
@@ -0,0 +1,355 @@
+/*
+ * (C) 2020-2022 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/conv.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
+#include <osmocom/coding/gsm0503_parity.h>
+#include <osmocom/gsm/gsm0503.h>
+
+#define S2U(b) ((b) < 0)
+
+/* See also: 3GPP TS 05.03, chapter 3.10.1.3, 3.10.5.2 Identification marker */
+static const ubit_t id_marker_1[] = { 1, 0, 1, 1, 0, 0, 0, 0, 1 };
+
+/* See also: 3GPP TS 05.03, chapter 3.9.1.3, 3.10.2.2, 3.10.2.2 Identification marker */
+static const ubit_t id_marker_0[] = { 0, 1, 0, 0, 1, 1, 1, 1, 0 };
+
+/* See also: 3GPP TS 05.03, chapter 3.9 Adaptive multi rate speech channel at full rate (TCH/AFS) */
+static const ubit_t codec_mode_1_sid[] = { 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0 };
+static const ubit_t codec_mode_2_sid[] = { 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0 };
+static const ubit_t codec_mode_3_sid[] = { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 };
+static const ubit_t codec_mode_4_sid[] = { 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1 };
+
+const struct value_string gsm0503_amr_dtx_frame_names[] = {
+ { AMR_OTHER, "AMR_OTHER (audio)" },
+ { AFS_SID_FIRST, "AFS_SID_FIRST" },
+ { AFS_SID_UPDATE, "AFS_SID_UPDATE (marker)" },
+ { AFS_SID_UPDATE_CN, "AFS_SID_UPDATE_CN (audio)" },
+ { AFS_ONSET, "AFS_ONSET" },
+ { AHS_SID_UPDATE, "AHS_SID_UPDATE (marker)" },
+ { AHS_SID_UPDATE_CN, "AHS_SID_UPDATE_CN (audio)" },
+ { AHS_SID_FIRST_P1, "AHS_SID_FIRST_P1" },
+ { AHS_SID_FIRST_P2, "AHS_SID_FIRST_P2" },
+ { AHS_ONSET, "AHS_ONSET" },
+ { AHS_SID_FIRST_INH, "AHS_SID_FIRST_INH" },
+ { AHS_SID_UPDATE_INH, "AHS_SID_UPDATE_INH" },
+ { 0, NULL }
+};
+
+static bool detect_afs_id_marker(int *n_errors, int *n_bits_total, const sbit_t *sbits, uint8_t offset, uint8_t count,
+ const ubit_t * id_marker, uint8_t id_marker_len)
+{
+ unsigned int i, k;
+ unsigned int id_bit_nr = 0;
+ int errors = 0;
+ int bits = 0;
+
+ /* Override coded in-band data */
+ sbits += offset;
+
+ /* Check for identification marker bits */
+ for (i = 0; i < count; i++) {
+ for (k = 0; k < 4; k++) {
+ if (*sbits == 0 || id_marker[id_bit_nr % id_marker_len] != S2U(*sbits))
+ errors++;
+ id_bit_nr++;
+ sbits++;
+ bits++;
+ }
+
+ /* Jump to the next block of 4 bits */
+ sbits += 4;
+ }
+
+ *n_errors = errors;
+ *n_bits_total = bits;
+
+ /* Tolerate up to 1/8 errornous bits */
+ return *n_errors < *n_bits_total / 8;
+}
+
+static bool detect_ahs_id_marker(int *n_errors, int *n_bits_total, const sbit_t *sbits, const ubit_t *id_marker)
+{
+ unsigned int i, k;
+ int errors = 0;
+ int bits = 0;
+
+ /* Override coded in-band data */
+ sbits += 16;
+
+ /* Check first identification marker bits (23*9 bits) */
+ for (i = 0; i < 23; i++) {
+ for (k = 0; k < 9; k++) {
+ if (*sbits == 0 || id_marker[k] != S2U(*sbits))
+ errors++;
+ sbits++;
+ bits++;
+ }
+ }
+
+ /* Check remaining identification marker bits (5 bits) */
+ for (k = 0; k < 5; k++) {
+ if (*sbits == 0 || id_marker[k] != S2U(*sbits))
+ errors++;
+ sbits++;
+ bits++;
+ }
+
+ *n_errors = errors;
+ *n_bits_total = bits;
+
+ /* Tolerate up to 1/8 errornous bits */
+ return *n_errors < *n_bits_total / 8;
+}
+
+static bool detect_interleaved_ahs_id_marker(int *n_errors, int *n_bits_total, const sbit_t *sbits, uint8_t offset,
+ uint8_t n_bits, const ubit_t * id_marker, uint8_t id_marker_len)
+{
+ unsigned int i, k;
+ int errors = 0;
+ int bits = 0;
+ uint8_t full_rounds = n_bits / id_marker_len;
+ uint8_t remainder = n_bits % id_marker_len;
+
+ /* Override coded in-band data */
+ sbits += offset;
+
+ /* Check first identification marker bits (23*9 bits) */
+ for (i = 0; i < full_rounds; i++) {
+ for (k = 0; k < id_marker_len; k++) {
+ if (*sbits == 0 || id_marker[k] != S2U(*sbits))
+ errors++;
+ sbits += 2;
+ bits++;
+ }
+ }
+
+ /* Check remaining identification marker bits (5 bits) */
+ for (k = 0; k < remainder; k++) {
+ if (*sbits == 0 || id_marker[k] != S2U(*sbits))
+ errors++;
+ sbits += 2;
+ bits++;
+ }
+
+ *n_errors = errors;
+ *n_bits_total = bits;
+
+ /* Tolerate up to 1/8 errornous bits */
+ return *n_errors < *n_bits_total / 8;
+}
+
+/* Detect an FR AMR SID_FIRST frame by its identifcation marker */
+static bool detect_afs_sid_first(int *n_errors, int *n_bits_total, const sbit_t *sbits)
+{
+ return detect_afs_id_marker(n_errors, n_bits_total, sbits, 32, 53, id_marker_0, 9);
+}
+
+/* Detect an FR AMR SID_UPDATE frame by its identification marker */
+static bool detect_afs_sid_update(int *n_errors, int *n_bits_total, const sbit_t *sbits)
+{
+ return detect_afs_id_marker(n_errors, n_bits_total, sbits, 36, 53, id_marker_0, 9);
+}
+
+/* Detect an FR AMR ONSET frame by its repeating coded inband data */
+static int detect_afs_onset(int *n_errors, int *n_bits_total, const sbit_t *sbits)
+{
+ bool rc;
+
+ rc = detect_afs_id_marker(n_errors, n_bits_total, sbits, 4, 57, codec_mode_1_sid, 16);
+ if (rc)
+ return 0;
+
+ rc = detect_afs_id_marker(n_errors, n_bits_total, sbits, 4, 57, codec_mode_2_sid, 16);
+ if (rc)
+ return 1;
+
+ rc = detect_afs_id_marker(n_errors, n_bits_total, sbits, 4, 57, codec_mode_3_sid, 16);
+ if (rc)
+ return 2;
+
+ rc = detect_afs_id_marker(n_errors, n_bits_total, sbits, 4, 57, codec_mode_4_sid, 16);
+ if (rc)
+ return 3;
+
+ return -1;
+}
+
+/* Detect an HR AMR SID UPDATE frame by its identification marker */
+static bool detect_ahs_sid_update(int *n_errors, int *n_bits_total, const sbit_t *sbits)
+{
+ return detect_ahs_id_marker(n_errors, n_bits_total, sbits, id_marker_1);
+}
+
+/* Detect an HR AMR SID FIRST (part 1) frame by its identification marker */
+static bool detect_ahs_sid_first_p1(int *n_errors, int *n_bits_total, const sbit_t *sbits)
+{
+ return detect_ahs_id_marker(n_errors, n_bits_total, sbits, id_marker_0);
+}
+
+/* Detect an HR AMR SID FIRST (part 2) frame by its repeating coded inband data */
+static int detect_ahs_sid_first_p2(int *n_errors, int *n_bits_total, const sbit_t *sbits)
+{
+ bool rc;
+
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 0, 114, codec_mode_1_sid, 16);
+ if (rc)
+ return 0;
+
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 0, 114, codec_mode_2_sid, 16);
+ if (rc)
+ return 1;
+
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 0, 114, codec_mode_3_sid, 16);
+ if (rc)
+ return 2;
+
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 0, 114, codec_mode_4_sid, 16);
+ if (rc)
+ return 3;
+
+ return -1;
+}
+
+/* Detect an HR AMR ONSET frame by its repeating coded inband data */
+static int detect_ahs_onset(int *n_errors, int *n_bits_total, const sbit_t *sbits)
+{
+ bool rc;
+
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 1, 114, codec_mode_1_sid, 16);
+ if (rc)
+ return 0;
+
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 1, 114, codec_mode_2_sid, 16);
+ if (rc)
+ return 1;
+
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 1, 114, codec_mode_3_sid, 16);
+ if (rc)
+ return 2;
+
+ rc = detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 1, 114, codec_mode_4_sid, 16);
+ if (rc)
+ return 3;
+
+ return -1;
+}
+
+/* Detect an HR AMR SID FIRST INHIBIT frame by its identification marker */
+static bool detect_ahs_sid_first_inh(int *n_errors, int *n_bits_total, const sbit_t *sbits)
+{
+ return detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 33, 212, id_marker_1, 9);
+}
+
+/* Detect an HR AMR SID UPDATE INHIBIT frame by its identification marker */
+static bool detect_ahs_sid_update_inh(int *n_errors, int *n_bits_total, const sbit_t *sbits)
+{
+ return detect_interleaved_ahs_id_marker(n_errors, n_bits_total, sbits, 33, 212, id_marker_0, 9);
+}
+
+/*! Detect FR AMR DTX frame in unmapped, deinterleaved frame bits.
+ * \param[out] n_errors number of errornous bits.
+ * \param[out] n_bits_total number of checked bits.
+ * \param[out] mode_id codec mode ID (0..3 or -1).
+ * \param[in] sbits input soft-bits (456 bit).
+ * \returns dtx frame type. */
+enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame2(int *n_errors, int *n_bits_total,
+ int *mode_id, const sbit_t *sbits)
+{
+ if (detect_afs_sid_first(n_errors, n_bits_total, sbits))
+ return AFS_SID_FIRST;
+ if (detect_afs_sid_update(n_errors, n_bits_total, sbits))
+ return AFS_SID_UPDATE;
+ if ((*mode_id = detect_afs_onset(n_errors, n_bits_total, sbits)) != -1)
+ return AFS_ONSET;
+
+ *n_errors = 0;
+ *n_bits_total = 0;
+ return AMR_OTHER;
+}
+
+/*! Detect FR AMR DTX frame in unmapped, deinterleaved frame bits.
+ * DEPRECATED: use gsm0503_detect_afs_dtx_frame2() instead.
+ * \param[out] n_errors number of errornous bits.
+ * \param[out] n_bits_total number of checked bits.
+ * \param[in] ubits input bits (456 bit).
+ * \returns dtx frame type. */
+enum gsm0503_amr_dtx_frames gsm0503_detect_afs_dtx_frame(int *n_errors, int *n_bits_total,
+ const ubit_t *ubits)
+{
+ int mode_id; /* unused */
+ sbit_t sbits[456];
+
+ osmo_ubit2sbit(&sbits[0], &ubits[0], sizeof(sbits));
+ return gsm0503_detect_afs_dtx_frame2(n_errors, n_bits_total, &mode_id, &sbits[0]);
+}
+
+/*! Detect HR AMR DTX frame in unmapped, deinterleaved frame bits.
+ * \param[out] n_errors number of errornous bits.
+ * \param[out] n_bits_total number of checked bits.
+ * \param[out] mode_id codec ID (CMI or CMC/CMR).
+ * \param[out] mode_id codec mode ID (0..3 or -1).
+ * \param[in] sbits input soft-bits (456 bit).
+ * \returns dtx frame type, */
+enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame2(int *n_errors, int *n_bits_total,
+ int *mode_id, const sbit_t *sbits)
+{
+ if (detect_ahs_sid_update(n_errors, n_bits_total, sbits))
+ return AHS_SID_UPDATE;
+ if (detect_ahs_sid_first_inh(n_errors, n_bits_total, sbits))
+ return AHS_SID_FIRST_INH;
+ if (detect_ahs_sid_update_inh(n_errors, n_bits_total, sbits))
+ return AHS_SID_UPDATE_INH;
+ if (detect_ahs_sid_first_p1(n_errors, n_bits_total, sbits))
+ return AHS_SID_FIRST_P1;
+ if ((*mode_id = detect_ahs_sid_first_p2(n_errors, n_bits_total, sbits)) != -1)
+ return AHS_SID_FIRST_P2;
+ if ((*mode_id = detect_ahs_onset(n_errors, n_bits_total, sbits)) != -1)
+ return AHS_ONSET;
+
+ *n_errors = 0;
+ *n_bits_total = 0;
+ return AMR_OTHER;
+}
+
+/*! Detect HR AMR DTX frame in unmapped, deinterleaved frame bits.
+ * DEPRECATED: use gsm0503_detect_ahs_dtx_frame2() instead.
+ * \param[out] n_errors number of errornous bits.
+ * \param[out] n_bits_total number of checked bits.
+ * \param[in] ubits input bits (456 bit).
+ * \returns dtx frame type, */
+enum gsm0503_amr_dtx_frames gsm0503_detect_ahs_dtx_frame(int *n_errors, int *n_bits_total,
+ const ubit_t *ubits)
+{
+ int mode_id; /* unused */
+ sbit_t sbits[456];
+
+ osmo_ubit2sbit(&sbits[0], &ubits[0], sizeof(sbits));
+ return gsm0503_detect_ahs_dtx_frame2(n_errors, n_bits_total, &mode_id, &sbits[0]);
+}
diff --git a/src/coding/gsm0503_coding.c b/src/coding/gsm0503_coding.c
index 7385d233..022f4656 100644
--- a/src/coding/gsm0503_coding.c
+++ b/src/coding/gsm0503_coding.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -35,9 +31,7 @@
#include <osmocom/core/crcgen.h>
#include <osmocom/core/endian.h>
-#include <osmocom/gprs/protocol/gsm_04_60.h>
-#include <osmocom/gprs/gprs_rlc.h>
-
+#include <osmocom/gsm/protocol/gsm_44_060.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm0503.h>
#include <osmocom/codec/codec.h>
@@ -47,6 +41,7 @@
#include <osmocom/coding/gsm0503_tables.h>
#include <osmocom/coding/gsm0503_coding.h>
#include <osmocom/coding/gsm0503_parity.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
/*! \mainpage libosmocoding Documentation
*
@@ -541,24 +536,29 @@ static int osmo_conv_decode_ber_punctured(const struct osmo_conv_code *code,
int *n_errors, int *n_bits_total,
const uint8_t *data_punc)
{
- int res, i, coded_len;
+ int res, coded_len;
ubit_t recoded[EGPRS_DATA_C_MAX];
res = osmo_conv_decode(code, input, output);
- if (n_bits_total || n_errors) {
- coded_len = osmo_conv_encode(code, output, recoded);
- OSMO_ASSERT(sizeof(recoded) / sizeof(recoded[0]) >= coded_len);
- }
+ if (!n_bits_total && !n_errors)
+ return res;
+
+ coded_len = osmo_conv_encode(code, output, recoded);
+ OSMO_ASSERT(ARRAY_SIZE(recoded) >= coded_len);
/* Count bit errors */
if (n_errors) {
*n_errors = 0;
- for (i = 0; i < coded_len; i++) {
- if (((!data_punc) || (data_punc && !data_punc[i])) &&
- !((recoded[i] && input[i] < 0) ||
- (!recoded[i] && input[i] > 0)) )
- *n_errors += 1;
+ for (unsigned int i = 0; i < coded_len; i++) {
+ /* punctured bits do not count as bit errors */
+ if (data_punc != NULL && data_punc[i])
+ continue;
+ if (recoded[i] == 1 && input[i] < 0)
+ continue;
+ if (recoded[i] == 0 && input[i] > 0)
+ continue;
+ *n_errors += 1;
}
}
@@ -610,7 +610,7 @@ static int _xcch_decode_cB(uint8_t *l2_data, const sbit_t *cB,
/*! convenience wrapper for encoding to coded bits
* \param[out] cB caller-allocated buffer for 456 coded bits as per TS 05.03 4.1.3
- * \param[out] l2_data to-be-encoded L2 Frame
+ * \param[in] l2_data to-be-encoded L2 Frame
* \returns 0 */
static int _xcch_encode_cB(ubit_t *cB, const uint8_t *l2_data)
{
@@ -925,10 +925,10 @@ static int egprs_decode_data(uint8_t *l2_data, const sbit_t *c,
* \param[out] l2_data caller-allocated buffer for L2 Frame
* \param[in] bursts burst input data as soft unpacked bits
* \param[in] nbits number of bits in \a bursts
- * \param usf_p unused argument ?!?
+ * \param usf_p Uplink State Flag, FIXME: not implemented
* \param[out] n_errors number of detected bit-errors
* \param[out] n_bits_total total number of decoded bits
- * \returns 0 on success; negative on error */
+ * \returns number of bytes decoded; negative on error */
int gsm0503_pdtch_egprs_decode(uint8_t *l2_data, const sbit_t *bursts, uint16_t nbits,
uint8_t *usf_p, int *n_errors, int *n_bits_total)
{
@@ -1014,10 +1014,10 @@ int gsm0503_pdtch_egprs_decode(uint8_t *l2_data, const sbit_t *bursts, uint16_t
/*! Decode GPRS PDTCH
* \param[out] l2_data caller-allocated buffer for L2 Frame
* \param[in] bursts burst input data as soft unpacked bits
- * \param[out] usf_p uplink stealing flag
+ * \param[out] usf_p Uplink State Flag, only relevant for DL blocks
* \param[out] n_errors number of detected bit-errors
- * \param[out] n_bits_total total number of dcoded bits
- * \returns 0 on success; negative on error */
+ * \param[out] n_bits_total total number of decoded bits
+ * \returns number of bytes decoded; negative on error */
int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
int *n_errors, int *n_bits_total)
{
@@ -1046,6 +1046,10 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
osmo_conv_decode_ber(&gsm0503_xcch, cB,
conv, n_errors, n_bits_total);
+ /* the three USF bits d(0),d(1),d(2) are *not* precoded */
+ if (usf_p)
+ *usf_p = (conv[0] << 2) | (conv[1] << 1) | (conv[2] << 0);
+
rv = osmo_crc64gen_check_bits(&gsm0503_fire_crc40,
conv, 184, conv + 184);
if (rv)
@@ -1055,6 +1059,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
return 23;
case 2:
+ /* reorder, set punctured bits to 0 (unknown state) */
for (i = 587, j = 455; i >= 0; i--) {
if (!gsm0503_puncture_cs2[i])
cB[i] = cB[j--];
@@ -1062,9 +1067,15 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
cB[i] = 0;
}
- osmo_conv_decode_ber(&gsm0503_cs2_np, cB,
- conv, n_errors, n_bits_total);
+ /* decode as if puncturing was not employed (note '_np') */
+ osmo_conv_decode_ber_punctured(&gsm0503_cs2_np, cB, conv,
+ n_errors, NULL,
+ gsm0503_puncture_cs2);
+ /* indicate the actual amount of coded bits (excluding punctured ones) */
+ if (n_bits_total != NULL)
+ *n_bits_total = 456;
+ /* 5.1.2.2 a) the three USF bits d(0),d(1),d(2) are precoded into six bits */
for (i = 0; i < 8; i++) {
for (j = 0, k = 0; j < 6; j++)
k += abs(((int)gsm0503_usf2six[i][j]) - ((int)conv[j]));
@@ -1090,6 +1101,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
return 34;
case 3:
+ /* reorder, set punctured bits to 0 (unknown state) */
for (i = 675, j = 455; i >= 0; i--) {
if (!gsm0503_puncture_cs3[i])
cB[i] = cB[j--];
@@ -1097,9 +1109,15 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
cB[i] = 0;
}
- osmo_conv_decode_ber(&gsm0503_cs3_np, cB,
- conv, n_errors, n_bits_total);
+ /* decode as if puncturing was not employed (note '_np') */
+ osmo_conv_decode_ber_punctured(&gsm0503_cs3_np, cB, conv,
+ n_errors, NULL,
+ gsm0503_puncture_cs3);
+ /* indicate the actual amount of coded bits (excluding punctured ones) */
+ if (n_bits_total != NULL)
+ *n_bits_total = 456;
+ /* 5.1.3.2 a) the three USF bits d(0),d(1),d(2) are precoded into six bits */
for (i = 0; i < 8; i++) {
for (j = 0, k = 0; j < 6; j++)
k += abs(((int)gsm0503_usf2six[i][j]) - ((int)conv[j]));
@@ -1128,6 +1146,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
for (i = 12; i < 456; i++)
conv[i] = (cB[i] < 0) ? 1 : 0;
+ /* 5.1.4.2 a) the three USF bits d(0),d(1),d(2) are precoded into twelve bits */
for (i = 0; i < 8; i++) {
for (j = 0, k = 0; j < 12; j++)
k += abs(((int)gsm0503_usf2twelve_sbit[i][j]) - ((int)cB[j]));
@@ -1168,7 +1187,7 @@ int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
}
/*
- * EGPRS PDTCH UL block encoding
+ * EGPRS PDTCH DL block encoding
*/
static int egprs_type3_map(ubit_t *bursts, const ubit_t *hc, const ubit_t *dc, int usf)
{
@@ -1176,7 +1195,7 @@ static int egprs_type3_map(ubit_t *bursts, const ubit_t *hc, const ubit_t *dc, i
ubit_t iB[456];
const ubit_t *hl_hn = gsm0503_pdtch_hl_hn_ubit[3];
- gsm0503_mcs1_dl_interleave(gsm0503_usf2six[usf], hc, dc, iB);
+ gsm0503_mcs1_dl_interleave(gsm0503_usf2twelve_ubit[usf], hc, dc, iB);
for (i = 0; i < 4; i++) {
gsm0503_xcch_burst_map(&iB[i * 114], &bursts[i * 116],
@@ -1332,7 +1351,7 @@ static int egprs_parse_dl_cps(struct egprs_cps *cps,
* \param[out] bursts caller-allocated buffer for unpacked burst bits
* \param[in] l2_data L2 (MAC) block to be encoded
* \param[in] l2_len length of l2_data in bytes, used to determine MCS
- * \returns 0 on success; negative on error */
+ * \returns number of bits encoded; negative on error */
int gsm0503_pdtch_egprs_encode(ubit_t *bursts,
const uint8_t *l2_data, uint8_t l2_len)
{
@@ -1427,7 +1446,7 @@ bad_header:
* \param[out] bursts caller-allocated buffer for unpacked burst bits
* \param[in] l2_data L2 (MAC) block to be encoded
* \param[in] l2_len length of l2_data in bytes, used to determine CS
- * \returns 0 on success; negative on error */
+ * \returns number of bits encoded; negative on error */
int gsm0503_pdtch_encode(ubit_t *bursts, const uint8_t *l2_data, uint8_t l2_len)
{
ubit_t iB[456], cB[676];
@@ -1576,24 +1595,22 @@ static void tch_fr_disassemble(ubit_t *b_bits,
}
}
-/* assemble a HR codec frame in format as used inside RTP */
+/* assemble a HR codec frame in the canonical format of ETSI TS 101 318 */
static void tch_hr_reassemble(uint8_t *tch_data, const ubit_t *b_bits)
{
- int i, j;
-
- tch_data[0] = 0x00; /* F = 0, FT = 000 */
- memset(tch_data + 1, 0, 14);
+ int i;
- for (i = 0, j = 8; i < 112; i++, j++)
- tch_data[j >> 3] |= (b_bits[i] << (7 - (j & 7)));
+ memset(tch_data, 0, GSM_HR_BYTES);
+ for (i = 0; i < 112; i++)
+ tch_data[i >> 3] |= (b_bits[i] << (7 - (i & 7)));
}
static void tch_hr_disassemble(ubit_t *b_bits, const uint8_t *tch_data)
{
- int i, j;
+ int i;
- for (i = 0, j = 8; i < 112; i++, j++)
- b_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1;
+ for (i = 0; i < 112; i++)
+ b_bits[i] = (tch_data[i >> 3] >> (7 - (i & 7))) & 1;
}
/* assemble a EFR codec frame in format as used inside RTP */
@@ -1635,6 +1652,39 @@ static void tch_amr_disassemble(ubit_t *d_bits, const uint8_t *tch_data, int len
d_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1;
}
+/* Append STI and MI bits to the SID_UPDATE frame, see also
+ * 3GPP TS 26.101, chapter 4.2.3 AMR Core Frame with comfort noise bits */
+static void tch_amr_sid_update_append(ubit_t *sid_update, uint8_t sti, uint8_t mi)
+{
+ /* Zero out the space that had been used by the CRC14 */
+ memset(sid_update + 35, 0, 14);
+
+ /* Append STI and MI parameters */
+ sid_update[35] = sti & 1;
+ sid_update[36] = mi & 1;
+ sid_update[37] = mi >> 1 & 1;
+ sid_update[38] = mi >> 2 & 1;
+}
+
+/* Extract a SID UPDATE fram the sbits of an FR AMR frame */
+static void extract_afs_sid_update(sbit_t *sid_update, const sbit_t *sbits)
+{
+
+ unsigned int i;
+
+ sbits += 32;
+
+ for (i = 0; i < 53; i++) {
+ sid_update[0] = sbits[0];
+ sid_update[1] = sbits[1];
+ sid_update[2] = sbits[2];
+ sid_update[3] = sbits[3];
+ sid_update += 4;
+ sbits += 8;
+ }
+
+}
+
/* re-arrange according to TS 05.03 Table 2 (receiver) */
static void tch_fr_d_to_b(ubit_t *b_bits, const ubit_t *d_bits)
{
@@ -1775,7 +1825,7 @@ static void tch_efr_unreorder(ubit_t *s, ubit_t *p, const ubit_t *w)
s[119] = (sum >= 2);
memcpy(s + 121, w + 125, 53);
sum = s[172] + w[178] + w[179];
- s[172] = (sum > 2);
+ s[172] = (sum >= 2);
memcpy(s + 174, w + 180, 50);
sum = s[222] + w[230] + w[231];
s[222] = (sum >= 2);
@@ -1831,7 +1881,7 @@ int gsm0503_tch_fr_decode(uint8_t *tch_data, const sbit_t *bursts,
return -1;
}
- return 23;
+ return GSM_MACBLOCK_LEN;
}
osmo_conv_decode_ber(&gsm0503_tch_fr, cB, conv, n_errors, n_bits_total);
@@ -1898,40 +1948,39 @@ int gsm0503_tch_fr_encode(ubit_t *bursts, const uint8_t *tch_data,
switch (len) {
case GSM_EFR_BYTES: /* TCH EFR */
-
tch_efr_disassemble(s, tch_data);
-
tch_efr_protected(s, b);
-
osmo_crc8gen_set_bits(&gsm0503_tch_efr_crc8, b, 65, p);
-
tch_efr_reorder(w, s, p);
-
tch_efr_w_to_d(d, w);
-
goto coding_efr_fr;
case GSM_FR_BYTES: /* TCH FR */
tch_fr_disassemble(w, tch_data, net_order);
-
tch_fr_b_to_d(d, w);
-
coding_efr_fr:
osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d, 50, p);
-
tch_fr_reorder(conv, d, p);
-
memcpy(cB + 378, d + 182, 78);
-
osmo_conv_encode(&gsm0503_tch_fr, conv, cB);
-
h = 0;
-
+ break;
+ case 0: /* no data, induce BFI in the receiver */
+ /* Do the same thing that sysmoBTS PHY does when fed a 0-length
+ * payload for DL: set all u(k) bits to 0, and do the same
+ * with all class 2 bits. This operation is NOT the same as
+ * an FR codec frame of all zero bits: with all-zeros d(k) input
+ * the CRC3 function will produce 111 output, whereas we
+ * transmit 000 in those parity bits too. The result will be
+ * an induced BFI (bad frame indication) condition in the
+ * receiver, for both TCH/FS and TCH/EFS decoders. */
+ memset(conv, 0, sizeof(conv));
+ memset(cB + 378, 0, 78);
+ osmo_conv_encode(&gsm0503_tch_fr, conv, cB);
+ h = 0;
break;
case GSM_MACBLOCK_LEN: /* FACCH */
_xcch_encode_cB(cB, tch_data);
-
h = 1;
-
break;
default:
return -1;
@@ -1948,13 +1997,13 @@ coding_efr_fr:
}
/*! Perform channel decoding of a HR(v1) channel according TS 05.03
- * \param[out] tch_data Codec frame in RTP payload format
- * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[out] tch_data Codec frame in TS 101 318 canonical format
+ * \param[in] bursts buffer containing the symbols of 6 bursts
* \param[in] odd Odd (1) or even (0) frame number
* \param[out] n_errors Number of detected bit errors
* \param[out] n_bits_total Total number of bits
* \returns length of bytes used in \a tch_data output buffer; negative on error */
-int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+int gsm0503_tch_hr_decode2(uint8_t *tch_data, const sbit_t *bursts, int odd,
int *n_errors, int *n_bits_total)
{
sbit_t iB[912], cB[456], h;
@@ -1968,7 +2017,7 @@ int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
steal -= h;
}
- for (i = 2; i < 5; i++) {
+ for (i = 2; i < 6; i++) {
gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 1);
steal -= h;
}
@@ -2021,7 +2070,35 @@ int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
tch_hr_reassemble(tch_data, b);
- return 15;
+ return GSM_HR_BYTES;
+}
+
+/*! Perform channel decoding of a HR(v1) channel according TS 05.03,
+ * deprecated legacy API.
+ * \param[out] tch_data Codec frame in pseudo-RFC5993 format
+ * \param[in] bursts buffer containing the symbols of 6 bursts
+ * \param[in] odd Odd (1) or even (0) frame number
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \returns length of bytes used in \a tch_data output buffer; negative on error
+ *
+ * The HR1 codec frame format returned by this function is pseudo-RFC5993,
+ * not true RFC 5993, as there is no SID classification being done
+ * and the FT bits in the ToC octet are always set to 0 - but this
+ * arguably-bogus format is the legacy public API.
+ */
+int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int *n_errors, int *n_bits_total)
+{
+ int rc;
+
+ rc = gsm0503_tch_hr_decode2(tch_data, bursts, odd, n_errors,
+ n_bits_total);
+ if (rc != GSM_HR_BYTES)
+ return rc;
+ memmove(tch_data + 1, tch_data, GSM_HR_BYTES);
+ tch_data[0] = 0x00; /* FT=0, note absence of SID classification */
+ return GSM_HR_BYTES_RTP_RFC5993;
}
/*! Perform channel encoding on a TCH/HS channel according to TS 05.03
@@ -2036,46 +2113,41 @@ int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len)
int i;
switch (len) {
- case 15: /* TCH HR */
+ case GSM_HR_BYTES_RTP_RFC5993: /* TCH HR with RFC 5993 prefix */
+ tch_data++;
+ /* fall-through */
+ case GSM_HR_BYTES: /* TCH HR in "pure" form */
tch_hr_disassemble(b, tch_data);
-
tch_hr_b_to_d(d, b);
-
osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d + 73, 22, p);
-
tch_hr_reorder(conv, d, p);
-
- osmo_conv_encode(&gsm0503_tch_hr, conv, cB);
-
memcpy(cB + 211, d + 95, 17);
-
+hr_conv_coding:
+ osmo_conv_encode(&gsm0503_tch_hr, conv, cB);
h = 0;
-
gsm0503_tch_hr_interleave(cB, iB);
-
for (i = 0; i < 4; i++) {
gsm0503_tch_burst_map(&iB[i * 114],
&bursts[i * 116], &h, i >> 1);
}
-
break;
+ case 0: /* no data, induce BFI in the receiver */
+ /* see comments in gsm0503_tch_fr_encode() - same deal here */
+ memset(conv, 0, sizeof(conv));
+ memset(cB + 211, 0, 17);
+ goto hr_conv_coding;
case GSM_MACBLOCK_LEN: /* FACCH */
_xcch_encode_cB(cB, tch_data);
-
h = 1;
-
gsm0503_tch_fr_interleave(cB, iB);
-
for (i = 0; i < 6; i++) {
gsm0503_tch_burst_map(&iB[i * 114],
&bursts[i * 116], &h, i >> 2);
}
-
for (i = 2; i < 4; i++) {
gsm0503_tch_burst_map(&iB[i * 114 + 456],
&bursts[i * 116], &h, 1);
}
-
break;
default:
return -1;
@@ -2084,6 +2156,26 @@ int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len)
return 0;
}
+/* TCH/AFS: parse codec ID (CMI or CMC/CMR) from coded in-band data (16 bit) */
+static uint8_t gsm0503_tch_afs_decode_inband(const sbit_t *cB)
+{
+ unsigned int id = 0, best = 0;
+ unsigned int i, j, k;
+
+ for (i = 0; i < 4; i++) {
+ /* FIXME: why not using remaining (16 - 8) soft-bits here? */
+ for (j = 0, k = 0; j < 8; j++)
+ k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - ((int)cB[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ id = i;
+ }
+ }
+
+ return id;
+}
+
/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
* \param[out] tch_data Codec frame in RTP payload format
* \param[in] bursts buffer containing the symbols of 8 bursts
@@ -2101,10 +2193,35 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
uint8_t *cmr, int *n_errors, int *n_bits_total)
{
+ return gsm0503_tch_afs_decode_dtx(tch_data, bursts, codec_mode_req,
+ codec, codecs, ft, cmr, n_errors,
+ n_bits_total, NULL);
+}
+
+/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
+ * \param[out] tch_data Codec frame in RTP payload format
+ * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[in] codec_mode_req is this CMR (1) or CMC (0)
+ * \param[in] codec array of active codecs (active codec set)
+ * \param[in] codecs number of codecs in \a codec
+ * \param ft Frame Type; Input if \a codec_mode_req = 1, Output * otherwise
+ * \param[out] cmr Output in \a codec_mode_req = 1
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \param[inout] dtx DTX frame type output, previous DTX frame type input
+ * \returns (>=4) length of bytes used in \a tch_data output buffer; ([0,3])
+ * codec out of range; negative on error
+ */
+int gsm0503_tch_afs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
+ uint8_t *cmr, int *n_errors, int *n_bits_total, uint8_t *dtx)
+{
sbit_t iB[912], cB[456], h;
ubit_t d[244], p[6], conv[250];
- int i, j, k, best = 0, rv, len, steal = 0, id = 0;
+ int i, rv, len, steal = 0, id = -1;
*n_errors = 0; *n_bits_total = 0;
+ static ubit_t sid_first_dummy[64] = { 0 };
+ sbit_t sid_update_enc[256];
for (i=0; i<8; i++) {
gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
@@ -2114,6 +2231,12 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
gsm0503_tch_fr_deinterleave(cB, iB);
if (steal > 0) {
+ /* If not NULL, dtx indicates type of previously decoded TCH/AFS frame.
+ * It's normally updated by gsm0503_detect_afs_dtx_frame2(), which is not
+ * reached in case of FACCH. Reset it here to avoid FACCH/F frames being
+ * misinterpreted as AMR's special DTX frames. */
+ if (dtx != NULL)
+ *dtx = AMR_OTHER;
rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total);
if (rv) {
/* Error decoding FACCH frame */
@@ -2123,18 +2246,59 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
return GSM_MACBLOCK_LEN;
}
- for (i = 0; i < 4; i++) {
- for (j = 0, k = 0; j < 8; j++)
- k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - ((int)cB[j]));
+ /* Determine the DTX frame type (SID_UPDATE, ONSET etc...) */
+ if (dtx) {
+ const enum gsm0503_amr_dtx_frames dtx_prev = *dtx;
+
+ *dtx = gsm0503_detect_afs_dtx_frame2(n_errors, n_bits_total, &id, cB);
+
+ switch (*dtx) {
+ case AMR_OTHER:
+ /* NOTE: The AFS_SID_UPDATE frame is splitted into
+ * two half rate frames. If the id marker frame
+ * (AFS_SID_UPDATE) is detected the following frame
+ * contains the actual comfort noised data part of
+ * (AFS_SID_UPDATE_CN). */
+ if (dtx_prev != AFS_SID_UPDATE)
+ break;
+ /* TODO: parse CMI _and_ CMC/CMR (16 + 16 bit) */
+ *dtx = AFS_SID_UPDATE_CN;
+
+ extract_afs_sid_update(sid_update_enc, cB);
+ osmo_conv_decode_ber(&gsm0503_tch_axs_sid_update,
+ sid_update_enc, conv, n_errors,
+ n_bits_total);
+ rv = osmo_crc16gen_check_bits(&gsm0503_amr_crc14, conv,
+ 35, conv + 35);
+ if (rv != 0) {
+ /* Error checking CRC14 for an AMR SID_UPDATE frame */
+ return -1;
+ }
- if (i == 0 || k < best) {
- best = k;
- id = i;
+ tch_amr_sid_update_append(conv, 1,
+ (codec_mode_req) ? codec[*ft]
+ : codec[id > 0 ? id : 0]);
+ tch_amr_reassemble(tch_data, conv, 39);
+ len = 5;
+ goto out;
+ case AFS_SID_FIRST: /* TODO: parse CMI or CMC/CMR (16 bit) */
+ tch_amr_sid_update_append(sid_first_dummy, 0,
+ (codec_mode_req) ? codec[*ft]
+ : codec[id > 0 ? id : 0]);
+ tch_amr_reassemble(tch_data, sid_first_dummy, 39);
+ len = 5;
+ goto out;
+ case AFS_SID_UPDATE: /* TODO: parse CMI _and_ CMC/CMR (16 + 16 bit) */
+ case AFS_ONSET:
+ len = 0;
+ goto out;
+ default:
+ break;
}
}
- /* Check if indicated codec fits into range of codecs */
- if (id >= codecs) {
+ /* Parse codec ID (CMI or CMC/CMR) and check if it fits into range of codecs */
+ if ((id = gsm0503_tch_afs_decode_inband(&cB[0])) >= codecs) {
/* Codec mode out of range, return id */
return id;
}
@@ -2283,11 +2447,14 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
return -1;
}
+out:
/* Change codec request / indication, if frame is valid */
- if (codec_mode_req)
- *cmr = id;
- else
- *ft = id;
+ if (id != -1) {
+ if (codec_mode_req)
+ *cmr = id;
+ else
+ *ft = id;
+ }
return len;
}
@@ -2295,7 +2462,7 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
/*! Perform channel encoding on a TCH/AFS channel according to TS 05.03
* \param[out] bursts caller-allocated output buffer for bursts bits
* \param[in] tch_data Codec input data in RTP payload format
- * \param[in] len Length of \a tch_data in bytes
+ * \param[in] len Length of \a tch_data in bytes or 0 to generate a bad frame
* \param[in] codec_mode_req Use CMR (1) or FT (0)
* \param[in] codec Array of codecs (active codec set)
* \param[in] codecs Number of entries in \a codec
@@ -2303,7 +2470,7 @@ int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
* \param[in] cmr Codec Mode Request (used in codec_mode_req = 1 only)
* \returns 0 in case of success; negative on error */
int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
- int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
+ int codec_mode_req, const uint8_t *codec, int codecs, uint8_t ft,
uint8_t cmr)
{
ubit_t iB[912], cB[456], h;
@@ -2321,28 +2488,27 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
h = 0;
- if (codec_mode_req) {
- if (cmr >= codecs) {
- /* FIXME: CMR ID is not in codec list! */
- return -1;
- }
- id = cmr;
- } else {
- if (ft >= codecs) {
- /* FIXME: FT ID is not in codec list! */
- return -1;
- }
- id = ft;
- }
+ id = codec_mode_req ? cmr : ft;
+ if (OSMO_UNLIKELY(id >= ARRAY_SIZE(gsm0503_afs_ic_ubit)))
+ return -1;
+ if (OSMO_UNLIKELY(ft >= codecs))
+ return -1;
switch (codec[ft]) {
case 7: /* TCH/AFS12.2 */
- if (len != 31)
- goto invalid_length;
-
- tch_amr_disassemble(d, tch_data, 244);
-
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 81, p);
+ if (!len) {
+ /* No data, induce BFI in the receiver by inverted CRC bits.
+ * The data bit are all 0, so the correct parity bits would be 111111. */
+ memset(d, 0, 244);
+ memset(p, 0, 6);
+ } else {
+ if (len != 31)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 244);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 81, p);
+ }
tch_amr_merge(conv, d, p, 244, 81);
@@ -2350,12 +2516,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 6: /* TCH/AFS10.2 */
- if (len != 26)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 204);
+ memset(p, 0, 6);
+ } else {
+ if (len != 26)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 204);
+ tch_amr_disassemble(d, tch_data, 204);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 65, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 65, p);
+ }
tch_amr_merge(conv, d, p, 204, 65);
@@ -2363,12 +2535,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 5: /* TCH/AFS7.95 */
- if (len != 20)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 159);
+ memset(p, 0, 6);
+ } else {
+ if (len != 20)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 159);
+ tch_amr_disassemble(d, tch_data, 159);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 75, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 75, p);
+ }
tch_amr_merge(conv, d, p, 159, 75);
@@ -2376,12 +2554,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 4: /* TCH/AFS7.4 */
- if (len != 19)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 148);
+ memset(p, 0, 6);
+ } else {
+ if (len != 19)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 148);
+ tch_amr_disassemble(d, tch_data, 148);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+ }
tch_amr_merge(conv, d, p, 148, 61);
@@ -2389,12 +2573,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 3: /* TCH/AFS6.7 */
- if (len != 17)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 134);
+ memset(p, 0, 6);
+ } else {
+ if (len != 17)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 134);
+ tch_amr_disassemble(d, tch_data, 134);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ }
tch_amr_merge(conv, d, p, 134, 55);
@@ -2402,12 +2592,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 2: /* TCH/AFS5.9 */
- if (len != 15)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 118);
+ memset(p, 0, 6);
+ } else {
+ if (len != 15)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 118);
+ tch_amr_disassemble(d, tch_data, 118);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ }
tch_amr_merge(conv, d, p, 118, 55);
@@ -2415,12 +2611,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 1: /* TCH/AFS5.15 */
- if (len != 13)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 103);
+ memset(p, 0, 6);
+ } else {
+ if (len != 13)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 103);
+ tch_amr_disassemble(d, tch_data, 103);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+ }
tch_amr_merge(conv, d, p, 103, 49);
@@ -2428,12 +2630,18 @@ int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 0: /* TCH/AFS4.75 */
- if (len != 12)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 95);
+ memset(p, 0, 6);
+ } else {
+ if (len != 12)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 95);
+ tch_amr_disassemble(d, tch_data, 95);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+ }
tch_amr_merge(conv, d, p, 95, 39);
@@ -2462,9 +2670,29 @@ invalid_length:
return -1;
}
-/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
+/* TCH/AHS: parse codec ID (CMI or CMC/CMR) from coded in-band data (16 bit) */
+static uint8_t gsm0503_tch_ahs_decode_inband(const sbit_t *cB)
+{
+ unsigned int id = 0, best = 0;
+ unsigned int i, j, k;
+
+ for (i = 0, k = 0; i < 4; i++) {
+ /* FIXME: why not using remaining (16 - 4) soft-bits here? */
+ for (j = 0, k = 0; j < 4; j++)
+ k += abs(((int)gsm0503_ahs_ic_sbit[i][j]) - ((int)cB[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ id = i;
+ }
+ }
+
+ return id;
+}
+
+/*! Perform channel decoding of a TCH/AHS channel according TS 05.03
* \param[out] tch_data Codec frame in RTP payload format
- * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[in] bursts buffer containing the symbols of 6 bursts
* \param[in] odd Is this an odd (1) or even (0) frame number?
* \param[in] codec_mode_req is this CMR (1) or CMC (0)
* \param[in] codec array of active codecs (active codec set)
@@ -2480,9 +2708,34 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
uint8_t *cmr, int *n_errors, int *n_bits_total)
{
+ return gsm0503_tch_ahs_decode_dtx(tch_data, bursts, odd, codec_mode_req,
+ codec, codecs, ft, cmr, n_errors,
+ n_bits_total, NULL);
+}
+
+/*! Perform channel decoding of a TCH/AHS channel according TS 05.03
+ * \param[out] tch_data Codec frame in RTP payload format
+ * \param[in] bursts buffer containing the symbols of 6 bursts
+ * \param[in] odd Is this an odd (1) or even (0) frame number?
+ * \param[in] codec_mode_req is this CMR (1) or CMC (0)
+ * \param[in] codec array of active codecs (active codec set)
+ * \param[in] codecs number of codecs in \a codec
+ * \param ft Frame Type; Input if \a codec_mode_req = 1, Output * otherwise
+ * \param[out] cmr Output in \a codec_mode_req = 1
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \param[inout] dtx DTX frame type output, previous DTX frame type input
+ * \returns (>=4) length of bytes used in \a tch_data output buffer; ([0,3])
+ * codec out of range; negative on error
+ */
+int gsm0503_tch_ahs_decode_dtx(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
+ uint8_t *cmr, int *n_errors, int *n_bits_total, uint8_t *dtx)
+{
sbit_t iB[912], cB[456], h;
ubit_t d[244], p[6], conv[135];
- int i, j, k, best = 0, rv, len, steal = 0, id = 0;
+ int i, rv, len, steal = 0, id = -1;
+ static ubit_t sid_first_dummy[64] = { 0 };
/* only unmap the stealing bits */
if (!odd) {
@@ -2498,6 +2751,13 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
/* if we found a stole FACCH, but only at correct alignment */
if (steal > 0) {
+ /* If not NULL, dtx indicates type of previously decoded TCH/AHS frame.
+ * It's normally updated by gsm0503_detect_ahs_dtx_frame2(), which is not
+ * reached in case of FACCH. Reset it here to avoid FACCH/H frames being
+ * misinterpreted as AMR's special DTX frames. */
+ if (dtx != NULL)
+ *dtx = AMR_OTHER;
+
for (i = 0; i < 6; i++) {
gsm0503_tch_burst_unmap(&iB[i * 114],
&bursts[i * 116], NULL, i >> 2);
@@ -2526,18 +2786,72 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
gsm0503_tch_hr_deinterleave(cB, iB);
- for (i = 0; i < 4; i++) {
- for (j = 0, k = 0; j < 4; j++)
- k += abs(((int)gsm0503_ahs_ic_sbit[i][j]) - ((int)cB[j]));
+ /* Determine the DTX frame type (SID_UPDATE, ONSET etc...) */
+ if (dtx) {
+ int n_bits_total_sid;
+ int n_errors_sid;
+
+ *dtx = gsm0503_detect_ahs_dtx_frame2(n_errors, n_bits_total, &id, cB);
+ /* TODO: detect and handle AHS_SID_UPDATE + AHS_SID_UPDATE_INH */
+
+ switch (*dtx) {
+ case AHS_SID_UPDATE: /* TODO: parse CMI _and_ CMC/CMR (16 + 16 bit) */
+ /* cB[] contains 16 bits of coded in-band data and 212 bits containing
+ * the identification marker. We need to unmap/deinterleave 114 odd
+ * bits from the last two blocks, 114 even bits from the first two
+ * blocks and combine them together. */
+ gsm0503_tch_burst_unmap(&iB[0 * 114], &bursts[2 * 116], NULL, 0);
+ gsm0503_tch_burst_unmap(&iB[1 * 114], &bursts[3 * 116], NULL, 0);
+ gsm0503_tch_burst_unmap(&iB[2 * 114], &bursts[0 * 116], NULL, 1);
+ gsm0503_tch_burst_unmap(&iB[3 * 114], &bursts[1 * 116], NULL, 1);
+ gsm0503_tch_hr_deinterleave(cB, iB);
+
+ /* cB[] is expected to contain 16 bits of coded in-band data and
+ * 212 bits containing the coded data (53 bits coded at 1/4 rate). */
+ *dtx = AHS_SID_UPDATE_CN;
+
+ osmo_conv_decode_ber(&gsm0503_tch_axs_sid_update,
+ cB + 16, conv, &n_errors_sid,
+ &n_bits_total_sid);
+ /* gsm0503_detect_ahs_dtx_frame2() calculates BER for the marker,
+ * osmo_conv_decode_ber() calculates BER for the coded data. */
+ if (n_errors != NULL)
+ *n_errors += n_errors_sid;
+ if (n_bits_total != NULL)
+ *n_bits_total += n_bits_total_sid;
+ rv = osmo_crc16gen_check_bits(&gsm0503_amr_crc14, conv,
+ 35, conv + 35);
+ if (rv != 0) {
+ /* Error checking CRC14 for an AMR SID_UPDATE frame */
+ return -1;
+ }
- if (i == 0 || k < best) {
- best = k;
- id = i;
+ tch_amr_sid_update_append(conv, 1,
+ (codec_mode_req) ? codec[*ft]
+ : codec[id > 0 ? id : 0]);
+ tch_amr_reassemble(tch_data, conv, 39);
+ len = 5;
+ goto out;
+ case AHS_SID_FIRST_P2:
+ tch_amr_sid_update_append(sid_first_dummy, 0,
+ (codec_mode_req) ? codec[*ft]
+ : codec[id > 0 ? id : 0]);
+ tch_amr_reassemble(tch_data, sid_first_dummy, 39);
+ len = 5;
+ goto out;
+ case AHS_ONSET:
+ case AHS_SID_FIRST_INH: /* TODO: parse CMI or CMC/CMR (16 bit) */
+ case AHS_SID_UPDATE_INH: /* TODO: parse CMI or CMC/CMR (16 bit) */
+ case AHS_SID_FIRST_P1: /* TODO: parse CMI or CMC/CMR (16 bit) */
+ len = 0;
+ goto out;
+ default:
+ break;
}
}
- /* Check if indicated codec fits into range of codecs */
- if (id >= codecs) {
+ /* Parse codec ID (CMI or CMC/CMR) and check if it fits into range of codecs */
+ if ((id = gsm0503_tch_ahs_decode_inband(&cB[0])) >= codecs) {
/* Codec mode out of range, return id */
return id;
}
@@ -2670,11 +2984,14 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
return -1;
}
+out:
/* Change codec request / indication, if frame is valid */
- if (codec_mode_req)
- *cmr = id;
- else
- *ft = id;
+ if (id != -1) {
+ if (codec_mode_req)
+ *cmr = id;
+ else
+ *ft = id;
+ }
return len;
}
@@ -2682,7 +2999,7 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
/*! Perform channel encoding on a TCH/AHS channel according to TS 05.03
* \param[out] bursts caller-allocated output buffer for bursts bits
* \param[in] tch_data Codec input data in RTP payload format
- * \param[in] len Length of \a tch_data in bytes
+ * \param[in] len Length of \a tch_data in bytes or 0 to generate a bad frame
* \param[in] codec_mode_req Use CMR (1) or FT (0)
* \param[in] codec Array of codecs (active codec set)
* \param[in] codecs Number of entries in \a codec
@@ -2690,7 +3007,7 @@ int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
* \param[in] cmr Codec Mode Request (used in codec_mode_req = 1 only)
* \returns 0 in case of success; negative on error */
int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
- int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
+ int codec_mode_req, const uint8_t *codec, int codecs, uint8_t ft,
uint8_t cmr)
{
ubit_t iB[912], cB[456], h;
@@ -2717,28 +3034,27 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
h = 0;
- if (codec_mode_req) {
- if (cmr >= codecs) {
- /* FIXME: CMR ID %d not in codec list */
- return -1;
- }
- id = cmr;
- } else {
- if (ft >= codecs) {
- /* FIXME: FT ID %d not in codec list */
- return -1;
- }
- id = ft;
- }
+ id = codec_mode_req ? cmr : ft;
+ if (OSMO_UNLIKELY(id >= ARRAY_SIZE(gsm0503_ahs_ic_ubit)))
+ return -1;
+ if (OSMO_UNLIKELY(ft >= codecs))
+ return -1;
switch (codec[ft]) {
case 5: /* TCH/AHS7.95 */
- if (len != 20)
- goto invalid_length;
-
- tch_amr_disassemble(d, tch_data, 159);
-
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 67, p);
+ if (!len) {
+ /* No data, induce BFI in the receiver by inverted CRC bits.
+ * The data bit are all 0, so the correct parity bits would be 111111. */
+ memset(d, 0, 159);
+ memset(p, 0, 6);
+ } else {
+ if (len != 20)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 159);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 67, p);
+ }
tch_amr_merge(conv, d, p, 123, 67);
@@ -2748,12 +3064,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 4: /* TCH/AHS7.4 */
- if (len != 19)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 148);
+ memset(p, 0, 6);
+ } else {
+ if (len != 19)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 148);
+ tch_amr_disassemble(d, tch_data, 148);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+ }
tch_amr_merge(conv, d, p, 120, 61);
@@ -2763,12 +3085,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 3: /* TCH/AHS6.7 */
- if (len != 17)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 134);
+ memset(p, 0, 6);
+ } else {
+ if (len != 17)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 134);
+ tch_amr_disassemble(d, tch_data, 134);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ }
tch_amr_merge(conv, d, p, 110, 55);
@@ -2778,12 +3106,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 2: /* TCH/AHS5.9 */
- if (len != 15)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 118);
+ memset(p, 0, 6);
+ } else {
+ if (len != 15)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 118);
+ tch_amr_disassemble(d, tch_data, 118);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+ }
tch_amr_merge(conv, d, p, 102, 55);
@@ -2793,12 +3127,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 1: /* TCH/AHS5.15 */
- if (len != 13)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 103);
+ memset(p, 0, 6);
+ } else {
+ if (len != 13)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 103);
+ tch_amr_disassemble(d, tch_data, 103);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+ }
tch_amr_merge(conv, d, p, 91, 49);
@@ -2808,12 +3148,18 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
break;
case 0: /* TCH/AHS4.75 */
- if (len != 12)
- goto invalid_length;
+ if (!len) {
+ /* See comment above. */
+ memset(d, 0, 95);
+ memset(p, 0, 6);
+ } else {
+ if (len != 12)
+ goto invalid_length;
- tch_amr_disassemble(d, tch_data, 95);
+ tch_amr_disassemble(d, tch_data, 95);
- osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+ }
tch_amr_merge(conv, d, p, 83, 39);
@@ -2827,7 +3173,7 @@ int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
return -1;
}
- memcpy(cB, gsm0503_afs_ic_ubit[id], 4);
+ memcpy(cB, gsm0503_ahs_ic_ubit[id], 4);
gsm0503_tch_hr_interleave(cB, iB);
@@ -2879,7 +3225,7 @@ static inline int16_t rach_decode_ber(const sbit_t *burst, uint8_t bsic, bool is
osmo_ubit2pbit_ext(ra, 0, conv, 0, nbits, 1);
- return is_11bit ? osmo_load16le(ra) : ra[0];
+ return is_11bit ? ((ra[0] << 3) | (ra[1] & 0x07)) : ra[0];
}
/*! Decode the Extended (11-bit) RACH according to 3GPP TS 45.003
@@ -2974,7 +3320,8 @@ int gsm0503_rach_ext_encode(ubit_t *burst, uint16_t ra11, uint8_t bsic, bool is_
uint8_t ra[2] = { 0 }, nbits = 8;
if (is_11bit) {
- osmo_store16le(ra11, ra);
+ ra[0] = (uint8_t) (ra11 >> 3);
+ ra[1] = (uint8_t) (ra11 & 0x07);
nbits = 11;
} else
ra[0] = (uint8_t)ra11;
@@ -3031,4 +3378,491 @@ int gsm0503_sch_encode(ubit_t *burst, const uint8_t *sb_info)
return 0;
}
+/*
+ * GSM CSD transcoding
+ */
+
+static inline void _tch_csd_burst_map(ubit_t *burst, const ubit_t *iB)
+{
+ unsigned int i;
+
+ /* hu(B): copy *even* numbered bits if not stolen by FACCH */
+ if (burst[58] == 0) {
+ for (i = 0; i < 57; i += 2)
+ burst[i] |= iB[i];
+ for (i = 58; i < 114; i += 2)
+ burst[i + 2] |= iB[i];
+ }
+
+ /* hl(B): copy *odd* numbered bits if not stolen by FACCH */
+ if (burst[57] == 0) {
+ for (i = 1; i < 57; i += 2)
+ burst[i] |= iB[i];
+ for (i = 57; i < 114; i += 2)
+ burst[i + 2] |= iB[i];
+ }
+}
+
+/*! Perform channel encoding of a TCH/F9.6 channel as per section 3.3.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (240 unpacked bits).
+ * \returns 0 in case of success; negative on error. */
+int gsm0503_tch_fr96_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[4 * 60 + 4];
+
+ /* 3.3.2 Block code: b1(60) + b2(60) + b3(60) + b4(60) + pad(4) */
+ memcpy(&conv[0], &data[0], 4 * 60);
+ /* pad(4) is set to 0 by osmo_conv_encode() below */
+
+ /* 3.3.3 Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_f96, &conv[0], &cB[0]);
+
+ /* 3.3.4 Interleaving */
+ memset(&iB[0], 0, sizeof(iB));
+ gsm0503_tch_f96_interleave(&cB[0], &iB[0]);
+
+ /* 3.3.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++)
+ _tch_csd_burst_map(&bursts[i * 116], &iB[i * 114]);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/F9.6 channel as per section 3.3.
+ * \param[out] data Caller-allocated buffer for decoded data (240 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_fr96_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[4 * 60 + 4];
+
+ /* 3.3.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++) {
+ memcpy(&iB[i * 114], &bursts[i * 116], 57);
+ memcpy(&iB[i * 114 + 57], &bursts[i * 116 + 59], 57);
+ }
+
+ /* 3.3.4 Interleaving */
+ gsm0503_tch_f96_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.3.3 Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_f96, &cB[0], &conv[0], n_errors, n_bits_total);
+
+ /* 3.3.2 Block code: b1(60) + b2(60) + b3(60) + b4(60) + pad(4) */
+ memcpy(&data[0], &conv[0], 4 * 60);
+
+ return 4 * 60;
+}
+
+/*! Perform channel encoding of a TCH/F4.8 channel as per section 3.4.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (120 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_fr48_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[2 * 60 + 32];
+
+ /* 3.4.2 Block code:
+ *
+ * Sixteen bits equal to 0 are added to the 60 information bits, the result
+ * being a block of 76 bits, {u(0),u(1),...,u(75)}, with:
+ *
+ * u(19k+p) = d(15k+p) for k = 0,1,2,3 and p = 0,1,...,14;
+ * u(19k+p) = 0 for k = 0,1,2,3 and p = 15,16,17,18.
+ *
+ * Two such blocks forming a block of 152 bits: u1 + u2. */
+ for (unsigned int k = 0; k < 2 * 4; k++) {
+ memcpy(&conv[19 * k], &data[15 * k], 15);
+ memset(&conv[19 * k + 15], 0, 4);
+ }
+
+ /* 3.4.3 Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_f48, &conv[0], &cB[0]);
+
+ /* 3.4.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ memset(&iB[0], 0, sizeof(iB));
+ gsm0503_tch_f96_interleave(&cB[0], &iB[0]);
+
+ /* 3.4.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++)
+ _tch_csd_burst_map(&bursts[i * 116], &iB[i * 114]);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/F4.8 channel as per section 3.4.
+ * \param[out] data Caller-allocated buffer for decoded data (120 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_fr48_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[2 * 60 + 32];
+
+ /* 3.4.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++) {
+ memcpy(&iB[i * 114], &bursts[i * 116], 57);
+ memcpy(&iB[i * 114 + 57], &bursts[i * 116 + 59], 57);
+ }
+
+ /* 3.4.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ gsm0503_tch_f96_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.4.3 Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_f48, &cB[0], &conv[0], n_errors, n_bits_total);
+
+ /* 3.4.2 Block code:
+ *
+ * Sixteen bits equal to 0 are added to the 60 information bits, the result
+ * being a block of 76 bits, {u(0),u(1),...,u(75)}, with:
+ *
+ * u(19k+p) = d(15k+p) for k = 0,1,2,3 and p = 0,1,...,14;
+ * u(19k+p) = 0 for k = 0,1,2,3 and p = 15,16,17,18.
+ *
+ * Two such blocks forming a block of 152 bits: u1 + u2. */
+ for (unsigned int k = 0; k < 2 * 4; k++)
+ memcpy(&data[15 * k], &conv[19 * k], 15);
+
+ return 2 * 60;
+}
+
+/*! Perform channel encoding of a TCH/H4.8 channel as per section 3.5.
+ * The algorithm is identical to TCH/F9.6, so it's just a wrapper.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (240 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_hr48_encode(ubit_t *bursts, const ubit_t *data)
+{
+ return gsm0503_tch_fr96_encode(bursts, data);
+}
+
+/*! Perform channel decoding of a TCH/H4.8 channel as per section 3.5.
+ * The algorithm is identical to TCH/F9.6, so it's just a wrapper.
+ * \param[out] data Caller-allocated buffer for decoded data (240 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_hr48_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ return gsm0503_tch_fr96_decode(data, bursts, n_errors, n_bits_total);
+}
+
+/*! Perform channel encoding of a TCH/F2.4 channel as per section 3.6.
+ * \param[out] bursts Caller-allocated buffer for symbols of 8 bursts,
+ * 8 * 2 * 58 == 928 bits total.
+ * \param[in] data Data to be encoded (72 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_fr24_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[8 * 114], cB[4 * 114];
+ const ubit_t h = 0;
+
+ /* 3.6.{1-3} Block code and Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_f24, &data[0], &cB[0]);
+
+ /* 3.6.4 Interleaving: as specified for the TCH/FS in subclause 3.1.3 */
+ gsm0503_tch_fr_interleave(&cB[0], &iB[0]);
+
+ /* 3.6.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 8; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/F2.4 channel as per section 3.6.
+ * \param[out] data Caller-allocated buffer for decoded data (72 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 8 bursts,
+ * 8 * 2 * 58 == 928 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_fr24_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[8 * 114], cB[4 * 114];
+
+ /* 3.6.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 8; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, i >> 2);
+
+ /* 3.6.4 Interleaving: as specified for the TCH/FS in subclause 3.1.3 */
+ gsm0503_tch_fr_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.6.{1-3} Block code and Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_f24, &cB[0], &data[0], n_errors, n_bits_total);
+
+ return 72;
+}
+
+/*! Perform channel encoding of a TCH/H2.4 channel as per section 3.7.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (144 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_hr24_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[22 * 114], cB[4 * 114];
+
+ /* 3.7.{1-3} Block code and Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_h24, &data[ 0], &cB[ 0]);
+ osmo_conv_encode(&gsm0503_tch_h24, &data[72], &cB[228]);
+
+ /* 3.7.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ memset(&iB[0], 0, sizeof(iB));
+ gsm0503_tch_f96_interleave(&cB[0], &iB[0]);
+
+ /* 3.7.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++)
+ _tch_csd_burst_map(&bursts[i * 116], &iB[i * 114]);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/H2.4 channel as per section 3.7.
+ * \param[out] data Caller-allocated buffer for decoded data (144 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_hr24_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ int n_errors_l[2], n_bits_total_l[2];
+ sbit_t iB[22 * 114], cB[4 * 114];
+
+ /* 3.7.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++) {
+ memcpy(&iB[i * 114], &bursts[i * 116], 57);
+ memcpy(&iB[i * 114 + 57], &bursts[i * 116 + 59], 57);
+ }
+
+ /* 3.7.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ gsm0503_tch_f96_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.7.{1-3} Block code and Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_h24, &cB[ 0], &data[ 0], &n_errors_l[0], &n_bits_total_l[0]);
+ osmo_conv_decode_ber(&gsm0503_tch_h24, &cB[228], &data[72], &n_errors_l[1], &n_bits_total_l[1]);
+
+ if (n_errors)
+ *n_errors = n_errors_l[0] + n_errors_l[1];
+
+ if (n_bits_total)
+ *n_bits_total = n_bits_total_l[0] + n_bits_total_l[1];
+
+ return 2 * 72;
+}
+
+/*! Perform channel encoding of a TCH/F14.4 channel as per section 3.8.
+ * \param[out] bursts Caller-allocated buffer for symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[in] data Data to be encoded (290 unpacked bits).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_fr144_encode(ubit_t *bursts, const ubit_t *data)
+{
+ ubit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[290 + 4];
+
+ /* 3.8.2 Block code: b(290) + pad(4) */
+ memcpy(&conv[0], &data[0], 290);
+ /* pad(4) is set to 0 by osmo_conv_encode() below */
+
+ /* 3.8.3 Convolutional encoder */
+ osmo_conv_encode(&gsm0503_tch_f144, &conv[0], &cB[0]);
+
+ /* 3.8.4 Interleaving */
+ memset(&iB[0], 0, sizeof(iB));
+ gsm0503_tch_f96_interleave(&cB[0], &iB[0]);
+
+ /* 3.8.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++)
+ _tch_csd_burst_map(&bursts[i * 116], &iB[i * 114]);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/14.4 channel as per section 3.8.
+ * \param[out] data Caller-allocated buffer for decoded data (290 unpacked bits).
+ * \param[in] bursts Buffer containing the symbols of 22 bursts,
+ * 22 * 2 * 58 == 2552 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of unpacked bits used in the output buffer; negative on error. */
+int gsm0503_tch_fr144_decode(ubit_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[22 * 114], cB[4 * 114];
+ ubit_t conv[294];
+
+ /* 3.8.5 Mapping on a burst: as specified for TCH/FS in subclause 3.1.4 */
+ for (unsigned int i = 0; i < 22; i++) {
+ memcpy(&iB[i * 114], &bursts[i * 116], 57);
+ memcpy(&iB[i * 114 + 57], &bursts[i * 116 + 59], 57);
+ }
+
+ /* 3.8.4 Interleaving: as specified for the TCH/F9.6 in subclause 3.3.4 */
+ gsm0503_tch_f96_deinterleave(&cB[0], &iB[0]);
+
+ /* 3.8.3 Convolutional encoder */
+ osmo_conv_decode_ber(&gsm0503_tch_f144, &cB[0], &conv[0], n_errors, n_bits_total);
+
+ /* 3.8.2 Block code: b(290) + pad(4) */
+ memcpy(&data[0], &conv[0], 290);
+
+ return 290;
+}
+
+/*
+ * FACCH/[FH] transcoding
+ */
+
+/*! Perform channel encoding of a FACCH/F data as per section 4.2.
+ * \param[out] bursts Caller-allocated buffer for symbols of 8 bursts,
+ * 8 * 2 * 58 == 928 bits total.
+ * \param[in] data FACCH MAC block to be encoded (GSM_MACBLOCK_LEN).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_fr_facch_encode(ubit_t *bursts, const uint8_t *data)
+{
+ ubit_t iB[8 * 114], cB[4 * 114];
+ const ubit_t h = 1;
+
+ /* 4.2.1-3 as specified for the SACCH in 4.1.1-3 */
+ _xcch_encode_cB(&cB[0], &data[0]);
+
+ /* 4.2.4 Interleaving: as specified for the TCH/FS in subclause 3.1.3 */
+ gsm0503_tch_fr_interleave(&cB[0], &iB[0]);
+
+ /* 4.2.5 Mapping on a Burst:
+ * - hu(B)=1 the even numbered bits in the first 4 bursts and
+ * - hl(B)=1 the odd numbered bits of the last 4 bursts are stolen. */
+ for (unsigned int i = 0; i < 8; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a FACCH/F data as per section 4.2.
+ * \param[out] data Caller-allocated buffer for decoded FACCH (GSM_MACBLOCK_LEN).
+ * \param[in] bursts Buffer containing the symbols of 8 bursts,
+ * 8 * 2 * 58 == 928 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of bytes used in the output buffer; negative on error. */
+int gsm0503_tch_fr_facch_decode(uint8_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[8 * 114], cB[4 * 114];
+ int steal = 0;
+
+ /* FACCH decision: sum of 4 first hu(B) and 4 last hl(B) soft-bits */
+ for (unsigned int i = 0; i < 4; i++)
+ steal -= bursts[i * 116 + 58]; /* hu(B) */
+ for (unsigned int i = 4; i < 8; i++)
+ steal -= bursts[i * 116 + 57]; /* hl(B) */
+ if (steal <= 0)
+ return -1;
+
+ /* 4.2.5 Mapping on a Burst:
+ * - hu(B)=1 the even numbered bits in the first 4 bursts and
+ * - hl(B)=1 the odd numbered bits of the last 4 bursts are stolen. */
+ for (unsigned int i = 0; i < 8; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, i >> 2);
+
+ /* 4.2.4 Interleaving: as specified for the TCH/FS in subclause 3.1.3 */
+ gsm0503_tch_fr_deinterleave(&cB[0], &iB[0]);
+
+ /* 4.2.1-3 as specified for the SACCH in 4.1.1-3 */
+ if (_xcch_decode_cB(&data[0], &cB[0], n_errors, n_bits_total) != 0)
+ return -1;
+
+ return GSM_MACBLOCK_LEN;
+}
+
+/*! Perform channel encoding of a FACCH/H data as per section 4.3.
+ * \param[out] bursts Caller-allocated buffer for symbols of 6 bursts,
+ * 6 * 2 * 58 == 696 bits total.
+ * \param[in] data FACCH MAC block to be encoded (GSM_MACBLOCK_LEN).
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_hr_facch_encode(ubit_t *bursts, const uint8_t *data)
+{
+ ubit_t iB[8 * 114], cB[4 * 114];
+ const ubit_t h = 1;
+
+ /* 4.3.1-3 as specified for the SACCH in 4.1.1-3 */
+ _xcch_encode_cB(&cB[0], &data[0]);
+
+ /* 4.3.4 Interleaving */
+ gsm0503_tch_fr_interleave(&cB[0], &iB[0]);
+
+ /* 4.3.5 Mapping on a Burst:
+ * - hu(B)=1 the even numbered bits of the first 2 bursts,
+ * - hu(B)=1 & hl(B)=1 all bits of the middle 2 bursts and
+ * - hl(B)=1 the odd numbered bits of the last 2 bursts are stolen. */
+ for (unsigned int i = 0; i < 6; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
+ for (unsigned int i = 2; i < 4; i++)
+ gsm0503_tch_burst_map(&iB[i * 114 + 456], &bursts[i * 116], &h, 1);
+
+ return 0;
+}
+
+/*! Perform channel decoding of a FACCH/H data as per section 4.3.
+ * \param[out] data Caller-allocated buffer for decoded FACCH (GSM_MACBLOCK_LEN).
+ * \param[in] bursts Buffer containing the symbols of 6 bursts,
+ * 6 * 2 * 58 == 696 bits total.
+ * \param[out] n_errors Number of detected bit errors.
+ * \param[out] n_bits_total Total number of bits.
+ * \returns Number of bytes used in the output buffer; negative on error. */
+int gsm0503_tch_hr_facch_decode(uint8_t *data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[8 * 114], cB[4 * 114];
+ int steal = 0;
+
+ /* FACCH decision: sum of 4 first hu(B) and 4 last hl(B) soft-bits */
+ for (unsigned int i = 0; i < 4; i++)
+ steal -= bursts[i * 116 + 58]; /* hu(B) */
+ for (unsigned int i = 2; i < 6; i++)
+ steal -= bursts[i * 116 + 57]; /* hl(B) */
+ if (steal <= 0)
+ return -1;
+
+ /* 4.3.5 Mapping on a Burst:
+ * - hu(B)=1 the even numbered bits of the first 2 bursts,
+ * - hu(B)=1 & hl(B)=1 all bits of the middle 2 bursts and
+ * - hl(B)=1 the odd numbered bits of the last 2 bursts are stolen. */
+ for (unsigned int i = 0; i < 6; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, i >> 2);
+ for (unsigned int i = 2; i < 4; i++)
+ gsm0503_tch_burst_unmap(&iB[i * 114 + 456], &bursts[i * 116], NULL, 1);
+
+ /* 4.3.4 Interleaving */
+ gsm0503_tch_fr_deinterleave(&cB[0], &iB[0]);
+
+ /* 4.3.1-3 as specified for the SACCH in 4.1.1-3 */
+ if (_xcch_decode_cB(&data[0], &cB[0], n_errors, n_bits_total) != 0)
+ return -1;
+
+ return GSM_MACBLOCK_LEN;
+}
+
/*! @} */
diff --git a/src/coding/gsm0503_interleaving.c b/src/coding/gsm0503_interleaving.c
index d5008d07..570d65aa 100644
--- a/src/coding/gsm0503_interleaving.c
+++ b/src/coding/gsm0503_interleaving.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
@@ -682,4 +678,56 @@ void gsm0503_tch_hr_interleave(const ubit_t *cB, ubit_t *iB)
}
}
+/* 3GPP TS 45.003 Section 3.3.4
+ * The coded bits are reordered and interleaved according to the following rule:
+ * i(B,j) = c(n,k) for k = 0,1,...,455
+ * n = 0,1,...,N,N + 1,...
+ * B = B0 +4n + (k mod 19) + (k div 114)
+ * j = (k mod 19) + 19 (k mod 6)
+ *
+ * The result of the interleaving is a distribution of the reordered 114
+ * bit of a given data block, n = N, over 19 blocks, 6 bits equally
+ * distributed in each block, in a diagonal way over consecutive blocks.
+ *
+ * Or in other words the interleaving is a distribution of the encoded,
+ * reordered 456 bits from four given input data blocks, which taken
+ * together give n = N, over 22 bursts, 6 bits equally distributed in
+ * the first and 22 nd bursts, 12 bits distributed in the second and 21
+ * st bursts, 18 bits distributed in the third and 20 th bursts and 24
+ * bits distributed in the other 16 bursts.
+ *
+ * The block of coded data is interleaved "diagonal", where a new block
+ * of coded data starts with every fourth burst and is distributed over
+ * 22 bursts.
+ *
+ * Also used for TCH/F4.8, TCH/H4.8, and TCH/H2.4 and TCH/F14.4 */
+void gsm0503_tch_f96_interleave(const ubit_t *cB, ubit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 456; k++) {
+ /* upper bound for B: 4*n + 18 + 4 = 4*n + 22 */
+ B = /* B0 + 4n + */ (k % 19) + (k / 114);
+ /* upper bound for j: 18 + 19*5 = 113 */
+ j = (k % 19) + 19 * (k % 6);
+ /* upper iB index: 4*n+23*114-1 */
+ iB[B * 114 + j] = cB[k];
+ }
+}
+
+void gsm0503_tch_f96_deinterleave(sbit_t *cB, const sbit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 456; k++) {
+ /* upper bound for B: 4*n + 18 + 4 = 4*n + 22 */
+ B = /* B0 + 4n + */ (k % 19) + (k / 114);
+ /* upper bound for j: 18 + 19*5 = 113 */
+ j = (k % 19) + 19 * (k % 6);
+ /* upper iB index: 4*n+23*114-1 */
+ cB[k] = iB[B * 114 + j];
+ }
+}
+
+
/*! @} */
diff --git a/src/coding/gsm0503_mapping.c b/src/coding/gsm0503_mapping.c
index f7532eb2..04acfd07 100644
--- a/src/coding/gsm0503_mapping.c
+++ b/src/coding/gsm0503_mapping.c
@@ -15,10 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
diff --git a/src/coding/gsm0503_parity.c b/src/coding/gsm0503_parity.c
index 874114ff..ef71d351 100644
--- a/src/coding/gsm0503_parity.c
+++ b/src/coding/gsm0503_parity.c
@@ -15,10 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
@@ -134,4 +130,15 @@ const struct osmo_crc8gen_code gsm0503_amr_crc6 = {
.remainder = 0x3f,
};
+/*! GSM AMR parity (SID_UPDATE)
+ *
+ * g(x) = x^14 + x^13 + x^5 + x^3 + x^2 + 1
+ */
+const struct osmo_crc16gen_code gsm0503_amr_crc14 = {
+ .bits = 14,
+ .poly = 0x202d,
+ .init = 0x0000,
+ .remainder = 0x3fff,
+};
+
/*! @} */
diff --git a/src/coding/gsm0503_tables.c b/src/coding/gsm0503_tables.c
index 5fe634bf..25ea2fa3 100644
--- a/src/coding/gsm0503_tables.c
+++ b/src/coding/gsm0503_tables.c
@@ -15,10 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
@@ -63,6 +59,9 @@ const sbit_t gsm0503_pdtch_edge_hl_hn_sbit[3][8] = {
{ -127,-127, -127, 127, 127,-127, -127,-127 },
};
+/*
+ * 3GPP TS 05.03 sec 5.1.2.2 "Block code". Rows re-ordered to be indxed by USF in host bit order.
+ */
const ubit_t gsm0503_usf2six[8][6] = {
{ 0,0,0, 0,0,0 },
{ 1,0,0, 1,0,1 },
@@ -74,6 +73,9 @@ const ubit_t gsm0503_usf2six[8][6] = {
{ 1,1,1, 0,0,0 },
};
+/*
+ * 3GPP TS 05.03 sec 5.1.4.2 "Block code". Rows re-ordered to be indxed by USF in host bit order.
+ */
const ubit_t gsm0503_usf2twelve_ubit[8][12] = {
{ 0,0,0, 0,0,0, 0,0,0, 0,0,0 },
{ 1,1,0, 1,0,0, 0,0,1, 0,1,1 },
diff --git a/src/coding/libosmocoding.map b/src/coding/libosmocoding.map
index 87b38864..0444690e 100644
--- a/src/coding/libosmocoding.map
+++ b/src/coding/libosmocoding.map
@@ -56,6 +56,7 @@ gsm0503_sch_crc10;
gsm0503_tch_fr_crc3;
gsm0503_tch_efr_crc8;
gsm0503_amr_crc6;
+gsm0503_amr_crc14;
gsm0503_xcch_burst_unmap;
gsm0503_xcch_burst_map;
@@ -75,6 +76,8 @@ gsm0503_xcch_deinterleave;
gsm0503_xcch_interleave;
gsm0503_tch_fr_deinterleave;
gsm0503_tch_fr_interleave;
+gsm0503_tch_f96_deinterleave;
+gsm0503_tch_f96_interleave;
gsm0503_tch_hr_deinterleave;
gsm0503_tch_hr_interleave;
gsm0503_mcs1_ul_deinterleave;
@@ -104,10 +107,13 @@ gsm0503_tch_fr_encode;
gsm0503_tch_fr_decode;
gsm0503_tch_hr_encode;
gsm0503_tch_hr_decode;
+gsm0503_tch_hr_decode2;
gsm0503_tch_afs_encode;
gsm0503_tch_afs_decode;
+gsm0503_tch_afs_decode_dtx;
gsm0503_tch_ahs_encode;
gsm0503_tch_ahs_decode;
+gsm0503_tch_ahs_decode_dtx;
gsm0503_rach_ext_encode;
gsm0503_rach_ext_decode;
gsm0503_rach_ext_decode_ber;
@@ -116,6 +122,30 @@ gsm0503_rach_decode;
gsm0503_rach_decode_ber;
gsm0503_sch_encode;
gsm0503_sch_decode;
+gsm0503_amr_dtx_frame_names;
+gsm0503_amr_dtx_frame_name;
+gsm0503_detect_afs_dtx_frame;
+gsm0503_detect_ahs_dtx_frame;
+gsm0503_detect_afs_dtx_frame2;
+gsm0503_detect_ahs_dtx_frame2;
+
+gsm0503_tch_fr96_encode;
+gsm0503_tch_fr96_decode;
+gsm0503_tch_fr48_encode;
+gsm0503_tch_fr48_decode;
+gsm0503_tch_hr48_encode;
+gsm0503_tch_hr48_decode;
+gsm0503_tch_fr24_encode;
+gsm0503_tch_fr24_decode;
+gsm0503_tch_hr24_encode;
+gsm0503_tch_hr24_decode;
+gsm0503_tch_fr144_encode;
+gsm0503_tch_fr144_decode;
+
+gsm0503_tch_fr_facch_encode;
+gsm0503_tch_fr_facch_decode;
+gsm0503_tch_hr_facch_encode;
+gsm0503_tch_hr_facch_decode;
local: *;
};
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
new file mode 100644
index 00000000..2efebd8d
--- /dev/null
+++ b/src/core/Makefile.am
@@ -0,0 +1,166 @@
+# This is _NOT_ the library release version, it's an API version.
+# Please read chapter "Library interface versions" of the libtool documentation
+# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
+LIBVERSION=21:0:0
+
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) $(LIBSCTP_CFLAGS) $(LIBMNL_CFLAGS) $(URING_CFLAGS)
+
+if ENABLE_PSEUDOTALLOC
+AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
+endif
+
+lib_LTLIBRARIES = libosmocore.la
+
+libosmocore_la_LIBADD = \
+ $(BACKTRACE_LIB) \
+ $(TALLOC_LIBS) \
+ $(LIBRARY_RT) \
+ $(PTHREAD_LIBS) \
+ $(LIBSCTP_LIBS) \
+ $(URING_LIBS) \
+ $(NULL)
+
+libosmocore_la_SOURCES = \
+ application.c \
+ backtrace.c \
+ base64.c \
+ bits.c \
+ bitvec.c \
+ bitcomp.c \
+ context.c \
+ conv.c \
+ conv_acc.c \
+ conv_acc_generic.c \
+ counter.c \
+ crc16.c \
+ crc8gen.c \
+ crc16gen.c \
+ crc32gen.c \
+ crc64gen.c \
+ exec.c \
+ fsm.c \
+ gsmtap_util.c \
+ isdnhdlc.c \
+ it_q.c \
+ logging.c \
+ logging_syslog.c \
+ logging_gsmtap.c \
+ loggingrb.c \
+ macaddr.c \
+ msgb.c \
+ netdev.c \
+ netns.c \
+ osmo_io.c \
+ osmo_io_poll.c \
+ panic.c \
+ prbs.c \
+ prim.c \
+ rate_ctr.c \
+ rbtree.c \
+ select.c \
+ sercomm.c \
+ signal.c \
+ sockaddr_str.c \
+ socket.c \
+ soft_uart.c \
+ stat_item.c \
+ stats.c \
+ stats_statsd.c \
+ stats_tcp.c \
+ strrb.c \
+ tdef.c \
+ thread.c \
+ time_cc.c \
+ timer.c \
+ timer_gettimeofday.c \
+ timer_clockgettime.c \
+ tun.c \
+ use_count.c \
+ utils.c \
+ write_queue.c \
+ probes.d \
+ $(NULL)
+
+if HAVE_SSSE3
+libosmocore_la_SOURCES += conv_acc_sse.c
+if HAVE_SSE4_1
+conv_acc_sse.lo : AM_CFLAGS += -mssse3 -msse4.1
+else
+conv_acc_sse.lo : AM_CFLAGS += -mssse3
+endif
+
+if HAVE_AVX2
+libosmocore_la_SOURCES += conv_acc_sse_avx.c
+if HAVE_SSE4_1
+conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2 -msse4.1
+else
+conv_acc_sse_avx.lo : AM_CFLAGS += -mssse3 -mavx2
+endif
+endif
+endif
+
+if HAVE_NEON
+libosmocore_la_SOURCES += conv_acc_neon.c
+# conv_acc_neon.lo : AM_CFLAGS += -mfpu=neon no, could as well be vfp with neon
+endif
+
+BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c
+
+EXTRA_DIST = \
+ conv_acc_sse_impl.h \
+ conv_acc_neon_impl.h \
+ crcXXgen.c.tpl \
+ osmo_io_internal.h \
+ stat_item_internal.h \
+ libosmocore.map \
+ $(NULL)
+
+EXTRA_libosmocore_la_DEPENDENCIES = libosmocore.map
+
+libosmocore_la_LDFLAGS = \
+ $(LTLDFLAGS_OSMOCORE) \
+ -version-info \
+ $(LIBVERSION) \
+ -no-undefined
+
+if ENABLE_PLUGIN
+libosmocore_la_SOURCES += plugin.c
+libosmocore_la_LIBADD += $(LIBRARY_DLOPEN)
+endif
+
+if ENABLE_MSGFILE
+libosmocore_la_SOURCES += msgfile.c
+endif
+
+if ENABLE_SERIAL
+libosmocore_la_SOURCES += serial.c
+endif
+
+if ENABLE_SYSTEMD_LOGGING
+libosmocore_la_SOURCES += logging_systemd.c
+libosmocore_la_LIBADD += $(SYSTEMD_LIBS)
+endif
+
+if ENABLE_LIBMNL
+libosmocore_la_SOURCES += mnl.c
+libosmocore_la_LIBADD += $(LIBMNL_LIBS)
+endif
+
+if ENABLE_SYSTEMTAP
+probes.h: probes.d
+ $(DTRACE) -C -h -s $< -o $@
+
+probes.lo: probes.d
+ $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -C -G -s $< -o $@
+
+BUILT_SOURCES += probes.h probes.lo
+libosmocore_la_LIBADD += probes.lo
+endif
+
+if ENABLE_URING
+libosmocore_la_SOURCES += osmo_io_uring.c
+endif
+
+crc%gen.c: crcXXgen.c.tpl
+ $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@
diff --git a/src/application.c b/src/core/application.c
index 7fd62808..f7e5816f 100644
--- a/src/application.c
+++ b/src/core/application.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \mainpage libosmocore Documentation
diff --git a/src/backtrace.c b/src/core/backtrace.c
index a18bde02..60bd2381 100644
--- a/src/backtrace.c
+++ b/src/core/backtrace.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/src/core/base64.c b/src/core/base64.c
new file mode 100644
index 00000000..0c161ceb
--- /dev/null
+++ b/src/core/base64.c
@@ -0,0 +1,195 @@
+/*
+ * RFC 1521 base64 encoding/decoding
+ *
+ * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
+ *
+ * This file is part of mbed TLS (https://tls.mbed.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <osmocom/core/base64.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+
+static const unsigned char base64_enc_map[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '+', '/'
+};
+
+static const unsigned char base64_dec_map[128] = {
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 62, 127, 127, 127, 63, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 127, 127,
+ 127, 64, 127, 127, 127, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 127, 127, 127, 127, 127, 127, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 127, 127, 127, 127, 127
+};
+
+/*
+ * Encode a buffer into base64 format
+ */
+int osmo_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
+ const unsigned char *src, size_t slen)
+{
+ size_t i, n;
+ int C1, C2, C3;
+ unsigned char *p;
+
+ if (slen == 0) {
+ *olen = 0;
+ return 0;
+ }
+
+ n = (slen << 3) / 6;
+
+ switch ((slen << 3) - (n * 6)) {
+ case 2:
+ n += 3;
+ break;
+ case 4:
+ n += 2;
+ break;
+ default:
+ break;
+ }
+
+ if (dlen < n + 1) {
+ *olen = n + 1;
+ return -ENOBUFS;
+ }
+
+ n = (slen / 3) * 3;
+
+ for (i = 0, p = dst; i < n; i += 3) {
+ C1 = *src++;
+ C2 = *src++;
+ C3 = *src++;
+
+ *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+ *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
+ *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
+ *p++ = base64_enc_map[C3 & 0x3F];
+ }
+
+ if (i < slen) {
+ C1 = *src++;
+ C2 = ((i + 1) < slen) ? *src++ : 0;
+
+ *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
+ *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
+
+ if ((i + 1) < slen)
+ *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
+ else
+ *p++ = '=';
+
+ *p++ = '=';
+ }
+
+ *olen = p - dst;
+ *p = 0;
+
+ return 0;
+}
+
+/*
+ * Decode a base64-formatted buffer
+ */
+int osmo_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
+ const unsigned char *src, size_t slen)
+{
+ size_t i, n;
+ uint32_t j, x;
+ unsigned char *p;
+
+ /* First pass: check for validity and get output length */
+ for (i = n = j = 0; i < slen; i++) {
+ /* Skip spaces before checking for EOL */
+ x = 0;
+ while (i < slen && src[i] == ' ') {
+ ++i;
+ ++x;
+ }
+
+ /* Spaces at end of buffer are OK */
+ if (i == slen)
+ break;
+
+ if ((slen - i) >= 2 && src[i] == '\r' && src[i + 1] == '\n')
+ continue;
+
+ if (src[i] == '\n')
+ continue;
+
+ /* Space inside a line is an error */
+ if (x != 0)
+ return -EINVAL;
+
+ if (src[i] == '=' && ++j > 2)
+ return -EINVAL;
+
+ if (src[i] > 127 || base64_dec_map[src[i]] == 127)
+ return -EINVAL;
+
+ if (base64_dec_map[src[i]] < 64 && j != 0)
+ return -EINVAL;
+
+ n++;
+ }
+
+ if (n == 0)
+ return 0;
+
+ n = ((n * 6) + 7) >> 3;
+ n -= j;
+
+ if (dst == NULL || dlen < n) {
+ *olen = n;
+ return -ENOBUFS;
+ }
+
+ for (j = 3, n = x = 0, p = dst; i > 0; i--, src++) {
+ if (*src == '\r' || *src == '\n' || *src == ' ')
+ continue;
+
+ j -= (base64_dec_map[*src] == 64);
+ x = (x << 6) | (base64_dec_map[*src] & 0x3F);
+
+ if (++n == 4) {
+ n = 0;
+ if (j > 0)
+ *p++ = (unsigned char)(x >> 16);
+ if (j > 1)
+ *p++ = (unsigned char)(x >> 8);
+ if (j > 2)
+ *p++ = (unsigned char)(x);
+ }
+ }
+
+ *olen = p - dst;
+
+ return 0;
+}
diff --git a/src/bitcomp.c b/src/core/bitcomp.c
index 6f2fb62b..5fb2cba2 100644
--- a/src/bitcomp.c
+++ b/src/core/bitcomp.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \defgroup bitcomp Bit compression
diff --git a/src/bits.c b/src/core/bits.c
index 8837c1fb..3da7d9b9 100644
--- a/src/bits.c
+++ b/src/core/bits.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -185,14 +181,14 @@ int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits)
* \param[in] in input buffer of unpacked bits
* \param[in] in_ofs offset into input buffer
* \param[in] num_bits number of bits
- * \param[in] lsb_mode Encode bits in LSB orde instead of MSB
+ * \param[in] lsb_mode Encode bits in LSB order instead of MSB
* \returns length in bytes (max written offset of output buffer + 1)
*/
int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
const ubit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode)
{
- int i, op, bn;
+ unsigned int i, op, bn;
for (i=0; i<num_bits; i++) {
op = out_ofs + i;
bn = lsb_mode ? (op&7) : (7-(op&7));
@@ -210,14 +206,14 @@ int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
* \param[in] in input buffer of packed bits
* \param[in] in_ofs offset into input buffer
* \param[in] num_bits number of bits
- * \param[in] lsb_mode Encode bits in LSB orde instead of MSB
+ * \param[in] lsb_mode Encode bits in LSB order instead of MSB
* \returns length in bytes (max written offset of output buffer + 1)
*/
int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
const pbit_t *in, unsigned int in_ofs,
unsigned int num_bits, int lsb_mode)
{
- int i, ip, bn;
+ unsigned int i, ip, bn;
for (i=0; i<num_bits; i++) {
ip = in_ofs + i;
bn = lsb_mode ? (ip&7) : (7-(ip&7));
@@ -226,6 +222,35 @@ int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
return out_ofs + num_bits;
}
+/* look-up table for bit-reversal within a byte. Generated using:
+ int i,k;
+ for (i = 0 ; i < 256 ; i++) {
+ uint8_t sample = 0 ;
+ for (k = 0; k<8; k++) {
+ if ( i & 1 << k ) sample |= 0x80 >> k;
+ }
+ flip_table[i] = sample;
+ }
+ */
+static const uint8_t flip_table[256] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+};
+
/*! generalized bit reversal function
* \param[in] x the 32bit value to be reversed
* \param[in] k the type of reversal requested
@@ -265,16 +290,10 @@ uint32_t osmo_revbytebits_32(uint32_t x)
/*! reverse the bit order in a byte
* \param[in] x 8bit input value
* \returns 8bit value where bits order has been reversed
- *
- * See Chapter 7 "Hackers Delight"
*/
uint32_t osmo_revbytebits_8(uint8_t x)
{
- x = (x & 0x55) << 1 | (x & 0xAA) >> 1;
- x = (x & 0x33) << 2 | (x & 0xCC) >> 2;
- x = (x & 0x0F) << 4 | (x & 0xF0) >> 4;
-
- return x;
+ return flip_table[x];
}
/*! reverse bit-order of each byte in a buffer
@@ -285,27 +304,10 @@ uint32_t osmo_revbytebits_8(uint8_t x)
*/
void osmo_revbytebits_buf(uint8_t *buf, int len)
{
- unsigned int i;
- unsigned int unaligned_cnt;
- int len_remain = len;
-
- unaligned_cnt = ((unsigned long)buf & 3);
- for (i = 0; i < unaligned_cnt; i++) {
- buf[i] = osmo_revbytebits_8(buf[i]);
- len_remain--;
- if (len_remain <= 0)
- return;
- }
+ int i;
- for (i = unaligned_cnt; i + 3 < len; i += 4) {
- osmo_store32be(osmo_revbytebits_32(osmo_load32be(buf + i)), buf + i);
- len_remain -= 4;
- }
-
- for (i = len - len_remain; i < len; i++) {
- buf[i] = osmo_revbytebits_8(buf[i]);
- len_remain--;
- }
+ for (i = 0; i < len; i++)
+ buf[i] = flip_table[buf[i]];
}
/*! @} */
diff --git a/src/bitvec.c b/src/core/bitvec.c
index 0c263ad6..ac702b94 100644
--- a/src/bitvec.c
+++ b/src/core/bitvec.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup bitvec
@@ -45,6 +41,7 @@
#include <osmocom/core/bits.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/core/panic.h>
+#include <osmocom/core/utils.h>
#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit)
@@ -200,7 +197,8 @@ int bitvec_get_bit_high(struct bitvec *bv)
* \return 0 on success; negative in case of error */
int bitvec_set_bits(struct bitvec *bv, const enum bit_value *bits, unsigned int count)
{
- int i, rc;
+ unsigned int i;
+ int rc;
for (i = 0; i < count; i++) {
rc = bitvec_set_bit(bv, bits[i]);
@@ -263,7 +261,7 @@ int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits)
* \return integer value retrieved from bit vector */
int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits)
{
- int i;
+ unsigned int i;
unsigned int ui = 0;
for (i = 0; i < num_bits; i++) {
@@ -271,7 +269,7 @@ int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits)
if (bit < 0)
return bit;
if (bit)
- ui |= (1 << (num_bits - i - 1));
+ ui |= ((unsigned)1 << (num_bits - i - 1));
bv->cur_bit++;
}
@@ -291,7 +289,7 @@ int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill)
return 0;
}
-/*! pad all remaining bits up to num_bits
+/*! pad all remaining bits up to a given bit number
* \return 0 on success; negative otherwise */
int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit)
{
@@ -397,9 +395,9 @@ int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count
* \param[in] size Number of bytes in the vector
* \param[in] ctx Context from which to allocate
* \return pointer to allocated vector; NULL in case of error */
-struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *ctx)
+struct bitvec *bitvec_alloc(unsigned int size, void *ctx)
{
- struct bitvec *bv = talloc_zero(ctx, struct bitvec);
+ struct bitvec *bv = talloc(ctx, struct bitvec);
if (!bv)
return NULL;
@@ -418,6 +416,8 @@ struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *ctx)
* \param[in] bit vector to free */
void bitvec_free(struct bitvec *bv)
{
+ if (bv == NULL)
+ return;
talloc_free(bv->data);
talloc_free(bv);
}
@@ -428,7 +428,7 @@ void bitvec_free(struct bitvec *bv)
* \return number of bytes (= bits) copied */
unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer)
{
- unsigned int i = 0;
+ unsigned int i;
for (i = 0; i < bv->data_len; i++)
buffer[i] = bv->data[i];
@@ -441,7 +441,7 @@ unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer)
* \return number of bytes (= bits) copied */
unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer)
{
- unsigned int i = 0;
+ unsigned int i;
for (i = 0; i < bv->data_len; i++)
bv->data[i] = buffer[i];
@@ -455,17 +455,13 @@ unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer)
*/
int bitvec_unhex(struct bitvec *bv, const char *src)
{
- unsigned i;
- unsigned val;
- unsigned write_index = 0;
- unsigned digits = bv->data_len * 2;
+ int rc;
- for (i = 0; i < digits; i++) {
- if (sscanf(src + i, "%1x", &val) < 1) {
- return 1;
- }
- bitvec_write_field(bv, &write_index, val, 4);
- }
+ rc = osmo_hexparse(src, bv->data, bv->data_len);
+ if (rc < 0) /* turn -1 into 1 in case of error */
+ return 1;
+
+ bv->cur_bit = rc * 8;
return 0;
}
@@ -473,19 +469,29 @@ int bitvec_unhex(struct bitvec *bv, const char *src)
* \param[in] bv The boolean vector to work on
* \param[in,out] read_index Where reading supposed to start in the vector
* \param[in] len How many bits to read from vector
- * \returns read bits or negative value on error
+ * \returns An integer made up of the bits read.
+ *
+ * In case of an error, errno is set to a non-zero value. Otherwise it holds 0.
*/
uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned int len)
{
unsigned int i;
uint64_t ui = 0;
+
+ /* Prevent bitvec overrun due to incorrect index and/or length */
+ if (len && bytenum_from_bitnum(*read_index + len - 1) >= bv->data_len) {
+ errno = EOVERFLOW;
+ return 0;
+ }
+
bv->cur_bit = *read_index;
+ errno = 0;
for (i = 0; i < len; i++) {
- int bit = bitvec_get_bit_pos((const struct bitvec *)bv, bv->cur_bit);
- if (bit < 0)
- return bit;
- if (bit)
+ unsigned int bytenum = bytenum_from_bitnum(bv->cur_bit);
+ unsigned int bitnum = 7 - (bv->cur_bit % 8);
+
+ if (bv->data[bytenum] & (1 << bitnum))
ui |= ((uint64_t)1 << (len - i - 1));
bv->cur_bit++;
}
@@ -497,7 +503,7 @@ uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned
* \param[in] bv The boolean vector to work on
* \param[in,out] write_index Where writing supposed to start in the vector
* \param[in] len How many bits to write
- * \returns next write index or negative value on error
+ * \returns 0 on success, negative value on error
*/
int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t val, unsigned int len)
{
@@ -534,13 +540,11 @@ char bit_value_to_char(enum bit_value v)
*/
void bitvec_to_string_r(const struct bitvec *bv, char *str)
{
- unsigned i, pos = 0;
char *cur = str;
- for (i = 0; i < bv->cur_bit; i++) {
+ for (unsigned int i = 0; i < bv->cur_bit; i++) {
if (0 == i % 8)
*cur++ = ' ';
*cur++ = bit_value_to_char(bitvec_get_bit_pos(bv, i));
- pos++;
}
*cur = 0;
}
@@ -598,7 +602,7 @@ unsigned bitvec_rl(const struct bitvec *bv, bool b)
* \returns Number of consecutive bits of \p b in \p bv and cur_bit will
* \go to cur_bit + number of consecutive bit
*/
-unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits)
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, unsigned int max_bits)
{
unsigned i = 0;
unsigned j = 8;
diff --git a/src/context.c b/src/core/context.c
index bad012bd..a0b3a555 100644
--- a/src/context.c
+++ b/src/core/context.c
@@ -2,7 +2,7 @@
* talloc context handling.
*
* (C) 2019 by Harald Welte <laforge@gnumonks.org>
- * All Rights Reserverd.
+ * All Rights Reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*
@@ -15,11 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
#include <string.h>
#include <errno.h>
@@ -44,7 +39,7 @@ int osmo_ctx_init(const char *id)
}
/* initialize osmo_ctx on main tread */
-static __attribute__((constructor)) void on_dso_load_ctx(void)
+static __attribute__((constructor(101))) void on_dso_load_ctx(void)
{
OSMO_ASSERT(osmo_ctx_init("main") == 0);
}
diff --git a/src/conv.c b/src/core/conv.c
index a2c13def..8963018b 100644
--- a/src/conv.c
+++ b/src/core/conv.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \addtogroup conv
@@ -36,6 +32,7 @@
#include <stdlib.h>
#include <string.h>
+#include <osmocom/core/utils.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/conv.h>
@@ -66,7 +63,7 @@ osmo_conv_get_output_length(const struct osmo_conv_code *code, int len)
/* Count punctured bits */
if (code->puncture) {
- for (pbits=0; code->puncture[pbits] >= 0; pbits++);
+ for (pbits = 0; code->puncture[pbits] >= 0; pbits++) {}
out_len -= pbits;
}
@@ -84,20 +81,21 @@ osmo_conv_get_output_length(const struct osmo_conv_code *code, int len)
*/
void
osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
- const struct osmo_conv_code *code)
+ const struct osmo_conv_code *code)
{
memset(encoder, 0x00, sizeof(struct osmo_conv_encoder));
+ OSMO_ASSERT(code != NULL);
encoder->code = code;
}
void
osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder,
- const ubit_t *input)
+ const ubit_t *input)
{
int i;
uint8_t state = 0;
- for (i=0; i<(encoder->code->K-1); i++)
+ for (i = 0; i < (encoder->code->K - 1); i++)
state = (state << 1) | input[i];
encoder->state = state;
@@ -105,15 +103,14 @@ osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder,
static inline int
_conv_encode_do_output(struct osmo_conv_encoder *encoder,
- uint8_t out, ubit_t *output)
+ uint8_t out, ubit_t *output)
{
const struct osmo_conv_code *code = encoder->code;
int o_idx = 0;
int j;
if (code->puncture) {
- for (j=0; j<code->N; j++)
- {
+ for (j = 0; j < code->N; j++) {
int bit_no = code->N - j - 1;
int r_idx = encoder->i_idx * code->N + j;
@@ -123,8 +120,7 @@ _conv_encode_do_output(struct osmo_conv_encoder *encoder,
output[o_idx++] = (out >> bit_no) & 1;
}
} else {
- for (j=0; j<code->N; j++)
- {
+ for (j = 0; j < code->N; j++) {
int bit_no = code->N - j - 1;
output[o_idx++] = (out >> bit_no) & 1;
}
@@ -135,7 +131,7 @@ _conv_encode_do_output(struct osmo_conv_encoder *encoder,
int
osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
- const ubit_t *input, ubit_t *output, int n)
+ const ubit_t *input, ubit_t *output, int n)
{
const struct osmo_conv_code *code = encoder->code;
uint8_t state;
@@ -145,11 +141,11 @@ osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
o_idx = 0;
state = encoder->state;
- for (i=0; i<n; i++) {
+ for (i = 0; i < n; i++) {
int bit = input[i];
uint8_t out;
- out = code->next_output[state][bit];
+ out = code->next_output[state][bit];
state = code->next_state[state][bit];
o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
@@ -163,8 +159,7 @@ osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
}
int
-osmo_conv_encode_flush(struct osmo_conv_encoder *encoder,
- ubit_t *output)
+osmo_conv_encode_flush(struct osmo_conv_encoder *encoder, ubit_t *output)
{
const struct osmo_conv_code *code = encoder->code;
uint8_t state;
@@ -177,14 +172,14 @@ osmo_conv_encode_flush(struct osmo_conv_encoder *encoder,
o_idx = 0;
state = encoder->state;
- for (i=0; i<n; i++) {
+ for (i = 0; i < n; i++) {
uint8_t out;
if (code->next_term_output) {
- out = code->next_term_output[state];
+ out = code->next_term_output[state];
state = code->next_term_state[state];
} else {
- out = code->next_output[state][0];
+ out = code->next_output[state][0];
state = code->next_state[state][0];
}
@@ -210,7 +205,7 @@ osmo_conv_encode_flush(struct osmo_conv_encoder *encoder,
*/
int
osmo_conv_encode(const struct osmo_conv_code *code,
- const ubit_t *input, ubit_t *output)
+ const ubit_t *input, ubit_t *output)
{
struct osmo_conv_encoder encoder;
int l;
@@ -240,17 +235,18 @@ osmo_conv_encode(const struct osmo_conv_code *code,
/* Forward declaration for accerlated decoding with certain codes */
int
osmo_conv_decode_acc(const struct osmo_conv_code *code,
- const sbit_t *input, ubit_t *output);
+ const sbit_t *input, ubit_t *output);
void
osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
- const struct osmo_conv_code *code, int len, int start_state)
+ const struct osmo_conv_code *code, int len,
+ int start_state)
{
int n_states;
/* Init */
if (len <= 0)
- len = code->len;
+ len = code->len;
n_states = 1 << (code->K - 1);
@@ -261,7 +257,7 @@ osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
decoder->len = len;
/* Allocate arrays */
- decoder->ae = malloc(sizeof(unsigned int) * n_states);
+ decoder->ae = malloc(sizeof(unsigned int) * n_states);
decoder->ae_next = malloc(sizeof(unsigned int) * n_states);
decoder->state_history = malloc(sizeof(uint8_t) * n_states * (len + decoder->code->K - 1));
@@ -285,7 +281,7 @@ osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state)
memset(decoder->ae, 0x00, sizeof(unsigned int) * decoder->n_states);
} else {
/* Fixed start state */
- for (i=0; i<decoder->n_states; i++) {
+ for (i = 0; i < decoder->n_states; i++) {
decoder->ae[i] = (i == start_state) ? 0 : MAX_AE;
}
}
@@ -302,12 +298,12 @@ osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder)
decoder->p_idx = 0;
/* Initial error normalize (remove constant) */
- for (i=0; i<decoder->n_states; i++) {
+ for (i = 0; i < decoder->n_states; i++) {
if (decoder->ae[i] < min_ae)
min_ae = decoder->ae[i];
}
- for (i=0; i<decoder->n_states; i++)
+ for (i = 0; i < decoder->n_states; i++)
decoder->ae[i] -= min_ae;
}
@@ -323,7 +319,7 @@ osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder)
int
osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
- const sbit_t *input, int n)
+ const sbit_t *input, int n)
{
const struct osmo_conv_code *code = decoder->code;
@@ -340,27 +336,25 @@ osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
/* Prepare */
n_states = decoder->n_states;
- ae = decoder->ae;
+ ae = decoder->ae;
ae_next = decoder->ae_next;
state_history = &decoder->state_history[n_states * decoder->o_idx];
- in_sym = alloca(sizeof(sbit_t) * code->N);
+ in_sym = alloca(sizeof(sbit_t) * code->N);
i_idx = 0;
p_idx = decoder->p_idx;
/* Scan the treillis */
- for (i=0; i<n; i++)
- {
+ for (i = 0; i < n; i++) {
/* Reset next accumulated error */
- for (s=0; s<n_states; s++) {
+ for (s = 0; s < n_states; s++)
ae_next[s] = MAX_AE;
- }
/* Get input */
if (code->puncture) {
/* Hard way ... */
- for (j=0; j<code->N; j++) {
+ for (j = 0; j < code->N; j++) {
int idx = ((decoder->o_idx + i) * code->N) + j;
if (idx == code->puncture[p_idx]) {
in_sym[j] = 0; /* Undefined */
@@ -377,23 +371,21 @@ osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
}
/* Scan all state */
- for (s=0; s<n_states; s++)
- {
+ for (s = 0; s < n_states; s++) {
/* Scan possible input bits */
- for (b=0; b<2; b++)
- {
+ for (b = 0; b < 2; b++) {
int nae, ov, e;
uint8_t m;
/* Next output and state */
- uint8_t out = code->next_output[s][b];
+ uint8_t out = code->next_output[s][b];
uint8_t state = code->next_state[s][b];
/* New error for this path */
nae = ae[s]; /* start from last error */
m = 1 << (code->N - 1); /* mask for 'out' bit selection */
- for (j=0; j<code->N; j++) {
+ for (j = 0; j < code->N; j++) {
int is = (int)in_sym[j];
if (is) {
ov = (out & m) ? -127 : 127; /* sbit_t value for it */
@@ -423,8 +415,7 @@ osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
}
int
-osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
- const sbit_t *input)
+osmo_conv_decode_flush(struct osmo_conv_decoder *decoder, const sbit_t *input)
{
const struct osmo_conv_code *code = decoder->code;
@@ -441,27 +432,25 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
/* Prepare */
n_states = decoder->n_states;
- ae = decoder->ae;
+ ae = decoder->ae;
ae_next = decoder->ae_next;
state_history = &decoder->state_history[n_states * decoder->o_idx];
- in_sym = alloca(sizeof(sbit_t) * code->N);
+ in_sym = alloca(sizeof(sbit_t) * code->N);
i_idx = 0;
p_idx = decoder->p_idx;
/* Scan the treillis */
- for (i=0; i<code->K-1; i++)
- {
+ for (i = 0; i < code->K - 1; i++) {
/* Reset next accumulated error */
- for (s=0; s<n_states; s++) {
+ for (s = 0; s < n_states; s++)
ae_next[s] = MAX_AE;
- }
/* Get input */
if (code->puncture) {
/* Hard way ... */
- for (j=0; j<code->N; j++) {
+ for (j = 0; j < code->N; j++) {
int idx = ((decoder->o_idx + i) * code->N) + j;
if (idx == code->puncture[p_idx]) {
in_sym[j] = 0; /* Undefined */
@@ -478,8 +467,7 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
}
/* Scan all state */
- for (s=0; s<n_states; s++)
- {
+ for (s = 0; s < n_states; s++) {
int nae, ov, e;
uint8_t m;
@@ -488,10 +476,10 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
uint8_t state;
if (code->next_term_output) {
- out = code->next_term_output[s];
+ out = code->next_term_output[s];
state = code->next_term_state[s];
} else {
- out = code->next_output[s][0];
+ out = code->next_output[s][0];
state = code->next_state[s][0];
}
@@ -499,7 +487,7 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
nae = ae[s]; /* start from last error */
m = 1 << (code->N - 1); /* mask for 'out' bit selection */
- for (j=0; j<code->N; j++) {
+ for (j = 0; j < code->N; j++) {
int is = (int)in_sym[j];
if (is) {
ov = (out & m) ? -127 : 127; /* sbit_t value for it */
@@ -528,38 +516,86 @@ osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
}
int
-osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
- ubit_t *output, int has_flush, int end_state)
+osmo_conv_decode_get_best_end_state(struct osmo_conv_decoder *decoder)
{
const struct osmo_conv_code *code = decoder->code;
- int min_ae;
- uint8_t min_state, cur_state;
- int i, s, n;
+ int min_ae, min_state;
+ int s;
- uint8_t *sh_ptr;
+ /* If flushed, we _know_ the end state */
+ if (code->term == CONV_TERM_FLUSH)
+ return 0;
- /* End state ? */
- if (end_state < 0) {
- /* Find state with least error */
- min_ae = MAX_AE;
- min_state = 0xff;
+ /* Search init */
+ min_state = -1;
+ min_ae = MAX_AE;
- for (s=0; s<decoder->n_states; s++)
- {
+ /* If tail biting, we search for the minimum path metric that
+ * gives a circular traceback (i.e. start_state == end_state */
+ if (code->term == CONV_TERM_TAIL_BITING) {
+ int t, n, i;
+ uint8_t *sh_ptr;
+
+ for (s = 0; s < decoder->n_states; s++) {
+ /* Check if that state traces back to itself */
+ n = decoder->o_idx;
+ sh_ptr = &decoder->state_history[decoder->n_states * (n-1)];
+ t = s;
+
+ for (i = n - 1; i >= 0; i--) {
+ t = sh_ptr[t];
+ sh_ptr -= decoder->n_states;
+ }
+
+ if (s != t)
+ continue;
+
+ /* If it does, consider it */
if (decoder->ae[s] < min_ae) {
min_ae = decoder->ae[s];
min_state = s;
}
}
- if (min_state == 0xff)
- return -1;
- } else {
- min_state = (uint8_t) end_state;
- min_ae = decoder->ae[end_state];
+ if (min_ae < MAX_AE)
+ return min_state;
+ }
+
+ /* Finally, just the lowest path metric */
+ for (s = 0; s < decoder->n_states; s++) {
+ /* Is it smaller ? */
+ if (decoder->ae[s] < min_ae) {
+ min_ae = decoder->ae[s];
+ min_state = s;
+ }
}
+ return min_state;
+}
+
+int
+osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
+ ubit_t *output, int has_flush, int end_state)
+{
+ const struct osmo_conv_code *code = decoder->code;
+
+ int min_ae;
+ uint8_t min_state, cur_state;
+ int i, n;
+
+ uint8_t *sh_ptr;
+
+ /* End state ? */
+ if (end_state < 0)
+ end_state = osmo_conv_decode_get_best_end_state(decoder);
+
+ if (end_state < 0)
+ return -1;
+
+ min_state = (uint8_t) end_state;
+ min_ae = decoder->ae[end_state];
+
/* Traceback */
cur_state = min_state;
@@ -569,16 +605,15 @@ osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
/* No output for the K-1 termination input bits */
if (has_flush) {
- for (i=0; i<code->K-1; i++) {
+ for (i = 0; i < code->K - 1; i++) {
cur_state = sh_ptr[cur_state];
sh_ptr -= decoder->n_states;
}
n -= code->K - 1;
}
- /* Generate output backward */
- for (i=n-1; i>=0; i--)
- {
+ /* Generate output backward */
+ for (i = n - 1; i >= 0; i--) {
min_state = cur_state;
cur_state = sh_ptr[cur_state];
@@ -600,12 +635,12 @@ osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
*
* This is an all-in-one function, taking care of
* \ref osmo_conv_decode_init, \ref osmo_conv_decode_scan,
- * \ref osmo_conv_decode_flush, \ref osmo_conv_decode_get_output and
- * \ref osmo_conv_decode_deinit.
+ * \ref osmo_conv_decode_flush, \ref osmo_conv_decode_get_best_end_state,
+ * \ref osmo_conv_decode_get_output and \ref osmo_conv_decode_deinit.
*/
int
osmo_conv_decode(const struct osmo_conv_code *code,
- const sbit_t *input, ubit_t *output)
+ const sbit_t *input, ubit_t *output)
{
struct osmo_conv_decoder decoder;
int rv, l;
@@ -628,7 +663,7 @@ osmo_conv_decode(const struct osmo_conv_code *code,
rv = osmo_conv_decode_get_output(&decoder, output,
code->term == CONV_TERM_FLUSH, /* has_flush */
- code->term == CONV_TERM_FLUSH ? 0 : -1 /* end_state */
+ -1 /* end_state */
);
osmo_conv_decode_deinit(&decoder);
diff --git a/src/conv_acc.c b/src/core/conv_acc.c
index c16e4364..4bd3b076 100644
--- a/src/conv_acc.c
+++ b/src/core/conv_acc.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
@@ -85,6 +81,11 @@ int16_t *osmo_conv_sse_avx_vdec_malloc(size_t n);
void osmo_conv_sse_avx_vdec_free(int16_t *ptr);
#endif
+#ifdef HAVE_NEON
+int16_t *osmo_conv_neon_vdec_malloc(size_t n);
+void osmo_conv_neon_vdec_free(int16_t *ptr);
+#endif
+
/* Forward Metric Units */
void osmo_conv_gen_metrics_k5_n2(const int8_t *seq, const int16_t *out,
int16_t *sums, int16_t *paths, int norm);
@@ -129,6 +130,21 @@ void osmo_conv_sse_avx_metrics_k7_n4(const int8_t *seq, const int16_t *out,
int16_t *sums, int16_t *paths, int norm);
#endif
+#if defined(HAVE_NEON)
+void osmo_conv_neon_metrics_k5_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_neon_metrics_k5_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_neon_metrics_k5_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_neon_metrics_k7_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_neon_metrics_k7_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_neon_metrics_k7_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+#endif
+
/* Trellis State
* state - Internal lshift register value
* prev - Register values of previous 0 and 1 states
@@ -467,10 +483,27 @@ static void _traceback_rec(struct vdecoder *dec,
*/
static int traceback(struct vdecoder *dec, uint8_t *out, int term, int len)
{
- int i, sum, max = -1;
- unsigned path, state = 0;
+ int i, j, sum, max = -1;
+ unsigned path, state = 0, state_scan;
- if (term != CONV_TERM_FLUSH) {
+ if (term == CONV_TERM_TAIL_BITING) {
+ for (i = 0; i < dec->trellis.num_states; i++) {
+ state_scan = i;
+ for (j = len - 1; j >= 0; j--) {
+ path = dec->paths[j][state_scan] + 1;
+ state_scan = vstate_lshift(state_scan, dec->k, path);
+ }
+ if (state_scan != i)
+ continue;
+ sum = dec->trellis.sums[i];
+ if (sum > max) {
+ max = sum;
+ state = i;
+ }
+ }
+ }
+
+ if ((max < 0) && (term != CONV_TERM_FLUSH)) {
for (i = 0; i < dec->trellis.num_states; i++) {
sum = dec->trellis.sums[i];
if (sum > max) {
@@ -528,6 +561,12 @@ static int vdec_init(struct vdecoder *dec, const struct osmo_conv_code *code)
if (dec->k == 5) {
switch (dec->n) {
case 2:
+/* rach len 14 is too short for neon */
+#ifdef HAVE_NEON
+ if (code->len < 100)
+ dec->metric_func = osmo_conv_gen_metrics_k5_n2;
+ else
+#endif
dec->metric_func = osmo_conv_metrics_k5_n2;
break;
case 3:
@@ -681,6 +720,8 @@ static void osmo_conv_init(void)
} else {
INIT_POINTERS(gen);
}
+#elif defined(HAVE_NEON)
+ INIT_POINTERS(neon);
#else
INIT_POINTERS(gen);
#endif
diff --git a/src/conv_acc_generic.c b/src/core/conv_acc_generic.c
index 28876738..2257e6a9 100644
--- a/src/conv_acc_generic.c
+++ b/src/core/conv_acc_generic.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
diff --git a/src/core/conv_acc_neon.c b/src/core/conv_acc_neon.c
new file mode 100644
index 00000000..fb180e3d
--- /dev/null
+++ b/src/core/conv_acc_neon.c
@@ -0,0 +1,106 @@
+/*! \file conv_acc_neon.c
+ * Accelerated Viterbi decoder implementation
+ * for architectures with only NEON available. */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH
+ * Author: Eric Wild
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <malloc.h>
+#include "config.h"
+
+#if defined(HAVE_NEON)
+#include <arm_neon.h>
+#endif
+
+/* align req is 16 on android because google was confused, 8 on sane platforms */
+#define NEON_ALIGN 8
+
+#include <conv_acc_neon_impl.h>
+
+/* Aligned Memory Allocator
+ * NEON requires 8-byte memory alignment. We store relevant trellis values
+ * (accumulated sums, outputs, and path decisions) as 16 bit signed integers
+ * so the allocated memory is casted as such.
+ */
+__attribute__ ((visibility("hidden")))
+int16_t *osmo_conv_neon_vdec_malloc(size_t n)
+{
+ return (int16_t *) memalign(NEON_ALIGN, sizeof(int16_t) * n);
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_neon_vdec_free(int16_t *ptr)
+{
+ free(ptr);
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_neon_metrics_k5_n2(const int8_t *val, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ const int16_t _val[4] = { val[0], val[1], val[0], val[1] };
+
+ _neon_metrics_k5_n2(_val, out, sums, paths, norm);
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_neon_metrics_k5_n3(const int8_t *val, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ const int16_t _val[4] = { val[0], val[1], val[2], 0 };
+
+ _neon_metrics_k5_n4(_val, out, sums, paths, norm);
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_neon_metrics_k5_n4(const int8_t *val, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ const int16_t _val[4] = { val[0], val[1], val[2], val[3] };
+
+ _neon_metrics_k5_n4(_val, out, sums, paths, norm);
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_neon_metrics_k7_n2(const int8_t *val, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ const int16_t _val[4] = { val[0], val[1], val[0], val[1] };
+
+ _neon_metrics_k7_n2(_val, out, sums, paths, norm);
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_neon_metrics_k7_n3(const int8_t *val, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ const int16_t _val[4] = { val[0], val[1], val[2], 0 };
+
+ _neon_metrics_k7_n4(_val, out, sums, paths, norm);
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_neon_metrics_k7_n4(const int8_t *val, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ const int16_t _val[4] = { val[0], val[1], val[2], val[3] };
+
+ _neon_metrics_k7_n4(_val, out, sums, paths, norm);
+}
diff --git a/src/core/conv_acc_neon_impl.h b/src/core/conv_acc_neon_impl.h
new file mode 100644
index 00000000..8a78c75b
--- /dev/null
+++ b/src/core/conv_acc_neon_impl.h
@@ -0,0 +1,350 @@
+/*! \file conv_acc_neon_impl.h
+ * Accelerated Viterbi decoder implementation:
+ * straight port of SSE to NEON based on Tom Tsous work */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH
+ * Author: Eric Wild
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* Some distributions (notably Alpine Linux) for some strange reason
+ * don't have this #define */
+#ifndef __always_inline
+#define __always_inline inline __attribute__((always_inline))
+#endif
+
+#define NEON_BUTTERFLY(M0,M1,M2,M3,M4) \
+{ \
+ M3 = vqaddq_s16(M0, M2); \
+ M4 = vqsubq_s16(M1, M2); \
+ M0 = vqsubq_s16(M0, M2); \
+ M1 = vqaddq_s16(M1, M2); \
+ M2 = vmaxq_s16(M3, M4); \
+ M3 = vreinterpretq_s16_u16(vcgtq_s16(M3, M4)); \
+ M4 = vmaxq_s16(M0, M1); \
+ M1 = vreinterpretq_s16_u16(vcgtq_s16(M0, M1)); \
+}
+
+#define NEON_DEINTERLEAVE_K5(M0,M1,M2,M3) \
+{ \
+ int16x8x2_t tmp; \
+ tmp = vuzpq_s16(M0, M1); \
+ M2 = tmp.val[0]; \
+ M3 = tmp.val[1]; \
+}
+
+#define NEON_DEINTERLEAVE_K7(M0,M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15) \
+{ \
+ int16x8x2_t tmp; \
+ tmp = vuzpq_s16(M0, M1); \
+ M8 = tmp.val[0]; M9 = tmp.val[1]; \
+ tmp = vuzpq_s16(M2, M3); \
+ M10 = tmp.val[0]; M11 = tmp.val[1]; \
+ tmp = vuzpq_s16(M4, M5); \
+ M12 = tmp.val[0]; M13 = tmp.val[1]; \
+ tmp = vuzpq_s16(M6, M7); \
+ M14 = tmp.val[0]; M15 = tmp.val[1]; \
+}
+
+#define NEON_BRANCH_METRIC_N2(M0,M1,M2,M3,M4,M6,M7) \
+{ \
+ M0 = vmulq_s16(M4, M0); \
+ M1 = vmulq_s16(M4, M1); \
+ M2 = vmulq_s16(M4, M2); \
+ M3 = vmulq_s16(M4, M3); \
+ M6 = vcombine_s16(vpadd_s16(vget_low_s16(M0), vget_high_s16(M0)), vpadd_s16(vget_low_s16(M1), vget_high_s16(M1))); \
+ M7 = vcombine_s16(vpadd_s16(vget_low_s16(M2), vget_high_s16(M2)), vpadd_s16(vget_low_s16(M3), vget_high_s16(M3))); \
+}
+
+#define NEON_BRANCH_METRIC_N4(M0,M1,M2,M3,M4,M5) \
+{ \
+ M0 = vmulq_s16(M4, M0); \
+ M1 = vmulq_s16(M4, M1); \
+ M2 = vmulq_s16(M4, M2); \
+ M3 = vmulq_s16(M4, M3); \
+ int16x4_t t1 = vpadd_s16(vpadd_s16(vget_low_s16(M0), vget_high_s16(M0)), vpadd_s16(vget_low_s16(M1), vget_high_s16(M1))); \
+ int16x4_t t2 = vpadd_s16(vpadd_s16(vget_low_s16(M2), vget_high_s16(M2)), vpadd_s16(vget_low_s16(M3), vget_high_s16(M3))); \
+ M5 = vcombine_s16(t1, t2); \
+}
+
+#define NEON_NORMALIZE_K5(M0,M1,M2,M3) \
+{ \
+ M2 = vminq_s16(M0, M1); \
+ int16x4_t t = vpmin_s16(vget_low_s16(M2), vget_high_s16(M2)); \
+ t = vpmin_s16(t, t); \
+ t = vpmin_s16(t, t); \
+ M2 = vdupq_lane_s16(t, 0); \
+ M0 = vqsubq_s16(M0, M2); \
+ M1 = vqsubq_s16(M1, M2); \
+}
+
+#define NEON_NORMALIZE_K7(M0,M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11) \
+{ \
+ M8 = vminq_s16(M0, M1); \
+ M9 = vminq_s16(M2, M3); \
+ M10 = vminq_s16(M4, M5); \
+ M11 = vminq_s16(M6, M7); \
+ M8 = vminq_s16(M8, M9); \
+ M10 = vminq_s16(M10, M11); \
+ M8 = vminq_s16(M8, M10); \
+ int16x4_t t = vpmin_s16(vget_low_s16(M8), vget_high_s16(M8)); \
+ t = vpmin_s16(t, t); \
+ t = vpmin_s16(t, t); \
+ M8 = vdupq_lane_s16(t, 0); \
+ M0 = vqsubq_s16(M0, M8); \
+ M1 = vqsubq_s16(M1, M8); \
+ M2 = vqsubq_s16(M2, M8); \
+ M3 = vqsubq_s16(M3, M8); \
+ M4 = vqsubq_s16(M4, M8); \
+ M5 = vqsubq_s16(M5, M8); \
+ M6 = vqsubq_s16(M6, M8); \
+ M7 = vqsubq_s16(M7, M8); \
+}
+
+__always_inline void _neon_metrics_k5_n2(const int16_t *val, const int16_t *outa, int16_t *sumsa, int16_t *paths,
+ int norm)
+{
+ int16_t *__restrict out = __builtin_assume_aligned(outa, 8);
+ int16_t *__restrict sums = __builtin_assume_aligned(sumsa, 8);
+ int16x8_t m0, m1, m2, m3, m4, m5, m6;
+ int16x4_t input;
+
+ /* (BMU) Load and expand 8-bit input out to 16-bits */
+ input = vld1_s16(val);
+ m2 = vcombine_s16(input, input);
+
+ /* (BMU) Load and compute branch metrics */
+ m0 = vld1q_s16(&out[0]);
+ m1 = vld1q_s16(&out[8]);
+
+ m0 = vmulq_s16(m2, m0);
+ m1 = vmulq_s16(m2, m1);
+ m2 = vcombine_s16(vpadd_s16(vget_low_s16(m0), vget_high_s16(m0)),
+ vpadd_s16(vget_low_s16(m1), vget_high_s16(m1)));
+
+ /* (PMU) Load accumulated path matrics */
+ m0 = vld1q_s16(&sums[0]);
+ m1 = vld1q_s16(&sums[8]);
+
+ NEON_DEINTERLEAVE_K5(m0, m1, m3, m4)
+
+ /* (PMU) Butterflies: 0-7 */
+ NEON_BUTTERFLY(m3, m4, m2, m5, m6)
+
+ if (norm)
+ NEON_NORMALIZE_K5(m2, m6, m0, m1)
+
+ vst1q_s16(&sums[0], m2);
+ vst1q_s16(&sums[8], m6);
+ vst1q_s16(&paths[0], m5);
+ vst1q_s16(&paths[8], m4);
+}
+
+__always_inline void _neon_metrics_k5_n4(const int16_t *val, const int16_t *outa, int16_t *sumsa, int16_t *paths,
+ int norm)
+{
+ int16_t *__restrict out = __builtin_assume_aligned(outa, 8);
+ int16_t *__restrict sums = __builtin_assume_aligned(sumsa, 8);
+ int16x8_t m0, m1, m2, m3, m4, m5, m6;
+ int16x4_t input;
+
+ /* (BMU) Load and expand 8-bit input out to 16-bits */
+ input = vld1_s16(val);
+ m4 = vcombine_s16(input, input);
+
+ /* (BMU) Load and compute branch metrics */
+ m0 = vld1q_s16(&out[0]);
+ m1 = vld1q_s16(&out[8]);
+ m2 = vld1q_s16(&out[16]);
+ m3 = vld1q_s16(&out[24]);
+
+ NEON_BRANCH_METRIC_N4(m0, m1, m2, m3, m4, m2)
+
+ /* (PMU) Load accumulated path matrics */
+ m0 = vld1q_s16(&sums[0]);
+ m1 = vld1q_s16(&sums[8]);
+
+ NEON_DEINTERLEAVE_K5(m0, m1, m3, m4)
+
+ /* (PMU) Butterflies: 0-7 */
+ NEON_BUTTERFLY(m3, m4, m2, m5, m6)
+
+ if (norm)
+ NEON_NORMALIZE_K5(m2, m6, m0, m1)
+
+ vst1q_s16(&sums[0], m2);
+ vst1q_s16(&sums[8], m6);
+ vst1q_s16(&paths[0], m5);
+ vst1q_s16(&paths[8], m4);
+}
+
+__always_inline static void _neon_metrics_k7_n2(const int16_t *val, const int16_t *outa, int16_t *sumsa, int16_t *paths,
+ int norm)
+{
+ int16_t *__restrict out = __builtin_assume_aligned(outa, 8);
+ int16_t *__restrict sums = __builtin_assume_aligned(sumsa, 8);
+ int16x8_t m0, m1, m2, m3, m4, m5, m6, m7;
+ int16x8_t m8, m9, m10, m11, m12, m13, m14, m15;
+ int16x4_t input;
+
+ /* (PMU) Load accumulated path matrics */
+ m0 = vld1q_s16(&sums[0]);
+ m1 = vld1q_s16(&sums[8]);
+ m2 = vld1q_s16(&sums[16]);
+ m3 = vld1q_s16(&sums[24]);
+ m4 = vld1q_s16(&sums[32]);
+ m5 = vld1q_s16(&sums[40]);
+ m6 = vld1q_s16(&sums[48]);
+ m7 = vld1q_s16(&sums[56]);
+
+ /* (PMU) Deinterleave into even and odd packed registers */
+ NEON_DEINTERLEAVE_K7(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15)
+
+ /* (BMU) Load and expand 8-bit input out to 16-bits */
+ input = vld1_s16(val);
+ m7 = vcombine_s16(input, input);
+
+ /* (BMU) Load and compute branch metrics */
+ m0 = vld1q_s16(&out[0]);
+ m1 = vld1q_s16(&out[8]);
+ m2 = vld1q_s16(&out[16]);
+ m3 = vld1q_s16(&out[24]);
+
+ NEON_BRANCH_METRIC_N2(m0, m1, m2, m3, m7, m4, m5)
+
+ m0 = vld1q_s16(&out[32]);
+ m1 = vld1q_s16(&out[40]);
+ m2 = vld1q_s16(&out[48]);
+ m3 = vld1q_s16(&out[56]);
+
+ NEON_BRANCH_METRIC_N2(m0, m1, m2, m3, m7, m6, m7)
+
+ /* (PMU) Butterflies: 0-15 */
+ NEON_BUTTERFLY(m8, m9, m4, m0, m1)
+ NEON_BUTTERFLY(m10, m11, m5, m2, m3)
+
+ vst1q_s16(&paths[0], m0);
+ vst1q_s16(&paths[8], m2);
+ vst1q_s16(&paths[32], m9);
+ vst1q_s16(&paths[40], m11);
+
+ /* (PMU) Butterflies: 17-31 */
+ NEON_BUTTERFLY(m12, m13, m6, m0, m2)
+ NEON_BUTTERFLY(m14, m15, m7, m9, m11)
+
+ vst1q_s16(&paths[16], m0);
+ vst1q_s16(&paths[24], m9);
+ vst1q_s16(&paths[48], m13);
+ vst1q_s16(&paths[56], m15);
+
+ if (norm)
+ NEON_NORMALIZE_K7(m4, m1, m5, m3, m6, m2, m7, m11, m0, m8, m9, m10)
+
+ vst1q_s16(&sums[0], m4);
+ vst1q_s16(&sums[8], m5);
+ vst1q_s16(&sums[16], m6);
+ vst1q_s16(&sums[24], m7);
+ vst1q_s16(&sums[32], m1);
+ vst1q_s16(&sums[40], m3);
+ vst1q_s16(&sums[48], m2);
+ vst1q_s16(&sums[56], m11);
+}
+
+__always_inline static void _neon_metrics_k7_n4(const int16_t *val, const int16_t *outa, int16_t *sumsa, int16_t *paths,
+ int norm)
+{
+ int16_t *__restrict out = __builtin_assume_aligned(outa, 8);
+ int16_t *__restrict sums = __builtin_assume_aligned(sumsa, 8);
+ int16x8_t m0, m1, m2, m3, m4, m5, m6, m7;
+ int16x8_t m8, m9, m10, m11, m12, m13, m14, m15;
+ int16x4_t input;
+
+ /* (PMU) Load accumulated path matrics */
+ m0 = vld1q_s16(&sums[0]);
+ m1 = vld1q_s16(&sums[8]);
+ m2 = vld1q_s16(&sums[16]);
+ m3 = vld1q_s16(&sums[24]);
+ m4 = vld1q_s16(&sums[32]);
+ m5 = vld1q_s16(&sums[40]);
+ m6 = vld1q_s16(&sums[48]);
+ m7 = vld1q_s16(&sums[56]);
+
+ /* (PMU) Deinterleave into even and odd packed registers */
+ NEON_DEINTERLEAVE_K7(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15)
+
+ /* (BMU) Load and expand 8-bit input out to 16-bits */
+ input = vld1_s16(val);
+ m7 = vcombine_s16(input, input);
+
+ /* (BMU) Load and compute branch metrics */
+ m0 = vld1q_s16(&out[0]);
+ m1 = vld1q_s16(&out[8]);
+ m2 = vld1q_s16(&out[16]);
+ m3 = vld1q_s16(&out[24]);
+
+ NEON_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m4)
+
+ m0 = vld1q_s16(&out[32]);
+ m1 = vld1q_s16(&out[40]);
+ m2 = vld1q_s16(&out[48]);
+ m3 = vld1q_s16(&out[56]);
+
+ NEON_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m5)
+
+ m0 = vld1q_s16(&out[64]);
+ m1 = vld1q_s16(&out[72]);
+ m2 = vld1q_s16(&out[80]);
+ m3 = vld1q_s16(&out[88]);
+
+ NEON_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m6)
+
+ m0 = vld1q_s16(&out[96]);
+ m1 = vld1q_s16(&out[104]);
+ m2 = vld1q_s16(&out[112]);
+ m3 = vld1q_s16(&out[120]);
+
+ NEON_BRANCH_METRIC_N4(m0, m1, m2, m3, m7, m7)
+
+ /* (PMU) Butterflies: 0-15 */
+ NEON_BUTTERFLY(m8, m9, m4, m0, m1)
+ NEON_BUTTERFLY(m10, m11, m5, m2, m3)
+
+ vst1q_s16(&paths[0], m0);
+ vst1q_s16(&paths[8], m2);
+ vst1q_s16(&paths[32], m9);
+ vst1q_s16(&paths[40], m11);
+
+ /* (PMU) Butterflies: 17-31 */
+ NEON_BUTTERFLY(m12, m13, m6, m0, m2)
+ NEON_BUTTERFLY(m14, m15, m7, m9, m11)
+
+ vst1q_s16(&paths[16], m0);
+ vst1q_s16(&paths[24], m9);
+ vst1q_s16(&paths[48], m13);
+ vst1q_s16(&paths[56], m15);
+
+ if (norm)
+ NEON_NORMALIZE_K7(m4, m1, m5, m3, m6, m2, m7, m11, m0, m8, m9, m10)
+
+ vst1q_s16(&sums[0], m4);
+ vst1q_s16(&sums[8], m5);
+ vst1q_s16(&sums[16], m6);
+ vst1q_s16(&sums[24], m7);
+ vst1q_s16(&sums[32], m1);
+ vst1q_s16(&sums[40], m3);
+ vst1q_s16(&sums[48], m2);
+ vst1q_s16(&sums[56], m11);
+}
diff --git a/src/conv_acc_sse.c b/src/core/conv_acc_sse.c
index 63d8722a..513ab052 100644
--- a/src/conv_acc_sse.c
+++ b/src/core/conv_acc_sse.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
diff --git a/src/conv_acc_sse_avx.c b/src/core/conv_acc_sse_avx.c
index 5ac3c163..82b4fa62 100644
--- a/src/conv_acc_sse_avx.c
+++ b/src/core/conv_acc_sse_avx.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
diff --git a/src/conv_acc_sse_impl.h b/src/core/conv_acc_sse_impl.h
index 9ebbfe9c..807dbe5e 100644
--- a/src/conv_acc_sse_impl.h
+++ b/src/core/conv_acc_sse_impl.h
@@ -18,10 +18,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Some distributions (notably Alpine Linux) for some strange reason
diff --git a/src/counter.c b/src/core/counter.c
index 0fa31661..dace15f3 100644
--- a/src/counter.c
+++ b/src/core/counter.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
@@ -79,7 +75,7 @@ int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *),
/*! Counts the registered counter
* \returns amount of counters */
-int osmo_counters_count()
+int osmo_counters_count(void)
{
return llist_count(&counters);
}
diff --git a/src/crc16.c b/src/core/crc16.c
index 29dace2e..29dace2e 100644
--- a/src/crc16.c
+++ b/src/core/crc16.c
diff --git a/src/crcXXgen.c.tpl b/src/core/crcXXgen.c.tpl
index 74e6d521..154291cc 100644
--- a/src/crcXXgen.c.tpl
+++ b/src/core/crcXXgen.c.tpl
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \addtogroup crc
diff --git a/src/core/exec.c b/src/core/exec.c
new file mode 100644
index 00000000..2e33788e
--- /dev/null
+++ b/src/core/exec.c
@@ -0,0 +1,301 @@
+/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "config.h"
+#ifndef EMBEDDED
+
+#define _GNU_SOURCE
+#include <unistd.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/exec.h>
+
+/*! suggested list of environment variables to pass (if they exist) to a sub-process/script */
+const char *osmo_environment_whitelist[] = {
+ "USER", "LOGNAME", "HOME",
+ "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME",
+ "PATH",
+ "PWD",
+ "SHELL",
+ "TERM",
+ "TMPDIR",
+ "LD_LIBRARY_PATH",
+ "LD_PRELOAD",
+ "POSIXLY_CORRECT",
+ "HOSTALIASES",
+ "TZ", "TZDIR",
+ "TERMCAP",
+ "COLUMNS", "LINES",
+ NULL
+};
+
+static bool str_in_list(const char **list, const char *key)
+{
+ const char **ent;
+
+ for (ent = list; *ent; ent++) {
+ if (!strcmp(*ent, key))
+ return true;
+ }
+ return false;
+}
+
+/*! filtered a process environment by whitelist; only copying pointers, no actual strings.
+ *
+ * This function is useful if you'd like to generate an environment to pass exec*e()
+ * functions. It will create a new environment containing only those entries whose
+ * keys (as per environment convention KEY=VALUE) are contained in the whitelist. The
+ * function will not copy the actual strings, but just create a new pointer array, pointing
+ * to the same memory as the input strings.
+ *
+ * Constraints: Keys up to a maximum length of 255 characters are supported.
+ *
+ * \param[out] out caller-allocated array of pointers for the generated output
+ * \param[in] out_len size of out (number of pointers)
+ * \param[in] in input environment (NULL-terminated list of pointers like **environ)
+ * \param[in] whitelist whitelist of permitted keys in environment (like **environ)
+ * \returns number of entries filled in 'out'; negtive on error */
+int osmo_environment_filter(char **out, size_t out_len, char **in, const char **whitelist)
+{
+ char tmp[256];
+ char **ent;
+ size_t out_used = 0;
+
+ /* invalid calls */
+ if (!out || out_len == 0 || !whitelist)
+ return -EINVAL;
+
+ /* legal, but unusual: no input to filter should generate empty, terminated out */
+ if (!in) {
+ out[0] = NULL;
+ return 1;
+ }
+
+ /* iterate over input entries */
+ for (ent = in; *ent; ent++) {
+ char *eq = strchr(*ent, '=');
+ unsigned long eq_pos;
+ if (!eq) {
+ /* no '=' in string, skip it */
+ continue;
+ }
+ eq_pos = eq - *ent;
+ if (eq_pos >= ARRAY_SIZE(tmp))
+ continue;
+ strncpy(tmp, *ent, eq_pos);
+ tmp[eq_pos] = '\0';
+ if (str_in_list(whitelist, tmp)) {
+ if (out_used == out_len-1)
+ break;
+ /* append to output */
+ out[out_used++] = *ent;
+ }
+ }
+ OSMO_ASSERT(out_used < out_len);
+ out[out_used++] = NULL;
+ return out_used;
+}
+
+/*! append one environment to another; only copying pointers, not actual strings.
+ *
+ * This function is useful if you'd like to append soem entries to an environment
+ * befoer passing it to exec*e() functions.
+ *
+ * It will append all entries from 'in' to the environment in 'out', as long as
+ * 'out' has space (determined by 'out_len').
+ *
+ * Constraints: If the same key exists in 'out' and 'in', duplicate keys are
+ * generated. It is a simple append, without any duplicate checks.
+ *
+ * \param[out] out caller-allocated array of pointers for the generated output
+ * \param[in] out_len size of out (number of pointers)
+ * \param[in] in input environment (NULL-terminated list of pointers like **environ)
+ * \returns number of entries filled in 'out'; negative on error */
+int osmo_environment_append(char **out, size_t out_len, char **in)
+{
+ size_t out_used = 0;
+
+ if (!out || out_len == 0)
+ return -EINVAL;
+
+ /* seek to end of existing output */
+ for (out_used = 0; out[out_used]; out_used++) {}
+
+ if (!in) {
+ if (out_used == 0)
+ out[out_used++] = NULL;
+ return out_used;
+ }
+
+ for (; *in && out_used < out_len-1; in++)
+ out[out_used++] = *in;
+
+ OSMO_ASSERT(out_used < out_len);
+ out[out_used++] = NULL;
+
+ return out_used;
+}
+
+/* Iterate over files in /proc/self/fd and close all above lst_fd_to_keep */
+int osmo_close_all_fds_above(int last_fd_to_keep)
+{
+ struct dirent *ent;
+ DIR *dir;
+ int rc;
+
+ dir = opendir("/proc/self/fd");
+ if (!dir) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Cannot open /proc/self/fd: %s\n", strerror(errno));
+ return -ENODEV;
+ }
+
+ while ((ent = readdir(dir))) {
+ int fd = atoi(ent->d_name);
+ if (fd <= last_fd_to_keep)
+ continue;
+ if (fd == dirfd(dir))
+ continue;
+ rc = close(fd);
+ if (rc)
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error closing fd=%d: %s\n", fd, strerror(errno));
+ }
+ closedir(dir);
+ return 0;
+}
+
+/* Seems like POSIX has no header file for this, and even glibc + __USE_GNU doesn't help */
+extern char **environ;
+
+/*! call an external shell command as 'user' without waiting for it.
+ *
+ * This mimics the behavior of system(3), with the following differences:
+ * - it doesn't wait for completion of the child process
+ * - it closes all non-stdio file descriptors by iterating /proc/self/fd
+ * - it constructs a reduced environment where only whitelisted keys survive
+ * - it (optionally) appends additional variables to the environment
+ * - it (optionally) changes the user ID to that of 'user' (requires execution as root)
+ *
+ * \param[in] command the shell command to be executed, see system(3)
+ * \param[in] env_whitelist A white-list of keys for environment variables
+ * \param[in] addl_env any additional environment variables to be appended
+ * \param[in] user name of the user to which we should switch before executing the command
+ * \returns PID of generated child process; negative on error
+ */
+int osmo_system_nowait2(const char *command, const char **env_whitelist, char **addl_env, const char *user)
+{
+ struct passwd _pw;
+ struct passwd *pw = NULL;
+ int getpw_buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ int rc;
+
+ if (user) {
+ if (getpw_buflen == -1) /* Value was indeterminate */
+ getpw_buflen = 16384; /* Should be more than enough */
+ char buf[getpw_buflen];
+ rc = getpwnam_r(user, &_pw, buf, sizeof(buf), &pw);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "getpwnam_r(\"%s\") failed: %s\n", user, strerror(-rc));
+ return rc;
+ }
+ if (!pw) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "getpwnam_r(\"%s\"): user not found!\n", user);
+ return -EINVAL;
+ }
+ }
+
+ rc = fork();
+ if (rc == 0) {
+ /* we are in the child */
+ char *new_env[1024];
+
+ /* close all file descriptors above stdio */
+ osmo_close_all_fds_above(2);
+
+ /* man execle: "an array of pointers *must* be terminated by a null pointer" */
+ new_env[0] = NULL;
+
+ /* build the new environment */
+ if (env_whitelist) {
+ rc = osmo_environment_filter(new_env, ARRAY_SIZE(new_env), environ, env_whitelist);
+ if (rc < 0)
+ return rc;
+ }
+ if (addl_env) {
+ rc = osmo_environment_append(new_env, ARRAY_SIZE(new_env), addl_env);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* drop privileges */
+ if (pw) {
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0) {
+ perror("setresgid() during privilege drop");
+ exit(1);
+ }
+
+ if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0) {
+ perror("setresuid() during privilege drop");
+ exit(1);
+ }
+
+ }
+
+ /* if we want to behave like system(3), we must go via the shell */
+ execle("/bin/sh", "sh", "-c", command, (char *) NULL, new_env);
+ /* only reached in case of error */
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error executing command '%s' after fork: %s\n",
+ command, strerror(errno));
+ return -EIO;
+ } else {
+ /* we are in the parent */
+ if (rc == -1)
+ LOGP(DLGLOBAL, LOGL_ERROR, "fork() error executing command '%s': %s\n",
+ command, strerror(errno));
+ return rc;
+ }
+}
+
+/*! call an external shell command without waiting for it.
+ *
+ * This mimics the behavior of system(3), with the following differences:
+ * - it doesn't wait for completion of the child process
+ * - it closes all non-stdio file descriptors by iterating /proc/self/fd
+ * - it constructs a reduced environment where only whitelisted keys survive
+ * - it (optionally) appends additional variables to the environment
+ *
+ * \param[in] command the shell command to be executed, see system(3)
+ * \param[in] env_whitelist A white-list of keys for environment variables
+ * \param[in] addl_env any additional environment variables to be appended
+ * \returns PID of generated child process; negative on error
+ */
+int osmo_system_nowait(const char *command, const char **env_whitelist, char **addl_env)
+{
+ return osmo_system_nowait2(command, env_whitelist, addl_env, NULL);
+}
+
+
+#endif /* EMBEDDED */
diff --git a/src/fsm.c b/src/core/fsm.c
index 1e8909ec..9333cac5 100644
--- a/src/fsm.c
+++ b/src/core/fsm.c
@@ -14,11 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
#include <errno.h>
@@ -450,8 +445,8 @@ struct osmo_fsm_inst *osmo_fsm_inst_alloc(struct osmo_fsm *fsm, void *ctx, void
osmo_timer_setup(&fi->timer, fsm_tmr_cb, fi);
if (osmo_fsm_inst_update_id(fi, id) < 0) {
- fsm_free_or_steal(fi);
- return NULL;
+ fsm_free_or_steal(fi);
+ return NULL;
}
INIT_LLIST_HEAD(&fi->proc.children);
@@ -581,7 +576,7 @@ void osmo_fsm_inst_free(struct osmo_fsm_inst *fi)
* \param[in] event Event integer value
* \returns string rendering of the event
*/
-const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event)
+const char *osmo_fsm_event_name(const struct osmo_fsm *fsm, uint32_t event)
{
static __thread char buf[32];
if (!fsm->event_names) {
@@ -595,7 +590,7 @@ const char *osmo_fsm_event_name(struct osmo_fsm *fsm, uint32_t event)
* \param[in] fi FSM instance
* \returns string rendering of the FSM identity
*/
-const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi)
+const char *osmo_fsm_inst_name(const struct osmo_fsm_inst *fi)
{
if (!fi)
return "NULL";
@@ -611,7 +606,7 @@ const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi)
* \param[in] state FSM state number
* \returns string rendering of the FSM state
*/
-const char *osmo_fsm_state_name(struct osmo_fsm *fsm, uint32_t state)
+const char *osmo_fsm_state_name(const struct osmo_fsm *fsm, uint32_t state)
{
static __thread char buf[32];
if (state >= fsm->num_states) {
@@ -690,8 +685,11 @@ static int state_chg(struct osmo_fsm_inst *fi, uint32_t new_state,
if (!keep_timer
|| (keep_timer && !osmo_timer_pending(&fi->timer))) {
fi->T = T;
- if (timeout_ms)
- osmo_timer_schedule(&fi->timer, timeout_ms / 1000, timeout_ms % 1000);
+ if (timeout_ms) {
+ osmo_timer_schedule(&fi->timer,
+ /* seconds */ (timeout_ms / 1000),
+ /* microseconds */ (timeout_ms % 1000) * 1000);
+ }
}
/* Call 'onenter' last, user might terminate FSM from there */
@@ -1016,6 +1014,26 @@ void _osmo_fsm_inst_term_children(struct osmo_fsm_inst *fi,
}
}
+/*! Broadcast an event to all the FSMs children.
+ *
+ * Iterate over all children and send them the specified event.
+ *
+ * \param[in] fi FSM instance of the parent
+ * \param[in] event Event to send to children of FSM instance
+ * \param[in] data Data to pass along with the event
+ * \param[in] file Calling source file (from osmo_fsm_inst_dispatch macro)
+ * \param[in] line Calling source line (from osmo_fsm_inst_dispatch macro)
+ */
+void _osmo_fsm_inst_broadcast_children(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data,
+ const char *file, int line)
+{
+ struct osmo_fsm_inst *child, *tmp;
+ llist_for_each_entry_safe(child, tmp, &fi->proc.children, proc.child) {
+ _osmo_fsm_inst_dispatch(child, event, data, file, line);
+ }
+}
+
const struct value_string osmo_fsm_term_cause_names[] = {
OSMO_VALUE_STRING(OSMO_FSM_TERM_PARENT),
OSMO_VALUE_STRING(OSMO_FSM_TERM_REQUEST),
diff --git a/src/gsmtap_util.c b/src/core/gsmtap_util.c
index 2fb18a48..b64c7b05 100644
--- a/src/gsmtap_util.c
+++ b/src/core/gsmtap_util.c
@@ -17,13 +17,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
-#include "../config.h"
+#include "config.h"
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/logging.h>
@@ -33,6 +29,7 @@
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/byteswap.h>
+#include <osmocom/core/utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/rsl.h>
@@ -50,22 +47,68 @@
*
* \file gsmtap_util.c */
+/*! one gsmtap instance
+ * Until gsmtap_inst_fd() is removed from the API at some point in the future, we have to keep the first member as
+ * 'int' and the second as 'struct osmo_wqueue' (this effectively makes sure that the struct member wq.bfd.fd maintains
+ * the same memory offset from the start of the struct) to ensure that inlined static 'instances' of gsmtap_inst_fd() in
+ * old binaries keep working the way they used to even with gsmtap_inst objects obtained from newer versions of libosmocore */
+struct gsmtap_inst {
+ int osmo_io_mode; /*!< Indicates whether or not to use Osmo IO mode for message output (thus enabling use of tx queues).
+ * This field member may not be changed or moved (backwards compatibility) */
+ struct osmo_wqueue wq; /*!< the wait queue. This field member may not be changed or moved (backwards compatibility) */
+
+ struct osmo_io_fd *out; /*!< Used when osmo_io_mode is nonzero */
+ int sink_fd;
+};
+
+struct _gsmtap_inst_legacy {
+ int ofd_wq_mode;
+ struct osmo_wqueue wq;
+ struct osmo_fd sink_ofd;
+};
+osmo_static_assert(offsetof(struct gsmtap_inst, wq) == offsetof(struct _gsmtap_inst_legacy, wq),
+ gsmtap_inst_new_wq_offset_equals_legacy_wq_offset);
+
+/*! Deprecated, use gsmtap_inst_fd2() instead
+ * \param[in] gti GSMTAP instance
+ * \returns file descriptor of GSMTAP instance */
+int gsmtap_inst_fd(struct gsmtap_inst *gti)
+{
+ return gsmtap_inst_fd2(gti);
+}
+
+/*! obtain the file descriptor associated with a gsmtap instance
+ * \param[in] gti GSMTAP instance
+ * \returns file descriptor of GSMTAP instance */
+int gsmtap_inst_fd2(const struct gsmtap_inst *gti)
+{
+ return gti->wq.bfd.fd;
+}
/*! convert RSL channel number to GSMTAP channel type
* \param[in] rsl_chantype RSL channel type
* \param[in] link_id RSL link identifier
+ * \param[in] user_plane Is this voice/csd user plane (1) or signaling (0)
* \returns GSMTAP channel type
*/
-uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
+uint8_t chantype_rsl2gsmtap2(uint8_t rsl_chantype, uint8_t link_id, bool user_plane)
{
uint8_t ret = GSMTAP_CHANNEL_UNKNOWN;
switch (rsl_chantype) {
case RSL_CHAN_Bm_ACCHs:
- ret = GSMTAP_CHANNEL_TCH_F;
+ case RSL_CHAN_OSMO_VAMOS_Bm_ACCHs:
+ if (user_plane)
+ ret = GSMTAP_CHANNEL_VOICE_F;
+ else
+ ret = GSMTAP_CHANNEL_FACCH_F;
break;
case RSL_CHAN_Lm_ACCHs:
- ret = GSMTAP_CHANNEL_TCH_H;
+ case RSL_CHAN_OSMO_VAMOS_Lm_ACCHs:
+ if (user_plane)
+ ret = GSMTAP_CHANNEL_VOICE_H;
+ else
+ ret = GSMTAP_CHANNEL_FACCH_H;
break;
case RSL_CHAN_SDCCH4_ACCH:
ret = GSMTAP_CHANNEL_SDCCH4;
@@ -86,6 +129,12 @@ uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
case RSL_CHAN_OSMO_PDCH:
ret = GSMTAP_CHANNEL_PDCH;
break;
+ case RSL_CHAN_OSMO_CBCH4:
+ ret = GSMTAP_CHANNEL_CBCH51;
+ break;
+ case RSL_CHAN_OSMO_CBCH8:
+ ret = GSMTAP_CHANNEL_CBCH52;
+ break;
}
if (link_id & 0x40)
@@ -94,6 +143,16 @@ uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
return ret;
}
+/*! convert RSL channel number to GSMTAP channel type
+ * \param[in] rsl_chantype RSL channel type
+ * \param[in] link_id RSL link identifier
+ * \returns GSMTAP channel type
+ */
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id)
+{
+ return chantype_rsl2gsmtap2(rsl_chantype, link_id, false);
+}
+
/*! convert GSMTAP channel type to RSL channel number + Link ID
* \param[in] gsmtap_chantype GSMTAP channel type
* \param[out] rsl_chantype RSL channel mumber
@@ -103,10 +162,12 @@ void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype,
uint8_t *link_id)
{
switch (gsmtap_chantype & ~GSMTAP_CHANNEL_ACCH & 0xff) {
- case GSMTAP_CHANNEL_TCH_F: // TCH/F, FACCH/F
+ case GSMTAP_CHANNEL_FACCH_F:
+ case GSMTAP_CHANNEL_VOICE_F: // TCH/F
*rsl_chantype = RSL_CHAN_Bm_ACCHs;
break;
- case GSMTAP_CHANNEL_TCH_H: // TCH/H, FACCH/H
+ case GSMTAP_CHANNEL_FACCH_H:
+ case GSMTAP_CHANNEL_VOICE_H: // TCH/H
*rsl_chantype = RSL_CHAN_Lm_ACCHs;
break;
case GSMTAP_CHANNEL_SDCCH4: // SDCCH/4
@@ -151,7 +212,7 @@ void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype,
*/
struct msgb *gsmtap_makemsg_ex(uint8_t type, 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)
+ int8_t snr, const uint8_t *data, unsigned int len)
{
struct msgb *msg;
struct gsmtap_hdr *gh;
@@ -198,7 +259,7 @@ struct msgb *gsmtap_makemsg_ex(uint8_t type, uint16_t arfcn, uint8_t ts, uint8_t
*/
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)
+ int8_t snr, const uint8_t *data, unsigned int len)
{
return gsmtap_makemsg_ex(GSMTAP_TYPE_UM, arfcn, ts, chan_type,
ss, fn, signal_dbm, snr, data, len);
@@ -208,13 +269,14 @@ struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type,
#include <sys/socket.h>
#include <netinet/in.h>
+#include <osmocom/core/osmo_io.h>
-/*! Create a new (sending) GSMTAP source socket
+/*! Create a new (sending) GSMTAP source socket
* \param[in] host host name or IP address in string format
* \param[in] port UDP port number in host byte order
* \return file descriptor of the new socket
*
- * Opens a GSMTAP source (sending) socket, conncet it to host/port and
+ * Opens a GSMTAP source (sending) socket, connect it to host/port and
* return resulting fd. If \a host is NULL, the destination address
* will be localhost. If \a port is 0, the default \ref
* GSMTAP_UDP_PORT will be used.
@@ -230,6 +292,30 @@ int gsmtap_source_init_fd(const char *host, uint16_t port)
OSMO_SOCK_F_CONNECT);
}
+/*! Create a new (sending) GSMTAP source socket
+ * \param[in] local_host local host name or IP address in string format
+ * \param[in] local_port local UDP port number in host byte order
+ * \param[in] rem_host remote host name or IP address in string format
+ * \param[in] rem_port remote UDP port number in host byte order
+ * \return file descriptor of the new socket
+ *
+ * Opens a GSMTAP source (sending) socket, connect it to remote host/port,
+ * bind to local host/port and return resulting fd.
+ * If \a local_host is NULL, the default address is used.
+ * If \a local_port is 0, than random unused port will be selected by OS.
+ * If \a rem_host is NULL, the destination address will be localhost.
+ * If \a rem_port is 0, the default \ref GSMTAP_UDP_PORT will be used.
+ */
+int gsmtap_source_init_fd2(const char *local_host, uint16_t local_port, const char *rem_host, uint16_t rem_port)
+{
+ if (!local_host)
+ return gsmtap_source_init_fd(rem_host, rem_port);
+
+ return osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, local_host, local_port,
+ rem_host ? rem_host : "localhost", rem_port ? rem_port : GSMTAP_UDP_PORT,
+ OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
+}
+
/*! Add a local sink to an existing GSMTAP source and return fd
* \param[in] gsmtap_fd file descriptor of the gsmtap socket
* \returns file descriptor of locally bound receive socket
@@ -277,13 +363,13 @@ int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg)
if (!gti)
return -ENODEV;
- if (gti->ofd_wq_mode)
- return osmo_wqueue_enqueue(&gti->wq, msg);
+ if (gti->osmo_io_mode)
+ return osmo_iofd_write_msgb(gti->out, msg);
else {
/* try immediate send and return error if any */
int rc;
- rc = write(gsmtap_inst_fd(gti), msg->data, msg->len);
+ rc = write(gsmtap_inst_fd2(gti), msg->data, msg->len);
if (rc < 0) {
return rc;
} else if (rc >= msg->len) {
@@ -296,12 +382,26 @@ int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg)
}
}
+/*! Send a \ref msgb through a GSMTAP source; free the message even if tx queue full.
+ * \param[in] gti GSMTAP instance
+ * \param[in] msg message buffer; always freed, caller must not reference it later.
+ * \return 0 in case of success; negative in case of error
+ */
+int gsmtap_sendmsg_free(struct gsmtap_inst *gti, struct msgb *msg)
+{
+ int rc;
+ rc = gsmtap_sendmsg(gti, msg);
+ if (rc < 0)
+ msgb_free(msg);
+ return rc;
+}
+
/*! send an arbitrary type through GSMTAP.
* See \ref gsmtap_makemsg_ex for arguments
*/
int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, 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,
+ int8_t signal_dbm, int8_t snr, const uint8_t *data,
unsigned int len)
{
struct msgb *msg;
@@ -326,47 +426,13 @@ int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, uint16_t arfcn, uint8_
*/
int gsmtap_send(struct gsmtap_inst *gti, 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,
+ int8_t signal_dbm, int8_t snr, const uint8_t *data,
unsigned int len)
{
return gsmtap_send_ex(gti, GSMTAP_TYPE_UM, arfcn, ts, chan_type, ss, fn,
signal_dbm, snr, data, len);
}
-/* Callback from select layer if we can write to the socket */
-static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg)
-{
- int rc;
-
- rc = write(ofd->fd, msg->data, msg->len);
- if (rc < 0) {
- return rc;
- }
- if (rc != msg->len) {
- return -EIO;
- }
-
- return 0;
-}
-
-/* Callback from select layer if we can read from the sink socket */
-static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
-{
- int rc;
- uint8_t buf[4096];
-
- if (!(flags & OSMO_FD_READ))
- return 0;
-
- rc = read(fd->fd, buf, sizeof(buf));
- if (rc < 0) {
- return rc;
- }
- /* simply discard any data arriving on the socket */
-
- return 0;
-}
-
/*! Add a local sink to an existing GSMTAP source and return fd
* \param[in] gti existing GSMTAP source
* \returns file descriptor of locally bound receive socket
@@ -384,30 +450,63 @@ static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags)
*/
int gsmtap_source_add_sink(struct gsmtap_inst *gti)
{
- int fd, rc;
+ return gti->sink_fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd2(gti));
+}
- fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti));
- if (fd < 0)
- return fd;
+/* Registered in Osmo IO as a no-op to set the write callback. */
+static void gsmtap_ops_noop_cb(struct osmo_io_fd *iofd, int res, struct msgb *msg)
+{
+}
- if (gti->ofd_wq_mode) {
- struct osmo_fd *sink_ofd;
+static struct osmo_io_ops gsmtap_ops = { .write_cb = gsmtap_ops_noop_cb };
- sink_ofd = &gti->sink_ofd;
- sink_ofd->fd = fd;
- sink_ofd->when = OSMO_FD_READ;
- sink_ofd->cb = gsmtap_sink_fd_cb;
+/*! Open GSMTAP source socket, connect and register osmo_fd
+ * \param[in] local_host IP address in string format
+ * \param[in] local_port UDP port number in host byte order
+ * \param[in] rem_host host name or IP address in string format
+ * \param[in] rem_port UDP port number in host byte order
+ * \param[in] ofd_wq_mode Register \ref osmo_wqueue (1) or not (0)
+ * \return callee-allocated \ref gsmtap_inst
+ *
+ * Open GSMTAP source (sending) socket, connect it to remote host/port,
+ * bind it local host/port,
+ * allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue
+ * registration.
+ */
+struct gsmtap_inst *gsmtap_source_init2(const char *local_host, uint16_t local_port,
+ const char *rem_host, uint16_t rem_port, int ofd_wq_mode)
+{
+ struct gsmtap_inst *gti;
+ int fd;
- rc = osmo_fd_register(sink_ofd);
- if (rc < 0) {
- close(fd);
- return rc;
- }
+ fd = gsmtap_source_init_fd2(local_host, local_port, rem_host, rem_port);
+ if (fd < 0)
+ return NULL;
+
+ gti = talloc_zero(NULL, struct gsmtap_inst);
+ gti->osmo_io_mode = ofd_wq_mode;
+ /* Still using the wq member for its 'fd' field only, since we are keeping it for now, anyways */
+ gti->wq.bfd.fd = fd;
+ gti->sink_fd = -1;
+
+ if (ofd_wq_mode) {
+ gti->out = osmo_iofd_setup(gti, gti->wq.bfd.fd, "gsmtap_inst.io_fd", OSMO_IO_FD_MODE_READ_WRITE, &gsmtap_ops, NULL);
+ if (gti->out == NULL)
+ goto err_cleanup;
+ if (osmo_iofd_register(gti->out, gti->wq.bfd.fd) < 0)
+ goto err_cleanup;
+
+ /* osmo write queue previously used was set up with value of 64 */
+ osmo_iofd_set_txqueue_max_length(gti->out, 64);
}
- return fd;
-}
+ return gti;
+err_cleanup:
+ talloc_free(gti);
+ close(fd);
+ return NULL;
+}
/*! Open GSMTAP source socket, connect and register osmo_fd
* \param[in] host host name or IP address in string format
@@ -422,31 +521,25 @@ int gsmtap_source_add_sink(struct gsmtap_inst *gti)
struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port,
int ofd_wq_mode)
{
- struct gsmtap_inst *gti;
- int fd, rc;
-
- fd = gsmtap_source_init_fd(host, port);
- if (fd < 0)
- return NULL;
+ return gsmtap_source_init2(NULL, 0, host, port, ofd_wq_mode);
+}
- gti = talloc_zero(NULL, struct gsmtap_inst);
- gti->ofd_wq_mode = ofd_wq_mode;
- gti->wq.bfd.fd = fd;
- gti->sink_ofd.fd = -1;
+void gsmtap_source_free(struct gsmtap_inst *gti)
+{
+ if (!gti)
+ return;
- if (ofd_wq_mode) {
- osmo_wqueue_init(&gti->wq, 64);
- gti->wq.write_cb = &gsmtap_wq_w_cb;
+ if (gti->osmo_io_mode) {
+ osmo_iofd_free(gti->out);
- rc = osmo_fd_register(&gti->wq.bfd);
- if (rc < 0) {
- talloc_free(gti);
- close(fd);
- return NULL;
+ if (gti->sink_fd != -1) {
+ close(gti->sink_fd);
+ gti->sink_fd = -1;
}
+
}
- return gti;
+ talloc_free(gti);
}
#endif /* HAVE_SYS_SOCKET_H */
@@ -461,8 +554,8 @@ const struct value_string gsmtap_gsm_channel_names[] = {
{ GSMTAP_CHANNEL_SDCCH, "SDCCH" },
{ GSMTAP_CHANNEL_SDCCH4, "SDCCH/4" },
{ GSMTAP_CHANNEL_SDCCH8, "SDCCH/8" },
- { GSMTAP_CHANNEL_TCH_F, "TCH/F/FACCH/F" },
- { GSMTAP_CHANNEL_TCH_H, "TCH/H/FACCH/H" },
+ { GSMTAP_CHANNEL_FACCH_F, "FACCH/F" },
+ { GSMTAP_CHANNEL_FACCH_H, "FACCH/H" },
{ GSMTAP_CHANNEL_PACCH, "PACCH" },
{ GSMTAP_CHANNEL_CBCH52, "CBCH" },
{ GSMTAP_CHANNEL_PDCH, "PDCH" } ,
@@ -471,8 +564,10 @@ const struct value_string gsmtap_gsm_channel_names[] = {
{ 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_H, "SACCH/H" },
+ { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_F, "SACCH/F" },
+ { GSMTAP_CHANNEL_ACCH | GSMTAP_CHANNEL_FACCH_H, "SACCH/H" },
+ { GSMTAP_CHANNEL_VOICE_F, "TCH/F" },
+ { GSMTAP_CHANNEL_VOICE_H, "TCH/H" },
{ 0, NULL }
};
@@ -485,6 +580,8 @@ const struct value_string gsmtap_type_names[] = {
{ GSMTAP_TYPE_TETRA_I1, "TETRA V+D" },
{ GSMTAP_TYPE_TETRA_I1_BURST, "TETRA bursts" },
{ GSMTAP_TYPE_WMX_BURST, "WiMAX burst" },
+ { GSMTAP_TYPE_GB_LLC, "GPRS Gb LLC" },
+ { GSMTAP_TYPE_GB_SNDCP, "GPRS Gb SNDCP" },
{ GSMTAP_TYPE_GMR1_UM, "GMR-1 air interfeace (MES-MS<->GTS)"},
{ GSMTAP_TYPE_UMTS_RLC_MAC, "UMTS RLC/MAC" },
{ GSMTAP_TYPE_UMTS_RRC, "UMTS RRC" },
@@ -493,6 +590,9 @@ const struct value_string gsmtap_type_names[] = {
{ GSMTAP_TYPE_LTE_MAC_FRAMED, "LTE MAC with context hdr" },
{ GSMTAP_TYPE_OSMOCORE_LOG, "libosmocore logging" },
{ GSMTAP_TYPE_QC_DIAG, "Qualcomm DIAG" },
+ { GSMTAP_TYPE_LTE_NAS, "LTE Non-Access Stratum" },
+ { GSMTAP_TYPE_E1T1, "E1/T1 lines" },
+ { GSMTAP_TYPE_GSM_RLP, "GSM Radio Link Protocol" },
{ 0, NULL }
};
diff --git a/src/isdnhdlc.c b/src/core/isdnhdlc.c
index 58b4a661..4ced5afd 100644
--- a/src/isdnhdlc.c
+++ b/src/core/isdnhdlc.c
@@ -18,10 +18,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <string.h>
@@ -101,13 +97,12 @@ check_frame(struct osmo_isdnhdlc_vars *hdlc)
When a new flag is found, the complete frame has been received
and the CRC is checked.
If a valid frame is found, the function returns the frame length
- excluding the CRC with the bit HDLC_END_OF_FRAME set.
+ excluding the CRC.
If the beginning of a valid frame is found, the function returns
the length.
If a framing error is found (too many 1s and not a flag) the function
- returns the length with the bit OSMO_HDLC_FRAMING_ERROR set.
- If a CRC error is found the function returns the length with the
- bit OSMO_HDLC_CRC_ERROR set.
+ returns -OSMO_HDLC_FRAMING_ERROR.
+ If a CRC error is found the function returns -OSMO_HDLC_CRC_ERROR.
If the frame length exceeds the destination buffer size, the function
returns the length with the bit OSMO_HDLC_LENGTH_ERROR set.
diff --git a/src/core/it_q.c b/src/core/it_q.c
new file mode 100644
index 00000000..810dc903
--- /dev/null
+++ b/src/core/it_q.c
@@ -0,0 +1,269 @@
+/*! \file it_q.c
+ * Osmocom Inter-Thread queue implementation */
+/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*! \addtogroup it_q
+ * @{
+ * Inter-Thread Message Queue.
+ *
+ * This implements a general-purpose queue between threads. It uses
+ * user-provided data types (containing a llist_head as initial member)
+ * as elements in the queue and an eventfd-based notification mechanism.
+ * Hence, it can be used for pretty much anything, including but not
+ * limited to msgbs, including msgb-wrapped osmo_prim.
+ *
+ * The idea is that the sending thread simply calls osmo_it_q_enqueue().
+ * The receiving thread is woken up from its osmo_select_main() loop by eventfd,
+ * and a general osmo_fd callback function for the eventfd will dequeue each item
+ * and call a queue-specific callback function.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_SYS_EVENTFD_H
+
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/eventfd.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/it_q.h>
+
+/* "increment" the eventfd by specified 'inc' */
+static int eventfd_increment(int fd, uint64_t inc)
+{
+ int rc;
+
+ rc = write(fd, &inc, sizeof(inc));
+ if (rc != sizeof(inc))
+ return -1;
+
+ return 0;
+}
+
+/* global (for all threads) list of message queues in a program + associated lock */
+static LLIST_HEAD(it_queues);
+static pthread_rwlock_t it_queues_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+/* resolve it-queue by its [globally unique] name; must be called with rwlock held */
+static struct osmo_it_q *_osmo_it_q_by_name(const char *name)
+{
+ struct osmo_it_q *q;
+ llist_for_each_entry(q, &it_queues, entry) {
+ if (!strcmp(q->name, name))
+ return q;
+ }
+ return NULL;
+}
+
+/*! resolve it-queue by its [globally unique] name */
+struct osmo_it_q *osmo_it_q_by_name(const char *name)
+{
+ struct osmo_it_q *q;
+ pthread_rwlock_rdlock(&it_queues_rwlock);
+ q = _osmo_it_q_by_name(name);
+ pthread_rwlock_unlock(&it_queues_rwlock);
+ return q;
+}
+
+/* osmo_fd call-back when eventfd is readable */
+static int osmo_it_q_fd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ struct osmo_it_q *q = (struct osmo_it_q *) ofd->data;
+ uint64_t val;
+ int i, rc;
+
+ if (!(what & OSMO_FD_READ))
+ return 0;
+
+ rc = read(ofd->fd, &val, sizeof(val));
+ if (rc < sizeof(val))
+ return rc;
+
+ for (i = 0; i < val; i++) {
+ struct llist_head *item = _osmo_it_q_dequeue(q);
+ /* in case the user might have called osmo_it_q_flush() we may
+ * end up in the eventfd-dispatch but without any messages left in the queue,
+ * otherwise I'd have loved to OSMO_ASSERT(msg) here. */
+ if (!item)
+ break;
+ q->read_cb(q, item);
+ }
+
+ return 0;
+}
+
+/*! Allocate a new inter-thread message queue.
+ * \param[in] ctx talloc context from which to allocate the queue
+ * \param[in] name human-readable string name of the queue; function creates a copy.
+ * \param[in] read_cb call-back function to be called for each de-queued message; may be
+ * NULL in case you don't want eventfd/osmo_select integration and
+ * will manually take care of noticing if and when to dequeue.
+ * \returns a newly-allocated inter-thread message queue; NULL in case of error */
+struct osmo_it_q *osmo_it_q_alloc(void *ctx, const char *name, unsigned int max_length,
+ void (*read_cb)(struct osmo_it_q *q, struct llist_head *item),
+ void *data)
+{
+ struct osmo_it_q *q;
+ int fd;
+
+ q = talloc_zero(ctx, struct osmo_it_q);
+ if (!q)
+ return NULL;
+ q->data = data;
+ q->name = talloc_strdup(q, name);
+ q->current_length = 0;
+ q->max_length = max_length;
+ q->read_cb = read_cb;
+ INIT_LLIST_HEAD(&q->list);
+ pthread_mutex_init(&q->mutex, NULL);
+ q->event_ofd.fd = -1;
+
+ if (q->read_cb) {
+ /* create eventfd *if* the user has provided a read_cb function */
+ fd = eventfd(0, 0);
+ if (fd < 0) {
+ talloc_free(q);
+ return NULL;
+ }
+
+ /* initialize BUT NOT REGISTER the osmo_fd. The receiving thread must
+ * take are to select/poll/read/... on it */
+ osmo_fd_setup(&q->event_ofd, fd, OSMO_FD_READ, osmo_it_q_fd_cb, q, 0);
+ }
+
+ /* add to global list of queues, checking for duplicate names */
+ pthread_rwlock_wrlock(&it_queues_rwlock);
+ if (_osmo_it_q_by_name(q->name)) {
+ pthread_rwlock_unlock(&it_queues_rwlock);
+ if (q->event_ofd.fd >= 0)
+ osmo_fd_close(&q->event_ofd);
+ talloc_free(q);
+ return NULL;
+ }
+ llist_add_tail(&q->entry, &it_queues);
+ pthread_rwlock_unlock(&it_queues_rwlock);
+
+ return q;
+}
+
+static void *item_dequeue(struct llist_head *queue)
+{
+ struct llist_head *lh;
+
+ if (llist_empty(queue))
+ return NULL;
+
+ lh = queue->next;
+ if (lh) {
+ llist_del(lh);
+ return lh;
+ } else
+ return NULL;
+}
+
+/*! Flush all messages currently present in queue */
+static void _osmo_it_q_flush(struct osmo_it_q *q)
+{
+ void *item;
+ while ((item = item_dequeue(&q->list))) {
+ talloc_free(item);
+ }
+ q->current_length = 0;
+}
+
+/*! Flush all messages currently present in queue */
+void osmo_it_q_flush(struct osmo_it_q *q)
+{
+ OSMO_ASSERT(q);
+
+ pthread_mutex_lock(&q->mutex);
+ _osmo_it_q_flush(q);
+ pthread_mutex_unlock(&q->mutex);
+}
+
+/*! Destroy a message queue */
+void osmo_it_q_destroy(struct osmo_it_q *q)
+{
+ OSMO_ASSERT(q);
+
+ /* first remove from global list of queues */
+ pthread_rwlock_wrlock(&it_queues_rwlock);
+ llist_del(&q->entry);
+ pthread_rwlock_unlock(&it_queues_rwlock);
+ /* next, close the eventfd */
+ if (q->event_ofd.fd >= 0)
+ osmo_fd_close(&q->event_ofd);
+ /* flush all messages still present */
+ osmo_it_q_flush(q);
+ pthread_mutex_destroy(&q->mutex);
+ /* and finally release memory */
+ talloc_free(q);
+}
+
+/*! Thread-safe en-queue to an inter-thread message queue.
+ * \param[in] queue Inter-thread queue on which to enqueue
+ * \param[in] item Item to enqueue. Must have llist_head as first member!
+ * \returns 0 on success; negative on error */
+int _osmo_it_q_enqueue(struct osmo_it_q *queue, struct llist_head *item)
+{
+ OSMO_ASSERT(queue);
+ OSMO_ASSERT(item);
+
+ pthread_mutex_lock(&queue->mutex);
+ if (queue->current_length+1 > queue->max_length) {
+ pthread_mutex_unlock(&queue->mutex);
+ return -ENOSPC;
+ }
+ llist_add_tail(item, &queue->list);
+ queue->current_length++;
+ pthread_mutex_unlock(&queue->mutex);
+ /* increment eventfd counter by one */
+ if (queue->event_ofd.fd >= 0)
+ eventfd_increment(queue->event_ofd.fd, 1);
+ return 0;
+}
+
+
+/*! Thread-safe de-queue from an inter-thread message queue.
+ * \param[in] queue Inter-thread queue from which to dequeue
+ * \returns llist_head of dequeued message; NULL if none available
+ */
+struct llist_head *_osmo_it_q_dequeue(struct osmo_it_q *queue)
+{
+ struct llist_head *l;
+ OSMO_ASSERT(queue);
+
+ pthread_mutex_lock(&queue->mutex);
+
+ l = item_dequeue(&queue->list);
+ if (l != NULL)
+ queue->current_length--;
+
+ pthread_mutex_unlock(&queue->mutex);
+
+ return l;
+}
+
+
+#endif /* HAVE_SYS_EVENTFD_H */
+
+/*! @} */
diff --git a/src/core/libosmocore.map b/src/core/libosmocore.map
new file mode 100644
index 00000000..c5ab6e37
--- /dev/null
+++ b/src/core/libosmocore.map
@@ -0,0 +1,631 @@
+LIBOSMOCORE_1.0 {
+global:
+
+assert_loginfo;
+bit_value_to_char;
+bitvec_add_array;
+bitvec_alloc;
+bitvec_fill;
+bitvec_find_bit_pos;
+bitvec_free;
+bitvec_get_bit_high;
+bitvec_get_bit_pos;
+bitvec_get_bit_pos_high;
+bitvec_get_bytes;
+bitvec_get_int16_msb;
+bitvec_get_nth_set_bit;
+bitvec_get_uint;
+bitvec_pack;
+bitvec_read_field;
+bitvec_rl;
+bitvec_rl_curbit;
+bitvec_set_bit;
+bitvec_set_bit_pos;
+bitvec_set_bits;
+bitvec_set_bytes;
+bitvec_set_u64;
+bitvec_set_uint;
+bitvec_shiftl;
+bitvec_spare_padding;
+bitvec_to_string_r;
+bitvec_unhex;
+bitvec_unpack;
+bitvec_write_field;
+bitvec_zero;
+chantype_gsmtap2rsl;
+chantype_rsl2gsmtap;
+chantype_rsl2gsmtap2;
+get_string_value;
+get_value_string;
+get_value_string_or_null;
+gsmtap_gsm_channel_names;
+gsmtap_inst_fd;
+gsmtap_inst_fd2;
+gsmtap_makemsg;
+gsmtap_makemsg_ex;
+gsmtap_send;
+gsmtap_send_ex;
+gsmtap_sendmsg;
+gsmtap_sendmsg_free;
+gsmtap_source_add_sink;
+gsmtap_source_add_sink_fd;
+gsmtap_source_free;
+gsmtap_source_init;
+gsmtap_source_init2;
+gsmtap_source_init_fd;
+gsmtap_source_init_fd2;
+gsmtap_type_names;
+log_add_target;
+log_category_name;
+log_check_level;
+log_cache_enable;
+log_cache_update;
+log_del_target;
+log_enable_multithread;
+log_fini;
+log_init;
+log_level_str;
+loglevel_strs;
+logp;
+logp2;
+log_parse_category;
+log_parse_category_mask;
+log_parse_level;
+logp_stub;
+log_reset_context;
+log_set_all_filter;
+log_set_category_filter;
+log_set_context;
+log_set_log_level;
+log_set_print_category;
+log_set_print_category_hex;
+log_set_print_extended_timestamp;
+log_set_print_filename;
+log_set_print_filename2;
+log_set_print_filename_pos;
+log_set_print_level;
+log_set_print_tid;
+log_set_print_timestamp;
+log_set_use_color;
+log_target_create;
+log_target_create_file;
+log_target_create_file_stream;
+log_target_create_gsmtap;
+log_target_create_rb;
+log_target_create_stderr;
+log_target_create_syslog;
+log_target_create_systemd;
+log_target_destroy;
+log_target_file_reopen;
+log_target_file_switch_to_stream;
+log_target_file_switch_to_wqueue;
+log_target_find;
+log_target_rb_avail_size;
+log_target_rb_get;
+log_target_rb_used_size;
+log_target_systemd_set_raw;
+log_targets_reopen;
+log_tgt_mutex_lock_impl;
+log_tgt_mutex_unlock_impl;
+msgb_alloc;
+msgb_alloc_c;
+msgb_copy;
+msgb_copy_c;
+msgb_copy_resize;
+msgb_copy_resize_c;
+msgb_data;
+msgb_dequeue;
+msgb_enqueue;
+_msgb_eq;
+msgb_free;
+msgb_hexdump;
+msgb_hexdump_buf;
+msgb_hexdump_c;
+msgb_length;
+msgb_printf;
+msgb_reset;
+msgb_resize_area;
+msgb_set_talloc_ctx;
+msgb_talloc_ctx_init;
+osmo_base64_decode;
+osmo_base64_encode;
+osmo_bcd2char;
+osmo_bcd2str;
+osmo_bit_reversal;
+osmo_char2bcd;
+osmo_clock_gettime;
+osmo_clock_override_add;
+osmo_clock_override_enable;
+osmo_clock_override_gettimespec;
+osmo_close_all_fds_above;
+osmo_config_list_parse;
+osmo_constant_time_cmp;
+osmo_conv_decode;
+osmo_conv_decode_acc;
+osmo_conv_decode_deinit;
+osmo_conv_decode_flush;
+osmo_conv_decode_get_best_end_state;
+osmo_conv_decode_get_output;
+osmo_conv_decode_init;
+osmo_conv_decode_reset;
+osmo_conv_decode_rewind;
+osmo_conv_decode_scan;
+osmo_conv_encode;
+osmo_conv_encode_flush;
+osmo_conv_encode_init;
+osmo_conv_encode_load_state;
+osmo_conv_encode_raw;
+osmo_conv_get_input_length;
+osmo_conv_get_output_length;
+osmo_counter_alloc;
+osmo_counter_difference;
+osmo_counter_free;
+osmo_counter_get_by_name;
+osmo_counters_count;
+osmo_counters_for_each;
+osmo_crc16;
+osmo_crc16_ccitt;
+osmo_crc16_ccitt_table;
+osmo_crc16_table;
+osmo_crc16gen_check_bits;
+osmo_crc16gen_compute_bits;
+osmo_crc16gen_set_bits;
+osmo_crc32gen_check_bits;
+osmo_crc32gen_compute_bits;
+osmo_crc32gen_set_bits;
+osmo_crc64gen_check_bits;
+osmo_crc64gen_compute_bits;
+osmo_crc64gen_set_bits;
+osmo_crc8gen_check_bits;
+osmo_crc8gen_compute_bits;
+osmo_crc8gen_set_bits;
+osmo_ctx;
+osmo_ctx_init;
+osmo_daemonize;
+osmo_decode_big_endian;
+osmo_encode_big_endian;
+osmo_environment_append;
+osmo_environment_filter;
+osmo_environment_whitelist;
+osmo_escape_cstr_buf;
+osmo_escape_cstr_c;
+osmo_escape_str;
+osmo_escape_str_buf;
+osmo_escape_str_buf2;
+osmo_escape_str_buf3;
+osmo_escape_str_c;
+osmo_event_for_prim;
+osmo_fd_close;
+osmo_fd_disp_fds;
+osmo_fd_fill_fds;
+osmo_fd_get_by_fd;
+osmo_fd_is_registered;
+osmo_fd_register;
+osmo_fd_setup;
+osmo_fd_unregister;
+osmo_fd_update_when;
+osmo_float_str_to_int;
+osmo_fsm_event_name;
+osmo_fsm_find_by_name;
+osmo_fsm_inst_alloc;
+osmo_fsm_inst_alloc_child;
+_osmo_fsm_inst_broadcast_children;
+osmo_fsm_inst_change_parent;
+_osmo_fsm_inst_dispatch;
+osmo_fsm_inst_find_by_id;
+osmo_fsm_inst_find_by_name;
+osmo_fsm_inst_free;
+osmo_fsm_inst_name;
+_osmo_fsm_inst_state_chg;
+_osmo_fsm_inst_state_chg_keep_or_start_timer;
+_osmo_fsm_inst_state_chg_keep_or_start_timer_ms;
+_osmo_fsm_inst_state_chg_keep_timer;
+_osmo_fsm_inst_state_chg_ms;
+_osmo_fsm_inst_term;
+_osmo_fsm_inst_term_children;
+osmo_fsm_inst_unlink_parent;
+osmo_fsm_inst_update_id;
+osmo_fsm_inst_update_id_f;
+osmo_fsm_inst_update_id_f_sanitize;
+osmo_fsm_log_addr;
+osmo_fsm_log_timeouts;
+osmo_fsm_register;
+osmo_fsm_set_dealloc_ctx;
+osmo_fsm_state_name;
+osmo_fsm_term_cause_names;
+osmo_fsm_term_safely;
+osmo_fsm_unregister;
+osmo_generate_backtrace;
+osmo_get_macaddr;
+osmo_gettid;
+osmo_gettimeofday;
+osmo_gettimeofday_override;
+osmo_gettimeofday_override_add;
+osmo_gettimeofday_override_time;
+osmo_g_fsms;
+osmo_hexdump;
+osmo_hexdump_buf;
+osmo_hexdump_c;
+osmo_hexdump_nospc;
+osmo_hexdump_nospc_c;
+osmo_hexparse;
+osmo_identifier_sanitize_buf;
+osmo_identifier_valid;
+osmo_init_ignore_signals;
+osmo_init_logging;
+osmo_init_logging2;
+osmo_int_to_float_str_buf;
+osmo_int_to_float_str_c;
+osmo_io_backend_names;
+osmo_iofd_close;
+osmo_iofd_free;
+osmo_iofd_get_data;
+osmo_iofd_get_ioops;
+osmo_iofd_get_fd;
+osmo_iofd_get_name;
+osmo_iofd_set_name;
+osmo_iofd_get_priv_nr;
+osmo_iofd_init;
+osmo_iofd_mode_names;
+osmo_iofd_ops;
+osmo_iofd_register;
+osmo_iofd_sendto_msgb;
+osmo_iofd_sctp_send_msgb;
+osmo_iofd_sendmsg_msgb;
+osmo_iofd_set_alloc_info;
+osmo_iofd_set_cmsg_size;
+osmo_iofd_set_data;
+osmo_iofd_set_ioops;
+osmo_iofd_set_priv_nr;
+osmo_iofd_set_txqueue_max_length;
+osmo_iofd_setup;
+osmo_iofd_txqueue_clear;
+osmo_iofd_txqueue_len;
+osmo_iofd_unregister;
+osmo_iofd_uring_init;
+osmo_iofd_notify_connected;
+osmo_iofd_write_msgb;
+osmo_ip_str_type;
+osmo_isdnhdlc_decode;
+osmo_isdnhdlc_encode;
+osmo_isdnhdlc_out_init;
+osmo_isdnhdlc_rcv_init;
+osmo_is_hexstr;
+osmo_isqrt32;
+osmo_it_q_alloc;
+osmo_it_q_by_name;
+_osmo_it_q_dequeue;
+osmo_it_q_destroy;
+_osmo_it_q_enqueue;
+osmo_it_q_flush;
+osmo_log_backtrace;
+osmo_log_info;
+osmo_log_target_list;
+osmo_luhn;
+osmo_macaddr_parse;
+osmo_mnl_destroy;
+osmo_mnl_init;
+osmo_multiaddr_ip_and_port_snprintf;
+osmo_netdev_add_addr;
+osmo_netdev_add_route;
+osmo_netdev_alloc;
+osmo_netdev_free;
+osmo_netdev_get_dev_name;
+osmo_netdev_get_ifindex;
+osmo_netdev_get_name;
+osmo_netdev_get_netns_name;
+osmo_netdev_get_priv_data;
+osmo_netdev_ifupdown;
+osmo_netdev_is_registered;
+osmo_netdev_register;
+osmo_netdev_set_dev_name_chg_cb;
+osmo_netdev_set_ifindex;
+osmo_netdev_set_ifupdown_ind_cb;
+osmo_netdev_set_mtu_chg_cb;
+osmo_netdev_set_netns_name;
+osmo_netdev_set_priv_data;
+osmo_netdev_unregister;
+osmo_netns_open_fd;
+osmo_netns_switch_enter;
+osmo_netns_switch_exit;
+osmo_nibble_shift_left_unal;
+osmo_nibble_shift_right;
+osmo_panic;
+osmo_pbit2ubit;
+osmo_pbit2ubit_ext;
+osmo_plugin_load_all;
+osmo_prbs11;
+osmo_prbs15;
+osmo_prbs7;
+osmo_prbs9;
+osmo_prbs_get_ubit;
+osmo_prbs_get_ubits;
+osmo_prbs_state_init;
+osmo_prim_op_names;
+osmo_print_n;
+osmo_quote_cstr_buf;
+osmo_quote_cstr_c;
+osmo_quote_str;
+osmo_quote_str_buf;
+osmo_quote_str_buf2;
+osmo_quote_str_buf3;
+osmo_quote_str_c;
+osmo_revbytebits_32;
+osmo_revbytebits_8;
+osmo_revbytebits_buf;
+osmo_sbit2ubit;
+osmo_select_init;
+osmo_select_main;
+osmo_select_main_ctx;
+osmo_select_shutdown_done;
+osmo_select_shutdown_request;
+osmo_select_shutdown_requested;
+osmo_separated_identifiers_valid;
+osmo_sercomm_change_speed;
+osmo_sercomm_drv_pull;
+osmo_sercomm_drv_rx_char;
+osmo_sercomm_init;
+osmo_sercomm_initialized;
+osmo_sercomm_register_rx_cb;
+osmo_sercomm_sendmsg;
+osmo_sercomm_tx_queue_depth;
+osmo_serial_clear_custom_baudrate;
+osmo_serial_init;
+osmo_serial_set_baudrate;
+osmo_serial_set_custom_baudrate;
+osmo_serial_speed_t;
+osmo_set_panic_handler;
+osmo_signal_dispatch;
+osmo_signalfd_setup;
+osmo_signal_register_handler;
+osmo_signal_talloc_ctx_init;
+osmo_signal_unregister_handler;
+osmo_sockaddr_cmp;
+osmo_sockaddr_from_octets;
+osmo_sockaddr_from_str_and_uint;
+osmo_sockaddr_in_to_str_and_uint;
+osmo_sockaddr_is_any;
+osmo_sockaddr_is_local;
+osmo_sockaddr_local_ip;
+osmo_sockaddr_netmask_to_prefixlen;
+osmo_sockaddr_ntop;
+osmo_sockaddr_port;
+osmo_sockaddr_set_port;
+osmo_sockaddr_str_cmp;
+osmo_sockaddr_str_from_32;
+osmo_sockaddr_str_from_32h;
+osmo_sockaddr_str_from_32n;
+osmo_sockaddr_str_from_in6_addr;
+osmo_sockaddr_str_from_in_addr;
+osmo_sockaddr_str_from_sockaddr;
+osmo_sockaddr_str_from_sockaddr_in;
+osmo_sockaddr_str_from_sockaddr_in6;
+osmo_sockaddr_str_from_osa;
+osmo_sockaddr_str_from_str;
+osmo_sockaddr_str_from_str2;
+osmo_sockaddr_str_is_nonzero;
+osmo_sockaddr_str_is_set;
+osmo_sockaddr_str_to_32;
+osmo_sockaddr_str_to_32h;
+osmo_sockaddr_str_to_32n;
+osmo_sockaddr_str_to_in6_addr;
+osmo_sockaddr_str_to_in_addr;
+osmo_sockaddr_str_to_sockaddr;
+osmo_sockaddr_str_to_sockaddr_in;
+osmo_sockaddr_str_to_sockaddr_in6;
+osmo_sockaddr_str_to_osa;
+osmo_sockaddr_to_octets;
+osmo_sockaddr_to_str;
+osmo_sockaddr_to_str_and_uint;
+osmo_sockaddr_to_str_buf;
+osmo_sockaddr_to_str_buf2;
+osmo_sockaddr_to_str_c;
+osmo_sock_get_ip_and_port;
+osmo_sock_get_local_ip;
+osmo_sock_get_local_ip_port;
+osmo_sock_get_name;
+osmo_sock_get_name2;
+osmo_sock_get_name2_c;
+osmo_sock_get_name_buf;
+osmo_sock_get_remote_ip;
+osmo_sock_get_remote_ip_port;
+osmo_sock_init;
+osmo_sock_init2;
+osmo_sock_init2_multiaddr;
+osmo_sock_init2_multiaddr2;
+osmo_sock_init2_ofd;
+osmo_sock_init_ofd;
+osmo_sock_init_osa;
+osmo_sock_init_osa_ofd;
+osmo_sock_init_sa;
+osmo_sock_local_ip;
+osmo_sock_mcast_all_set;
+osmo_sock_mcast_iface_set;
+osmo_sock_mcast_loop_set;
+osmo_sock_mcast_subscribe;
+osmo_sock_mcast_ttl_set;
+osmo_sock_multiaddr_add_local_addr;
+osmo_sock_multiaddr_del_local_addr;
+osmo_sock_multiaddr_get_ip_and_port;
+osmo_sock_multiaddr_get_name_buf;
+osmo_sock_sctp_get_peer_addr_info;
+osmo_sock_set_dscp;
+osmo_sock_set_priority;
+osmo_sock_unix_init;
+osmo_sock_unix_init_ofd;
+osmo_soft_uart_default_cfg;
+osmo_soft_uart_alloc;
+osmo_soft_uart_free;
+osmo_soft_uart_configure;
+osmo_soft_uart_get_name;
+osmo_soft_uart_set_name;
+osmo_soft_uart_set_rx;
+osmo_soft_uart_set_tx;
+osmo_soft_uart_rx_ubits;
+osmo_soft_uart_tx_ubits;
+osmo_soft_uart_get_status;
+osmo_soft_uart_set_status;
+osmo_soft_uart_set_status_line;
+osmo_soft_uart_flush_rx;
+osmo_stat_item_dec;
+osmo_stat_item_flush;
+osmo_stat_item_for_each_group;
+osmo_stat_item_for_each_item;
+osmo_stat_item_get_by_name;
+osmo_stat_item_get_desc;
+osmo_stat_item_get_group_by_name_idx;
+osmo_stat_item_get_group_by_name_idxname;
+osmo_stat_item_get_last;
+osmo_stat_item_group_alloc;
+osmo_stat_item_group_free;
+osmo_stat_item_group_get_item;
+osmo_stat_item_group_reset;
+osmo_stat_item_group_set_name;
+osmo_stat_item_inc;
+osmo_stat_item_init;
+osmo_stat_item_reset;
+osmo_stat_item_set;
+osmo_stats_config;
+osmo_stats_init;
+osmo_stats_report;
+osmo_stats_reporter_alloc;
+osmo_stats_reporter_create_log;
+osmo_stats_reporter_create_statsd;
+osmo_stats_reporter_disable;
+osmo_stats_reporter_enable;
+osmo_stats_reporter_find;
+osmo_stats_reporter_free;
+osmo_stats_reporter_list;
+osmo_stats_reporter_send;
+osmo_stats_reporter_send_buffer;
+osmo_stats_reporter_set_flush_period;
+osmo_stats_reporter_set_local_addr;
+osmo_stats_reporter_set_max_class;
+osmo_stats_reporter_set_mtu;
+osmo_stats_reporter_set_name_prefix;
+osmo_stats_reporter_set_remote_addr;
+osmo_stats_reporter_set_remote_port;
+osmo_stats_reporter_udp_close;
+osmo_stats_reporter_udp_open;
+osmo_stats_set_interval;
+osmo_stats_tcp_osmo_fd_register;
+osmo_stats_tcp_osmo_fd_unregister;
+osmo_stats_tcp_set_interval;
+osmo_stderr_target;
+osmo_str2bcd;
+osmo_str2lower;
+osmo_str2upper;
+osmo_strlcpy;
+osmo_strnchr;
+osmo_strrb_add;
+osmo_strrb_create;
+osmo_strrb_elements;
+osmo_strrb_get_nth;
+_osmo_strrb_is_bufindex_valid;
+osmo_strrb_is_empty;
+osmo_strbuf_drop_tail;
+osmo_strbuf_added_tail;
+osmo_str_startswith;
+osmo_str_to_int;
+osmo_str_to_int64;
+osmo_str_tolower;
+osmo_str_tolower_buf;
+osmo_str_tolower_c;
+osmo_str_toupper;
+osmo_str_toupper_buf;
+osmo_str_toupper_c;
+osmo_system_nowait;
+osmo_system_nowait2;
+osmo_t4_encode;
+osmo_talloc_replace_string_fmt;
+osmo_tcp_stats_config;
+_osmo_tdef_fsm_inst_state_chg;
+osmo_tdef_get;
+osmo_tdef_get_entry;
+osmo_tdef_get_state_timeout;
+osmo_tdef_range_str_buf;
+osmo_tdef_set;
+osmo_tdefs_reset;
+osmo_tdef_unit_names;
+osmo_tdef_val_in_range;
+osmo_time_cc_cleanup;
+osmo_time_cc_init;
+osmo_time_cc_set_flag;
+osmo_timer_add;
+osmo_timer_del;
+osmo_timerfd_disable;
+osmo_timerfd_schedule;
+osmo_timerfd_setup;
+osmo_timer_pending;
+osmo_timer_remaining;
+osmo_timers_check;
+osmo_timer_schedule;
+osmo_timer_setup;
+osmo_timers_nearest;
+osmo_timers_nearest_ms;
+osmo_timers_prepare;
+osmo_timers_update;
+osmo_tundev_alloc;
+osmo_tundev_close;
+osmo_tundev_free;
+osmo_tundev_get_dev_name;
+osmo_tundev_get_name;
+osmo_tundev_get_netdev;
+osmo_tundev_get_netns_name;
+osmo_tundev_get_priv_data;
+osmo_tundev_is_open;
+osmo_tundev_open;
+osmo_tundev_send;
+osmo_tundev_set_data_ind_cb;
+osmo_tundev_set_dev_name;
+osmo_tundev_set_netns_name;
+osmo_tundev_set_priv_data;
+osmo_ubit2pbit;
+osmo_ubit2pbit_ext;
+osmo_ubit2sbit;
+osmo_ubit_dump;
+osmo_ubit_dump_buf;
+osmo_use_count_by;
+osmo_use_count_find;
+osmo_use_count_free;
+_osmo_use_count_get_put;
+osmo_use_count_make_static_entries;
+osmo_use_count_name_buf;
+osmo_use_count_to_str_buf;
+osmo_use_count_to_str_c;
+osmo_use_count_total;
+osmo_vlogp;
+osmo_wqueue_bfd_cb;
+osmo_wqueue_clear;
+osmo_wqueue_enqueue;
+osmo_wqueue_enqueue_quiet;
+osmo_wqueue_set_maxlen;
+osmo_wqueue_init;
+rate_ctr_add;
+rate_ctr_difference;
+rate_ctr_for_each_counter;
+rate_ctr_for_each_group;
+rate_ctr_get_by_name;
+rate_ctr_get_group_by_name_idx;
+rate_ctr_group_alloc;
+rate_ctr_group_free;
+rate_ctr_group_get_ctr;
+rate_ctr_group_reset;
+rate_ctr_group_set_name;
+rate_ctr_init;
+rate_ctr_reset;
+rb_erase;
+rb_first;
+rb_insert_color;
+rb_last;
+rb_next;
+rb_prev;
+rb_replace_node;
+sercomm_drv_lock;
+sercomm_drv_unlock;
+tall_ctr_ctx; /* deprecated */
+tall_log_ctx;
+tall_msgb_ctx; /* deprecated */
+
+local: *;
+};
diff --git a/src/logging.c b/src/core/logging.c
index b030f8a6..e1721241 100644
--- a/src/logging.c
+++ b/src/core/logging.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup logging
@@ -29,18 +25,38 @@
*
* \file logging.c */
-#include "../config.h"
+#include "config.h"
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_SYSTEMTAP
+/* include the generated probes header and put markers in code */
+#include "probes.h"
+#define TRACE(probe) probe
+#define TRACE_ENABLED(probe) probe ## _ENABLED()
+#else
+/* Wrap the probe to allow it to be removed when no systemtap available */
+#define TRACE(probe)
+#define TRACE_ENABLED(probe) (0)
+#endif /* HAVE_SYSTEMTAP */
+
#include <time.h>
#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
@@ -48,9 +64,19 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
+#include <osmocom/core/thread.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/gsmtap_util.h>
#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
+/* maximum length of the log string of a single log event (typically line) */
+#define MAX_LOG_SIZE 4096
+
+/* maximum number of log statements we queue in file/stderr target write queue */
+#define LOG_WQUEUE_LEN 156
+
osmo_static_assert(_LOG_CTX_COUNT <= ARRAY_SIZE(((struct log_context*)NULL)->ctx),
enum_logging_ctx_items_fit_in_struct_log_context);
osmo_static_assert(_LOG_FLT_COUNT <= ARRAY_SIZE(((struct log_target*)NULL)->filter_data),
@@ -64,7 +90,83 @@ static struct log_context log_context;
void *tall_log_ctx = NULL;
LLIST_HEAD(osmo_log_target_list);
+static __thread long int logging_tid;
+
#if (!EMBEDDED)
+/*! One global copy that contains the union of log levels for all targets
+* for all categories, used for quick lock free checks of log targets. */
+static volatile uint8_t *log_level_lookup_cache;
+
+/*! Updates cache for all targets for all categies, caller must hold osmo_log_tgt_mutex. */
+static void log_cache_update_all(void)
+{
+ struct log_target *tgt;
+ uint8_t tmp_en[osmo_log_info->num_cat];
+ uint8_t tmp_level[osmo_log_info->num_cat];
+
+ if (!log_level_lookup_cache)
+ return;
+
+ memset(tmp_en, 0, osmo_log_info->num_cat);
+ memset(tmp_level, UINT8_MAX, osmo_log_info->num_cat);
+
+ /* values can also decrease.. */
+ llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
+ for (int i = 0; i < osmo_log_info->num_cat; i++) {
+ struct log_category *cat = &tgt->categories[i];
+ tmp_en[i] = OSMO_MAX(tmp_en[i], cat->enabled);
+ tmp_level[i] = OSMO_MIN(tmp_level[i], cat->loglevel);
+ tmp_level[i] = tgt->loglevel ? OSMO_MIN(tmp_level[i], tgt->loglevel) : tmp_level[i];
+ }
+ }
+
+ for (int i = 0; i < osmo_log_info->num_cat; i++)
+ log_level_lookup_cache[i] = tmp_en[i] ? tmp_level[i] : UINT8_MAX;
+}
+
+/*! Updates single cache entry, caller must hold osmo_log_tgt_mutex.
+ *
+ * \param[in] mapped_subsys plain category index (after mapping)
+ * \param[in] enabled log category enabled?
+ * \param[in] level log level
+ */
+void log_cache_update(int mapped_subsys, uint8_t enabled, uint8_t level)
+{
+ struct log_target *tgt;
+ struct log_category tmp = { UINT8_MAX, 0 };
+
+ if (!log_level_lookup_cache)
+ return;
+
+ /* values can also decrease.. */
+ llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
+ struct log_category *cat = &tgt->categories[mapped_subsys];
+ tmp.enabled = OSMO_MAX(tmp.enabled, cat->enabled);
+ tmp.loglevel = OSMO_MIN(tmp.loglevel, cat->loglevel);
+ tmp.loglevel = tgt->loglevel ? OSMO_MIN(tmp.loglevel, tgt->loglevel) : tmp.loglevel;
+ }
+ tmp.enabled = OSMO_MAX(tmp.enabled, enabled);
+ tmp.loglevel = OSMO_MIN(tmp.loglevel, level);
+
+ log_level_lookup_cache[mapped_subsys] = tmp.enabled ? tmp.loglevel : UINT8_MAX;
+}
+
+/*! Queries log level cache.
+ *
+ * \param[in] mapped_subsys plain category index (after mapping)
+ * \param[in] level log level
+ * \returns true if logging should happen for at least one log target
+*/
+static bool log_cache_check(int mapped_subsys, int level)
+{
+ if (!log_level_lookup_cache) {
+ /* log-cache is not enabled, so we simply behave like we did before the cache */
+ return true;
+ }
+
+ return (level < log_level_lookup_cache[mapped_subsys]) ? false : true;
+}
+
/*! This mutex must be held while using osmo_log_target_list or any of its
log_targets in a multithread program. Prevents race conditions between threads
like producing unordered timestamps or VTY deleting a target while another
@@ -123,6 +225,7 @@ const struct value_string loglevel_strs[] = {
{ 0, NULL },
};
+/* 256 color palette see https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit */
#define INT2IDX(x) (-1*(x)-1)
static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
[INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
@@ -136,94 +239,159 @@ static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
.description = "LAPD in libosmogsm",
.loglevel = LOGL_NOTICE,
.enabled = 1,
+ .color = "\033[38;5;12m",
},
[INT2IDX(DLINP)] = {
.name = "DLINP",
.description = "A-bis Intput Subsystem",
.loglevel = LOGL_NOTICE,
.enabled = 1,
+ .color = "\033[38;5;23m",
},
[INT2IDX(DLMUX)] = {
.name = "DLMUX",
.description = "A-bis B-Subchannel TRAU Frame Multiplex",
.loglevel = LOGL_NOTICE,
.enabled = 1,
+ .color = "\033[38;5;25m",
},
[INT2IDX(DLMI)] = {
.name = "DLMI",
.description = "A-bis Input Driver for Signalling",
.enabled = 0, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;27m",
},
[INT2IDX(DLMIB)] = {
.name = "DLMIB",
.description = "A-bis Input Driver for B-Channels (voice)",
.enabled = 0, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;29m",
},
[INT2IDX(DLSMS)] = {
.name = "DLSMS",
.description = "Layer3 Short Message Service (SMS)",
.enabled = 1, .loglevel = LOGL_NOTICE,
- .color = "\033[1;38m",
+ .color = "\033[38;5;31m",
},
[INT2IDX(DLCTRL)] = {
.name = "DLCTRL",
.description = "Control Interface",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;33m",
},
[INT2IDX(DLGTP)] = {
.name = "DLGTP",
.description = "GPRS GTP library",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;35m",
},
[INT2IDX(DLSTATS)] = {
.name = "DLSTATS",
.description = "Statistics messages and logging",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;37m",
},
[INT2IDX(DLGSUP)] = {
.name = "DLGSUP",
.description = "Generic Subscriber Update Protocol",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;39m",
},
[INT2IDX(DLOAP)] = {
.name = "DLOAP",
.description = "Osmocom Authentication Protocol",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;41m",
},
[INT2IDX(DLSS7)] = {
.name = "DLSS7",
.description = "libosmo-sigtran Signalling System 7",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;43m",
},
[INT2IDX(DLSCCP)] = {
.name = "DLSCCP",
.description = "libosmo-sigtran SCCP Implementation",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;45m",
},
[INT2IDX(DLSUA)] = {
.name = "DLSUA",
.description = "libosmo-sigtran SCCP User Adaptation",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;47m",
},
[INT2IDX(DLM3UA)] = {
.name = "DLM3UA",
.description = "libosmo-sigtran MTP3 User Adaptation",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;49m",
},
[INT2IDX(DLMGCP)] = {
.name = "DLMGCP",
.description = "libosmo-mgcp Media Gateway Control Protocol",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;51m",
},
[INT2IDX(DLJIBUF)] = {
.name = "DLJIBUF",
.description = "libosmo-netif Jitter Buffer",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;53m",
},
[INT2IDX(DLRSPRO)] = {
.name = "DLRSPRO",
.description = "Remote SIM protocol",
.enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;55m",
+ },
+ [INT2IDX(DLNS)] = {
+ .name = "DLNS",
+ .description = "GPRS NS layer",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;57m",
+ },
+ [INT2IDX(DLBSSGP)] = {
+ .name = "DLBSSGP",
+ .description = "GPRS BSSGP layer",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;59m",
+ },
+ [INT2IDX(DLNSDATA)] = {
+ .name = "DLNSDATA",
+ .description = "GPRS NS layer data PDU",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;61m",
+ },
+ [INT2IDX(DLNSSIGNAL)] = {
+ .name = "DLNSSIGNAL",
+ .description = "GPRS NS layer signal PDU",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;63m",
+ },
+ [INT2IDX(DLIUUP)] = {
+ .name = "DLIUUP",
+ .description = "Iu UP layer",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;65m",
+ },
+ [INT2IDX(DLPFCP)] = {
+ .name = "DLPFCP",
+ .description = "libosmo-pfcp Packet Forwarding Control Protocol",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;51m",
+ },
+ [INT2IDX(DLCSN1)] = {
+ .name = "DLCSN1",
+ .description = "libosmo-csn1 Concrete Syntax Notation 1 codec",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;11m",
+ },
+ [INT2IDX(DLIO)] = {
+ .name = "DLIO",
+ .description = "libosmocore IO Subsystem",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ .color = "\033[38;5;67m",
},
};
@@ -332,6 +500,10 @@ void log_parse_category_mask(struct log_target* target, const char *_mask)
}
} while ((category_token = strtok(NULL, ":")));
+#if !defined(EMBEDDED)
+ log_cache_update_all();
+#endif
+
free(mask);
}
@@ -344,11 +516,11 @@ static const char* color(int subsys)
}
static const struct value_string level_colors[] = {
- { LOGL_DEBUG, "\033[1;34m" },
- { LOGL_INFO, "\033[1;32m" },
- { LOGL_NOTICE, "\033[1;33m" },
- { LOGL_ERROR, "\033[1;31m" },
- { LOGL_FATAL, "\033[1;31m" },
+ { LOGL_DEBUG, OSMO_LOGCOLOR_BLUE },
+ { LOGL_INFO, OSMO_LOGCOLOR_GREEN },
+ { LOGL_NOTICE, OSMO_LOGCOLOR_YELLOW },
+ { LOGL_ERROR, OSMO_LOGCOLOR_RED },
+ { LOGL_FATAL, OSMO_LOGCOLOR_RED },
{ 0, NULL }
};
@@ -376,23 +548,34 @@ static const char *const_basename(const char *path)
return bn + 1;
}
-static void _output(struct log_target *target, unsigned int subsys,
- unsigned int level, const char *file, int line, int cont,
- const char *format, va_list ap)
+/*! main output formatting function for log lines.
+ * \param[out] buf caller-allocated output buffer for the generated string
+ * \param[in] buf_len number of bytes available in buf
+ * \param[in] target log target for which the string is to be formatted
+ * \param[in] subsys Log sub-system number
+ * \param[in] level Log level
+ * \param[in] file name of source code file generating the log
+ * \param[in] line line source code line number within 'file' generating the log
+ * \param[in] cont is this a continuation (true) or not (false)
+ * \param[in] format format string
+ * \param[in] ap variable argument list for format
+ * \returns number of bytes written to out */
+static int _output_buf(char *buf, int buf_len, struct log_target *target, unsigned int subsys,
+ unsigned int level, const char *file, int line, int cont,
+ const char *format, va_list ap)
{
- char buf[4096];
- int ret, len = 0, offset = 0, rem = sizeof(buf);
+ int ret;
const char *c_subsys = NULL;
+ struct osmo_strbuf sb = { .buf = buf, .pos = buf, .len = buf_len };
+
+ /* safety net in case of encountering errors and returning nothing */
+ buf[0] = '\0';
/* are we using color */
if (target->use_color) {
c_subsys = color(subsys);
- if (c_subsys) {
- ret = snprintf(buf + offset, rem, "%s", c_subsys);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
- }
+ if (c_subsys)
+ OSMO_STRBUF_PRINTF(sb, "%s", c_subsys);
}
if (!cont) {
if (target->print_ext_timestamp) {
@@ -401,13 +584,10 @@ static void _output(struct log_target *target, unsigned int subsys,
struct timeval tv;
osmo_gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &tm);
- ret = snprintf(buf + offset, rem, "%04d%02d%02d%02d%02d%02d%03d ",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- (int)(tv.tv_usec / 1000));
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_PRINTF(sb, "%04d%02d%02d%02d%02d%02d%03d ",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ (int)(tv.tv_usec / 1000));
#endif
} else if (target->print_timestamp) {
time_t tm;
@@ -415,100 +595,95 @@ static void _output(struct log_target *target, unsigned int subsys,
goto err;
/* Get human-readable representation of time.
man ctime: we need at least 26 bytes in buf */
- if (rem < 26 || !ctime_r(&tm, buf + offset))
+ if (OSMO_STRBUF_REMAIN(sb) < 26 || !ctime_r(&tm, sb.pos))
goto err;
- ret = strlen(buf + offset);
+ ret = strnlen(sb.pos, 26);
if (ret <= 0)
goto err;
+ OSMO_STRBUF_ADDED_TAIL(sb, ret);
/* Get rid of useless final '\n' added by ctime_r. We want a space instead. */
- buf[offset + ret - 1] = ' ';
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
- }
- if (target->print_category) {
- ret = snprintf(buf + offset, rem, "%s%s%s%s ",
- target->use_color ? level_color(level) : "",
- log_category_name(subsys),
- target->use_color ? "\033[0;m" : "",
- c_subsys ? c_subsys : "");
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
- }
- if (target->print_level) {
- ret = snprintf(buf + offset, rem, "%s%s%s%s ",
- target->use_color ? level_color(level) : "",
- log_level_str(level),
- target->use_color ? "\033[0;m" : "",
- c_subsys ? c_subsys : "");
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ OSMO_STRBUF_PRINTF(sb, " ");
}
- if (target->print_category_hex) {
- ret = snprintf(buf + offset, rem, "<%4.4x> ", subsys);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ if (target->print_tid) {
+ if (logging_tid == 0)
+ logging_tid = (long int)osmo_gettid();
+ OSMO_STRBUF_PRINTF(sb, "%ld ", logging_tid);
}
+ if (target->print_category)
+ OSMO_STRBUF_PRINTF(sb, "%s%s%s%s ",
+ target->use_color ? level_color(level) : "",
+ log_category_name(subsys),
+ target->use_color ? OSMO_LOGCOLOR_END : "",
+ c_subsys ? c_subsys : "");
+ if (target->print_level)
+ OSMO_STRBUF_PRINTF(sb, "%s%s%s%s ",
+ target->use_color ? level_color(level) : "",
+ log_level_str(level),
+ target->use_color ? OSMO_LOGCOLOR_END : "",
+ c_subsys ? c_subsys : "");
+ if (target->print_category_hex)
+ OSMO_STRBUF_PRINTF(sb, "<%4.4x> ", subsys);
if (target->print_filename_pos == LOG_FILENAME_POS_HEADER_END) {
switch (target->print_filename2) {
case LOG_FILENAME_NONE:
break;
case LOG_FILENAME_PATH:
- ret = snprintf(buf + offset, rem, "%s:%d ", file, line);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_PRINTF(sb, "%s:%d ", file, line);
break;
case LOG_FILENAME_BASENAME:
- ret = snprintf(buf + offset, rem, "%s:%d ", const_basename(file), line);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_PRINTF(sb, "%s:%d ", const_basename(file), line);
break;
}
}
}
- ret = vsnprintf(buf + offset, rem, format, ap);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_APPEND(sb, vsnprintf, format, ap);
/* For LOG_FILENAME_POS_LAST, print the source file info only when the caller ended the log
* message in '\n'. If so, nip the last '\n' away, insert the source file info and re-append an
* '\n'. All this to allow LOGP("start..."); LOGPC("...end\n") constructs. */
if (target->print_filename_pos == LOG_FILENAME_POS_LINE_END
- && offset > 0 && buf[offset-1] == '\n') {
+ && sb.pos > sb.buf && *(sb.pos - 1) == '\n') {
switch (target->print_filename2) {
case LOG_FILENAME_NONE:
break;
case LOG_FILENAME_PATH:
- offset --;
- ret = snprintf(buf + offset, rem, " (%s:%d)\n", file, line);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ OSMO_STRBUF_PRINTF(sb, " (%s:%d)\n", file, line);
break;
case LOG_FILENAME_BASENAME:
- offset --;
- ret = snprintf(buf + offset, rem, " (%s:%d)\n", const_basename(file), line);
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ OSMO_STRBUF_PRINTF(sb, " (%s:%d)\n", const_basename(file), line);
break;
}
}
- if (target->use_color) {
- ret = snprintf(buf + offset, rem, "\033[0;m");
- if (ret < 0)
- goto err;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ if (target->use_color && c_subsys) {
+ /* Ensure the last color escape is sent before the newline
+ * (to not clobber journald, which works on single-lines only) */
+ if (sb.pos > sb.buf && *(sb.pos - 1) == '\n') {
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ OSMO_STRBUF_PRINTF(sb, OSMO_LOGCOLOR_END "\n");
+ } else {
+ OSMO_STRBUF_PRINTF(sb, OSMO_LOGCOLOR_END);
+ }
}
err:
- buf[sizeof(buf)-1] = '\0';
- target->output(target, level, buf);
+ return OSMO_STRBUF_CHAR_COUNT(sb);
+}
+
+/* Format the log line for given target; use a stack buffer and call target->output */
+static void _output(struct log_target *target, unsigned int subsys,
+ unsigned int level, const char *file, int line, int cont,
+ const char *format, va_list ap)
+{
+ char buf[MAX_LOG_SIZE];
+ int rc;
+
+ rc = _output_buf(buf, sizeof(buf), target, subsys, level, file, line, cont, format, ap);
+ if (rc > 0)
+ target->output(target, level, buf);
}
/* Catch internal logging category indexes as well as out-of-bounds indexes.
@@ -583,6 +758,11 @@ void osmo_vlogp(int subsys, int level, const char *file, int line,
subsys = map_subsys(subsys);
+#if !defined(EMBEDDED)
+ if (!log_cache_check(subsys, level))
+ return;
+#endif
+
log_tgt_mutex_lock();
llist_for_each_entry(tar, &osmo_log_target_list, entry) {
@@ -632,9 +812,23 @@ void logp2(int subsys, unsigned int level, const char *file, int line, int cont,
{
va_list ap;
+ TRACE(LIBOSMOCORE_LOG_START());
va_start(ap, format);
osmo_vlogp(subsys, level, file, line, cont, format, ap);
va_end(ap);
+ TRACE(LIBOSMOCORE_LOG_DONE());
+}
+
+/* This logging function is used as a fallback when the logging framework is
+ * not is not properly initialized. */
+void logp_stub(const char *file, int line, int cont, const char *format, ...)
+{
+ va_list ap;
+ if (!cont)
+ fprintf(stderr, "%s:%d ", file, line);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
}
/*! Register a new log target with the logging core
@@ -643,6 +837,9 @@ void logp2(int subsys, unsigned int level, const char *file, int line, int cont,
void log_add_target(struct log_target *target)
{
llist_add_tail(&target->entry, &osmo_log_target_list);
+#if (!EMBEDDED)
+ log_cache_update_all();
+#endif
}
/*! Unregister a log target from the logging core
@@ -651,6 +848,9 @@ void log_add_target(struct log_target *target)
void log_del_target(struct log_target *target)
{
llist_del(&target->entry);
+#if (!EMBEDDED)
+ log_cache_update_all();
+#endif
}
/*! Reset (clear) the logging context */
@@ -727,6 +927,15 @@ void log_set_print_extended_timestamp(struct log_target *target, int print_times
target->print_ext_timestamp = print_timestamp;
}
+/*! Enable or disable printing of timestamps while logging
+ * \param[in] target Log target to be affected
+ * \param[in] print_tid Enable (1) or disable (0) Thread ID logging
+ */
+void log_set_print_tid(struct log_target *target, int print_tid)
+{
+ target->print_tid = print_tid;
+}
+
/*! Use log_set_print_filename2() instead.
* Call log_set_print_filename2() with LOG_FILENAME_PATH or LOG_FILENAME_NONE, *as well as* call
* log_set_print_category_hex() with the argument passed to this function. This is to mirror legacy
@@ -802,6 +1011,9 @@ void log_set_print_level(struct log_target *target, int print_level)
void log_set_log_level(struct log_target *target, int log_level)
{
target->loglevel = log_level;
+#if !defined(EMBEDDED)
+ log_cache_update_all();
+#endif
}
/*! Set a category filter on a given log target
@@ -818,15 +1030,73 @@ void log_set_category_filter(struct log_target *target, int category,
category = map_subsys(category);
target->categories[category].enabled = !!enable;
target->categories[category].loglevel = level;
+
+#if !defined(EMBEDDED)
+ log_cache_update(category, !!enable, level);
+#endif
}
#if (!EMBEDDED)
-static void _file_output(struct log_target *target, unsigned int level,
+/* write-queue tells us we should write another msgb (log line) to the output fd */
+static int _file_wq_write_cb(struct osmo_fd *ofd, struct msgb *msg)
+{
+ int rc;
+
+ rc = write(ofd->fd, msgb_data(msg), msgb_length(msg));
+ if (rc < 0)
+ return rc;
+ if (rc != msgb_length(msg)) {
+ /* pull the number of bytes we have already written */
+ msgb_pull(msg, rc);
+ /* ask write_queue to re-insert the msgb at the head of the queue */
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+/* output via buffered, blocking stdio streams */
+static void _file_output_stream(struct log_target *target, unsigned int level,
const char *log)
{
- fprintf(target->tgt_file.out, "%s", log);
+ OSMO_ASSERT(target->tgt_file.out);
+ fputs(log, target->tgt_file.out);
fflush(target->tgt_file.out);
}
+
+/* output via non-blocking write_queue, doing internal buffering */
+static void _file_raw_output(struct log_target *target, int subsys, unsigned int level, const char *file,
+ int line, int cont, const char *format, va_list ap)
+{
+ struct msgb *msg;
+ int rc;
+
+ OSMO_ASSERT(target->tgt_file.wqueue);
+ msg = msgb_alloc_c(target->tgt_file.wqueue, MAX_LOG_SIZE, "log_file_msg");
+ if (!msg)
+ return;
+
+ /* we simply enqueue the log message to a write queue here, to avoid any blocking
+ * writes on the output file. The write queue will tell us once the file is writable
+ * and call _file_wq_write_cb() */
+ rc = _output_buf((char *)msgb_data(msg), msgb_tailroom(msg), target, subsys, level, file, line, cont, format, ap);
+ msgb_put(msg, rc);
+
+ /* attempt a synchronous, non-blocking write, if the write queue is empty */
+ if (target->tgt_file.wqueue->current_length == 0) {
+ rc = _file_wq_write_cb(&target->tgt_file.wqueue->bfd, msg);
+ if (rc == 0) {
+ /* the write was complete, we can exit early */
+ msgb_free(msg);
+ return;
+ }
+ }
+ /* if we reach here, either we already had elements in the write_queue, or the synchronous write
+ * failed: enqueue the message to the write_queue (backlog) */
+ if (osmo_wqueue_enqueue_quiet(target->tgt_file.wqueue, msg) < 0) {
+ msgb_free(msg);
+ /* TODO: increment some counter so we can see that messages were dropped */
+ }
+}
#endif
/*! Create a new log target skeleton
@@ -866,11 +1136,21 @@ struct log_target *log_target_create(void)
/* global settings */
target->use_color = 1;
target->print_timestamp = 0;
+ target->print_tid = 0;
target->print_filename2 = LOG_FILENAME_PATH;
target->print_category_hex = true;
/* global log level */
target->loglevel = 0;
+
+#if !defined(EMBEDDED)
+ /* update cache */
+ for (i = 0; i < osmo_log_info->num_cat; i++) {
+ const struct log_info_cat *c = &osmo_log_info->cat[i];
+ log_cache_update(i, c->enabled, c->loglevel);
+ }
+#endif
+
return target;
}
@@ -888,7 +1168,7 @@ struct log_target *log_target_create_stderr(void)
target->type = LOG_TGT_TYPE_STDERR;
target->tgt_file.out = stderr;
- target->output = _file_output;
+ target->output = _file_output_stream;
return target;
#else
return NULL;
@@ -896,11 +1176,11 @@ struct log_target *log_target_create_stderr(void)
}
#if (!EMBEDDED)
-/*! Create a new file-based log target
+/*! Create a new file-based log target using buffered, blocking stream output
* \param[in] fname File name of the new log file
* \returns Log target in case of success, NULL otherwise
*/
-struct log_target *log_target_create_file(const char *fname)
+struct log_target *log_target_create_file_stream(const char *fname)
{
struct log_target *target;
@@ -910,11 +1190,169 @@ struct log_target *log_target_create_file(const char *fname)
target->type = LOG_TGT_TYPE_FILE;
target->tgt_file.out = fopen(fname, "a");
- if (!target->tgt_file.out)
+ if (!target->tgt_file.out) {
+ log_target_destroy(target);
return NULL;
+ }
+ target->output = _file_output_stream;
+ target->tgt_file.fname = talloc_strdup(target, fname);
+
+ return target;
+}
- target->output = _file_output;
+/*! switch from non-blocking/write-queue to blocking + buffered stream output
+ * \param[in] target log target which we should switch
+ * \return 0 on success; 1 if already switched before; negative on error
+ * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
+ */
+int log_target_file_switch_to_stream(struct log_target *target)
+{
+ struct osmo_wqueue *wq;
+ if (!target)
+ return -ENODEV;
+
+ if (target->tgt_file.out) {
+ /* target has already been switched over */
+ return 1;
+ }
+
+ wq = target->tgt_file.wqueue;
+ OSMO_ASSERT(wq);
+
+ /* re-open output as stream */
+ if (target->type == LOG_TGT_TYPE_STDERR)
+ target->tgt_file.out = stderr;
+ else
+ target->tgt_file.out = fopen(target->tgt_file.fname, "a");
+ if (!target->tgt_file.out) {
+ return -EIO;
+ }
+
+ /* synchronously write anything left in the queue */
+ while (!llist_empty(&wq->msg_queue)) {
+ struct msgb *msg = msgb_dequeue(&wq->msg_queue);
+ fwrite(msgb_data(msg), msgb_length(msg), 1, target->tgt_file.out);
+ msgb_free(msg);
+ }
+
+ /* now that everything succeeded, we can finally close the old output fd */
+ if (target->type == LOG_TGT_TYPE_FILE) {
+ osmo_fd_unregister(&wq->bfd);
+ close(wq->bfd.fd);
+ wq->bfd.fd = -1;
+ }
+
+ /* release the queue itself */
+ talloc_free(wq);
+ target->tgt_file.wqueue = NULL;
+ target->output = _file_output_stream;
+ target->raw_output = NULL;
+
+ return 0;
+}
+
+/*! switch from blocking + buffered file output to non-blocking write-queue based output.
+ * \param[in] target log target which we should switch
+ * \return 0 on success; 1 if already switched before; negative on error
+ * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
+ */
+int log_target_file_switch_to_wqueue(struct log_target *target)
+{
+ struct osmo_wqueue *wq;
+ int rc;
+
+ if (!target)
+ return -ENODEV;
+
+ if (!target->tgt_file.out) {
+ /* target has already been switched over */
+ return 1;
+ }
+
+ /* we create a ~640kB sized talloc pool within the write-queue to ensure individual
+ * log lines (stored as msgbs) will not put result in malloc() calls, and also to
+ * reduce the OOM probability within logging, as the pool is already allocated */
+ wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN,
+ LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE));
+ if (!wq)
+ return -ENOMEM;
+ osmo_wqueue_init(wq, LOG_WQUEUE_LEN);
+
+ fflush(target->tgt_file.out);
+ if (target->type == LOG_TGT_TYPE_FILE) {
+ rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
+ if (rc < 0) {
+ talloc_free(wq);
+ return -errno;
+ }
+ } else {
+ rc = STDERR_FILENO;
+ }
+ wq->bfd.fd = rc;
+ wq->bfd.when = OSMO_FD_WRITE;
+ wq->write_cb = _file_wq_write_cb;
+
+ rc = osmo_fd_register(&wq->bfd);
+ if (rc < 0) {
+ talloc_free(wq);
+ return -EIO;
+ }
+ target->tgt_file.wqueue = wq;
+ target->raw_output = _file_raw_output;
+ target->output = NULL;
+
+ /* now that everything succeeded, we can finally close the old output stream */
+ if (target->type == LOG_TGT_TYPE_FILE)
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = NULL;
+
+ return 0;
+}
+
+/*! Create a new file-based log target using non-blocking write_queue
+ * \param[in] fname File name of the new log file
+ * \returns Log target in case of success, NULL otherwise
+ */
+struct log_target *log_target_create_file(const char *fname)
+{
+ struct log_target *target;
+ struct osmo_wqueue *wq;
+ int rc;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->type = LOG_TGT_TYPE_FILE;
+ /* we create a ~640kB sized talloc pool within the write-queue to ensure individual
+ * log lines (stored as msgbs) will not put result in malloc() calls, and also to
+ * reduce the OOM probability within logging, as the pool is already allocated */
+ wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN,
+ LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE));
+ if (!wq) {
+ log_target_destroy(target);
+ return NULL;
+ }
+ osmo_wqueue_init(wq, LOG_WQUEUE_LEN);
+ wq->bfd.fd = open(fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
+ if (wq->bfd.fd < 0) {
+ talloc_free(wq);
+ log_target_destroy(target);
+ return NULL;
+ }
+ wq->bfd.when = OSMO_FD_WRITE;
+ wq->write_cb = _file_wq_write_cb;
+
+ rc = osmo_fd_register(&wq->bfd);
+ if (rc < 0) {
+ talloc_free(wq);
+ log_target_destroy(target);
+ return NULL;
+ }
+
+ target->tgt_file.wqueue = wq;
+ target->raw_output = _file_raw_output;
target->tgt_file.fname = talloc_strdup(target, fname);
return target;
@@ -927,7 +1365,7 @@ struct log_target *log_target_create_file(const char *fname)
* \returns Log target (if found), NULL otherwise
* Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
*/
-struct log_target *log_target_find(int type, const char *fname)
+struct log_target *log_target_find(enum log_target_type type, const char *fname)
{
struct log_target *tgt;
@@ -954,21 +1392,45 @@ struct log_target *log_target_find(int type, const char *fname)
* \param[in] target log target to unregister, close and delete */
void log_target_destroy(struct log_target *target)
{
-
/* just in case, to make sure we don't have any references */
log_del_target(target);
#if (!EMBEDDED)
- if (target->output == &_file_output) {
-/* since C89/C99 says stderr is a macro, we can safely do this! */
-#ifdef stderr
- /* don't close stderr */
- if (target->tgt_file.out != stderr)
-#endif
- {
- fclose(target->tgt_file.out);
+ struct osmo_wqueue *wq;
+ switch (target->type) {
+ case LOG_TGT_TYPE_FILE:
+ case LOG_TGT_TYPE_STDERR:
+ if (target->tgt_file.out) {
+ if (target->type == LOG_TGT_TYPE_FILE)
+ fclose(target->tgt_file.out);
target->tgt_file.out = NULL;
}
+ wq = target->tgt_file.wqueue;
+ if (wq) {
+ if (wq->bfd.fd >= 0) {
+ osmo_fd_unregister(&wq->bfd);
+ if (target->type == LOG_TGT_TYPE_FILE)
+ close(wq->bfd.fd);
+ wq->bfd.fd = -1;
+ }
+ osmo_wqueue_clear(wq);
+ talloc_free(wq);
+ target->tgt_file.wqueue = NULL;
+ }
+ talloc_free((void *)target->tgt_file.fname);
+ target->tgt_file.fname = NULL;
+ break;
+ case LOG_TGT_TYPE_GSMTAP:
+ gsmtap_source_free(target->tgt_gsmtap.gsmtap_inst);
+ break;
+#ifdef HAVE_SYSLOG_H
+ case LOG_TGT_TYPE_SYSLOG:
+ closelog();
+ break;
+#endif /* HAVE_SYSLOG_H */
+ default:
+ /* make GCC happy */
+ break;
}
#endif
@@ -980,13 +1442,33 @@ void log_target_destroy(struct log_target *target)
* \returns 0 in case of success; negative otherwise */
int log_target_file_reopen(struct log_target *target)
{
- fclose(target->tgt_file.out);
-
- target->tgt_file.out = fopen(target->tgt_file.fname, "a");
- if (!target->tgt_file.out)
- return -errno;
+ struct osmo_wqueue *wq;
+ int rc;
+
+ OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE || target->type == LOG_TGT_TYPE_STDERR);
+ OSMO_ASSERT(target->tgt_file.out || target->tgt_file.wqueue);
+
+ if (target->tgt_file.out) {
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = fopen(target->tgt_file.fname, "a");
+ if (!target->tgt_file.out)
+ return -errno;
+ } else {
+ wq = target->tgt_file.wqueue;
+ if (wq->bfd.fd >= 0) {
+ osmo_fd_unregister(&wq->bfd);
+ close(wq->bfd.fd);
+ wq->bfd.fd = -1;
+ }
- /* we assume target->output already to be set */
+ rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
+ if (rc < 0)
+ return -errno;
+ wq->bfd.fd = rc;
+ rc = osmo_fd_register(&wq->bfd);
+ if (rc < 0)
+ return rc;
+ }
return 0;
}
@@ -1016,6 +1498,31 @@ int log_targets_reopen(void)
return rc;
}
+/*! Enable the log level lookup cache to bypass string formatting and other code for log statements which are
+ * not actually enabled/needed by any existing log target.
+ * \retruns 0 in case of success, negative -errno in case of error. */
+int log_cache_enable(void)
+{
+#if !defined(EMBEDDED)
+ if (log_level_lookup_cache)
+ return -EEXIST;
+
+ log_level_lookup_cache = talloc_zero_array(osmo_log_info, uint8_t, osmo_log_info->num_cat);
+ if (!log_level_lookup_cache)
+ return -ENOMEM;
+
+ /* copy everything for level lookup cache */
+ log_tgt_mutex_lock();
+ log_cache_update_all();
+ log_tgt_mutex_unlock();
+
+ return 0;
+#else
+ return -ENOTSUP;
+#endif
+}
+
+
/*! Initialize the Osmocom logging core
* \param[in] inf Information regarding logging categories, could be NULL
* \param[in] ctx talloc context for logging allocations
@@ -1026,6 +1533,10 @@ int log_targets_reopen(void)
int log_init(const struct log_info *inf, void *ctx)
{
int i;
+ struct log_info_cat *cat_ptr;
+
+ /* Ensure that log_init is not called multiple times */
+ OSMO_ASSERT(tall_log_ctx == NULL)
tall_log_ctx = talloc_named_const(ctx, 1, "logging");
if (!tall_log_ctx)
@@ -1043,29 +1554,36 @@ int log_init(const struct log_info *inf, void *ctx)
osmo_log_info->num_cat += inf->num_cat;
}
- osmo_log_info->cat = talloc_zero_array(osmo_log_info,
- struct log_info_cat,
- osmo_log_info->num_cat);
- if (!osmo_log_info->cat) {
+ cat_ptr = talloc_zero_array(osmo_log_info, struct log_info_cat,
+ osmo_log_info->num_cat);
+ if (!cat_ptr) {
talloc_free(osmo_log_info);
osmo_log_info = NULL;
return -ENOMEM;
}
- if (inf) { /* copy over the user part */
+ /* copy over the user part and sanitize loglevel */
+ if (inf) {
for (i = 0; i < inf->num_cat; i++) {
- memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
- &inf->cat[i], sizeof(struct log_info_cat));
+ memcpy(&cat_ptr[i], &inf->cat[i],
+ sizeof(struct log_info_cat));
+
+ /* Make sure that the loglevel is set to NOTICE in case
+ * no loglevel has been preset. */
+ if (!cat_ptr[i].loglevel) {
+ cat_ptr[i].loglevel = LOGL_NOTICE;
+ }
}
}
/* copy over the library part */
for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
unsigned int cn = osmo_log_info->num_cat_user + i;
- memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
- &internal_cat[i], sizeof(struct log_info_cat));
+ memcpy(&cat_ptr[cn], &internal_cat[i], sizeof(struct log_info_cat));
}
+ osmo_log_info->cat = cat_ptr;
+
return 0;
}
@@ -1098,6 +1616,11 @@ int log_check_level(int subsys, unsigned int level)
subsys = map_subsys(subsys);
+#if !defined(EMBEDDED)
+ if (!log_cache_check(subsys, level))
+ return 0;
+#endif
+
/* TODO: The following could/should be cached (update on config) */
log_tgt_mutex_lock();
diff --git a/src/logging_gsmtap.c b/src/core/logging_gsmtap.c
index bd642715..7775c27d 100644
--- a/src/logging_gsmtap.c
+++ b/src/core/logging_gsmtap.c
@@ -21,23 +21,20 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup logging
* @{
* \file logging_gsmtap.c */
-#include "../config.h"
+#include "config.h"
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
+#include <unistd.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
@@ -50,9 +47,12 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/byteswap.h>
+#include <osmocom/core/thread.h>
#define GSMTAP_LOG_MAX_SIZE 4096
+static __thread uint32_t logging_gsmtap_tid;
+
static void _gsmtap_raw_output(struct log_target *target, int subsys,
unsigned int level, const char *file,
int line, int cont, const char *format,
@@ -65,6 +65,7 @@ static void _gsmtap_raw_output(struct log_target *target, int subsys,
struct timeval tv;
int rc;
const char *file_basename;
+ unsigned int _level;
/* get timestamp ASAP */
osmo_gettimeofday(&tv, NULL);
@@ -82,6 +83,9 @@ static void _gsmtap_raw_output(struct log_target *target, int subsys,
/* Logging header */
golh = (struct gsmtap_osmocore_log_hdr *) msgb_put(msg, sizeof(*golh));
OSMO_STRLCPY_ARRAY(golh->proc_name, target->tgt_gsmtap.ident);
+ if (logging_gsmtap_tid == 0)
+ osmo_store32be((uint32_t)osmo_gettid(), &logging_gsmtap_tid);
+ golh->pid = logging_gsmtap_tid;
if (subsys_name)
OSMO_STRLCPY_ARRAY(golh->subsys, subsys_name + 1);
else
@@ -111,7 +115,11 @@ static void _gsmtap_raw_output(struct log_target *target, int subsys,
}
msgb_put(msg, rc);
+ /* Ensure that any error occurring when sending the log message doesn't cause infinite recursion */
+ _level = target->loglevel;
+ target->loglevel = UINT8_MAX;
rc = gsmtap_sendmsg(target->tgt_gsmtap.gsmtap_inst, msg);
+ target->loglevel = _level;
if (rc)
msgb_free(msg);
}
diff --git a/src/logging_syslog.c b/src/core/logging_syslog.c
index f980689d..1153bdf4 100644
--- a/src/logging_syslog.c
+++ b/src/core/logging_syslog.c
@@ -16,17 +16,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup logging
* @{
* \file logging_syslog.c */
-#include "../config.h"
+#include "config.h"
#ifdef HAVE_SYSLOG_H
diff --git a/src/core/logging_systemd.c b/src/core/logging_systemd.c
new file mode 100644
index 00000000..2e86feb6
--- /dev/null
+++ b/src/core/logging_systemd.c
@@ -0,0 +1,117 @@
+/*
+ * (C) 2020 by Vadim Yanitskiy <axilirator@gmail.com>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*! \addtogroup logging
+ * @{
+ * \file logging_systemd.c */
+
+#include <stdio.h>
+#include <syslog.h>
+
+/* Do not use this file as location in sd_journal_print() */
+#define SD_JOURNAL_SUPPRESS_LOCATION
+
+#include <systemd/sd-journal.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+/* FIXME: copy-pasted from logging_syslog.c */
+static int logp2syslog_level(unsigned int level)
+{
+ if (level >= LOGL_FATAL)
+ return LOG_CRIT;
+ else if (level >= LOGL_ERROR)
+ return LOG_ERR;
+ else if (level >= LOGL_NOTICE)
+ return LOG_NOTICE;
+ else if (level >= LOGL_INFO)
+ return LOG_INFO;
+ else
+ return LOG_DEBUG;
+}
+
+static void _systemd_output(struct log_target *target,
+ unsigned int level, const char *log)
+{
+ /* systemd accepts the same level constants as syslog */
+ sd_journal_print(logp2syslog_level(level), "%s", log);
+}
+
+static void _systemd_raw_output(struct log_target *target, int subsys,
+ unsigned int level, const char *file,
+ int line, int cont, const char *format,
+ va_list ap)
+{
+ char buf[4096];
+ int rc;
+
+ rc = vsnprintf(buf, sizeof(buf), format, ap);
+ if (rc < 0) {
+ sd_journal_print(LOG_ERR, "vsnprintf() failed to render a message "
+ "originated from %s:%d (rc=%d)\n",
+ file, line, rc);
+ return;
+ }
+
+ sd_journal_send("CODE_FILE=%s, CODE_LINE=%d", file, line,
+ "PRIORITY=%d", logp2syslog_level(level),
+ "OSMO_SUBSYS=%s", log_category_name(subsys),
+ "OSMO_SUBSYS_HEX=%4.4x", subsys,
+ "MESSAGE=%s", buf,
+ NULL);
+}
+
+/*! Create a new logging target for systemd journal logging.
+ * \param[in] raw whether to offload rendering of the meta information
+ * (location, category) to systemd-journal.
+ * \returns Log target in case of success, NULL in case of error.
+ */
+struct log_target *log_target_create_systemd(bool raw)
+{
+ struct log_target *target;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->type = LOG_TGT_TYPE_SYSTEMD;
+ log_target_systemd_set_raw(target, raw);
+
+ return target;
+}
+
+/*! Change meta information handling of an existing logging target.
+ * \param[in] target logging target to be modified.
+ * \param[in] raw whether to offload rendering of the meta information
+ * (location, category) to systemd-journal.
+ */
+void log_target_systemd_set_raw(struct log_target *target, bool raw)
+{
+ target->sd_journal.raw = raw;
+ if (raw) {
+ target->raw_output = _systemd_raw_output;
+ target->output = NULL;
+ } else {
+ target->output = _systemd_output;
+ target->raw_output = NULL;
+ }
+}
+
+/* @} */
diff --git a/src/loggingrb.c b/src/core/loggingrb.c
index 4a80cc8a..2bf7b665 100644
--- a/src/loggingrb.c
+++ b/src/core/loggingrb.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup loggingrb
diff --git a/src/macaddr.c b/src/core/macaddr.c
index de9d07af..3b231fb8 100644
--- a/src/macaddr.c
+++ b/src/core/macaddr.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup utils
@@ -34,6 +30,7 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
+#include <errno.h>
/*! Parse a MAC address from human-readable notation
* This function parses an ethernet MAC address in the commonly-used
@@ -77,11 +74,11 @@ int osmo_macaddr_parse(uint8_t *out, const char *in)
*/
int osmo_get_macaddr(uint8_t *mac_out, const char *dev_name)
{
- int rc = -1;
struct ifaddrs *ifa, *ifaddr;
+ int rc = -ENODEV;
if (getifaddrs(&ifaddr) != 0)
- return -1;
+ return -errno;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
struct sockaddr_dl *sdl;
@@ -102,7 +99,7 @@ int osmo_get_macaddr(uint8_t *mac_out, const char *dev_name)
}
freeifaddrs(ifaddr);
- return 0;
+ return rc;
}
#else
diff --git a/src/core/mnl.c b/src/core/mnl.c
new file mode 100644
index 00000000..d148e1b3
--- /dev/null
+++ b/src/core/mnl.c
@@ -0,0 +1,111 @@
+/*! \file mnl.c
+ *
+ * This code integrates libmnl (minimal netlink library) into the osmocom select
+ * loop abstraction. It allows other osmocom libraries or application code to
+ * create netlink sockets and subscribe to netlink events via libmnl. The completion
+ * handler / callbacks are dispatched via libosmocore select loop handling.
+ */
+
+/*
+ * (C) 2020 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/mnl.h>
+
+#include <libmnl/libmnl.h>
+
+#include <errno.h>
+#include <string.h>
+
+/* osmo_fd call-back for when RTNL socket is readable */
+static int osmo_mnl_fd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ uint8_t buf[MNL_SOCKET_BUFFER_SIZE];
+ struct osmo_mnl *omnl = ofd->data;
+ int rc;
+
+ if (!(what & OSMO_FD_READ))
+ return 0;
+
+ rc = mnl_socket_recvfrom(omnl->mnls, buf, sizeof(buf));
+ if (rc <= 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error in mnl_socket_recvfrom(): %s\n",
+ strerror(errno));
+ return -EIO;
+ }
+
+ return mnl_cb_run(buf, rc, 0, 0, omnl->mnl_cb, omnl);
+}
+
+/*! create an osmocom-wrapped limnl netlink socket.
+ * \param[in] ctx talloc context from which to allocate
+ * \param[in] bus netlink socket bus ID (see NETLINK_* constants)
+ * \param[in] groups groups of messages to bind/subscribe to
+ * \param[in] mnl_cb callback function called for each incoming message
+ * \param[in] priv opaque private user data
+ * \returns newly-allocated osmo_mnl or NULL in case of error. */
+struct osmo_mnl *osmo_mnl_init(void *ctx, int bus, unsigned int groups, mnl_cb_t mnl_cb, void *priv)
+{
+ struct osmo_mnl *olm = talloc_zero(ctx, struct osmo_mnl);
+
+ if (!olm)
+ return NULL;
+
+ olm->priv = priv;
+ olm->mnl_cb = mnl_cb;
+ olm->mnls = mnl_socket_open(bus);
+ if (!olm->mnls) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error creating netlink socket for bus %d: %s\n",
+ bus, strerror(errno));
+ goto out_free;
+ }
+
+ if (mnl_socket_bind(olm->mnls, groups, MNL_SOCKET_AUTOPID) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error binding netlink socket for bus %d to groups 0x%x: %s\n",
+ bus, groups, strerror(errno));
+ goto out_close;
+ }
+
+ osmo_fd_setup(&olm->ofd, mnl_socket_get_fd(olm->mnls), OSMO_FD_READ, osmo_mnl_fd_cb, olm, 0);
+
+ if (osmo_fd_register(&olm->ofd)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Error registering netlinks socket\n");
+ goto out_close;
+ }
+
+ return olm;
+
+out_close:
+ mnl_socket_close(olm->mnls);
+out_free:
+ talloc_free(olm);
+ return NULL;
+}
+
+/*! destroy an existing osmocom-wrapped mnl netlink socket: Unregister + close + free.
+ * \param[in] omnl osmo_mnl socket previously returned by osmo_mnl_init() */
+void osmo_mnl_destroy(struct osmo_mnl *omnl)
+{
+ if (!omnl)
+ return;
+
+ osmo_fd_unregister(&omnl->ofd);
+ mnl_socket_close(omnl->mnls);
+ talloc_free(omnl);
+}
diff --git a/src/msgb.c b/src/core/msgb.c
index 4edbdf34..713510c6 100644
--- a/src/msgb.c
+++ b/src/core/msgb.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup msgb
@@ -64,7 +60,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
-/*! Allocate a new message buffer from given talloc cotext
+/*! Allocate a new message buffer from given talloc context
* \param[in] ctx talloc context from which to allocate
* \param[in] size Length in octets, including headroom
* \param[in] name Human-readable name to be associated with msgb
@@ -318,24 +314,29 @@ void *msgb_talloc_ctx_init(void *root_ctx, unsigned int pool_size)
return tall_msgb_ctx;
}
-/*! Copy an msgb.
+/*! Copy an msgb with memory reallocation.
*
- * This function allocates a new msgb, copies the data buffer of msg,
- * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part
- * is not copied.
+ * This function allocates a new msgb with new_len size, copies the data buffer of msg,
+ * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part is not copied.
+ * \param[in] ctx talloc context on which allocation happens
* \param[in] msg The old msgb object
- * \param[in] name Human-readable name to be associated with msgb
+ * \param[in] new_len The length of new msgb object
+ * \param[in] name Human-readable name to be associated with new msgb
*/
-struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name)
+struct msgb *msgb_copy_resize_c(const void *ctx, const struct msgb *msg, uint16_t new_len, const char *name)
{
struct msgb *new_msg;
- new_msg = msgb_alloc_c(ctx, msg->data_len, name);
- if (!new_msg)
+ if (new_len < msgb_length(msg)) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "Data from old msgb (%u bytes) won't fit into new msgb (%u bytes) after reallocation\n",
+ msgb_length(msg), new_len);
return NULL;
+ }
- /* copy data */
- memcpy(new_msg->_data, msg->_data, new_msg->data_len);
+ new_msg = msgb_alloc_c(ctx, new_len, name);
+ if (!new_msg)
+ return NULL;
/* copy header */
new_msg->len = msg->len;
@@ -343,6 +344,9 @@ struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *na
new_msg->head += msg->head - msg->_data;
new_msg->tail += msg->tail - msg->_data;
+ /* copy data */
+ memcpy(new_msg->data, msg->data, msgb_length(msg));
+
if (msg->l1h)
new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data);
if (msg->l2h)
@@ -355,6 +359,32 @@ struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *na
return new_msg;
}
+/*! Copy an msgb with memory reallocation.
+ *
+ * This function allocates a new msgb with new_len size, copies the data buffer of msg,
+ * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part is not copied.
+ * \param[in] msg The old msgb object
+ * \param[in] name Human-readable name to be associated with new msgb
+ */
+struct msgb *msgb_copy_resize(const struct msgb *msg, uint16_t new_len, const char *name)
+{
+ return msgb_copy_resize_c(tall_msgb_ctx, msg, new_len, name);
+}
+
+/*! Copy an msgb.
+ *
+ * This function allocates a new msgb, copies the data buffer of msg,
+ * and adjusts the pointers (incl l1h-l4h) accordingly. The cb part
+ * is not copied.
+ * \param[in] ctx talloc context on which allocation happens
+ * \param[in] msg The old msgb object
+ * \param[in] name Human-readable name to be associated with msgb
+ */
+struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name)
+{
+ return msgb_copy_resize_c(ctx, msg, msg->data_len, name);
+}
+
/*! Copy an msgb.
*
* This function allocates a new msgb, copies the data buffer of msg,
@@ -430,11 +460,11 @@ int msgb_resize_area(struct msgb *msg, uint8_t *area,
*/
char *msgb_hexdump_buf(char *buf, size_t buf_len, const struct msgb *msg)
{
- int buf_offs = 0;
+ unsigned int buf_offs = 0;
int nchars;
const unsigned char *start = msg->data;
const unsigned char *lxhs[4];
- int i;
+ unsigned int i;
lxhs[0] = msg->l1h;
lxhs[1] = msg->l2h;
diff --git a/src/msgfile.c b/src/core/msgfile.c
index 1f11aa60..abb4e7cf 100644
--- a/src/msgfile.c
+++ b/src/core/msgfile.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#define _WITH_GETLINE
diff --git a/src/core/netdev.c b/src/core/netdev.c
new file mode 100644
index 00000000..318134af
--- /dev/null
+++ b/src/core/netdev.c
@@ -0,0 +1,962 @@
+
+/* network device (interface) functions.
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "config.h"
+
+/*! \addtogroup netdev
+ * @{
+ * network device (interface) convenience functions
+ *
+ * \file netdev.c
+ *
+ * Example lifecycle use of the API:
+ *
+ * struct osmo_sockaddr_str osa_str = {};
+ * struct osmo_sockaddr osa = {};
+ *
+ * // Allocate object:
+ * struct osmo_netdev *netdev = osmo_netdev_alloc(parent_talloc_ctx, name);
+ * OSMO_ASSERT(netdev);
+ *
+ * // Configure object (before registration):
+ * rc = osmo_netdev_set_netns_name(netdev, "some_netns_name_or_null");
+ * rc = osmo_netdev_set_ifindex(netdev, if_nametoindex("eth0"));
+ *
+ * // Register object:
+ * rc = osmo_netdev_register(netdev);
+ * // The network interface is now being monitored and the network interface
+ * // can be operated (see below)
+ *
+ * // Add a local IPv4 address:
+ * rc = osmo_sockaddr_str_from_str2(&osa_str, "192.168.200.1");
+ * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
+ * rc = osmo_netdev_add_addr(netdev, &osa, 24);
+ *
+ * // Bring network interface up:
+ * rc = osmo_netdev_ifupdown(netdev, true);
+ *
+ * // Add default route (0.0.0.0/0):
+ * rc = osmo_sockaddr_str_from_str2(&osa_str, "0.0.0.0");
+ * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
+ * rc = osmo_netdev_add_route(netdev, &osa, 0, NULL);
+ *
+ * // Unregister (can be freed directly too):
+ * rc = osmo_netdev_unregister(netdev);
+ * // Free the object:
+ * osmo_netdev_free(netdev);
+ */
+
+#if (!EMBEDDED)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#if defined(__linux__)
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+#else
+#error "Unknown platform!"
+#endif
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/netns.h>
+#include <osmocom/core/netdev.h>
+
+#if ENABLE_LIBMNL
+#include <osmocom/core/mnl.h>
+#endif
+
+#define IFINDEX_UNUSED 0
+
+#define LOGNETDEV(netdev, lvl, fmt, args ...) \
+ LOGP(DLGLOBAL, lvl, "NETDEV(%s,if=%s/%u,ns=%s): " fmt, \
+ (netdev)->name, osmo_netdev_get_dev_name(netdev) ? : "", \
+ (netdev)->ifindex, (netdev)->netns_name ? : "", ## args)
+
+static struct llist_head g_netdev_netns_ctx_list = LLIST_HEAD_INIT(g_netdev_netns_ctx_list);
+static struct llist_head g_netdev_list = LLIST_HEAD_INIT(g_netdev_list);
+
+/* One per netns, shared by all osmo_netdev in a given netns: */
+struct netdev_netns_ctx {
+ struct llist_head entry; /* entry in g_netdev_netns_ctx_list */
+ unsigned int refcount; /* Number of osmo_netdev currently registered on this netns */
+ const char *netns_name; /* default netns has empty string "" (never NULL!) */
+ int netns_fd; /* FD to the netns with name "netns_name" above */
+#if ENABLE_LIBMNL
+ struct osmo_mnl *omnl;
+#endif
+};
+
+static struct netdev_netns_ctx *netdev_netns_ctx_alloc(void *ctx, const char *netns_name)
+{
+ struct netdev_netns_ctx *netns_ctx;
+ OSMO_ASSERT(netns_name);
+
+ netns_ctx = talloc_zero(ctx, struct netdev_netns_ctx);
+ if (!netns_ctx)
+ return NULL;
+
+ netns_ctx->netns_name = talloc_strdup(netns_ctx, netns_name);
+ netns_ctx->netns_fd = -1;
+
+ llist_add_tail(&netns_ctx->entry, &g_netdev_netns_ctx_list);
+ return netns_ctx;
+
+}
+
+static void netdev_netns_ctx_free(struct netdev_netns_ctx *netns_ctx)
+{
+ if (!netns_ctx)
+ return;
+
+ llist_del(&netns_ctx->entry);
+
+#if ENABLE_LIBMNL
+ if (netns_ctx->omnl) {
+ osmo_mnl_destroy(netns_ctx->omnl);
+ netns_ctx->omnl = NULL;
+ }
+#endif
+
+ if (netns_ctx->netns_fd != -1) {
+ close(netns_ctx->netns_fd);
+ netns_ctx->netns_fd = -1;
+ }
+ talloc_free(netns_ctx);
+}
+
+#if ENABLE_LIBMNL
+static int netdev_netns_ctx_mnl_cb(const struct nlmsghdr *nlh, void *data);
+#endif
+
+static int netdev_netns_ctx_init(struct netdev_netns_ctx *netns_ctx)
+{
+ struct osmo_netns_switch_state switch_state;
+ int rc;
+
+ if (netns_ctx->netns_name[0] != '\0') {
+ LOGP(DLGLOBAL, LOGL_INFO, "Prepare netns: Switch to netns '%s'\n", netns_ctx->netns_name);
+ netns_ctx->netns_fd = osmo_netns_open_fd(netns_ctx->netns_name);
+ if (netns_ctx->netns_fd < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Prepare netns: Cannot switch to netns '%s': %s (%d)\n",
+ netns_ctx->netns_name, strerror(errno), errno);
+ return netns_ctx->netns_fd;
+ }
+
+ /* temporarily switch to specified namespace to create netlink socket */
+ rc = osmo_netns_switch_enter(netns_ctx->netns_fd, &switch_state);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Prepare netns: Cannot switch to netns '%s': %s (%d)\n",
+ netns_ctx->netns_name, strerror(errno), errno);
+ /* netns_ctx->netns_fd will be freed by future call to netdev_netns_ctx_free() */
+ return rc;
+ }
+ }
+
+#if ENABLE_LIBMNL
+ netns_ctx->omnl = osmo_mnl_init(NULL, NETLINK_ROUTE, RTMGRP_LINK, netdev_netns_ctx_mnl_cb, netns_ctx);
+ rc = (netns_ctx->omnl ? 0 : -EFAULT);
+#else
+ rc = 0;
+#endif
+
+ /* switch back to default namespace */
+ if (netns_ctx->netns_name[0] != '\0') {
+ int rc2 = osmo_netns_switch_exit(&switch_state);
+ if (rc2 < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Prepare netns: Cannot switch back from netns '%s': %s\n",
+ netns_ctx->netns_name, strerror(errno));
+ return rc2;
+ }
+ LOGP(DLGLOBAL, LOGL_INFO, "Prepare netns: Back from netns '%s'\n",
+ netns_ctx->netns_name);
+ }
+ return rc;
+}
+
+static struct netdev_netns_ctx *netdev_netns_ctx_find_by_netns_name(const char *netns_name)
+{
+ struct netdev_netns_ctx *netns_ctx;
+
+ llist_for_each_entry(netns_ctx, &g_netdev_netns_ctx_list, entry) {
+ if (strcmp(netns_ctx->netns_name, netns_name))
+ continue;
+ return netns_ctx;
+ }
+
+ return NULL;
+}
+
+static struct netdev_netns_ctx *netdev_netns_ctx_get(const char *netns_name)
+{
+ struct netdev_netns_ctx *netns_ctx;
+ int rc;
+
+ OSMO_ASSERT(netns_name);
+ netns_ctx = netdev_netns_ctx_find_by_netns_name(netns_name);
+ if (!netns_ctx) {
+ netns_ctx = netdev_netns_ctx_alloc(NULL, netns_name);
+ if (!netns_ctx)
+ return NULL;
+ rc = netdev_netns_ctx_init(netns_ctx);
+ if (rc < 0) {
+ netdev_netns_ctx_free(netns_ctx);
+ return NULL;
+ }
+ }
+ netns_ctx->refcount++;
+ return netns_ctx;
+}
+
+static void netdev_netns_ctx_put(struct netdev_netns_ctx *netns_ctx)
+{
+ OSMO_ASSERT(netns_ctx);
+ netns_ctx->refcount--;
+
+ if (netns_ctx->refcount == 0)
+ netdev_netns_ctx_free(netns_ctx);
+}
+
+struct osmo_netdev {
+ /* entry in g_netdev_list */
+ struct llist_head entry;
+
+ /* Pointer to struct shared (refcounted) by all osmo_netdev in the same netns: */
+ struct netdev_netns_ctx *netns_ctx;
+
+ /* Name used to identify the osmo_netdev */
+ char *name;
+
+ /* ifindex of the network interface (address space is per netns) */
+ unsigned int ifindex;
+
+ /* Network interface name. Can change over lifetime of the interface. */
+ char *dev_name;
+
+ /* netns name where the netdev interface is created (NULL = default netns) */
+ char *netns_name;
+
+ /* API user private data */
+ void *priv_data;
+
+ /* Whether the netdev is in operation (managing the netdev interface) */
+ bool registered;
+
+ /* Called by netdev each time a new up/down state change is detected. Can be NULL. */
+ osmo_netdev_ifupdown_ind_cb_t ifupdown_ind_cb;
+
+ /* Called by netdev each time the registered network interface is renamed by the system. Can be NULL. */
+ osmo_netdev_dev_name_chg_cb_t dev_name_chg_cb;
+
+ /* Called by netdev each time the configured MTU changes in registered network interface. Can be NULL. */
+ osmo_netdev_mtu_chg_cb_t mtu_chg_cb;
+
+ /* Whether the netdev interface is UP */
+ bool if_up;
+ /* Whether we know the interface updown state (aka if if_up holds information)*/
+ bool if_up_known;
+
+ /* The netdev interface MTU size */
+ uint32_t if_mtu;
+ /* Whether we know the interface MTU size (aka if if_mtu holds information)*/
+ bool if_mtu_known;
+};
+
+#define NETDEV_NETNS_ENTER(netdev, switch_state, str_prefix) \
+ do { \
+ if ((netdev)->netns_name) { \
+ LOGNETDEV(netdev, LOGL_DEBUG, str_prefix ": Switch to netns '%s'\n", \
+ (netdev)->netns_name); \
+ int rc2 = osmo_netns_switch_enter((netdev)->netns_ctx->netns_fd, switch_state); \
+ if (rc2 < 0) { \
+ LOGNETDEV(netdev, LOGL_ERROR, str_prefix ": Cannot switch to netns '%s': %s (%d)\n", \
+ (netdev)->netns_name, strerror(errno), errno); \
+ return -EACCES; \
+ } \
+ } \
+ } while (0)
+
+#define NETDEV_NETNS_EXIT(netdev, switch_state, str_prefix) \
+ do { \
+ if ((netdev)->netns_name) { \
+ int rc2 = osmo_netns_switch_exit(switch_state); \
+ if (rc2 < 0) { \
+ LOGNETDEV(netdev, LOGL_ERROR, str_prefix ": Cannot switch back from netns '%s': %s\n", \
+ (netdev)->netns_name, strerror(errno)); \
+ return rc2; \
+ } \
+ LOGNETDEV(netdev, LOGL_DEBUG, str_prefix ": Back from netns '%s'\n", \
+ (netdev)->netns_name); \
+ } \
+ } while (0)
+
+#if ENABLE_LIBMNL
+/* validate the netlink attributes */
+static int netdev_mnl_data_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case IFLA_ADDRESS:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case IFLA_MTU:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ return MNL_CB_ERROR;
+ break;
+ case IFLA_IFNAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ return MNL_CB_ERROR;
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void netdev_mnl_check_mtu_change(struct osmo_netdev *netdev, uint32_t mtu)
+{
+ if (netdev->if_mtu_known && netdev->if_mtu == mtu)
+ return;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "MTU changed: %u\n", mtu);
+ if (netdev->mtu_chg_cb)
+ netdev->mtu_chg_cb(netdev, mtu);
+
+ netdev->if_mtu_known = true;
+ netdev->if_mtu = mtu;
+}
+
+static void netdev_mnl_check_link_state_change(struct osmo_netdev *netdev, bool if_up)
+{
+ if (netdev->if_up_known && netdev->if_up == if_up)
+ return;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "Physical link state changed: %s\n",
+ if_up ? "UP" : "DOWN");
+ if (netdev->ifupdown_ind_cb)
+ netdev->ifupdown_ind_cb(netdev, if_up);
+
+ netdev->if_up_known = true;
+ netdev->if_up = if_up;
+}
+
+static int netdev_mnl_cb(struct osmo_netdev *netdev, struct ifinfomsg *ifm, struct nlattr **tb)
+{
+ char ifnamebuf[IF_NAMESIZE];
+ const char *ifname = NULL;
+ bool if_running;
+
+ if (tb[IFLA_IFNAME]) {
+ ifname = mnl_attr_get_str(tb[IFLA_IFNAME]);
+ LOGNETDEV(netdev, LOGL_DEBUG, "%s(): ifname=%s\n", __func__, ifname);
+ } else {
+ /* Try harder to obtain the ifname. This code path should in
+ * general not be triggered since usually IFLA_IFNAME is there */
+ struct osmo_netns_switch_state switch_state;
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "if_indextoname");
+ ifname = if_indextoname(ifm->ifi_index, ifnamebuf);
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "if_indextoname");
+ }
+ if (ifname) {
+ /* Update dev_name if it changed: */
+ if (strcmp(netdev->dev_name, ifname) != 0) {
+ if (netdev->dev_name_chg_cb)
+ netdev->dev_name_chg_cb(netdev, ifname);
+ osmo_talloc_replace_string(netdev, &netdev->dev_name, ifname);
+ }
+ }
+
+ if (tb[IFLA_MTU]) {
+ uint32_t mtu = mnl_attr_get_u32(tb[IFLA_MTU]);
+ LOGNETDEV(netdev, LOGL_DEBUG, "%s(): mtu=%u\n", __func__, mtu);
+ netdev_mnl_check_mtu_change(netdev, mtu);
+ }
+ if (tb[IFLA_ADDRESS]) {
+ uint8_t *hwaddr = mnl_attr_get_payload(tb[IFLA_ADDRESS]);
+ uint16_t hwaddr_len = mnl_attr_get_payload_len(tb[IFLA_ADDRESS]);
+ LOGNETDEV(netdev, LOGL_DEBUG, "%s(): hwaddress=%s\n",
+ __func__, osmo_hexdump(hwaddr, hwaddr_len));
+ }
+
+ if_running = !!(ifm->ifi_flags & IFF_RUNNING);
+ LOGNETDEV(netdev, LOGL_DEBUG, "%s(): up=%u running=%u\n",
+ __func__, !!(ifm->ifi_flags & IFF_UP), if_running);
+ netdev_mnl_check_link_state_change(netdev, if_running);
+
+ return MNL_CB_OK;
+}
+
+static int netdev_netns_ctx_mnl_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct osmo_mnl *omnl = data;
+ struct netdev_netns_ctx *netns_ctx = (struct netdev_netns_ctx *)omnl->priv;
+ struct nlattr *tb[IFLA_MAX+1] = {};
+ struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
+ struct osmo_netdev *netdev;
+
+ OSMO_ASSERT(omnl);
+ OSMO_ASSERT(ifm);
+
+ mnl_attr_parse(nlh, sizeof(*ifm), netdev_mnl_data_attr_cb, tb);
+
+ LOGP(DLGLOBAL, LOGL_DEBUG,
+ "%s(): index=%d type=%d flags=0x%x family=%d\n", __func__,
+ ifm->ifi_index, ifm->ifi_type, ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_index == IFINDEX_UNUSED)
+ return MNL_CB_ERROR;
+
+ /* Find the netdev (if any) using key <netns,ifindex>.
+ * Different users of the API may have its own osmo_netdev object
+ * tracking potentially same netif, hence we need to iterate the whole list
+ * and dispatch to all matches:
+ */
+ bool found_any = false;
+ llist_for_each_entry(netdev, &g_netdev_list, entry) {
+ if (!netdev->registered)
+ continue;
+ if (netdev->ifindex != ifm->ifi_index)
+ continue;
+ if (strcmp(netdev->netns_ctx->netns_name, netns_ctx->netns_name))
+ continue;
+ found_any = true;
+ netdev_mnl_cb(netdev, ifm, &tb[0]);
+ }
+
+ if (!found_any) {
+ LOGP(DLGLOBAL, LOGL_DEBUG, "%s(): device with ifindex %u on netns %s not registered\n", __func__,
+ ifm->ifi_index, netns_ctx->netns_name);
+ }
+ return MNL_CB_OK;
+}
+
+/* Trigger an initial dump of the iface to get link information */
+static int netdev_mnl_request_initial_dump(struct osmo_mnl *omnl, unsigned int if_index)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct ifinfomsg *ifm;
+
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = time(NULL);
+
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ ifm->ifi_index = if_index;
+
+ if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int netdev_mnl_set_ifupdown(struct osmo_mnl *omnl, unsigned int if_index,
+ const char *dev_name, bool up)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct ifinfomsg *ifm;
+ unsigned int change = 0;
+ unsigned int flags = 0;
+
+ if (up) {
+ change |= IFF_UP;
+ flags |= IFF_UP;
+ } else {
+ change |= IFF_UP;
+ flags &= ~IFF_UP;
+ }
+
+ nlh->nlmsg_type = RTM_NEWLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = time(NULL);
+
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ ifm->ifi_change = change;
+ ifm->ifi_flags = flags;
+ ifm->ifi_index = if_index;
+
+ if (dev_name)
+ mnl_attr_put_str(nlh, IFLA_IFNAME, dev_name);
+
+ if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int netdev_mnl_add_addr(struct osmo_mnl *omnl, unsigned int if_index, const struct osmo_sockaddr *osa, uint8_t prefix)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct ifaddrmsg *ifm;
+
+ nlh->nlmsg_type = RTM_NEWADDR;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
+ nlh->nlmsg_seq = time(NULL);
+
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifa_family = osa->u.sa.sa_family;
+ ifm->ifa_prefixlen = prefix;
+ ifm->ifa_flags = IFA_F_PERMANENT;
+ ifm->ifa_scope = RT_SCOPE_UNIVERSE;
+ ifm->ifa_index = if_index;
+
+ /*
+ * The exact meaning of IFA_LOCAL and IFA_ADDRESS depend
+ * on the address family being used and the device type.
+ * For broadcast devices (like the interfaces we use),
+ * for IPv4 we specify both and they are used interchangeably.
+ * For IPv6, only IFA_ADDRESS needs to be set.
+ */
+ switch (osa->u.sa.sa_family) {
+ case AF_INET:
+ mnl_attr_put_u32(nlh, IFA_LOCAL, osa->u.sin.sin_addr.s_addr);
+ mnl_attr_put_u32(nlh, IFA_ADDRESS, osa->u.sin.sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ mnl_attr_put(nlh, IFA_ADDRESS, sizeof(struct in6_addr), &osa->u.sin6.sin6_addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int netdev_mnl_add_route(struct osmo_mnl *omnl,
+ unsigned int if_index,
+ const struct osmo_sockaddr *dst_osa,
+ uint8_t dst_prefix,
+ const struct osmo_sockaddr *gw_osa)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ struct rtmsg *rtm;
+
+ nlh->nlmsg_type = RTM_NEWROUTE;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
+ nlh->nlmsg_seq = time(NULL);
+
+ rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(*rtm));
+ rtm->rtm_family = dst_osa->u.sa.sa_family;
+ rtm->rtm_dst_len = dst_prefix;
+ rtm->rtm_src_len = 0;
+ rtm->rtm_tos = 0;
+ rtm->rtm_protocol = RTPROT_STATIC;
+ rtm->rtm_table = RT_TABLE_MAIN;
+ rtm->rtm_type = RTN_UNICAST;
+ rtm->rtm_scope = gw_osa ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
+ rtm->rtm_flags = 0;
+
+ switch (dst_osa->u.sa.sa_family) {
+ case AF_INET:
+ mnl_attr_put_u32(nlh, RTA_DST, dst_osa->u.sin.sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), &dst_osa->u.sin6.sin6_addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mnl_attr_put_u32(nlh, RTA_OIF, if_index);
+
+ if (gw_osa) {
+ switch (gw_osa->u.sa.sa_family) {
+ case AF_INET:
+ mnl_attr_put_u32(nlh, RTA_GATEWAY, gw_osa->u.sin.sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr), &gw_osa->u.sin6.sin6_addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if (mnl_socket_sendto(omnl->mnls, nlh, nlh->nlmsg_len) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "mnl_socket_sendto\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+#endif /* if ENABLE_LIBMNL */
+
+/*! Allocate a new netdev object.
+ * \param[in] ctx talloc context to use as a parent when allocating the netdev object
+ * \param[in] name A name providen to identify the netdev object
+ * \returns newly allocated netdev object on success; NULL on error
+ */
+struct osmo_netdev *osmo_netdev_alloc(void *ctx, const char *name)
+{
+ struct osmo_netdev *netdev;
+
+ netdev = talloc_zero(ctx, struct osmo_netdev);
+ if (!netdev)
+ return NULL;
+
+ netdev->name = talloc_strdup(netdev, name);
+
+ llist_add_tail(&netdev->entry, &g_netdev_list);
+ return netdev;
+}
+
+/*! Free an allocated netdev object.
+ * \param[in] netdev The netdev object to free
+ */
+void osmo_netdev_free(struct osmo_netdev *netdev)
+{
+ if (!netdev)
+ return;
+ if (osmo_netdev_is_registered(netdev))
+ osmo_netdev_unregister(netdev);
+ llist_del(&netdev->entry);
+ talloc_free(netdev);
+}
+
+/*! Start managing the network device referenced by the netdev object.
+ * \param[in] netdev The netdev object to open
+ * \returns 0 on success; negative on error
+ */
+int osmo_netdev_register(struct osmo_netdev *netdev)
+{
+ char ifnamebuf[IF_NAMESIZE];
+ struct osmo_netns_switch_state switch_state;
+ int rc = 0;
+
+ if (netdev->registered)
+ return -EALREADY;
+
+ netdev->netns_ctx = netdev_netns_ctx_get(netdev->netns_name ? : "");
+ if (!netdev->netns_ctx)
+ return -EFAULT;
+
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "register");
+
+ if (!if_indextoname(netdev->ifindex, ifnamebuf)) {
+ rc = -ENODEV;
+ goto err_put_exit;
+ }
+ osmo_talloc_replace_string(netdev, &netdev->dev_name, ifnamebuf);
+
+#if ENABLE_LIBMNL
+ rc = netdev_mnl_request_initial_dump(netdev->netns_ctx->omnl, netdev->ifindex);
+#endif
+
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "register");
+
+ netdev->registered = true;
+ return rc;
+
+err_put_exit:
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "register");
+ netdev_netns_ctx_put(netdev->netns_ctx);
+ return rc;
+}
+
+/*! Unregister the netdev object (stop managing /moniutoring the interface)
+ * \param[in] netdev The netdev object to close
+ * \returns 0 on success; negative on error
+ */
+int osmo_netdev_unregister(struct osmo_netdev *netdev)
+{
+ if (!netdev->registered)
+ return -EALREADY;
+
+ netdev->if_up_known = false;
+ netdev->if_mtu_known = false;
+
+ netdev_netns_ctx_put(netdev->netns_ctx);
+ netdev->registered = false;
+ return 0;
+}
+
+/*! Retrieve whether the netdev object is in "registered" state.
+ * \param[in] netdev The netdev object to check
+ * \returns true if in state "registered"; false otherwise
+ */
+bool osmo_netdev_is_registered(struct osmo_netdev *netdev)
+{
+ return netdev->registered;
+}
+
+/*! Set private user data pointer on the netdev object.
+ * \param[in] netdev The netdev object where the field is set
+ */
+void osmo_netdev_set_priv_data(struct osmo_netdev *netdev, void *priv_data)
+{
+ netdev->priv_data = priv_data;
+}
+
+/*! Get private user data pointer from the netdev object.
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The current value of the priv_data field.
+ */
+void *osmo_netdev_get_priv_data(struct osmo_netdev *netdev)
+{
+ return netdev->priv_data;
+}
+
+/*! Set data_ind_cb callback, called when a new packet is received on the network interface.
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] data_ind_cb the user provided function to be called when the link status (UP/DOWN) changes
+ */
+void osmo_netdev_set_ifupdown_ind_cb(struct osmo_netdev *netdev, osmo_netdev_ifupdown_ind_cb_t ifupdown_ind_cb)
+{
+ netdev->ifupdown_ind_cb = ifupdown_ind_cb;
+}
+
+/*! Set dev_name_chg_cb callback, called when a change in the network name is detected
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] dev_name_chg_cb the user provided function to be called when a the interface is renamed
+ */
+void osmo_netdev_set_dev_name_chg_cb(struct osmo_netdev *netdev, osmo_netdev_dev_name_chg_cb_t dev_name_chg_cb)
+{
+ netdev->dev_name_chg_cb = dev_name_chg_cb;
+}
+
+/*! Set mtu_chg_cb callback, called when a change in the network name is detected
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] mtu_chg_cb the user provided function to be called when the configured MTU at the interface changes
+ */
+void osmo_netdev_set_mtu_chg_cb(struct osmo_netdev *netdev, osmo_netdev_mtu_chg_cb_t mtu_chg_cb)
+{
+ netdev->mtu_chg_cb = mtu_chg_cb;
+}
+
+/*! Get name used to identify the netdev object.
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The current value of the name used to identify the netdev object
+ */
+const char *osmo_netdev_get_name(const struct osmo_netdev *netdev)
+{
+ return netdev->name;
+}
+
+/*! Set (specify) interface index identifying the network interface to manage
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] ifindex The interface index identifying the interface
+ * \returns 0 on success; negative on error
+ *
+ * The ifindex, together with the netns_name (see
+ * osmo_netdev_netns_name_set()), form together the key identifiers of a
+ * network interface to manage.
+ * This field is used during osmo_netdev_register() time, and hence must be set
+ * before calling that API, and cannot be changed when the netdev object is in
+ * "registered" state.
+ */
+int osmo_netdev_set_ifindex(struct osmo_netdev *netdev, unsigned int ifindex)
+{
+ if (netdev->registered)
+ return -EALREADY;
+ netdev->ifindex = ifindex;
+ return 0;
+}
+
+/*! Get interface index identifying the interface managed by netdev
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The current value of the configured netdev interface ifindex to use (0 = unset)
+ */
+unsigned int osmo_netdev_get_ifindex(const struct osmo_netdev *netdev)
+{
+ return netdev->ifindex;
+}
+
+/*! Set (specify) name of the network namespace where the network interface to manage is located
+ * \param[in] netdev The netdev object where the field is set
+ * \param[in] netns_name The network namespace where the network interface is located
+ * \returns 0 on success; negative on error
+ *
+ * The netns_name, together with the ifindex (see
+ * osmo_netdev_ifindex_set()), form together the key identifiers of a
+ * network interface to manage.
+ * This field is used during osmo_netdev_register() time, and hence must be set
+ * before calling that API, and cannot be changed when the netdev object is in
+ * "registered" state.
+ * If left as NULL (default), the management will be done in the current network namespace.
+ */
+int osmo_netdev_set_netns_name(struct osmo_netdev *netdev, const char *netns_name)
+{
+ if (netdev->registered)
+ return -EALREADY;
+ osmo_talloc_replace_string(netdev, &netdev->netns_name, netns_name);
+ return 0;
+}
+
+/*! Get name of network namespace used when opening the netdev interface
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The current value of the configured network namespace
+ */
+const char *osmo_netdev_get_netns_name(const struct osmo_netdev *netdev)
+{
+ return netdev->netns_name;
+}
+
+/*! Get name used to name the network interface created by the netdev object
+ * \param[in] netdev The netdev object from where to retrieve the field
+ * \returns The interface name (or NULL if unknown)
+ *
+ * This information is retrieved internally once the netdev object enters the
+ * "registered" state. Hence, when not registered NULL can be returned.
+ */
+const char *osmo_netdev_get_dev_name(const struct osmo_netdev *netdev)
+{
+ return netdev->dev_name;
+}
+
+/*! Bring netdev interface UP or DOWN.
+ * \param[in] netdev The netdev object managing the netdev interface
+ * \param[in] ifupdown true to set the interface UP, false to set it DOWN
+ * \returns 0 on succes; negative on error.
+ */
+int osmo_netdev_ifupdown(struct osmo_netdev *netdev, bool ifupdown)
+{
+ struct osmo_netns_switch_state switch_state;
+ int rc;
+
+ if (!netdev->registered)
+ return -ENODEV;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "Bringing dev %s %s\n",
+ netdev->dev_name, ifupdown ? "UP" : "DOWN");
+
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "ifupdown");
+
+#if ENABLE_LIBMNL
+ rc = netdev_mnl_set_ifupdown(netdev->netns_ctx->omnl, netdev->ifindex,
+ netdev->dev_name, ifupdown);
+#else
+ LOGNETDEV(netdev, LOGL_ERROR, "%s: NOT SUPPORTED. Build libosmocore with --enable-libmnl.\n", __func__);
+ rc = -ENOTSUP;
+#endif
+
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "ifupdown");
+
+ return rc;
+}
+
+/*! Add IP address to netdev interface
+ * \param[in] netdev The netdev object managing the netdev interface
+ * \param[in] addr The local address to set on the interface
+ * \param[in] prefixlen The network prefix of addr
+ * \returns 0 on succes; negative on error.
+ */
+int osmo_netdev_add_addr(struct osmo_netdev *netdev, const struct osmo_sockaddr *addr, uint8_t prefixlen)
+{
+ struct osmo_netns_switch_state switch_state;
+ char buf[INET6_ADDRSTRLEN];
+ int rc;
+
+ if (!netdev->registered)
+ return -ENODEV;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "Adding address %s/%u to dev %s\n",
+ osmo_sockaddr_ntop(&addr->u.sa, buf), prefixlen, netdev->dev_name);
+
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "Add address");
+
+#if ENABLE_LIBMNL
+ rc = netdev_mnl_add_addr(netdev->netns_ctx->omnl, netdev->ifindex, addr, prefixlen);
+#else
+ LOGNETDEV(netdev, LOGL_ERROR, "%s: NOT SUPPORTED. Build libosmocore with --enable-libmnl.\n", __func__);
+ rc = -ENOTSUP;
+#endif
+
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "Add address");
+
+ return rc;
+}
+
+/*! Add IP route to netdev interface
+ * \param[in] netdev The netdev object managing the netdev interface
+ * \param[in] dst_addr The destination address of the route
+ * \param[in] dst_prefixlen The network prefix of dst_addr
+ * \param[in] gw_addr The gateway address. Optional, can be NULL.
+ * \returns 0 on succes; negative on error.
+ */
+int osmo_netdev_add_route(struct osmo_netdev *netdev, const struct osmo_sockaddr *dst_addr, uint8_t dst_prefixlen, const struct osmo_sockaddr *gw_addr)
+{
+ struct osmo_netns_switch_state switch_state;
+ char buf_dst[INET6_ADDRSTRLEN];
+ char buf_gw[INET6_ADDRSTRLEN];
+ int rc;
+
+ if (!netdev->registered)
+ return -ENODEV;
+
+ LOGNETDEV(netdev, LOGL_NOTICE, "Adding route %s/%u%s%s dev %s\n",
+ osmo_sockaddr_ntop(&dst_addr->u.sa, buf_dst), dst_prefixlen,
+ gw_addr ? " via " : "",
+ gw_addr ? osmo_sockaddr_ntop(&gw_addr->u.sa, buf_gw) : "",
+ netdev->dev_name);
+
+ NETDEV_NETNS_ENTER(netdev, &switch_state, "Add route");
+
+#if ENABLE_LIBMNL
+ rc = netdev_mnl_add_route(netdev->netns_ctx->omnl, netdev->ifindex, dst_addr, dst_prefixlen, gw_addr);
+#else
+ LOGNETDEV(netdev, LOGL_ERROR, "%s: NOT SUPPORTED. Build libosmocore with --enable-libmnl.\n", __func__);
+ rc = -ENOTSUP;
+#endif
+
+ NETDEV_NETNS_EXIT(netdev, &switch_state, "Add route");
+
+ return rc;
+}
+
+#endif /* (!EMBEDDED) */
+
+/*! @} */
diff --git a/src/core/netns.c b/src/core/netns.c
new file mode 100644
index 00000000..c1d75b1e
--- /dev/null
+++ b/src/core/netns.c
@@ -0,0 +1,208 @@
+
+/* Network namespace convenience functions
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "config.h"
+
+/*! \addtogroup netns
+ * @{
+ * Network namespace convenience functions
+ *
+ * \file netns.c */
+
+#if defined(__linux__)
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/netns.h>
+
+#define NETNS_PREFIX_PATH "/var/run/netns"
+#define NETNS_CURRENT_PATH "/proc/self/ns/net"
+
+/*! Open a file descriptor for the current network namespace.
+ * \returns fd of the current network namespace on success; negative in case of error
+ */
+static int netns_open_current_fd(void)
+{
+ int fd;
+ /* store the default namespace for later reference */
+ if ((fd = open(NETNS_CURRENT_PATH, O_RDONLY)) < 0)
+ return -errno;
+ return fd;
+}
+
+/*! switch to a (non-default) namespace, store existing signal mask in oldmask.
+ * \param[in] nsfd file descriptor representing the namespace to which we shall switch
+ * \param[out] state caller-provided memory location to which state of previous netns is stored
+ * \returns 0 on success; negative on error */
+int osmo_netns_switch_enter(int nsfd, struct osmo_netns_switch_state *state)
+{
+ sigset_t intmask;
+ int rc;
+
+ state->prev_nsfd = -1;
+
+ if (sigfillset(&intmask) < 0)
+ return -errno;
+ if ((rc = sigprocmask(SIG_BLOCK, &intmask, &state->prev_sigmask)) != 0)
+ return -rc;
+ state->prev_nsfd = netns_open_current_fd();
+
+ if (setns(nsfd, CLONE_NEWNET) < 0) {
+ /* restore old mask if we couldn't switch the netns */
+ sigprocmask(SIG_SETMASK, &state->prev_sigmask, NULL);
+ close(state->prev_nsfd);
+ state->prev_nsfd = -1;
+ return -errno;
+ }
+ return 0;
+}
+
+/*! switch back to the previous namespace, restoring signal mask.
+ * \param[in] state information about previous netns, filled by osmo_netns_switch_enter()
+ * \returns 0 on successs; negative on error */
+int osmo_netns_switch_exit(struct osmo_netns_switch_state *state)
+{
+ if (state->prev_nsfd < 0)
+ return -EINVAL;
+
+ int rc;
+ if (setns(state->prev_nsfd, CLONE_NEWNET) < 0)
+ return -errno;
+
+ close(state->prev_nsfd);
+ state->prev_nsfd = -1;
+
+ if ((rc = sigprocmask(SIG_SETMASK, &state->prev_sigmask, NULL)) != 0)
+ return -rc;
+ return 0;
+}
+
+static int create_netns(const char *name)
+{
+ char path[MAXPATHLEN];
+ sigset_t intmask, oldmask;
+ int fd, prev_nsfd;
+ int rc, rc2;
+
+ /* create /var/run/netns, if it doesn't exist already */
+ rc = mkdir(NETNS_PREFIX_PATH, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
+ if (rc < 0 && errno != EEXIST)
+ return rc;
+
+ /* create /var/run/netns/[name], if it doesn't exist already */
+ rc = snprintf(path, sizeof(path), "%s/%s", NETNS_PREFIX_PATH, name);
+ if (rc >= sizeof(path))
+ return -ENAMETOOLONG;
+ fd = open(path, O_RDONLY|O_CREAT|O_EXCL, 0);
+ if (fd < 0)
+ return -errno;
+ if (close(fd) < 0)
+ return -errno;
+
+ /* mask off all signals, store old signal mask */
+ if (sigfillset(&intmask) < 0)
+ return -errno;
+ if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
+ return -rc;
+
+ prev_nsfd = netns_open_current_fd();
+ if (prev_nsfd < 0)
+ return prev_nsfd;
+
+ /* create a new network namespace */
+ if (unshare(CLONE_NEWNET) < 0) {
+ rc = -errno;
+ goto restore_sigmask;
+ }
+ if (mount(NETNS_CURRENT_PATH, path, "none", MS_BIND, NULL) < 0) {
+ rc = -errno;
+ goto restore_sigmask;
+ }
+
+ /* switch back to previous namespace */
+ if (setns(prev_nsfd, CLONE_NEWNET) < 0) {
+ rc = -errno;
+ goto restore_sigmask;
+ }
+
+restore_sigmask:
+ close(prev_nsfd);
+
+ /* restore process mask */
+ if ((rc2 = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0)
+ return -rc2;
+
+ /* might have been set above in case mount fails */
+ if (rc < 0)
+ return rc;
+
+ /* finally, open the created namespace file descriptor from previous ns */
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return -errno;
+
+ return fd;
+}
+
+/*! Open a file descriptor for the network namespace with provided name.
+ * Creates /var/run/netns/ directory if it doesn't exist already.
+ * \param[in] name Name of the network namespace (in /var/run/netns/)
+ * \returns File descriptor of network namespace; negative in case of error
+ */
+int osmo_netns_open_fd(const char *name)
+{
+ int rc;
+ int fd;
+ char path[MAXPATHLEN];
+
+ /* path = /var/run/netns/[name] */
+ rc = snprintf(path, sizeof(path), "%s/%s", NETNS_PREFIX_PATH, name);
+ if (rc >= sizeof(path))
+ return -ENAMETOOLONG;
+
+ /* If netns already exists, simply open it: */
+ fd = open(path, O_RDONLY);
+ if (fd >= 0)
+ return fd;
+
+ /* The netns doesn't exist yet, let's create it: */
+ fd = create_netns(name);
+ return fd;
+}
+
+#endif /* defined(__linux__) */
+
+/*! @} */
diff --git a/src/core/osmo_io.c b/src/core/osmo_io.c
new file mode 100644
index 00000000..af096e6e
--- /dev/null
+++ b/src/core/osmo_io.c
@@ -0,0 +1,1013 @@
+/*
+ * New osmocom async I/O API.
+ *
+ * (C) 2022-2024 by Harald Welte <laforge@osmocom.org>
+ * (C) 2022-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Daniel Willmann <dwillmann@sysmocom.de>
+ *
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "../config.h"
+#ifndef EMBEDDED
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <talloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include "osmo_io_internal.h"
+
+/*! \addtogroup osmo_io
+ * @{
+ *
+ * \file osmo_io.c */
+
+/*! This environment variable can be set to manually set the backend used in osmo_io */
+#define OSMO_IO_BACKEND_ENV "LIBOSMO_IO_BACKEND"
+
+const struct value_string osmo_io_backend_names[] = {
+ { OSMO_IO_BACKEND_POLL, "poll" },
+ { OSMO_IO_BACKEND_IO_URING, "io_uring" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_iofd_mode_names[] = {
+ { OSMO_IO_FD_MODE_READ_WRITE, "read/write" },
+ { OSMO_IO_FD_MODE_RECVFROM_SENDTO, "recvfrom/sendto" },
+ { OSMO_IO_FD_MODE_RECVMSG_SENDMSG, "recvmsg/sendmsg" },
+ { 0, NULL }
+};
+
+static enum osmo_io_backend g_io_backend;
+
+/* Used by some tests, can't be static */
+struct iofd_backend_ops osmo_iofd_ops;
+
+#if defined(HAVE_URING)
+void osmo_iofd_uring_init(void);
+#endif
+
+/*! initialize osmo_io for the current thread */
+void osmo_iofd_init(void)
+{
+ switch (g_io_backend) {
+ case OSMO_IO_BACKEND_POLL:
+ break;
+#if defined(HAVE_URING)
+ case OSMO_IO_BACKEND_IO_URING:
+ osmo_iofd_uring_init();
+ break;
+#endif
+ default:
+ OSMO_ASSERT(0);
+ break;
+ }
+}
+
+/* ensure main thread always has pre-initialized osmo_io
+ * priority 103: run after on_dso_load_select */
+static __attribute__((constructor(103))) void on_dso_load_osmo_io(void)
+{
+ char *backend = getenv(OSMO_IO_BACKEND_ENV);
+ if (backend == NULL)
+ backend = OSMO_IO_BACKEND_DEFAULT;
+
+ if (!strcmp("POLL", backend)) {
+ g_io_backend = OSMO_IO_BACKEND_POLL;
+ osmo_iofd_ops = iofd_poll_ops;
+#if defined(HAVE_URING)
+ } else if (!strcmp("IO_URING", backend)) {
+ g_io_backend = OSMO_IO_BACKEND_IO_URING;
+ osmo_iofd_ops = iofd_uring_ops;
+#endif
+ } else {
+ fprintf(stderr, "Invalid osmo_io backend requested: \"%s\"\nCheck the environment variable %s\n", backend, OSMO_IO_BACKEND_ENV);
+ exit(1);
+ }
+
+ OSMO_ASSERT(osmo_iofd_ops.close);
+ OSMO_ASSERT(osmo_iofd_ops.register_fd);
+ OSMO_ASSERT(osmo_iofd_ops.unregister_fd);
+ OSMO_ASSERT(osmo_iofd_ops.write_enable);
+ OSMO_ASSERT(osmo_iofd_ops.write_disable);
+ OSMO_ASSERT(osmo_iofd_ops.read_enable);
+ OSMO_ASSERT(osmo_iofd_ops.read_disable);
+ OSMO_ASSERT(osmo_iofd_ops.notify_connected);
+
+ osmo_iofd_init();
+}
+
+/*! Allocate the msghdr.
+ * \param[in] iofd the osmo_io file structure
+ * \param[in] action the action this msg(hdr) is for (read, write, ..)
+ * \param[in] msg the msg buffer to use. Will allocate a new one if NULL
+ * \param[in] cmsg_size size (in bytes) of iofd_msghdr.cmsg buffer. Can be 0 if cmsg is not used.
+ * \returns the newly allocated msghdr or NULL in case of error */
+struct iofd_msghdr *iofd_msghdr_alloc(struct osmo_io_fd *iofd, enum iofd_msg_action action, struct msgb *msg,
+ size_t cmsg_size)
+{
+ bool free_msg = false;
+ struct iofd_msghdr *hdr;
+
+ if (!msg) {
+ msg = iofd_msgb_alloc(iofd);
+ if (!msg)
+ return NULL;
+ free_msg = true;
+ } else {
+ talloc_steal(iofd, msg);
+ }
+
+ hdr = talloc_zero_size(iofd, sizeof(struct iofd_msghdr) + cmsg_size);
+ if (!hdr) {
+ if (free_msg)
+ talloc_free(msg);
+ return NULL;
+ }
+
+ hdr->action = action;
+ hdr->iofd = iofd;
+ hdr->msg = msg;
+
+ return hdr;
+}
+
+/*! Free the msghdr.
+ * \param[in] msghdr the msghdr to free
+ */
+void iofd_msghdr_free(struct iofd_msghdr *msghdr)
+{
+ /* msghdr->msg is never owned by msghdr, it will either be freed in the send path or
+ * or passed on to the read callback which takes ownership. */
+ talloc_free(msghdr);
+}
+
+/*! convenience wrapper to call msgb_alloc with parameters from osmo_io_fd */
+struct msgb *iofd_msgb_alloc(struct osmo_io_fd *iofd)
+{
+ uint16_t headroom = iofd->msgb_alloc.headroom;
+
+ OSMO_ASSERT(iofd->msgb_alloc.size < 0xffff - headroom);
+ return msgb_alloc_headroom_c(iofd, iofd->msgb_alloc.size + headroom, headroom, "osmo_io_msgb");
+}
+
+/*! return the pending msgb in iofd or NULL if there is none*/
+struct msgb *iofd_msgb_pending(struct osmo_io_fd *iofd)
+{
+ struct msgb *msg = NULL;
+
+ msg = iofd->pending;
+ iofd->pending = NULL;
+
+ return msg;
+}
+
+/*! Return the pending msgb or allocate and return a new one */
+struct msgb *iofd_msgb_pending_or_alloc(struct osmo_io_fd *iofd)
+{
+ struct msgb *msg = NULL;
+
+ msg = iofd_msgb_pending(iofd);
+ if (!msg)
+ msg = iofd_msgb_alloc(iofd);
+
+ return msg;
+}
+
+/*! Enqueue a message to be sent.
+ *
+ * Enqueues the message at the back of the queue provided there is enough space.
+ * \param[in] iofd the file descriptor
+ * \param[in] msghdr the message to enqueue
+ * \returns 0 if the message was enqueued succcessfully,
+ * -ENOSPC if the queue already contains the maximum number of messages
+ */
+int iofd_txqueue_enqueue(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr)
+{
+ if (iofd->tx_queue.current_length >= iofd->tx_queue.max_length)
+ return -ENOSPC;
+
+ llist_add_tail(&msghdr->list, &iofd->tx_queue.msg_queue);
+ iofd->tx_queue.current_length++;
+
+ if (iofd->tx_queue.current_length == 1 && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ osmo_iofd_ops.write_enable(iofd);
+
+ return 0;
+}
+
+/*! Enqueue a message at the front.
+ *
+ * Used to enqueue a msgb from a partial send again. This function will always
+ * enqueue the message, even if the maximum number of messages is reached.
+ * \param[in] iofd the file descriptor
+ * \param[in] msghdr the message to enqueue
+ */
+void iofd_txqueue_enqueue_front(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr)
+{
+ llist_add(&msghdr->list, &iofd->tx_queue.msg_queue);
+ iofd->tx_queue.current_length++;
+
+ if (iofd->tx_queue.current_length == 1 && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ osmo_iofd_ops.write_enable(iofd);
+}
+
+/*! Dequeue a message from the front.
+ *
+ * \param[in] iofd the file descriptor
+ * \returns the msghdr from the front of the queue or NULL if the queue is empty
+ */
+struct iofd_msghdr *iofd_txqueue_dequeue(struct osmo_io_fd *iofd)
+{
+ struct llist_head *lh;
+
+ if (iofd->tx_queue.current_length == 0)
+ return NULL;
+
+ lh = iofd->tx_queue.msg_queue.next;
+
+ OSMO_ASSERT(lh);
+ iofd->tx_queue.current_length--;
+ llist_del(lh);
+
+ if (iofd->tx_queue.current_length == 0)
+ osmo_iofd_ops.write_disable(iofd);
+
+ return llist_entry(lh, struct iofd_msghdr, list);
+}
+
+/*! Handle segmentation of the msg. If this function returns *_HANDLE_ONE or MORE then the data in msg will contain
+ * one complete message.
+ * If there are bytes left over, *pending_out will point to a msgb with the remaining data.
+*/
+static enum iofd_seg_act iofd_handle_segmentation(struct osmo_io_fd *iofd, struct msgb *msg, struct msgb **pending_out)
+{
+ int extra_len, received_len, expected_len;
+ struct msgb *msg_pending;
+
+ /* Save the start of message before segmentation_cb (which could change it) */
+ uint8_t *data = msg->data;
+
+ received_len = msgb_length(msg);
+
+ if (iofd->io_ops.segmentation_cb2) {
+ expected_len = iofd->io_ops.segmentation_cb2(iofd, msg);
+ } else if (iofd->io_ops.segmentation_cb) {
+ expected_len = iofd->io_ops.segmentation_cb(msg);
+ } else {
+ *pending_out = NULL;
+ return IOFD_SEG_ACT_HANDLE_ONE;
+ }
+
+ if (expected_len == -EAGAIN) {
+ goto defer;
+ } else if (expected_len < 0) {
+ /* Something is wrong, skip this msgb */
+ LOGPIO(iofd, LOGL_ERROR, "segmentation_cb returned error (%d), skipping msg of size %d\n",
+ expected_len, received_len);
+ *pending_out = NULL;
+ msgb_free(msg);
+ return IOFD_SEG_ACT_DEFER;
+ }
+
+ extra_len = received_len - expected_len;
+ /* No segmentation needed, return the whole msgb */
+ if (extra_len == 0) {
+ *pending_out = NULL;
+ return IOFD_SEG_ACT_HANDLE_ONE;
+ /* segment is incomplete */
+ } else if (extra_len < 0) {
+ goto defer;
+ }
+
+ /* msgb contains more than one segment */
+ /* Copy the trailing data over */
+ msg_pending = iofd_msgb_alloc(iofd);
+ memcpy(msgb_data(msg_pending), data + expected_len, extra_len);
+ msgb_put(msg_pending, extra_len);
+ *pending_out = msg_pending;
+
+ /* Trim the original msgb to size. Don't use msgb_trim because we need to reference
+ * msg->data from before it might have been modified by the segmentation_cb(). */
+ msg->tail = data + expected_len;
+ msg->len = msg->tail - msg->data;
+ return IOFD_SEG_ACT_HANDLE_MORE;
+
+defer:
+ *pending_out = msg;
+ return IOFD_SEG_ACT_DEFER;
+}
+
+/*! Restore message boundaries on read() and pass individual messages to the read callback
+ */
+void iofd_handle_segmented_read(struct osmo_io_fd *iofd, struct msgb *msg, int rc)
+{
+ int res;
+ struct msgb *pending = NULL;
+
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE);
+
+ if (rc <= 0) {
+ iofd->io_ops.read_cb(iofd, rc, msg);
+ return;
+ }
+
+ do {
+ res = iofd_handle_segmentation(iofd, msg, &pending);
+ if (res != IOFD_SEG_ACT_DEFER || rc < 0)
+ iofd->io_ops.read_cb(iofd, rc, msg);
+ if (res == IOFD_SEG_ACT_HANDLE_MORE)
+ msg = pending;
+ } while (res == IOFD_SEG_ACT_HANDLE_MORE);
+
+ OSMO_ASSERT(iofd->pending == NULL);
+ iofd->pending = pending;
+}
+
+/*! completion handler: Internal function called by osmo_io_backend after a given I/O operation has completed
+ * \param[in] iofd I/O file-descriptor on which I/O has completed
+ * \param[in] msg message buffer containing data related to completed I/O
+ * \param[in] rc result code with read size or error (-errno)
+ * \param[in] hdr serialized msghdr containing state of completed I/O */
+void iofd_handle_recv(struct osmo_io_fd *iofd, struct msgb *msg, int rc, struct iofd_msghdr *hdr)
+{
+ talloc_steal(iofd->msgb_alloc.ctx, msg);
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ iofd_handle_segmented_read(iofd, msg, rc);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ iofd->io_ops.recvfrom_cb(iofd, rc, msg, &hdr->osa);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ iofd->io_ops.recvmsg_cb(iofd, rc, msg, &hdr->hdr);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/*! completion handler: Internal function called by osmo_io_backend after a given I/O operation has completed
+ * \param[in] iofd I/O file-descriptor on which I/O has completed
+ * \param[in] rc return value of the I/O operation
+ * \param[in] msghdr serialized msghdr containing state of completed I/O
+ */
+void iofd_handle_send_completion(struct osmo_io_fd *iofd, int rc, struct iofd_msghdr *msghdr)
+{
+ struct msgb *msg = msghdr->msg;
+
+ /* Incomplete write */
+ if (rc > 0 && rc < msgb_length(msg)) {
+ /* Re-enqueue remaining data */
+ msgb_pull(msg, rc);
+ msghdr->iov[0].iov_len = msgb_length(msg);
+ iofd_txqueue_enqueue_front(iofd, msghdr);
+ return;
+ }
+
+ /* Reenqueue the complete msgb */
+ if (rc == -EAGAIN) {
+ iofd_txqueue_enqueue_front(iofd, msghdr);
+ return;
+ }
+
+ /* All other failure and success cases are handled here */
+ switch (msghdr->action) {
+ case IOFD_ACT_WRITE:
+ if (iofd->io_ops.write_cb)
+ iofd->io_ops.write_cb(iofd, rc, msg);
+ break;
+ case IOFD_ACT_SENDTO:
+ if (iofd->io_ops.sendto_cb)
+ iofd->io_ops.sendto_cb(iofd, rc, msg, &msghdr->osa);
+ break;
+ case IOFD_ACT_SENDMSG:
+ if (iofd->io_ops.sendmsg_cb)
+ iofd->io_ops.sendmsg_cb(iofd, rc, msg);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ msgb_free(msghdr->msg);
+ iofd_msghdr_free(msghdr);
+}
+
+/* Public functions */
+
+/*! Write a message to a file descriptor / connected socket.
+ * The osmo_io_fd must be using OSMO_IO_FD_MODE_READ_WRITE.
+ *
+ * Appends the message to the internal transmit queue for eventual non-blocking
+ * write to the underlying socket/file descriptor.
+ *
+ * If the function returns success (0) it will take ownership of the msgb and
+ * internally call msgb_free() after the write request completes.
+ * In case of an error, the msgb needs to be freed by the caller.
+ *
+ * \param[in] iofd osmo_io_fd file descriptor to write data to
+ * \param[in] msg message buffer containing the data to write
+ * \returns 0 in case of success; a negative value in case of error
+ */
+int osmo_iofd_write_msgb(struct osmo_io_fd *iofd, struct msgb *msg)
+{
+ int rc;
+
+ if (OSMO_UNLIKELY(msgb_length(msg) == 0)) {
+ LOGPIO(iofd, LOGL_ERROR, "Length is 0, rejecting msgb.\n");
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE);
+
+ struct iofd_msghdr *msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_WRITE, msg, 0);
+ if (!msghdr)
+ return -ENOMEM;
+
+ msghdr->flags = MSG_NOSIGNAL;
+ msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
+ msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
+ msghdr->hdr.msg_iov = &msghdr->iov[0];
+ msghdr->hdr.msg_iovlen = 1;
+
+ rc = iofd_txqueue_enqueue(iofd, msghdr);
+ if (rc < 0) {
+ iofd_msghdr_free(msghdr);
+ LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*! Send a message through an unconnected socket.
+ * The osmo_io_fd must be using OSMO_IO_FD_MODE_RECVFROM_SENDTO.
+ *
+ * Appends the message to the internal transmit queue for eventual non-blocking
+ * sendto on the underlying socket/file descriptor.
+ *
+ * If the function returns success (0), it will take ownership of the msgb and
+ * internally call msgb_free() after the sendto request completes.
+ * In case of an error the msgb needs to be freed by the caller.
+ *
+ * \param[in] iofd file descriptor to write to
+ * \param[in] msg message buffer to send
+ * \param[in] sendto_flags Flags to pass to the send call
+ * \param[in] dest destination address to send the message to
+ * \returns 0 in case of success; a negative value in case of error
+ */
+int osmo_iofd_sendto_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendto_flags, const struct osmo_sockaddr *dest)
+{
+ int rc;
+
+ if (OSMO_UNLIKELY(msgb_length(msg) == 0)) {
+ LOGPIO(iofd, LOGL_ERROR, "Length is 0, rejecting msgb.\n");
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_RECVFROM_SENDTO);
+
+ struct iofd_msghdr *msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_SENDTO, msg, 0);
+ if (!msghdr)
+ return -ENOMEM;
+
+ if (dest) {
+ msghdr->osa = *dest;
+ msghdr->hdr.msg_name = &msghdr->osa.u.sa;
+ msghdr->hdr.msg_namelen = osmo_sockaddr_size(&msghdr->osa);
+ }
+ msghdr->flags = sendto_flags;
+ msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
+ msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
+ msghdr->hdr.msg_iov = &msghdr->iov[0];
+ msghdr->hdr.msg_iovlen = 1;
+
+ rc = iofd_txqueue_enqueue(iofd, msghdr);
+ if (rc < 0) {
+ iofd_msghdr_free(msghdr);
+ LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+/*! osmo_io equivalent of the sendmsg(2) socket API call.
+ * The osmo_io_fd must be using OSMO_IO_FD_MODE_RECVMSG_SENDMSG.
+ *
+ * Appends the message to the internal transmit queue for eventual non-blocking
+ * sendmsg on the underlying socket/file descriptor.
+ *
+ * If the function returns success (0), it will take ownership of the msgb and
+ * internally call msgb_free() after the sendmsg request completes.
+ * In case of an error the msgb needs to be freed by the caller.
+ *
+ * \param[in] iofd file descriptor to write to
+ * \param[in] msg message buffer to send; is used to fill msgh->iov[]
+ * \param[in] sendmsg_flags Flags to pass to the send call
+ * \param[in] msgh 'struct msghdr' for name/control/flags. iov must be empty!
+ * \returns 0 in case of success; a negative value in case of error
+ */
+int osmo_iofd_sendmsg_msgb(struct osmo_io_fd *iofd, struct msgb *msg, int sendmsg_flags, const struct msghdr *msgh)
+{
+ int rc;
+ struct iofd_msghdr *msghdr;
+
+ if (OSMO_UNLIKELY(msgb_length(msg) == 0)) {
+ LOGPIO(iofd, LOGL_ERROR, "Length is 0, rejecting msgb.\n");
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG);
+
+ if (OSMO_UNLIKELY(msgh->msg_namelen > sizeof(msghdr->osa))) {
+ LOGPIO(iofd, LOGL_ERROR, "osmo_iofd_sendmsg msg_namelen (%u) > supported %zu bytes\n",
+ msgh->msg_namelen, sizeof(msghdr->osa));
+ return -EINVAL;
+ }
+
+ if (OSMO_UNLIKELY(msgh->msg_iovlen)) {
+ LOGPIO(iofd, LOGL_ERROR, "osmo_iofd_sendmsg must have all in 'struct msgb', not in 'msg_iov'\n");
+ return -EINVAL;
+ }
+
+ msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_SENDMSG, msg, msgh->msg_controllen);
+ if (!msghdr)
+ return -ENOMEM;
+
+ /* copy over optional address */
+ if (msgh->msg_name) {
+ memcpy(&msghdr->osa, msgh->msg_name, msgh->msg_namelen);
+ msghdr->hdr.msg_name = &msghdr->osa.u.sa;
+ msghdr->hdr.msg_namelen = msgh->msg_namelen;
+ }
+
+ /* build iov from msgb */
+ msghdr->iov[0].iov_base = msgb_data(msghdr->msg);
+ msghdr->iov[0].iov_len = msgb_length(msghdr->msg);
+ msghdr->hdr.msg_iov = &msghdr->iov[0];
+ msghdr->hdr.msg_iovlen = 1;
+
+ /* copy over the cmsg from the msghdr */
+ if (msgh->msg_control && msgh->msg_controllen) {
+ msghdr->hdr.msg_control = msghdr->cmsg;
+ msghdr->hdr.msg_controllen = msgh->msg_controllen;
+ memcpy(msghdr->cmsg, msgh->msg_control, msgh->msg_controllen);
+ }
+
+ /* copy over msg_flags */
+ msghdr->hdr.msg_flags = sendmsg_flags;
+
+ rc = iofd_txqueue_enqueue(iofd, msghdr);
+ if (rc < 0) {
+ iofd_msghdr_free(msghdr);
+ LOGPIO(iofd, LOGL_ERROR, "enqueueing message failed (%d). Rejecting msgb\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int check_mode_callback_compat(enum osmo_io_fd_mode mode, const struct osmo_io_ops *ops)
+{
+ switch (mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ if (ops->recvfrom_cb || ops->sendto_cb)
+ return false;
+ if (ops->recvmsg_cb || ops->sendmsg_cb)
+ return false;
+ /* Forbid both segementation_cb set, something is wrong: */
+ if (ops->segmentation_cb && ops->segmentation_cb2)
+ return false;
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ if (ops->read_cb || ops->write_cb)
+ return false;
+ if (ops->recvmsg_cb || ops->sendmsg_cb)
+ return false;
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ if (ops->recvfrom_cb || ops->sendto_cb)
+ return false;
+ if (ops->read_cb || ops->write_cb)
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+/*! Allocate and setup a new iofd.
+ *
+ * Use this to create a new osmo_io_fd, specifying the osmo_io_fd_mode and osmo_io_ops, as well as optionally
+ * the file-descriptor number and a human-readable name. This is the first function you call for any
+ * osmo_io_fd.
+ *
+ * The created osmo_io_fd is not yet registered, and hence can not be used for any I/O until a subsequent
+ * call to osmo_iofd_register().
+ *
+ * The created osmo_io_fd is initialized with some default settings:
+ * * msgb allocations size: OSMO_IO_DEFAULT_MSGB_SIZE (1024)
+ * * msgb headroom: OSMO_IO_DEFAULT_MSGB_HEADROOM (128)
+ * * tx_queue depth: 32
+ *
+ * Those values may be adjusted from their defaults by using osmo_iofd_set_alloc_info() and
+ * osmo_iofd_set_txqueue_max_length() on the osmo_io_fd.
+ *
+ * \param[in] ctx the parent context from which to allocate
+ * \param[in] fd the underlying system file descriptor. May be -1 if not known yet; must then be specified
+ * at subsequent osmo_iofd_register() time.
+ * \param[in] name the optional human-readable name of the iofd; may be NULL
+ * \param[in] mode the osmo_io_fd_mode of the iofd, whether it should use read()/write(), sendto()/recvfrom()
+ * semantics.
+ * \param[in] ioops structure specifying the read/write/send/recv callbacks. Will be copied to the iofd, so
+ * the caller does not have to keep it around after issuing the osmo_iofd_setup call.
+ * \param[in] data opaque user data pointer accessible by the ioops callbacks
+ * \returns The newly allocated osmo_io_fd struct or NULL on failure
+ */
+struct osmo_io_fd *osmo_iofd_setup(const void *ctx, int fd, const char *name, enum osmo_io_fd_mode mode,
+ const struct osmo_io_ops *ioops, void *data)
+{
+ struct osmo_io_fd *iofd;
+
+ /* reject unsupported/unknown modes */
+ switch (mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ break;
+ default:
+ return NULL;
+ }
+
+ if (ioops && !check_mode_callback_compat(mode, ioops)) {
+ LOGP(DLIO, LOGL_ERROR, "iofd(%s): rejecting call-backs incompatible with mode %s\n",
+ name ? name : "unknown", osmo_iofd_mode_name(mode));
+ return NULL;
+ }
+
+ iofd = talloc_zero(ctx, struct osmo_io_fd);
+ if (!iofd)
+ return NULL;
+
+ iofd->fd = fd;
+ iofd->mode = mode;
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_CLOSED);
+
+ if (name)
+ iofd->name = talloc_strdup(iofd, name);
+
+ if (ioops)
+ iofd->io_ops = *ioops;
+
+ iofd->pending = NULL;
+
+ iofd->data = data;
+
+ iofd->msgb_alloc.ctx = ctx;
+ iofd->msgb_alloc.size = OSMO_IO_DEFAULT_MSGB_SIZE;
+ iofd->msgb_alloc.headroom = OSMO_IO_DEFAULT_MSGB_HEADROOM;
+
+ iofd->tx_queue.max_length = 32;
+ INIT_LLIST_HEAD(&iofd->tx_queue.msg_queue);
+
+ return iofd;
+}
+
+/*! Set the size of the control message buffer allocated when submitting recvmsg.
+ *
+ * If your osmo_io_fd is in OSMO_IO_FD_MODE_RECVMSG_SENDMSG mode, this API function can be used to tell the
+ * osmo_io code how much memory should be allocated for the cmsg (control message) buffer when performing
+ * recvmsg(). */
+int osmo_iofd_set_cmsg_size(struct osmo_io_fd *iofd, size_t cmsg_size)
+{
+ if (iofd->mode != OSMO_IO_FD_MODE_RECVMSG_SENDMSG)
+ return -EINVAL;
+
+ iofd->cmsg_size = cmsg_size;
+ return 0;
+}
+
+/*! Register the osmo_io_fd for active I/O.
+ *
+ * Calling this function will register a previously initialized osmo_io_fd for performing I/O.
+ *
+ * If the osmo_iofd has a read_cb/recvfrom_cb_recvmsg_cb set in its osmo_io_ops, read/receive will be
+ * automatically enabled and the respective call-back is called at any time data becomes available.
+ *
+ * If there is to-be-transmitted data in the transmit queue, write will be automatically enabled, allowing
+ * the transmit queue to be drained as soon as the fd/socket becomes writable.
+ *
+ * \param[in] iofd the iofd file descriptor
+ * \param[in] fd the system fd number that will be registered. If you did not yet specify the file descriptor
+ * number during osmo_fd_setup(), or if it has changed since then, you can state the [new] file descriptor
+ * number as argument. If you wish to proceed with the previously specified file descriptor number, pass -1.
+ * \returns zero on success, a negative value on error
+*/
+int osmo_iofd_register(struct osmo_io_fd *iofd, int fd)
+{
+ int rc = 0;
+
+ if (fd >= 0)
+ iofd->fd = fd;
+ else if (iofd->fd < 0) {
+ /* this might happen if both osmo_iofd_setup() and osmo_iofd_register() are called with -1 */
+ LOGPIO(iofd, LOGL_ERROR, "Cannot register io_fd using invalid fd == %d\n", iofd->fd);
+ return -EBADF;
+ }
+
+ rc = osmo_iofd_ops.register_fd(iofd);
+ if (rc)
+ return rc;
+
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_CLOSED);
+ if ((iofd->mode == OSMO_IO_FD_MODE_READ_WRITE && iofd->io_ops.read_cb) ||
+ (iofd->mode == OSMO_IO_FD_MODE_RECVFROM_SENDTO && iofd->io_ops.recvfrom_cb) ||
+ (iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG && iofd->io_ops.recvmsg_cb)) {
+ osmo_iofd_ops.read_enable(iofd);
+ }
+
+ if (iofd->tx_queue.current_length > 0)
+ osmo_iofd_ops.write_enable(iofd);
+
+ return rc;
+}
+
+/*! Unregister the given osmo_io_fd from osmo_io.
+ *
+ * After an osmo_io_fd has been successfully unregistered, it can no longer perform any I/O via osmo_io.
+ * However, it can be subsequently re-registered using osmo_iofd_register().
+ *
+ * \param[in] iofd the file descriptor
+ * \returns zero on success, a negative value on error
+ */
+int osmo_iofd_unregister(struct osmo_io_fd *iofd)
+{
+ return osmo_iofd_ops.unregister_fd(iofd);
+}
+
+/*! Retrieve the number of messages pending in the transmit queue.
+ *
+ * \param[in] iofd the file descriptor
+ */
+unsigned int osmo_iofd_txqueue_len(struct osmo_io_fd *iofd)
+{
+ return iofd->tx_queue.current_length;
+}
+
+/*! Clear the transmit queue of the given osmo_io_fd.
+ *
+ * This function frees all messages currently pending in the transmit queue
+ * \param[in] iofd the file descriptor
+ */
+void osmo_iofd_txqueue_clear(struct osmo_io_fd *iofd)
+{
+ struct iofd_msghdr *hdr;
+ while ((hdr = iofd_txqueue_dequeue(iofd))) {
+ msgb_free(hdr->msg);
+ iofd_msghdr_free(hdr);
+ }
+}
+
+/*! Free the given osmo_io_fd.
+ *
+ * The iofd will be automatically closed before via osmo_iofd_close() [which in turn will unregister
+ * it and clear any pending transmit queue items]. You must not reference the iofd
+ * after calling this function. However, it is safe to call this function from any of osmo_io
+ * call-backs; in this case, actual free will be internally delayed until that call-back completes.
+ *
+ * \param[in] iofd the file descriptor
+ */
+void osmo_iofd_free(struct osmo_io_fd *iofd)
+{
+ if (!iofd)
+ return;
+
+ osmo_iofd_close(iofd);
+
+ if (!IOFD_FLAG_ISSET(iofd, IOFD_FLAG_IN_CALLBACK)) {
+ talloc_free(iofd);
+ } else {
+ /* Prevent our parent context from freeing us prematurely */
+ talloc_steal(NULL, iofd);
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_TO_FREE);
+ }
+}
+
+/*! Close the given osmo_io_fd.
+ *
+ * This function closes the underlying fd, unregisters it from osmo_io and clears any messages in the tx
+ * queue. The iofd itself is not freed and can be assigned a new file descriptor with osmo_iofd_register()
+ * \param[in] iofd the file descriptor
+ * \returns 0 on success, a negative value otherwise
+ */
+int osmo_iofd_close(struct osmo_io_fd *iofd)
+{
+ int rc = 0;
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ return rc;
+
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_CLOSED);
+
+ /* Free pending msgs in tx queue */
+ osmo_iofd_txqueue_clear(iofd);
+ msgb_free(iofd->pending);
+
+ iofd->pending = NULL;
+
+ rc = osmo_iofd_ops.close(iofd);
+ iofd->fd = -1;
+ return rc;
+}
+
+/*! Set the size and headroom of the msgb allocated when receiving messages.
+ * \param[in] iofd the file descriptor
+ * \param[in] size the size of the msgb when receiving data
+ * \param[in] headroom the headroom of the msgb when receiving data
+ */
+void osmo_iofd_set_alloc_info(struct osmo_io_fd *iofd, unsigned int size, unsigned int headroom)
+{
+ iofd->msgb_alloc.headroom = headroom;
+ iofd->msgb_alloc.size = size;
+}
+
+/*! Set the maximum number of messages enqueued for sending.
+ * \param[in] iofd the file descriptor
+ * \param[in] size the maximum size of the transmit queue
+ */
+void osmo_iofd_set_txqueue_max_length(struct osmo_io_fd *iofd, unsigned int max_length)
+{
+ iofd->tx_queue.max_length = max_length;
+}
+
+/*! Retrieve the associated user-data from an osmo_io_fd.
+ *
+ * A call to this function will return the opaque user data pointer which was specified previously
+ * via osmo_iofd_setup() or via osmo_iofd_set_data().
+ *
+ * \param[in] iofd the file descriptor
+ * \returns the data that was previously set with \ref osmo_iofd_setup()
+ */
+void *osmo_iofd_get_data(const struct osmo_io_fd *iofd)
+{
+ return iofd->data;
+}
+
+/*! Set the associated user-data from an osmo_io_fd.
+ *
+ * Calling this function will set/overwrite the opaque user data pointer, which can later be retrieved using
+ * osmo_iofd_get_data().
+ *
+ * \param[in] iofd the file descriptor
+ * \param[in] data the data to set
+ */
+void osmo_iofd_set_data(struct osmo_io_fd *iofd, void *data)
+{
+ iofd->data = data;
+}
+
+/*! Retrieve the private number from an osmo_io_fd.
+ * Calling this function will retrieve the private user number previously set via osmo_iofd_set_priv_nr().
+ * \param[in] iofd the file descriptor
+ * \returns the private number that was previously set with \ref osmo_iofd_set_priv_nr()
+ */
+unsigned int osmo_iofd_get_priv_nr(const struct osmo_io_fd *iofd)
+{
+ return iofd->priv_nr;
+}
+
+/*! Set the private number of an osmo_io_fd.
+ * The priv_nr passed in via this call can later be retrieved via osmo_iofd_get_priv_nr(). It provides
+ * a way how additional context can be stored in the osmo_io_fd beyond the opaque 'data' pointer.
+ * \param[in] iofd the file descriptor
+ * \param[in] priv_nr the private number to set
+ */
+void osmo_iofd_set_priv_nr(struct osmo_io_fd *iofd, unsigned int priv_nr)
+{
+ iofd->priv_nr = priv_nr;
+}
+
+/*! Retrieve the underlying file descriptor from an osmo_io_fd.
+ * \param[in] iofd the file descriptor
+ * \returns the underlying file descriptor number */
+int osmo_iofd_get_fd(const struct osmo_io_fd *iofd)
+{
+ return iofd->fd;
+}
+
+/*! Retrieve the human-readable name of the given osmo_io_fd.
+ * \param[in] iofd the file descriptor
+ * \returns the name of the iofd as given in \ref osmo_iofd_setup() */
+const char *osmo_iofd_get_name(const struct osmo_io_fd *iofd)
+{
+ return iofd->name;
+}
+
+/*! Set the human-readable name of the file descriptor.
+ * The given name will be used as context by all related logging and future calls to osmo_iofd_get_name().
+ * \param[in] iofd the file descriptor
+ * \param[in] name the name to set on the file descriptor */
+void osmo_iofd_set_name(struct osmo_io_fd *iofd, const char *name)
+{
+ osmo_talloc_replace_string(iofd, &iofd->name, name);
+}
+
+/*! Set the osmo_io_ops calbacks for an osmo_io_fd.
+ * This function can be used to update/overwrite the call-back functions for the given osmo_io_fd; it
+ * replaces the currently-set call-back function pointers from a previous call to osmo_iofd_set_ioops()
+ * or the original osmo_iofd_setup().
+ * \param[in] iofd Target iofd file descriptor
+ * \param[in] ioops osmo_io_ops structure to be copied to the osmo_io_fd.
+ * \returns 0 on success, negative on error */
+int osmo_iofd_set_ioops(struct osmo_io_fd *iofd, const struct osmo_io_ops *ioops)
+{
+ if (!check_mode_callback_compat(iofd->mode, ioops)) {
+ LOGPIO(iofd, LOGL_ERROR, "rejecting call-backs incompatible with mode %s\n",
+ osmo_iofd_mode_name(iofd->mode));
+ return -EINVAL;
+ }
+
+ iofd->io_ops = *ioops;
+
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ if (iofd->io_ops.read_cb)
+ osmo_iofd_ops.read_enable(iofd);
+ else
+ osmo_iofd_ops.read_disable(iofd);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ if (iofd->io_ops.recvfrom_cb)
+ osmo_iofd_ops.read_enable(iofd);
+ else
+ osmo_iofd_ops.read_disable(iofd);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ if (iofd->io_ops.recvmsg_cb)
+ osmo_iofd_ops.read_enable(iofd);
+ else
+ osmo_iofd_ops.read_disable(iofd);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ return 0;
+}
+
+/*! Retrieve the osmo_io_ops for an iofd.
+ * \param[in] iofd Target iofd file descriptor
+ * \param[in] ioops caller-allocated osmo_io_ops structure to be filled */
+void osmo_iofd_get_ioops(struct osmo_io_fd *iofd, struct osmo_io_ops *ioops)
+{
+ *ioops = iofd->io_ops;
+}
+
+/*! Request notification of the user if/when a client socket is connected.
+ * Calling this function will request osmo_io to notify the user (via
+ * write call-back) once a non-blocking outbound connect() of the
+ * socket completes.
+ *
+ * This only works for connection oriented sockets in either
+ * OSMO_IO_FD_MODE_READ_WRITE or OSMO_IO_FD_MODE_RECVMSG_SENDMSG mode.
+ *
+ * \param[in] iofd the file descriptor */
+void osmo_iofd_notify_connected(struct osmo_io_fd *iofd)
+{
+ OSMO_ASSERT(iofd->mode == OSMO_IO_FD_MODE_READ_WRITE ||
+ iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG);
+ osmo_iofd_ops.notify_connected(iofd);
+}
+
+/*! @} */
+
+#endif /* ifndef(EMBEDDED) */
diff --git a/src/core/osmo_io_internal.h b/src/core/osmo_io_internal.h
new file mode 100644
index 00000000..a4b0749d
--- /dev/null
+++ b/src/core/osmo_io_internal.h
@@ -0,0 +1,166 @@
+/*! \file osmo_io_internal.h */
+
+#pragma once
+
+#include <unistd.h>
+#include <stdbool.h>
+#include <netinet/sctp.h>
+
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+
+#include "../config.h"
+
+#define OSMO_IO_DEFAULT_MSGB_SIZE 1024
+#define OSMO_IO_DEFAULT_MSGB_HEADROOM 128
+
+extern const struct iofd_backend_ops iofd_poll_ops;
+#define OSMO_IO_BACKEND_DEFAULT "POLL"
+
+#if defined(HAVE_URING)
+extern const struct iofd_backend_ops iofd_uring_ops;
+#endif
+
+struct iofd_backend_ops {
+ int (*register_fd)(struct osmo_io_fd *iofd);
+ int (*unregister_fd)(struct osmo_io_fd *iofd);
+ int (*close)(struct osmo_io_fd *iofd);
+ void (*write_enable)(struct osmo_io_fd *iofd);
+ void (*write_disable)(struct osmo_io_fd *iofd);
+ void (*read_enable)(struct osmo_io_fd *iofd);
+ void (*read_disable)(struct osmo_io_fd *iofd);
+ void (*notify_connected)(struct osmo_io_fd *iofd);
+};
+
+#define IOFD_FLAG_CLOSED (1<<0)
+#define IOFD_FLAG_IN_CALLBACK (1<<1)
+#define IOFD_FLAG_TO_FREE (1<<2)
+#define IOFD_FLAG_NOTIFY_CONNECTED (1<<3)
+#define IOFD_FLAG_FD_REGISTERED (1<<4)
+
+#define IOFD_FLAG_SET(iofd, flag) \
+ (iofd)->flags |= (flag)
+
+#define IOFD_FLAG_UNSET(iofd, flag) \
+ (iofd)->flags &= ~(flag)
+
+#define IOFD_FLAG_ISSET(iofd, flag) ((iofd)->flags & (flag))
+
+struct osmo_io_fd {
+ /*! linked list for internal management */
+ struct llist_head list;
+ /*! actual operating-system level file decriptor */
+ int fd;
+ /*! type of read/write mode to use */
+ enum osmo_io_fd_mode mode;
+
+ /*! flags to guard closing/freeing of iofd */
+ uint32_t flags;
+
+ /*! human-readable name to associte with fd */
+ char *name;
+
+ /*! send/recv (msg) callback functions */
+ struct osmo_io_ops io_ops;
+ /*! Pending msgb to keep partial data during segmentation */
+ struct msgb *pending;
+
+ /*! data pointer passed through to call-back function */
+ void *data;
+ /*! private number, extending \a data */
+ unsigned int priv_nr;
+
+ /*! size of iofd_msghdr.cmsg[] when allocated in recvmsg path */
+ size_t cmsg_size;
+
+ struct {
+ /*! talloc context from which to allocate msgb when reading */
+ const void *ctx;
+ /*! size of msgb to allocate (excluding headroom) */
+ unsigned int size;
+ /*! headroom to allocate when allocating msgb's */
+ unsigned int headroom;
+ } msgb_alloc;
+
+ struct {
+ /*! maximum length of write queue */
+ unsigned int max_length;
+ /*! current length of write queue */
+ unsigned int current_length;
+ /*! actual linked list implementing the transmit queue */
+ struct llist_head msg_queue;
+ } tx_queue;
+
+ union {
+ struct {
+ struct osmo_fd ofd;
+ } poll;
+ struct {
+ bool read_enabled;
+ bool write_enabled;
+ void *read_msghdr;
+ void *write_msghdr;
+ /* TODO: index into array of registered fd's? */
+ /* osmo_fd for non-blocking connect handling */
+ struct osmo_fd connect_ofd;
+ } uring;
+ } u;
+};
+
+enum iofd_msg_action {
+ IOFD_ACT_READ,
+ IOFD_ACT_WRITE,
+ IOFD_ACT_RECVFROM,
+ IOFD_ACT_SENDTO,
+ IOFD_ACT_RECVMSG,
+ IOFD_ACT_SENDMSG,
+};
+
+
+/*! serialized version of 'struct msghdr' employed by sendmsg/recvmsg */
+struct iofd_msghdr {
+ /*! entry into osmo_io_fd.tx_queue.msg_queue */
+ struct llist_head list;
+ enum iofd_msg_action action;
+ /*! the 'struct msghdr' we are wrapping/ecapsulating here */
+ struct msghdr hdr;
+ /*! socket address of the remote peer */
+ struct osmo_sockaddr osa;
+ /*! io-vector we need to pass as argument to sendmsg/recvmsg; is set up
+ * to point into msg below */
+ struct iovec iov[1];
+ /*! flags we pass as argument to sendmsg / recvmsg */
+ int flags;
+
+ /*! message-buffer containing data for this I/O operation */
+ struct msgb *msg;
+ /*! I/O file descriptor on which we perform this I/O operation */
+ struct osmo_io_fd *iofd;
+
+ /*! control message buffer for passing sctp_sndrcvinfo along */
+ char cmsg[0]; /* size is determined by iofd->cmsg_size on recvmsg, and by mcghdr->msg_controllen on sendmsg */
+};
+
+enum iofd_seg_act {
+ IOFD_SEG_ACT_HANDLE_ONE,
+ IOFD_SEG_ACT_HANDLE_MORE,
+ IOFD_SEG_ACT_DEFER,
+};
+
+struct iofd_msghdr *iofd_msghdr_alloc(struct osmo_io_fd *iofd, enum iofd_msg_action action, struct msgb *msg, size_t cmsg_size);
+void iofd_msghdr_free(struct iofd_msghdr *msghdr);
+
+struct msgb *iofd_msgb_alloc(struct osmo_io_fd *iofd);
+struct msgb *iofd_msgb_pending(struct osmo_io_fd *iofd);
+struct msgb *iofd_msgb_pending_or_alloc(struct osmo_io_fd *iofd);
+
+void iofd_handle_recv(struct osmo_io_fd *iofd, struct msgb *msg, int rc, struct iofd_msghdr *msghdr);
+void iofd_handle_send_completion(struct osmo_io_fd *iofd, int rc, struct iofd_msghdr *msghdr);
+void iofd_handle_segmented_read(struct osmo_io_fd *iofd, struct msgb *msg, int rc);
+
+int iofd_txqueue_enqueue(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr);
+void iofd_txqueue_enqueue_front(struct osmo_io_fd *iofd, struct iofd_msghdr *msghdr);
+struct iofd_msghdr *iofd_txqueue_dequeue(struct osmo_io_fd *iofd);
diff --git a/src/core/osmo_io_poll.c b/src/core/osmo_io_poll.c
new file mode 100644
index 00000000..c4bb376d
--- /dev/null
+++ b/src/core/osmo_io_poll.c
@@ -0,0 +1,201 @@
+/*! \file osmo_io_poll.c
+ * New osmocom async I/O API.
+ *
+ * (C) 2022 by Harald Welte <laforge@osmocom.org>
+ * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Daniel Willmann <dwillmann@sysmocom.de>
+ *
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "../config.h"
+#ifndef EMBEDDED
+
+#include <errno.h>
+#include <stdio.h>
+#include <talloc.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include "osmo_io_internal.h"
+
+static void iofd_poll_ofd_cb_recvmsg_sendmsg(struct osmo_fd *ofd, unsigned int what)
+{
+ struct osmo_io_fd *iofd = ofd->data;
+ struct msgb *msg;
+ int rc, flags = 0;
+
+ if (what & OSMO_FD_READ) {
+ struct iofd_msghdr hdr;
+
+ msg = iofd_msgb_pending_or_alloc(iofd);
+ if (!msg) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msgb for reading\n");
+ OSMO_ASSERT(0);
+ }
+
+ hdr.msg = msg;
+ hdr.iov[0].iov_base = msg->tail;
+ hdr.iov[0].iov_len = msgb_tailroom(msg);
+ hdr.hdr = (struct msghdr) {
+ .msg_iov = &hdr.iov[0],
+ .msg_iovlen = 1,
+ .msg_name = &hdr.osa.u.sa,
+ .msg_namelen = sizeof(struct osmo_sockaddr),
+ };
+ if (iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG) {
+ hdr.hdr.msg_control = alloca(iofd->cmsg_size);
+ hdr.hdr.msg_controllen = iofd->cmsg_size;
+ }
+
+ rc = recvmsg(ofd->fd, &hdr.hdr, flags);
+ if (rc > 0)
+ msgb_put(msg, rc);
+
+ iofd_handle_recv(iofd, msg, (rc < 0 && errno > 0) ? -errno : rc, &hdr);
+ }
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ return;
+
+ if (what & OSMO_FD_WRITE) {
+ struct iofd_msghdr *msghdr = iofd_txqueue_dequeue(iofd);
+ if (msghdr) {
+ rc = sendmsg(ofd->fd, &msghdr->hdr, msghdr->flags);
+ iofd_handle_send_completion(iofd, (rc < 0 && errno > 0) ? -errno : rc, msghdr);
+ } else {
+ /* Socket is writable, but we have no data to send. A non-blocking/async
+ connect() is signalled this way. */
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ iofd->io_ops.write_cb(iofd, 0, NULL);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ iofd->io_ops.sendto_cb(iofd, 0, NULL, NULL);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ iofd->io_ops.sendmsg_cb(iofd, 0, NULL);
+ break;
+ default:
+ break;
+ }
+ if (osmo_iofd_txqueue_len(iofd) == 0)
+ iofd_poll_ops.write_disable(iofd);
+ }
+ }
+}
+
+static int iofd_poll_ofd_cb_dispatch(struct osmo_fd *ofd, unsigned int what)
+{
+ struct osmo_io_fd *iofd = ofd->data;
+
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_IN_CALLBACK);
+ iofd_poll_ofd_cb_recvmsg_sendmsg(ofd, what);
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_IN_CALLBACK);
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_TO_FREE)) {
+ talloc_free(iofd);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int iofd_poll_register(struct osmo_io_fd *iofd)
+{
+ struct osmo_fd *ofd = &iofd->u.poll.ofd;
+ int rc;
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_FD_REGISTERED))
+ return 0;
+ osmo_fd_setup(ofd, iofd->fd, 0, &iofd_poll_ofd_cb_dispatch, iofd, 0);
+ rc = osmo_fd_register(ofd);
+ if (!rc)
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_FD_REGISTERED);
+ return rc;
+}
+
+static int iofd_poll_unregister(struct osmo_io_fd *iofd)
+{
+ struct osmo_fd *ofd = &iofd->u.poll.ofd;
+
+ if (!IOFD_FLAG_ISSET(iofd, IOFD_FLAG_FD_REGISTERED))
+ return 0;
+ osmo_fd_unregister(ofd);
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_FD_REGISTERED);
+
+ return 0;
+}
+
+static int iofd_poll_close(struct osmo_io_fd *iofd)
+{
+ iofd_poll_unregister(iofd);
+ osmo_fd_close(&iofd->u.poll.ofd);
+
+ return 0;
+}
+
+static void iofd_poll_read_enable(struct osmo_io_fd *iofd)
+{
+ osmo_fd_read_enable(&iofd->u.poll.ofd);
+}
+
+static void iofd_poll_read_disable(struct osmo_io_fd *iofd)
+{
+ osmo_fd_read_disable(&iofd->u.poll.ofd);
+}
+
+static void iofd_poll_write_enable(struct osmo_io_fd *iofd)
+{
+ osmo_fd_write_enable(&iofd->u.poll.ofd);
+}
+
+static void iofd_poll_write_disable(struct osmo_io_fd *iofd)
+{
+ osmo_fd_write_disable(&iofd->u.poll.ofd);
+}
+
+static void iofd_poll_notify_connected(struct osmo_io_fd *iofd)
+{
+ int rc;
+
+ rc = iofd_poll_register(iofd);
+ if (rc < 0)
+ return;
+ osmo_fd_write_enable(&iofd->u.poll.ofd);
+}
+
+const struct iofd_backend_ops iofd_poll_ops = {
+ .register_fd = iofd_poll_register,
+ .unregister_fd = iofd_poll_unregister,
+ .close = iofd_poll_close,
+ .write_enable = iofd_poll_write_enable,
+ .write_disable = iofd_poll_write_disable,
+ .read_enable = iofd_poll_read_enable,
+ .read_disable = iofd_poll_read_disable,
+ .notify_connected = iofd_poll_notify_connected,
+};
+
+#endif /* ifndef EMBEDDED */
diff --git a/src/core/osmo_io_uring.c b/src/core/osmo_io_uring.c
new file mode 100644
index 00000000..569f1505
--- /dev/null
+++ b/src/core/osmo_io_uring.c
@@ -0,0 +1,532 @@
+/*! \file osmo_io_uring.c
+ * io_uring backend for osmo_io.
+ *
+ * (C) 2022-2023 by sysmocom s.f.m.c.
+ * Author: Daniel Willmann <daniel@sysmocom.de>
+ * (C) 2023-2024 by Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* TODO:
+ * Parameters:
+ * - number of simultaneous read/write in uring for given fd
+ *
+ */
+
+#include "../config.h"
+#if defined(__linux__)
+
+#include <stdio.h>
+#include <talloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <netinet/sctp.h>
+#include <sys/eventfd.h>
+#include <liburing.h>
+
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/socket.h>
+
+#include "osmo_io_internal.h"
+
+#define IOFD_URING_ENTRIES 4096
+
+struct osmo_io_uring {
+ struct osmo_fd event_ofd;
+ struct io_uring ring;
+};
+
+static __thread struct osmo_io_uring g_ring;
+
+static void iofd_uring_cqe(struct io_uring *ring);
+
+/*! read call-back for eventfd notifying us if entries are in the completion queue */
+static int iofd_uring_poll_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ struct io_uring *ring = ofd->data;
+ eventfd_t val;
+ int rc;
+
+ if (what & OSMO_FD_READ) {
+ rc = eventfd_read(ofd->fd, &val);
+ if (rc < 0) {
+ LOGP(DLIO, LOGL_ERROR, "eventfd_read() returned error\n");
+ return rc;
+ }
+
+ iofd_uring_cqe(ring);
+ }
+ if (what & OSMO_FD_WRITE)
+ OSMO_ASSERT(0);
+
+ return 0;
+}
+
+/*! initialize the uring and tie it into our event loop */
+void osmo_iofd_uring_init(void)
+{
+ int rc, evfd;
+
+ rc = io_uring_queue_init(IOFD_URING_ENTRIES, &g_ring.ring, 0);
+ if (rc < 0)
+ osmo_panic("failure during io_uring_queue_init(): %s\n", strerror(-rc));
+
+ rc = eventfd(0, 0);
+ if (rc < 0) {
+ io_uring_queue_exit(&g_ring.ring);
+ osmo_panic("failure creating eventfd(0, 0) for io_uring: %s\n", strerror(-rc));
+ }
+ evfd = rc;
+
+ osmo_fd_setup(&g_ring.event_ofd, evfd, OSMO_FD_READ, iofd_uring_poll_cb, &g_ring.ring, 0);
+ rc = osmo_fd_register(&g_ring.event_ofd);
+ if (rc < 0) {
+ close(evfd);
+ io_uring_queue_exit(&g_ring.ring);
+ osmo_panic("failure registering io_uring-eventfd as osmo_fd: %d\n", rc);
+ }
+ rc = io_uring_register_eventfd(&g_ring.ring, evfd);
+ if (rc < 0) {
+ osmo_fd_unregister(&g_ring.event_ofd);
+ close(evfd);
+ io_uring_queue_exit(&g_ring.ring);
+ osmo_panic("failure registering eventfd with io_uring: %s\n", strerror(-rc));
+ }
+}
+
+
+static void iofd_uring_submit_recv(struct osmo_io_fd *iofd, enum iofd_msg_action action)
+{
+ struct msgb *msg;
+ struct iofd_msghdr *msghdr;
+ struct io_uring_sqe *sqe;
+
+ msg = iofd_msgb_pending_or_alloc(iofd);
+ if (!msg) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msgb for reading\n");
+ OSMO_ASSERT(0);
+ }
+
+ msghdr = iofd_msghdr_alloc(iofd, action, msg, iofd->cmsg_size);
+ if (!msghdr) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msghdr for reading\n");
+ OSMO_ASSERT(0);
+ }
+
+ msghdr->iov[0].iov_base = msg->tail;
+ msghdr->iov[0].iov_len = msgb_tailroom(msg);
+
+ switch (action) {
+ case IOFD_ACT_READ:
+ break;
+ case IOFD_ACT_RECVMSG:
+ msghdr->hdr.msg_control = msghdr->cmsg;
+ msghdr->hdr.msg_controllen = iofd->cmsg_size;
+ /* fall-through */
+ case IOFD_ACT_RECVFROM:
+ msghdr->hdr.msg_iov = &msghdr->iov[0];
+ msghdr->hdr.msg_iovlen = 1;
+ msghdr->hdr.msg_name = &msghdr->osa.u.sa;
+ msghdr->hdr.msg_namelen = osmo_sockaddr_size(&msghdr->osa);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ if (!sqe) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not get io_uring_sqe\n");
+ OSMO_ASSERT(0);
+ }
+
+ switch (action) {
+ case IOFD_ACT_READ:
+ io_uring_prep_readv(sqe, iofd->fd, msghdr->iov, 1, 0);
+ break;
+ case IOFD_ACT_RECVMSG:
+ case IOFD_ACT_RECVFROM:
+ io_uring_prep_recvmsg(sqe, iofd->fd, &msghdr->hdr, msghdr->flags);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ io_uring_sqe_set_data(sqe, msghdr);
+
+ io_uring_submit(&g_ring.ring);
+ /* NOTE: This only works if we have one read per fd */
+ iofd->u.uring.read_msghdr = msghdr;
+}
+
+/*! completion call-back for READ/RECVFROM */
+static void iofd_uring_handle_recv(struct iofd_msghdr *msghdr, int rc)
+{
+ struct osmo_io_fd *iofd = msghdr->iofd;
+ struct msgb *msg = msghdr->msg;
+
+ if (rc > 0)
+ msgb_put(msg, rc);
+
+ if (!IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_handle_recv(iofd, msg, rc, msghdr);
+
+ if (iofd->u.uring.read_enabled && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_uring_submit_recv(iofd, msghdr->action);
+ else
+ iofd->u.uring.read_msghdr = NULL;
+
+
+ iofd_msghdr_free(msghdr);
+}
+
+static int iofd_uring_submit_tx(struct osmo_io_fd *iofd);
+
+/*! completion call-back for WRITE/SENDTO */
+static void iofd_uring_handle_tx(struct iofd_msghdr *msghdr, int rc)
+{
+ struct osmo_io_fd *iofd = msghdr->iofd;
+
+ /* Detach msghdr from iofd. It might get freed here or it is freed during iofd_handle_send_completion().
+ * If there is pending data to send, iofd_uring_submit_tx() will attach it again.
+ * iofd_handle_send_completion() will invoke a callback function to signal the possibility of write/send.
+ * This callback function might close iofd, leading to the potential freeing of iofd->u.uring.write_msghdr if
+ * still attached. Since iofd_handle_send_completion() frees msghdr at the end of the function, detaching
+ * msghdr here prevents a double-free bug. */
+ if (iofd->u.uring.write_msghdr == msghdr)
+ iofd->u.uring.write_msghdr = NULL;
+
+ if (OSMO_UNLIKELY(IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))) {
+ msgb_free(msghdr->msg);
+ iofd_msghdr_free(msghdr);
+ } else {
+ iofd_handle_send_completion(iofd, rc, msghdr);
+ }
+
+ /* submit the next to-be-transmitted message for this file descriptor */
+ if (iofd->u.uring.write_enabled && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_uring_submit_tx(iofd);
+}
+
+/*! handle completion of a single I/O message */
+static void iofd_uring_handle_completion(struct iofd_msghdr *msghdr, int res)
+{
+ struct osmo_io_fd *iofd = msghdr->iofd;
+
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_IN_CALLBACK);
+
+ switch (msghdr->action) {
+ case IOFD_ACT_READ:
+ case IOFD_ACT_RECVFROM:
+ case IOFD_ACT_RECVMSG:
+ iofd_uring_handle_recv(msghdr, res);
+ break;
+ case IOFD_ACT_WRITE:
+ case IOFD_ACT_SENDTO:
+ case IOFD_ACT_SENDMSG:
+ iofd_uring_handle_tx(msghdr, res);
+ break;
+ default:
+ OSMO_ASSERT(0)
+ }
+
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_IN_CALLBACK);
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_TO_FREE) && !iofd->u.uring.read_msghdr && !iofd->u.uring.write_msghdr)
+ talloc_free(iofd);
+}
+
+/*! process all pending completion queue entries in given io_uring */
+static void iofd_uring_cqe(struct io_uring *ring)
+{
+ int rc;
+ struct io_uring_cqe *cqe;
+ struct iofd_msghdr *msghdr;
+
+ while (io_uring_peek_cqe(ring, &cqe) == 0) {
+
+ msghdr = io_uring_cqe_get_data(cqe);
+ if (!msghdr) {
+ LOGP(DLIO, LOGL_DEBUG, "Cancellation returned\n");
+ io_uring_cqe_seen(ring, cqe);
+ continue;
+ }
+ if (!msghdr->iofd) {
+ io_uring_cqe_seen(ring, cqe);
+ iofd_msghdr_free(msghdr);
+ continue;
+ }
+
+ rc = cqe->res;
+ /* Hand the entry back to the kernel before */
+ io_uring_cqe_seen(ring, cqe);
+
+ iofd_uring_handle_completion(msghdr, rc);
+
+ }
+}
+
+/*! will submit the next to-be-transmitted message for given iofd */
+static int iofd_uring_submit_tx(struct osmo_io_fd *iofd)
+{
+ struct io_uring_sqe *sqe;
+ struct iofd_msghdr *msghdr;
+
+ msghdr = iofd_txqueue_dequeue(iofd);
+ if (!msghdr)
+ return -ENODATA;
+
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ if (!sqe) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not get io_uring_sqe\n");
+ OSMO_ASSERT(0);
+ }
+
+ io_uring_sqe_set_data(sqe, msghdr);
+
+ switch (msghdr->action) {
+ case IOFD_ACT_WRITE:
+ case IOFD_ACT_SENDTO:
+ case IOFD_ACT_SENDMSG:
+ io_uring_prep_sendmsg(sqe, msghdr->iofd->fd, &msghdr->hdr, msghdr->flags);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ io_uring_submit(&g_ring.ring);
+ iofd->u.uring.write_msghdr = msghdr;
+
+ return 0;
+}
+
+static void iofd_uring_write_enable(struct osmo_io_fd *iofd);
+static void iofd_uring_read_enable(struct osmo_io_fd *iofd);
+
+static int iofd_uring_register(struct osmo_io_fd *iofd)
+{
+ return 0;
+}
+
+static int iofd_uring_unregister(struct osmo_io_fd *iofd)
+{
+ struct io_uring_sqe *sqe;
+ struct iofd_msghdr *msghdr;
+
+ if (iofd->u.uring.read_msghdr) {
+ msghdr = iofd->u.uring.read_msghdr;
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ OSMO_ASSERT(sqe != NULL);
+ io_uring_sqe_set_data(sqe, NULL);
+ LOGPIO(iofd, LOGL_DEBUG, "Cancelling read\n");
+ iofd->u.uring.read_msghdr = NULL;
+ talloc_steal(OTC_GLOBAL, msghdr);
+ msghdr->iofd = NULL;
+ io_uring_prep_cancel(sqe, msghdr, 0);
+ }
+
+ if (iofd->u.uring.write_msghdr) {
+ msghdr = iofd->u.uring.write_msghdr;
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ OSMO_ASSERT(sqe != NULL);
+ io_uring_sqe_set_data(sqe, NULL);
+ LOGPIO(iofd, LOGL_DEBUG, "Cancelling write\n");
+ iofd->u.uring.write_msghdr = NULL;
+ talloc_steal(OTC_GLOBAL, msghdr);
+ msgb_free(msghdr->msg);
+ msghdr->iofd = NULL;
+ io_uring_prep_cancel(sqe, msghdr, 0);
+ }
+ io_uring_submit(&g_ring.ring);
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED)) {
+ osmo_fd_unregister(&iofd->u.uring.connect_ofd);
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED);
+ }
+
+ return 0;
+}
+
+static void iofd_uring_write_enable(struct osmo_io_fd *iofd)
+{
+ iofd->u.uring.write_enabled = true;
+
+ if (iofd->u.uring.write_msghdr)
+ return;
+
+ /* This function is called again, once the socket is connected. */
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED))
+ return;
+
+ if (osmo_iofd_txqueue_len(iofd) > 0)
+ iofd_uring_submit_tx(iofd);
+ else if (iofd->mode == OSMO_IO_FD_MODE_READ_WRITE) {
+ /* Empty write request to check when the socket is connected */
+ struct iofd_msghdr *msghdr;
+ struct io_uring_sqe *sqe;
+ struct msgb *msg = msgb_alloc_headroom(0, 0, "io_uring write dummy");
+ if (!msg) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msgb for writing\n");
+ OSMO_ASSERT(0);
+ }
+ msghdr = iofd_msghdr_alloc(iofd, IOFD_ACT_WRITE, msg, 0);
+ if (!msghdr) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not allocate msghdr for writing\n");
+ OSMO_ASSERT(0);
+ }
+
+ msghdr->iov[0].iov_base = msgb_data(msg);
+ msghdr->iov[0].iov_len = msgb_length(msg);
+
+ sqe = io_uring_get_sqe(&g_ring.ring);
+ if (!sqe) {
+ LOGPIO(iofd, LOGL_ERROR, "Could not get io_uring_sqe\n");
+ OSMO_ASSERT(0);
+ }
+ io_uring_prep_writev(sqe, iofd->fd, msghdr->iov, 1, 0);
+ io_uring_sqe_set_data(sqe, msghdr);
+
+ io_uring_submit(&g_ring.ring);
+ iofd->u.uring.write_msghdr = msghdr;
+ }
+}
+
+static void iofd_uring_write_disable(struct osmo_io_fd *iofd)
+{
+ iofd->u.uring.write_enabled = false;
+}
+
+static void iofd_uring_read_enable(struct osmo_io_fd *iofd)
+{
+ iofd->u.uring.read_enabled = true;
+
+ if (iofd->u.uring.read_msghdr)
+ return;
+
+ /* This function is called again, once the socket is connected. */
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED))
+ return;
+
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ iofd_uring_submit_recv(iofd, IOFD_ACT_READ);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ iofd_uring_submit_recv(iofd, IOFD_ACT_RECVFROM);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ iofd_uring_submit_recv(iofd, IOFD_ACT_RECVMSG);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void iofd_uring_read_disable(struct osmo_io_fd *iofd)
+{
+ iofd->u.uring.read_enabled = false;
+}
+
+static int iofd_uring_close(struct osmo_io_fd *iofd)
+{
+ iofd_uring_read_disable(iofd);
+ iofd_uring_write_disable(iofd);
+ iofd_uring_unregister(iofd);
+ return close(iofd->fd);
+}
+
+/* called via osmocom poll/select main handling once outbound non-blocking connect() completes */
+static int iofd_uring_connected_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ struct osmo_io_fd *iofd = ofd->data;
+
+ LOGPIO(iofd, LOGL_DEBUG, "Socket connected or failed.\n");
+
+ if (!(what & OSMO_FD_WRITE))
+ return 0;
+
+ /* Unregister from poll/select handling. */
+ osmo_fd_unregister(ofd);
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED);
+
+ /* Notify the application about this via a zero-length write completion call-back. */
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_IN_CALLBACK);
+ switch (iofd->mode) {
+ case OSMO_IO_FD_MODE_READ_WRITE:
+ iofd->io_ops.write_cb(iofd, 0, NULL);
+ break;
+ case OSMO_IO_FD_MODE_RECVFROM_SENDTO:
+ iofd->io_ops.sendto_cb(iofd, 0, NULL, NULL);
+ break;
+ case OSMO_IO_FD_MODE_RECVMSG_SENDMSG:
+ iofd->io_ops.sendmsg_cb(iofd, 0, NULL);
+ break;
+ }
+ IOFD_FLAG_UNSET(iofd, IOFD_FLAG_IN_CALLBACK);
+
+ /* If write/read notifications are pending, enable it now. */
+ if (iofd->u.uring.write_enabled && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_uring_write_enable(iofd);
+ if (iofd->u.uring.read_enabled && !IOFD_FLAG_ISSET(iofd, IOFD_FLAG_CLOSED))
+ iofd_uring_read_enable(iofd);
+
+ if (IOFD_FLAG_ISSET(iofd, IOFD_FLAG_TO_FREE) && !iofd->u.uring.read_msghdr && !iofd->u.uring.write_msghdr)
+ talloc_free(iofd);
+ return 0;
+}
+
+static void iofd_uring_notify_connected(struct osmo_io_fd *iofd)
+{
+ if (iofd->mode == OSMO_IO_FD_MODE_RECVMSG_SENDMSG) {
+ /* Don't call this function after enabling read or write. */
+ OSMO_ASSERT(!iofd->u.uring.write_enabled && !iofd->u.uring.read_enabled);
+
+ /* Use a temporary osmo_fd which we can use to notify us once the connection is established
+ * or failed (indicated by FD becoming writable).
+ * This is needed as (at least for SCTP sockets) one cannot submit a zero-length writev/sendmsg
+ * in order to get notification when the socekt is writable.*/
+ if (!IOFD_FLAG_ISSET(iofd, IOFD_FLAG_NOTIFY_CONNECTED)) {
+ osmo_fd_setup(&iofd->u.uring.connect_ofd, iofd->fd, OSMO_FD_WRITE,
+ iofd_uring_connected_cb, iofd, 0);
+ if (osmo_fd_register(&iofd->u.uring.connect_ofd) < 0)
+ LOGPIO(iofd, LOGL_ERROR, "Failed to register FD for connect event.\n");
+ else
+ IOFD_FLAG_SET(iofd, IOFD_FLAG_NOTIFY_CONNECTED);
+ }
+ } else
+ iofd_uring_write_enable(iofd);
+}
+
+const struct iofd_backend_ops iofd_uring_ops = {
+ .register_fd = iofd_uring_register,
+ .unregister_fd = iofd_uring_unregister,
+ .close = iofd_uring_close,
+ .write_enable = iofd_uring_write_enable,
+ .write_disable = iofd_uring_write_disable,
+ .read_enable = iofd_uring_read_enable,
+ .read_disable = iofd_uring_read_disable,
+ .notify_connected = iofd_uring_notify_connected,
+};
+
+#endif /* defined(__linux__) */
diff --git a/src/panic.c b/src/core/panic.c
index 072f458b..bbf6d081 100644
--- a/src/panic.c
+++ b/src/core/panic.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup utils
@@ -31,7 +27,7 @@
#include <osmocom/core/panic.h>
#include <osmocom/core/backtrace.h>
-#include "../config.h"
+#include "config.h"
static osmo_panic_handler_t osmo_panic_handler = (void*)0;
diff --git a/src/plugin.c b/src/core/plugin.c
index 40de4f8c..687ad406 100644
--- a/src/plugin.c
+++ b/src/core/plugin.c
@@ -17,17 +17,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup utils
* @{
* \file plugin.c */
-#include "../config.h"
+#include "config.h"
#if HAVE_DLFCN_H
diff --git a/src/prbs.c b/src/core/prbs.c
index 8fa04bb7..8fa04bb7 100644
--- a/src/prbs.c
+++ b/src/core/prbs.c
diff --git a/src/prim.c b/src/core/prim.c
index 3c8a7f12..3c8a7f12 100644
--- a/src/prim.c
+++ b/src/core/prim.c
diff --git a/src/core/probes.d b/src/core/probes.d
new file mode 100644
index 00000000..e4150f0c
--- /dev/null
+++ b/src/core/probes.d
@@ -0,0 +1,6 @@
+provider libosmocore {
+ probe log_start();
+ probe log_done();
+ probe stats_start();
+ probe stats_done();
+};
diff --git a/src/rate_ctr.c b/src/core/rate_ctr.c
index 026670bd..44e26585 100644
--- a/src/rate_ctr.c
+++ b/src/core/rate_ctr.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup rate_ctr
@@ -57,14 +53,17 @@
*
* \file rate_ctr.c */
+#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
-#include <osmocom/core/timer.h>
+#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
@@ -263,6 +262,26 @@ void rate_ctr_group_free(struct rate_ctr_group *grp)
talloc_free(grp);
}
+/*! Get rate counter from group, identified by index idx
+ * \param[in] grp Rate counter group
+ * \param[in] idx Index of the counter to retrieve
+ * \returns rate counter requested
+ */
+struct rate_ctr *rate_ctr_group_get_ctr(struct rate_ctr_group *grp, unsigned int idx)
+{
+ return &grp->ctr[idx];
+}
+
+/*! Set a name for the group of counters be used instead of index value
+ at report time.
+ * \param[in] grp Rate counter group
+ * \param[in] name Name identifier to assign to the rate counter group
+ */
+void rate_ctr_group_set_name(struct rate_ctr_group *grp, const char *name)
+{
+ osmo_talloc_replace_string(grp, &grp->name, name);
+}
+
/*! Add a number to the counter */
void rate_ctr_add(struct rate_ctr *ctr, int inc)
{
@@ -287,14 +306,9 @@ static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv)
ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last;
/* save current counter for next interval */
ctr->intv[intv].last = ctr->current;
-
- /* update the rate of the next bigger interval. This will
- * be overwritten when that next larger interval expires */
- if (intv + 1 < ARRAY_SIZE(ctr->intv))
- ctr->intv[intv+1].rate += ctr->intv[intv].rate;
}
-static struct osmo_timer_list rate_ctr_timer;
+static struct osmo_fd rate_ctr_timer = { .fd = -1 };
static uint64_t timer_ticks;
/* The one-second interval has expired */
@@ -315,18 +329,35 @@ static void rate_ctr_group_intv(struct rate_ctr_group *grp)
}
}
-static void rate_ctr_timer_cb(void *data)
+static int rate_ctr_timer_cb(struct osmo_fd *ofd, unsigned int what)
{
struct rate_ctr_group *ctrg;
+ uint64_t expire_count;
+ int rc;
+
+ /* check that the timer has actually expired */
+ if (!(what & OSMO_FD_READ))
+ return 0;
+
+ /* read from timerfd: number of expirations of periodic timer */
+ rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count));
+ if (rc < 0 && errno == EAGAIN)
+ return 0;
+
+ OSMO_ASSERT(rc == sizeof(expire_count));
- /* Increment number of ticks before we calculate intervals,
- * as a counter value of 0 would already wrap all counters */
- timer_ticks++;
+ if (expire_count > 1)
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Stats timer expire_count=%" PRIu64 ": We missed %" PRIu64 " timers\n",
+ expire_count, expire_count - 1);
- llist_for_each_entry(ctrg, &rate_ctr_groups, list)
- rate_ctr_group_intv(ctrg);
+ do { /* Increment number of ticks before we calculate intervals,
+ * as a counter value of 0 would already wrap all counters */
+ timer_ticks++;
+ llist_for_each_entry(ctrg, &rate_ctr_groups, list)
+ rate_ctr_group_intv(ctrg);
+ } while (--expire_count);
- osmo_timer_schedule(&rate_ctr_timer, 1, 0);
+ return 0;
}
/*! Initialize the counter module. Call this once from your application.
@@ -334,9 +365,27 @@ static void rate_ctr_timer_cb(void *data)
* \returns 0 on success; negative on error */
int rate_ctr_init(void *tall_ctx)
{
+ struct timespec ts_interval = { .tv_sec = 1, .tv_nsec = 0 };
+ int rc;
+
+ /* ignore repeated initialization */
+ if (osmo_fd_is_registered(&rate_ctr_timer))
+ return 0;
+
tall_rate_ctr_ctx = tall_ctx;
- osmo_timer_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL);
- osmo_timer_schedule(&rate_ctr_timer, 1, 0);
+
+ rc = osmo_timerfd_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Failed to setup the timer with error code %d (fd=%d)\n",
+ rc, rate_ctr_timer.fd);
+ return rc;
+ }
+
+ rc = osmo_timerfd_schedule(&rate_ctr_timer, NULL, &ts_interval);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Failed to schedule the timer with error code %d (fd=%d)\n",
+ rc, rate_ctr_timer.fd);
+ }
return 0;
}
@@ -426,4 +475,25 @@ int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data)
return rc;
}
+/*! Reset a rate counter back to zero
+ * \param[in] ctr counter to reset
+ */
+void rate_ctr_reset(struct rate_ctr *ctr)
+{
+ memset(ctr, 0, sizeof(*ctr));
+}
+
+/*! Reset all counters in a group
+ * \param[in] ctrg counter group to reset
+ */
+void rate_ctr_group_reset(struct rate_ctr_group *ctrg)
+{
+ int i;
+
+ for (i = 0; i < ctrg->desc->num_ctr; i++) {
+ struct rate_ctr *ctr = &ctrg->ctr[i];
+ rate_ctr_reset(ctr);
+ }
+}
+
/*! @} */
diff --git a/src/rbtree.c b/src/core/rbtree.c
index 211978f1..f4dc219b 100644
--- a/src/rbtree.c
+++ b/src/core/rbtree.c
@@ -15,11 +15,6 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301, USA
-
linux/lib/rbtree.c
*/
diff --git a/src/core/select.c b/src/core/select.c
new file mode 100644
index 00000000..70047f09
--- /dev/null
+++ b/src/core/select.c
@@ -0,0 +1,731 @@
+/*! \file select.c
+ * select filedescriptor handling.
+ * Taken from:
+ * userspace logging daemon for the iptables ULOG target
+ * of the linux 2.4 netfilter subsystem. */
+/*
+ * (C) 2000-2020 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/stats_tcp.h>
+
+#include "config.h"
+
+#if defined(HAVE_SYS_SELECT_H) && defined(HAVE_POLL_H)
+#include <sys/select.h>
+#include <poll.h>
+
+/*! \addtogroup select
+ * @{
+ * select() loop abstraction
+ *
+ * \file select.c */
+
+/* keep a set of file descriptors per-thread, so that each thread can have its own
+ * distinct set of file descriptors to interact with */
+static __thread int maxfd = 0;
+static __thread struct llist_head osmo_fds; /* TLS cannot use LLIST_HEAD() */
+static __thread int unregistered_count;
+
+/* Array of struct osmo_fd * (size "max_fd") ordered by "ofd->fd" */
+static __thread struct {
+ struct osmo_fd **table;
+ unsigned int size;
+} osmo_fd_lookup;
+
+static void osmo_fd_lookup_table_extend(unsigned int new_max_fd)
+{
+ unsigned int min_new_size;
+ unsigned int pw2;
+ unsigned int new_size;
+
+ /* First calculate the minimally required new size of the array in bytes: */
+ min_new_size = (new_max_fd + 1) * sizeof(struct osmo_fd *);
+ /* Now find the lower power of two of min_new_size (in bytes): */
+ pw2 = 32 - __builtin_clz(min_new_size);
+ /* get next (upper side) power of 2: */
+ pw2++;
+ OSMO_ASSERT(pw2 <= 31); /* Avoid shifting more than 31 bits */
+ new_size = 1 << (pw2 + 1);
+
+ /* Let's make it at least 1024B, to avoid reallocating quickly at startup */
+ if (new_size < 1024)
+ new_size = 1024;
+ if (new_size > osmo_fd_lookup.size) {
+ uint8_t *ptr = talloc_realloc_size(OTC_GLOBAL, osmo_fd_lookup.table, new_size);
+ OSMO_ASSERT(ptr);
+ memset(ptr + osmo_fd_lookup.size, 0, new_size - osmo_fd_lookup.size);
+ osmo_fd_lookup.table = (struct osmo_fd **)ptr;
+ osmo_fd_lookup.size = new_size;
+ }
+}
+
+#ifndef FORCE_IO_SELECT
+struct poll_state {
+ /* array of pollfd */
+ struct pollfd *poll;
+ /* number of entries in pollfd allocated */
+ unsigned int poll_size;
+ /* number of osmo_fd registered */
+ unsigned int num_registered;
+};
+static __thread struct poll_state g_poll;
+#endif /* FORCE_IO_SELECT */
+
+/*! See osmo_select_shutdown_request() */
+static int _osmo_select_shutdown_requested = 0;
+/*! See osmo_select_shutdown_request() */
+static bool _osmo_select_shutdown_done = false;
+
+/*! Set up an osmo-fd. Will not register it.
+ * \param[inout] ofd Osmo FD to be set-up
+ * \param[in] fd OS-level file descriptor number
+ * \param[in] when bit-mask of OSMO_FD_{READ,WRITE,EXECEPT}
+ * \param[in] cb Call-back function to be called
+ * \param[in] data Private context pointer
+ * \param[in] priv_nr Private number
+ */
+void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when,
+ int (*cb)(struct osmo_fd *fd, unsigned int what),
+ void *data, unsigned int priv_nr)
+{
+ ofd->fd = fd;
+ ofd->when = when;
+ ofd->cb = cb;
+ ofd->data = data;
+ ofd->priv_nr = priv_nr;
+}
+
+/*! Update the 'when' field of osmo_fd. "ofd->when = (ofd->when & when_mask) | when".
+ * Use this function instead of directly modifying ofd->when, as the latter will be
+ * removed soon. */
+void osmo_fd_update_when(struct osmo_fd *ofd, unsigned int when_mask, unsigned int when)
+{
+ ofd->when &= when_mask;
+ ofd->when |= when;
+}
+
+/*! Check if a file descriptor is already registered
+ * \param[in] fd osmocom file descriptor to be checked
+ * \returns true if registered; otherwise false
+ */
+bool osmo_fd_is_registered(struct osmo_fd *fd)
+{
+ struct osmo_fd *entry;
+ llist_for_each_entry(entry, &osmo_fds, list) {
+ if (entry == fd) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*! Register a new file descriptor with select loop abstraction
+ * \param[in] fd osmocom file descriptor to be registered
+ * \returns 0 on success; negative in case of error
+ *
+ * The API expects fd field of the struct osmo_fd to remain unchanged while
+ * registered, ie until osmo_fd_unregister() is called on it.
+ */
+int osmo_fd_register(struct osmo_fd *fd)
+{
+ int flags;
+
+ /* make FD nonblocking */
+ flags = fcntl(fd->fd, F_GETFL);
+ if (flags < 0)
+ return flags;
+ flags |= O_NONBLOCK;
+ flags = fcntl(fd->fd, F_SETFL, flags);
+ if (flags < 0)
+ return flags;
+
+ /* set close-on-exec flag */
+ flags = fcntl(fd->fd, F_GETFD);
+ if (flags < 0)
+ return flags;
+ flags |= FD_CLOEXEC;
+ flags = fcntl(fd->fd, F_SETFD, flags);
+ if (flags < 0)
+ return flags;
+
+ /* Register FD */
+ if (fd->fd > maxfd) {
+ maxfd = fd->fd;
+ osmo_fd_lookup_table_extend(maxfd);
+ }
+
+#ifdef OSMO_FD_CHECK
+ if (osmo_fd_is_registered(fd)) {
+ fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
+ return 0;
+ }
+#endif
+#ifndef FORCE_IO_SELECT
+ if (g_poll.num_registered + 1 > g_poll.poll_size) {
+ struct pollfd *p;
+ unsigned int new_size = g_poll.poll_size ? g_poll.poll_size * 2 : 1024;
+ p = talloc_realloc(OTC_GLOBAL, g_poll.poll, struct pollfd, new_size);
+ if (!p)
+ return -ENOMEM;
+ memset(p + g_poll.poll_size, 0, new_size - g_poll.poll_size);
+ g_poll.poll = p;
+ g_poll.poll_size = new_size;
+ }
+ g_poll.num_registered++;
+#endif /* FORCE_IO_SELECT */
+
+ llist_add_tail(&fd->list, &osmo_fds);
+ osmo_fd_lookup.table[fd->fd] = fd;
+
+ return 0;
+}
+
+/*! Unregister a file descriptor from select loop abstraction
+ * \param[in] fd osmocom file descriptor to be unregistered
+ *
+ * Caller is responsible for ensuring the fd is really registered before calling this API.
+ * This function must be called before changing the value of the fd field in
+ * the struct osmo_fd.
+ */
+void osmo_fd_unregister(struct osmo_fd *fd)
+{
+ /* Note: when fd is inside the osmo_fds list (not registered before)
+ * this function will crash! If in doubt, check file descriptor with
+ * osmo_fd_is_registered() */
+ unregistered_count++;
+ llist_del(&fd->list);
+#ifndef FORCE_IO_SELECT
+ g_poll.num_registered--;
+#endif /* FORCE_IO_SELECT */
+
+ if (OSMO_UNLIKELY(fd->fd < 0 || fd->fd > maxfd)) {
+ /* Some old users used to incorrectly set fd = -1 *before* calling osmo_unregister().
+ * Hence, in order to keep backward compatibility it's not possible to assert() here.
+ * Instead, print an error message since this is actually a bug in the API user. */
+#ifdef OSMO_FD_CHECK
+ osmo_panic("osmo_fd_unregister(fd=%u) out of expected range (0..%u), fix your code!!!\n",
+ fd->fd, maxfd);
+#else
+ fprintf(stderr, "osmo_fd_unregister(fd=%u) out of expected range (0..%u), fix your code!!!\n",
+ fd->fd, maxfd);
+ return;
+#endif
+ }
+
+ osmo_fd_lookup.table[fd->fd] = NULL;
+ /* If existent, free any statistical data */
+ osmo_stats_tcp_osmo_fd_unregister(fd);
+}
+
+/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
+ * \param[in] fd osmocom file descriptor to be unregistered + closed
+ *
+ * If \a fd is registered, we unregister it from the select() loop
+ * abstraction. We then close the fd and set it to -1, as well as
+ * unsetting any 'when' flags */
+void osmo_fd_close(struct osmo_fd *fd)
+{
+ if (osmo_fd_is_registered(fd))
+ osmo_fd_unregister(fd);
+ if (fd->fd != -1)
+ close(fd->fd);
+ fd->fd = -1;
+ fd->when = 0;
+}
+
+/*! Populate the fd_sets and return the highest fd number
+ * \param[in] _rset The readfds to populate
+ * \param[in] _wset The wrtiefds to populate
+ * \param[in] _eset The errorfds to populate
+ *
+ * \returns The highest file descriptor seen or 0 on an empty list
+ */
+inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
+{
+ fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
+ struct osmo_fd *ufd;
+ int highfd = 0;
+
+ llist_for_each_entry(ufd, &osmo_fds, list) {
+ if (ufd->when & OSMO_FD_READ)
+ FD_SET(ufd->fd, readset);
+
+ if (ufd->when & OSMO_FD_WRITE)
+ FD_SET(ufd->fd, writeset);
+
+ if (ufd->when & OSMO_FD_EXCEPT)
+ FD_SET(ufd->fd, exceptset);
+
+ if (ufd->fd > highfd)
+ highfd = ufd->fd;
+ }
+
+ return highfd;
+}
+
+inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
+{
+ struct osmo_fd *ufd, *tmp;
+ int work = 0;
+ fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
+
+restart:
+ unregistered_count = 0;
+ llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
+ int flags = 0;
+
+ if (FD_ISSET(ufd->fd, readset)) {
+ flags |= OSMO_FD_READ;
+ FD_CLR(ufd->fd, readset);
+ }
+
+ if (FD_ISSET(ufd->fd, writeset)) {
+ flags |= OSMO_FD_WRITE;
+ FD_CLR(ufd->fd, writeset);
+ }
+
+ if (FD_ISSET(ufd->fd, exceptset)) {
+ flags |= OSMO_FD_EXCEPT;
+ FD_CLR(ufd->fd, exceptset);
+ }
+
+ if (flags) {
+ work = 1;
+ /* make sure to clear any log context before processing the next incoming message
+ * as part of some file descriptor callback. This effectively prevents "context
+ * leaking" from processing of one message into processing of the next message as part
+ * of one iteration through the list of file descriptors here. See OS#3813 */
+ log_reset_context();
+ ufd->cb(ufd, flags);
+ }
+ /* ugly, ugly hack. If more than one filedescriptor was
+ * unregistered, they might have been consecutive and
+ * llist_for_each_entry_safe() is no longer safe */
+ /* this seems to happen with the last element of the list as well */
+ if (unregistered_count >= 1)
+ goto restart;
+ }
+
+ return work;
+}
+
+
+#ifndef FORCE_IO_SELECT
+/* fill g_poll.poll and return the number of entries filled */
+static unsigned int poll_fill_fds(void)
+{
+ struct osmo_fd *ufd;
+ unsigned int i = 0;
+
+ llist_for_each_entry(ufd, &osmo_fds, list) {
+ struct pollfd *p;
+
+ if (!ufd->when)
+ continue;
+
+ p = &g_poll.poll[i++];
+
+ p->fd = ufd->fd;
+ p->events = 0;
+ p->revents = 0;
+
+ /* use the same mapping as the Linux kernel does in fs/select.c */
+ if (ufd->when & OSMO_FD_READ)
+ p->events |= POLLIN | POLLHUP | POLLERR;
+
+ if (ufd->when & OSMO_FD_WRITE)
+ p->events |= POLLOUT | POLLERR;
+
+ if (ufd->when & OSMO_FD_EXCEPT)
+ p->events |= POLLPRI;
+
+ }
+
+ return i;
+}
+
+/* iterate over first n_fd entries of g_poll.poll + dispatch */
+static int poll_disp_fds(unsigned int n_fd)
+{
+ struct osmo_fd *ufd;
+ unsigned int i;
+ int work = 0;
+ int shutdown_pending_writes = 0;
+
+ for (i = 0; i < n_fd; i++) {
+ struct pollfd *p = &g_poll.poll[i];
+ int flags = 0;
+
+ if (!p->revents)
+ continue;
+
+ ufd = osmo_fd_get_by_fd(p->fd);
+ if (!ufd) {
+ /* FD might have been unregistered meanwhile */
+ continue;
+ }
+ /* use the same mapping as the Linux kernel does in fs/select.c */
+ if (p->revents & (POLLIN | POLLHUP | POLLERR))
+ flags |= OSMO_FD_READ;
+ if (p->revents & (POLLOUT | POLLERR))
+ flags |= OSMO_FD_WRITE;
+ if (p->revents & POLLPRI)
+ flags |= OSMO_FD_EXCEPT;
+
+ /* make sure we never report more than the user requested */
+ flags &= ufd->when;
+
+ if (_osmo_select_shutdown_requested > 0) {
+ if (ufd->when & OSMO_FD_WRITE)
+ shutdown_pending_writes++;
+ }
+
+ if (flags) {
+ work = 1;
+ /* make sure to clear any log context before processing the next incoming message
+ * as part of some file descriptor callback. This effectively prevents "context
+ * leaking" from processing of one message into processing of the next message as part
+ * of one iteration through the list of file descriptors here. See OS#3813 */
+ log_reset_context();
+ ufd->cb(ufd, flags);
+ }
+ }
+
+ if (_osmo_select_shutdown_requested > 0 && !shutdown_pending_writes)
+ _osmo_select_shutdown_done = true;
+
+ return work;
+}
+
+static int _osmo_select_main(int polling)
+{
+ unsigned int n_poll;
+ int rc;
+ int timeout = 0;
+
+ /* prepare read and write fdsets */
+ n_poll = poll_fill_fds();
+
+ if (!polling) {
+ osmo_timers_prepare();
+ timeout = osmo_timers_nearest_ms();
+
+ if (_osmo_select_shutdown_requested && timeout == -1)
+ timeout = 0;
+ }
+
+ rc = poll(g_poll.poll, n_poll, timeout);
+ if (rc < 0)
+ return 0;
+
+ /* fire timers */
+ if (!_osmo_select_shutdown_requested)
+ osmo_timers_update();
+
+ OSMO_ASSERT(osmo_ctx->select);
+
+ /* call registered callback functions */
+ return poll_disp_fds(n_poll);
+}
+#else /* FORCE_IO_SELECT */
+/* the old implementation based on select, used 2008-2020 */
+static int _osmo_select_main(int polling)
+{
+ fd_set readset, writeset, exceptset;
+ int rc;
+ struct timeval no_time = {0, 0};
+
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_ZERO(&exceptset);
+
+ /* prepare read and write fdsets */
+ osmo_fd_fill_fds(&readset, &writeset, &exceptset);
+
+ if (!polling)
+ osmo_timers_prepare();
+ rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
+ if (rc < 0)
+ return 0;
+
+ /* fire timers */
+ osmo_timers_update();
+
+ OSMO_ASSERT(osmo_ctx->select);
+
+ /* call registered callback functions */
+ return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
+}
+#endif /* FORCE_IO_SELECT */
+
+/*! select main loop integration
+ * \param[in] polling should we pollonly (1) or block on select (0)
+ * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
+ */
+int osmo_select_main(int polling)
+{
+ int rc = _osmo_select_main(polling);
+#ifndef EMBEDDED
+ if (talloc_total_size(osmo_ctx->select) != 0) {
+ osmo_panic("You cannot use the 'select' volatile "
+ "context if you don't use osmo_select_main_ctx()!\n");
+ }
+#endif
+ return rc;
+}
+
+#ifndef EMBEDDED
+/*! select main loop integration with temporary select-dispatch talloc context
+ * \param[in] polling should we pollonly (1) or block on select (0)
+ * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
+ */
+int osmo_select_main_ctx(int polling)
+{
+ int rc = _osmo_select_main(polling);
+ /* free all the children of the volatile 'select' scope context */
+ talloc_free_children(osmo_ctx->select);
+ return rc;
+}
+#endif
+
+/*! find an osmo_fd based on the integer fd
+ * \param[in] fd file descriptor to use as search key
+ * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
+struct osmo_fd *osmo_fd_get_by_fd(int fd)
+{
+ if (fd > maxfd || fd < 0)
+ return NULL;
+ return osmo_fd_lookup.table[fd];
+}
+
+/*! initialize the osmocom select abstraction for the current thread */
+void osmo_select_init(void)
+{
+ INIT_LLIST_HEAD(&osmo_fds);
+ osmo_fd_lookup_table_extend(0);
+}
+
+/* ensure main thread always has pre-initialized osmo_fds
+ * priority 102: must run after on_dso_load_ctx */
+static __attribute__((constructor(102))) void on_dso_load_select(void)
+{
+ osmo_select_init();
+}
+
+#ifdef HAVE_SYS_TIMERFD_H
+#include <sys/timerfd.h>
+
+/*! disable the osmocom-wrapped timerfd */
+int osmo_timerfd_disable(struct osmo_fd *ofd)
+{
+ const struct itimerspec its_null = {
+ .it_value = { 0, 0 },
+ .it_interval = { 0, 0 },
+ };
+ return timerfd_settime(ofd->fd, 0, &its_null, NULL);
+}
+
+/*! schedule the osmocom-wrapped timerfd to occur first at \a first, then periodically at \a interval
+ * \param[in] ofd Osmocom wrapped timerfd
+ * \param[in] first Relative time at which the timer should first execute (NULL = \a interval)
+ * \param[in] interval Time interval at which subsequent timer shall fire
+ * \returns 0 on success; negative on error */
+int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
+ const struct timespec *interval)
+{
+ struct itimerspec its;
+
+ if (ofd->fd < 0)
+ return -EINVAL;
+
+ /* first expiration */
+ if (first)
+ its.it_value = *first;
+ else
+ its.it_value = *interval;
+ /* repeating interval */
+ its.it_interval = *interval;
+
+ return timerfd_settime(ofd->fd, 0, &its, NULL);
+}
+
+/*! setup osmocom-wrapped timerfd
+ * \param[inout] ofd Osmocom-wrapped timerfd on which to operate
+ * \param[in] cb Call-back function called when timerfd becomes readable
+ * \param[in] data Opaque data to be passed on to call-back
+ * \returns 0 on success; negative on error
+ *
+ * We simply initialize the data structures here, but do not yet
+ * schedule the timer.
+ */
+int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data)
+{
+ ofd->cb = cb;
+ ofd->data = data;
+ ofd->when = OSMO_FD_READ;
+
+ if (ofd->fd < 0) {
+ int rc;
+
+ ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
+ if (ofd->fd < 0)
+ return ofd->fd;
+
+ rc = osmo_fd_register(ofd);
+ if (rc < 0) {
+ osmo_fd_unregister(ofd);
+ close(ofd->fd);
+ ofd->fd = -1;
+ return rc;
+ }
+ }
+ return 0;
+}
+
+#endif /* HAVE_SYS_TIMERFD_H */
+
+#ifdef HAVE_SYS_SIGNALFD_H
+#include <sys/signalfd.h>
+
+static int signalfd_callback(struct osmo_fd *ofd, unsigned int what)
+{
+ struct osmo_signalfd *osfd = ofd->data;
+ struct signalfd_siginfo fdsi;
+ int rc;
+
+ rc = read(ofd->fd, &fdsi, sizeof(fdsi));
+ if (rc < 0) {
+ osmo_fd_unregister(ofd);
+ close(ofd->fd);
+ ofd->fd = -1;
+ return rc;
+ }
+
+ osfd->cb(osfd, &fdsi);
+
+ return 0;
+};
+
+/*! create a signalfd and register it with osmocom select loop.
+ * \param[in] ctx talloc context from which osmo_signalfd is to be allocated
+ * \param[in] set of signals to be accept via this file descriptor
+ * \param[in] cb call-back function to be called for each arriving signal
+ * \param[in] data opaque user-provided data to pass to callback
+ * \returns pointer to newly-allocated + registered osmo_signalfd; NULL on error */
+struct osmo_signalfd *
+osmo_signalfd_setup(void *ctx, sigset_t set, osmo_signalfd_cb *cb, void *data)
+{
+ struct osmo_signalfd *osfd = talloc_size(ctx, sizeof(*osfd));
+ int fd, rc;
+
+ if (!osfd)
+ return NULL;
+
+ osfd->data = data;
+ osfd->sigset = set;
+ osfd->cb = cb;
+
+ fd = signalfd(-1, &osfd->sigset, SFD_NONBLOCK);
+ if (fd < 0) {
+ talloc_free(osfd);
+ return NULL;
+ }
+
+ osmo_fd_setup(&osfd->ofd, fd, OSMO_FD_READ, signalfd_callback, osfd, 0);
+ rc = osmo_fd_register(&osfd->ofd);
+ if (rc < 0) {
+ close(fd);
+ talloc_free(osfd);
+ return NULL;
+ }
+
+ return osfd;
+}
+
+#endif /* HAVE_SYS_SIGNALFD_H */
+
+/*! Request osmo_select_* to only service pending OSMO_FD_WRITE requests. Once all writes are done,
+ * osmo_select_shutdown_done() returns true. This allows for example to send all outbound packets before terminating the
+ * process.
+ *
+ * Usage example:
+ *
+ * static void signal_handler(int signum)
+ * {
+ * fprintf(stdout, "signal %u received\n", signum);
+ *
+ * switch (signum) {
+ * case SIGINT:
+ * case SIGTERM:
+ * // If the user hits Ctrl-C the third time, just terminate immediately.
+ * if (osmo_select_shutdown_requested() >= 2)
+ * exit(-1);
+ * // Request write-only mode in osmo_select_main_ctx()
+ * osmo_select_shutdown_request();
+ * break;
+ * [...]
+ * }
+ *
+ * main()
+ * {
+ * signal(SIGINT, &signal_handler);
+ * signal(SIGTERM, &signal_handler);
+ *
+ * [...]
+ *
+ * // After the signal_handler issued osmo_select_shutdown_request(), osmo_select_shutdown_done() returns true
+ * // as soon as all write queues are empty.
+ * while (!osmo_select_shutdown_done()) {
+ * osmo_select_main_ctx(0);
+ * }
+ * }
+ */
+void osmo_select_shutdown_request(void)
+{
+ _osmo_select_shutdown_requested++;
+};
+
+/*! Return the number of times osmo_select_shutdown_request() was called before. */
+int osmo_select_shutdown_requested(void)
+{
+ return _osmo_select_shutdown_requested;
+};
+
+/*! Return true after osmo_select_shutdown_requested() was called, and after an osmo_select poll loop found no more
+ * pending OSMO_FD_WRITE on any registered socket. */
+bool osmo_select_shutdown_done(void) {
+ return _osmo_select_shutdown_done;
+};
+
+/*! @} */
+
+#endif /* _HAVE_SYS_SELECT_H */
diff --git a/src/sercomm.c b/src/core/sercomm.c
index 2639bf8a..1798acec 100644
--- a/src/sercomm.c
+++ b/src/core/sercomm.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup sercomm
diff --git a/src/serial.c b/src/core/serial.c
index 31cb81d1..117c049b 100644
--- a/src/serial.c
+++ b/src/core/serial.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \addtogroup serial
@@ -67,7 +63,7 @@ osmo_serial_init(const char *dev, speed_t baudrate)
return -errno;
}
- /* now put it into blcoking mode */
+ /* now put it into blocking mode */
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
dbg_perror("fcntl get flags");
@@ -91,8 +87,10 @@ osmo_serial_init(const char *dev, speed_t baudrate)
goto error;
}
- cfsetispeed(&tio, baudrate);
- cfsetospeed(&tio, baudrate);
+ if (cfsetispeed(&tio, baudrate) < 0)
+ dbg_perror("cfsetispeed()");
+ if (cfsetospeed(&tio, baudrate) < 0)
+ dbg_perror("cfsetospeed()");
tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
tio.c_cflag |= (CREAD | CLOCAL | CS8);
@@ -136,12 +134,15 @@ _osmo_serial_set_baudrate(int fd, speed_t baudrate)
dbg_perror("tcgetattr()");
return -errno;
}
- cfsetispeed(&tio, baudrate);
- cfsetospeed(&tio, baudrate);
+
+ if (cfsetispeed(&tio, baudrate) < 0)
+ dbg_perror("cfsetispeed()");
+ if (cfsetospeed(&tio, baudrate) < 0)
+ dbg_perror("cfsetospeed()");
rc = tcsetattr(fd, TCSANOW, &tio);
if (rc < 0) {
- dbg_perror("tcgetattr()");
+ dbg_perror("tcsetattr()");
return -errno;
}
@@ -240,4 +241,39 @@ osmo_serial_clear_custom_baudrate(int fd)
return 0;
}
+/*! Convert unsigned integer value to speed_t
+ * \param[in] baudrate integer value containing the desired standard baudrate
+ * \param[out] speed the standrd baudrate requested in speed_t format
+ * \returns 0 for success or negative errno.
+ */
+int
+osmo_serial_speed_t(unsigned int baudrate, speed_t *speed)
+{
+ switch(baudrate) {
+ case 0: *speed = B0; break;
+ case 50: *speed = B50; break;
+ case 75: *speed = B75; break;
+ case 110: *speed = B110; break;
+ case 134: *speed = B134; break;
+ case 150: *speed = B150; break;
+ case 200: *speed = B200; break;
+ case 300: *speed = B300; break;
+ case 600: *speed = B600; break;
+ case 1200: *speed = B1200; break;
+ case 1800: *speed = B1800; break;
+ case 2400: *speed = B2400; break;
+ case 4800: *speed = B4800; break;
+ case 9600: *speed = B9600; break;
+ case 19200: *speed = B19200; break;
+ case 38400: *speed = B38400; break;
+ case 57600: *speed = B57600; break;
+ case 115200: *speed = B115200; break;
+ case 230400: *speed = B230400; break;
+ default:
+ *speed = B0;
+ return -EINVAL;
+ }
+ return 0;
+}
+
/*! @} */
diff --git a/src/signal.c b/src/core/signal.c
index be3b7778..ba1555ae 100644
--- a/src/signal.c
+++ b/src/core/signal.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/signal.h>
diff --git a/src/sockaddr_str.c b/src/core/sockaddr_str.c
index f523050c..bea61849 100644
--- a/src/sockaddr_str.c
+++ b/src/core/sockaddr_str.c
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
@@ -37,6 +33,7 @@
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/byteswap.h>
+#include <osmocom/core/socket.h>
/*! \addtogroup sockaddr_str
*
@@ -95,6 +92,80 @@ bool osmo_sockaddr_str_is_nonzero(const struct osmo_sockaddr_str *sockaddr_str)
}
}
+/*! Compare two osmo_sockaddr_str instances by string comparison.
+ * Compare by strcmp() for the address and compare port numbers, ignore the AF_INET/AF_INET6 value.
+ * \param[in] a left side of comparison.
+ * \param[in] b right side of comparison.
+ * \return -1 if a < b, 0 if a == b, 1 if a > b.
+ */
+static int osmo_sockaddr_str_cmp_by_string(const struct osmo_sockaddr_str *a, const struct osmo_sockaddr_str *b)
+{
+ int cmp;
+ if (a == b)
+ return 0;
+ if (!a)
+ return -1;
+ if (!b)
+ return 1;
+ cmp = strncmp(a->ip, b->ip, sizeof(a->ip));
+ if (cmp)
+ return cmp;
+ return OSMO_CMP(a->port, b->port);
+}
+
+/*! Compare two osmo_sockaddr_str instances by resulting IP address.
+ * Compare IP versions (AF_INET vs AF_INET6), compare resulting IP address bytes and compare port numbers.
+ * If the IP address strings cannot be parsed successfully / if the 'af' is neither AF_INET nor AF_INET6, fall back to
+ * pure string comparison of the ip address.
+ * \param[in] a left side of comparison.
+ * \param[in] b right side of comparison.
+ * \return -1 if a < b, 0 if a == b, 1 if a > b.
+ */
+int osmo_sockaddr_str_cmp(const struct osmo_sockaddr_str *a, const struct osmo_sockaddr_str *b)
+{
+ int cmp;
+ uint32_t ipv4_a, ipv4_b;
+ struct in6_addr ipv6_a = {}, ipv6_b = {};
+
+ if (a == b)
+ return 0;
+ if (!a)
+ return -1;
+ if (!b)
+ return 1;
+ cmp = OSMO_CMP(a->af, b->af);
+ if (cmp)
+ return cmp;
+ switch (a->af) {
+ case AF_INET:
+ if (osmo_sockaddr_str_to_32(a, &ipv4_a)
+ || osmo_sockaddr_str_to_32(b, &ipv4_b))
+ goto fallback_to_strcmp;
+ cmp = OSMO_CMP(ipv4_a, ipv4_b);
+ break;
+
+ case AF_INET6:
+ if (osmo_sockaddr_str_to_in6_addr(a, &ipv6_a)
+ || osmo_sockaddr_str_to_in6_addr(b, &ipv6_b))
+ goto fallback_to_strcmp;
+ cmp = memcmp(&ipv6_a, &ipv6_b, sizeof(ipv6_a));
+ break;
+
+ default:
+ goto fallback_to_strcmp;
+ }
+ if (cmp)
+ return cmp;
+
+ cmp = OSMO_CMP(a->port, b->port);
+ if (cmp)
+ return cmp;
+ return 0;
+
+fallback_to_strcmp:
+ return osmo_sockaddr_str_cmp_by_string(a, b);
+}
+
/*! Distinguish between valid IPv4 and IPv6 strings.
* This does not verify whether the string is a valid IP address; it assumes that the input is a valid IP address, and
* on that premise returns whether it is an IPv4 or IPv6 string, by looking for '.' and ':' characters. It is safe to
@@ -114,25 +185,24 @@ int osmo_ip_str_type(const char *ip)
return AF_UNSPEC;
}
-/*! Safely copy the given ip string to sockaddr_str, classify to AF_INET or AF_INET6, and set the port.
+/*! Safely copy the given ip string to sockaddr_str, classify to AF_INET or AF_INET6.
* Data will be written to sockaddr_str even if an error is returned.
* \param[out] sockaddr_str The instance to copy to.
* \param[in] ip Valid IP address string.
- * \param[in] port Port number.
* \return 0 on success, negative if copying the address string failed (e.g. too long), if the address family could
* not be detected (i.e. if osmo_ip_str_type() returned AF_UNSPEC), or if sockaddr_str is NULL.
*/
-int osmo_sockaddr_str_from_str(struct osmo_sockaddr_str *sockaddr_str, const char *ip, uint16_t port)
+int osmo_sockaddr_str_from_str2(struct osmo_sockaddr_str *sockaddr_str, const char *ip)
{
int rc;
if (!sockaddr_str)
return -ENOSPC;
if (!ip)
ip = "";
- *sockaddr_str = (struct osmo_sockaddr_str){
- .af = osmo_ip_str_type(ip),
- .port = port,
- };
+ sockaddr_str->af = osmo_ip_str_type(ip);
+ /* to be compatible with previous behaviour, zero the full IP field.
+ * Allow the usage of memcmp(&sockaddr_str, ...) */
+ memset(sockaddr_str->ip, 0x0, sizeof(sockaddr_str->ip));
rc = osmo_strlcpy(sockaddr_str->ip, ip, sizeof(sockaddr_str->ip));
if (rc <= 0)
return -EIO;
@@ -143,6 +213,26 @@ int osmo_sockaddr_str_from_str(struct osmo_sockaddr_str *sockaddr_str, const cha
return 0;
}
+/*! Safely copy the given ip string to sockaddr_str, classify to AF_INET or AF_INET6, and set the port.
+ * Data will be written to sockaddr_str even if an error is returned.
+ * \param[out] sockaddr_str The instance to copy to.
+ * \param[in] ip Valid IP address string.
+ * \param[in] port Port number.
+ * \return 0 on success, negative if copying the address string failed (e.g. too long), if the address family could
+ * not be detected (i.e. if osmo_ip_str_type() returned AF_UNSPEC), or if sockaddr_str is NULL.
+ */
+int osmo_sockaddr_str_from_str(struct osmo_sockaddr_str *sockaddr_str, const char *ip, uint16_t port)
+{
+ int rc;
+ if (!sockaddr_str)
+ return -ENOSPC;
+
+ rc = osmo_sockaddr_str_from_str2(sockaddr_str, ip);
+ sockaddr_str->port = port;
+
+ return rc;
+}
+
/*! Convert IPv4 address to osmo_sockaddr_str, and set port.
* \param[out] sockaddr_str The instance to copy to.
* \param[in] addr IPv4 address data.
@@ -181,7 +271,7 @@ int osmo_sockaddr_str_from_in6_addr(struct osmo_sockaddr_str *sockaddr_str, cons
return 0;
}
-/*! Convert IPv4 address from 32bit host-byte-order to osmo_sockaddr_str, and set port.
+/*! Convert IPv4 address from 32bit network-byte-order to osmo_sockaddr_str, and set port.
* \param[out] sockaddr_str The instance to copy to.
* \param[in] addr 32bit IPv4 address data.
* \param[in] port Port number.
@@ -196,19 +286,27 @@ int osmo_sockaddr_str_from_32(struct osmo_sockaddr_str *sockaddr_str, uint32_t i
return osmo_sockaddr_str_from_in_addr(sockaddr_str, &addr, port);
}
-/*! Convert IPv4 address from 32bit network-byte-order to osmo_sockaddr_str, and set port.
+/*! Convert IPv4 address from 32bit host-byte-order to osmo_sockaddr_str, and set port.
+ * For legacy reasons, this function has a misleading 'n' in its name.
* \param[out] sockaddr_str The instance to copy to.
* \param[in] addr 32bit IPv4 address data.
* \param[in] port Port number.
* \return 0 on success, negative on error.
*/
-int osmo_sockaddr_str_from_32n(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port)
+int osmo_sockaddr_str_from_32h(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port)
{
if (!sockaddr_str)
return -ENOSPC;
return osmo_sockaddr_str_from_32(sockaddr_str, osmo_ntohl(ip), port);
}
+/*! DEPRECATED: the name suggests a conversion from network byte order, but actually converts from host byte order. Use
+ * osmo_sockaddr_str_from_32 for network byte order and osmo_sockaddr_str_from_32h for host byte order. */
+int osmo_sockaddr_str_from_32n(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port)
+{
+ return osmo_sockaddr_str_from_32h(sockaddr_str, ip, port);
+}
+
/*! Convert IPv4 address and port to osmo_sockaddr_str.
* \param[out] sockaddr_str The instance to copy to.
* \param[in] src IPv4 address and port data.
@@ -262,6 +360,17 @@ int osmo_sockaddr_str_from_sockaddr(struct osmo_sockaddr_str *sockaddr_str, cons
return -EINVAL;
}
+/*! Convert IPv4 or IPv6 address and port to osmo_sockaddr_str.
+ * \param[out] sockaddr_str The instance to copy to.
+ * \param[in] src IPv4 or IPv6 address and port data.
+ * \return 0 on success, negative if src does not indicate AF_INET nor AF_INET6 (or if the conversion fails, which
+ * should not be possible in practice).
+ */
+int osmo_sockaddr_str_from_osa(struct osmo_sockaddr_str *sockaddr_str, const struct osmo_sockaddr *src)
+{
+ return osmo_sockaddr_str_from_sockaddr(sockaddr_str, &src->u.sas);
+}
+
/*! Convert osmo_sockaddr_str address string to IPv4 address data.
* \param[in] sockaddr_str The instance to convert the IP of.
* \param[out] dst IPv4 address data to write to.
@@ -302,9 +411,9 @@ int osmo_sockaddr_str_to_in6_addr(const struct osmo_sockaddr_str *sockaddr_str,
return 0;
}
-/*! Convert osmo_sockaddr_str address string to IPv4 address data in host-byte-order.
+/*! Convert osmo_sockaddr_str address string to IPv4 address data in network-byte-order.
* \param[in] sockaddr_str The instance to convert the IP of.
- * \param[out] dst IPv4 address data in 32bit host-byte-order format to write to.
+ * \param[out] dst IPv4 address data in 32bit network-byte-order format to write to.
* \return 0 on success, negative on error (e.g. invalid IPv4 address string).
*/
int osmo_sockaddr_str_to_32(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip)
@@ -322,12 +431,13 @@ int osmo_sockaddr_str_to_32(const struct osmo_sockaddr_str *sockaddr_str, uint32
return 0;
}
-/*! Convert osmo_sockaddr_str address string to IPv4 address data in network-byte-order.
+/*! Convert osmo_sockaddr_str address string to IPv4 address data in host-byte-order.
+ * For legacy reasons, this function has a misleading 'n' in its name.
* \param[in] sockaddr_str The instance to convert the IP of.
- * \param[out] dst IPv4 address data in 32bit network-byte-order format to write to.
+ * \param[out] dst IPv4 address data in 32bit host-byte-order format to write to.
* \return 0 on success, negative on error (e.g. invalid IPv4 address string).
*/
-int osmo_sockaddr_str_to_32n(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip)
+int osmo_sockaddr_str_to_32h(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip)
{
int rc;
uint32_t ip_h;
@@ -342,6 +452,13 @@ int osmo_sockaddr_str_to_32n(const struct osmo_sockaddr_str *sockaddr_str, uint3
return 0;
}
+/*! DEPRECATED: the name suggests a conversion to network byte order, but actually converts to host byte order. Use
+ * osmo_sockaddr_str_to_32() for network byte order and osmo_sockaddr_str_to_32h() for host byte order. */
+int osmo_sockaddr_str_to_32n(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip)
+{
+ return osmo_sockaddr_str_to_32h(sockaddr_str, ip);
+}
+
/*! Convert osmo_sockaddr_str address string and port to IPv4 address and port data.
* \param[in] sockaddr_str The instance to convert the IP and port of.
* \param[out] dst IPv4 address and port data to write to.
@@ -404,5 +521,15 @@ int osmo_sockaddr_str_to_sockaddr(const struct osmo_sockaddr_str *sockaddr_str,
}
}
+/*! Convert osmo_sockaddr_str address string and port to IPv4 or IPv6 address and port data.
+ * \param[in] sockaddr_str The instance to convert the IP and port of.
+ * \param[out] dst IPv4/IPv6 address and port data to write to.
+ * \return 0 on success, negative on error (e.g. invalid IP address string for the family indicated by sockaddr_str->af).
+ */
+int osmo_sockaddr_str_to_osa(const struct osmo_sockaddr_str *sockaddr_str, struct osmo_sockaddr *dst)
+{
+ return osmo_sockaddr_str_to_sockaddr(sockaddr_str, &dst->u.sas);
+}
+
/*! @} */
#endif // HAVE_NETINET_IN_H
diff --git a/src/core/socket.c b/src/core/socket.c
new file mode 100644
index 00000000..80a9d0ec
--- /dev/null
+++ b/src/core/socket.c
@@ -0,0 +1,2803 @@
+/*
+ * (C) 2011-2017 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "config.h"
+
+/*! \addtogroup socket
+ * @{
+ * Osmocom socket convenience functions.
+ *
+ * \file socket.c */
+
+#ifdef HAVE_SYS_SOCKET_H
+#define _GNU_SOURCE /* for struct ucred on glibc >= 2.8 */
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+
+#ifdef HAVE_LIBSCTP
+#include <netinet/sctp.h>
+#endif
+
+static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto,
+ const char *host, uint16_t port, bool passive)
+{
+ struct addrinfo hints, *result, *rp;
+ char portbuf[6];
+ int rc;
+
+ snprintf(portbuf, sizeof(portbuf), "%u", port);
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = family;
+ if (type == SOCK_RAW) {
+ /* Workaround for glibc, that returns EAI_SERVICE (-8) if
+ * SOCK_RAW and IPPROTO_GRE is used.
+ * http://sourceware.org/bugzilla/show_bug.cgi?id=15015
+ */
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ } else {
+ hints.ai_socktype = type;
+ hints.ai_protocol = proto;
+ }
+
+ if (passive)
+ hints.ai_flags |= AI_PASSIVE;
+
+ rc = getaddrinfo(host, portbuf, &hints, &result);
+ if (rc != 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo(%s, %u) failed: %s\n",
+ host, port, gai_strerror(rc));
+ return NULL;
+ }
+
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ /* Workaround for glibc again */
+ if (type == SOCK_RAW) {
+ rp->ai_socktype = SOCK_RAW;
+ rp->ai_protocol = proto;
+ }
+ }
+
+ return result;
+}
+
+#ifdef HAVE_LIBSCTP
+/*! Retrieve an array of addrinfo with specified hints, one for each host in the hosts array.
+ * \param[out] addrinfo array of addrinfo pointers, will be filled by the function on success.
+ * Its size must be at least the one of hosts.
+ * \param[in] family Socket family like AF_INET, AF_INET6.
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM.
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP.
+ * \param[in] hosts array of char pointers (strings) containing the addresses to query.
+ * \param[in] host_cnt length of the hosts array (in items).
+ * \param[in] port port number in host byte order.
+ * \param[in] passive whether to include the AI_PASSIVE flag in getaddrinfo() hints.
+ * \returns 0 is returned on success together with a filled addrinfo array; negative on error
+ */
+static int addrinfo_helper_multi(struct addrinfo **addrinfo, uint16_t family, uint16_t type, uint8_t proto,
+ const char **hosts, size_t host_cnt, uint16_t port, bool passive)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < host_cnt; i++) {
+ addrinfo[i] = addrinfo_helper(family, type, proto, hosts[i], port, passive);
+ if (!addrinfo[i]) {
+ for (j = 0; j < i; j++)
+ freeaddrinfo(addrinfo[j]);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+#endif /* HAVE_LIBSCTP*/
+
+static int socket_helper_tail(int sfd, unsigned int flags)
+{
+ int rc, on = 1;
+ uint8_t dscp = GET_OSMO_SOCK_F_DSCP(flags);
+ uint8_t prio = GET_OSMO_SOCK_F_PRIO(flags);
+
+ if (flags & OSMO_SOCK_F_NONBLOCK) {
+ if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot set this socket unblocking: %s\n",
+ strerror(errno));
+ close(sfd);
+ return -EINVAL;
+ }
+ }
+
+ if (dscp) {
+ rc = osmo_sock_set_dscp(sfd, dscp);
+ if (rc) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "cannot set IP DSCP of socket to %u: %s\n",
+ dscp, strerror(errno));
+ /* we consider this a non-fatal error */
+ }
+ }
+
+ if (prio) {
+ rc = osmo_sock_set_priority(sfd, prio);
+ if (rc) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "cannot set priority of socket to %u: %s\n",
+ prio, strerror(errno));
+ /* we consider this a non-fatal error */
+ }
+ }
+
+ return 0;
+}
+
+static int socket_helper(const struct addrinfo *rp, unsigned int flags)
+{
+ int sfd, rc;
+
+ sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (sfd == -1) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "unable to create socket: %s\n", strerror(errno));
+ return sfd;
+ }
+
+ rc = socket_helper_tail(sfd, flags);
+ if (rc < 0)
+ return rc;
+
+ return sfd;
+}
+
+static int socket_helper_osa(const struct osmo_sockaddr *addr, uint16_t type, uint8_t proto, unsigned int flags)
+{
+ int sfd, rc;
+
+ sfd = socket(addr->u.sa.sa_family, type, proto);
+ if (sfd == -1) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "unable to create socket: %s\n", strerror(errno));
+ return sfd;
+ }
+
+ rc = socket_helper_tail(sfd, flags);
+ if (rc < 0)
+ return rc;
+
+ return sfd;
+}
+
+#ifdef HAVE_LIBSCTP
+/* Fill buf with a string representation of the address set, in the form:
+ * buf_len == 0: "()"
+ * buf_len == 1: "hostA"
+ * buf_len >= 2: (hostA|hostB|...|...)
+ */
+static int multiaddr_snprintf(char* buf, size_t buf_len, const char **hosts, size_t host_cnt)
+{
+ int len = 0, offset = 0, rem = buf_len;
+ size_t i;
+ int ret;
+ char *after;
+
+ if (buf_len < 3)
+ return -EINVAL;
+
+ if (host_cnt != 1) {
+ ret = snprintf(buf, rem, "(");
+ if (ret < 0)
+ return ret;
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+ for (i = 0; i < host_cnt; i++) {
+ if (host_cnt == 1)
+ after = "";
+ else
+ after = (i == (host_cnt - 1)) ? ")" : "|";
+ ret = snprintf(buf + offset, rem, "%s%s", hosts[i] ? : "0.0.0.0", after);
+ OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ }
+
+ return len;
+}
+#endif /* HAVE_LIBSCTP */
+
+static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags)
+{
+ int rc;
+
+ /* Make sure to call 'listen' on a bound, connection-oriented sock */
+ if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) {
+ switch (type) {
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ rc = listen(fd, 10);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to listen on socket: %s\n",
+ strerror(errno));
+ return -errno;
+ }
+ break;
+ }
+ }
+
+ if (flags & OSMO_SOCK_F_NO_MCAST_LOOP) {
+ rc = osmo_sock_mcast_loop_set(fd, false);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable multicast loop: %s\n",
+ strerror(errno));
+ return rc;
+ }
+ }
+
+ if (flags & OSMO_SOCK_F_NO_MCAST_ALL) {
+ rc = osmo_sock_mcast_all_set(fd, false);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable receive of all multicast: %s\n",
+ strerror(errno));
+ /* do not abort here, as this is just an
+ * optional additional optimization that only
+ * exists on Linux only */
+ }
+ }
+ return 0;
+}
+
+/*! Initialize a socket (including bind and/or connect)
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] local_host local host name or IP address in string form
+ * \param[in] local_port local port number in host byte order
+ * \param[in] remote_host remote host name or IP address in string form
+ * \param[in] remote_port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket file descriptor on success; negative on error
+ *
+ * This function creates a new socket of the designated \a family, \a
+ * type and \a proto and optionally binds it to the \a local_host and \a
+ * local_port as well as optionally connects it to the \a remote_host
+ * and \q remote_port, depending on the value * of \a flags parameter.
+ *
+ * As opposed to \ref osmo_sock_init(), this function allows to combine
+ * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This
+ * is useful if you want to connect to a remote host/port, but still
+ * want to bind that socket to either a specific local alias IP and/or a
+ * specific local source port.
+ *
+ * You must specify either \ref OSMO_SOCK_F_BIND, or \ref
+ * OSMO_SOCK_F_CONNECT, or both.
+ *
+ * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to
+ * non-blocking mode.
+ */
+int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
+ const char *local_host, uint16_t local_port,
+ const char *remote_host, uint16_t remote_port, unsigned int flags)
+{
+ struct addrinfo *local = NULL, *remote = NULL, *rp;
+ int sfd = -1, rc, on = 1;
+
+ bool local_ipv4 = false, local_ipv6 = false;
+ bool remote_ipv4 = false, remote_ipv6 = false;
+
+ if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
+ "BIND or CONNECT flags\n");
+ return -EINVAL;
+ }
+
+ /* figure out local address infos */
+ if (flags & OSMO_SOCK_F_BIND) {
+ local = addrinfo_helper(family, type, proto, local_host, local_port, true);
+ if (!local)
+ return -EINVAL;
+ }
+
+ /* figure out remote address infos */
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ remote = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
+ if (!remote) {
+ if (local)
+ freeaddrinfo(local);
+
+ return -EINVAL;
+ }
+ }
+
+ /* It must do a full run to ensure AF_UNSPEC does not fail.
+ * In case first local valid entry is IPv4 and only remote valid entry
+ * is IPv6 or vice versa */
+ if (family == AF_UNSPEC) {
+ for (rp = local; rp != NULL; rp = rp->ai_next) {
+ switch (rp->ai_family) {
+ case AF_INET:
+ local_ipv4 = true;
+ break;
+ case AF_INET6:
+ local_ipv6 = true;
+ break;
+ }
+ }
+
+ for (rp = remote; rp != NULL; rp = rp->ai_next) {
+ switch (rp->ai_family) {
+ case AF_INET:
+ remote_ipv4 = true;
+ break;
+ case AF_INET6:
+ remote_ipv6 = true;
+ break;
+ }
+ }
+
+ if ((flags & OSMO_SOCK_F_BIND) && (flags & OSMO_SOCK_F_CONNECT)) {
+ /* prioritize ipv6 as per RFC */
+ if (local_ipv6 && remote_ipv6)
+ family = AF_INET6;
+ else if (local_ipv4 && remote_ipv4)
+ family = AF_INET;
+ else {
+ if (local)
+ freeaddrinfo(local);
+ if (remote)
+ freeaddrinfo(remote);
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "Unable to find a common protocol (IPv4 or IPv6) "
+ "for local host: %s and remote host: %s.\n",
+ local_host, remote_host);
+ return -ENODEV;
+ }
+ } else if ((flags & OSMO_SOCK_F_BIND)) {
+ family = local_ipv6 ? AF_INET6 : AF_INET;
+ } else if ((flags & OSMO_SOCK_F_CONNECT)) {
+ family = remote_ipv6 ? AF_INET6 : AF_INET;
+ }
+ }
+
+ /* figure out local side of socket */
+ if (flags & OSMO_SOCK_F_BIND) {
+ for (rp = local; rp != NULL; rp = rp->ai_next) {
+ /* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */
+ if (rp->ai_family != family)
+ continue;
+
+ sfd = socket_helper(rp, flags);
+ if (sfd < 0)
+ continue;
+
+ if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) {
+ rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on));
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt socket:"
+ " %s:%u: %s\n",
+ local_host, local_port,
+ strerror(errno));
+ close(sfd);
+ continue;
+ }
+ }
+
+ if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
+ local_host, local_port, strerror(errno));
+ close(sfd);
+ continue;
+ }
+ break;
+ }
+
+ freeaddrinfo(local);
+ if (rp == NULL) {
+ if (remote)
+ freeaddrinfo(remote);
+ LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n",
+ local_host, local_port);
+ return -ENODEV;
+ }
+ }
+
+ /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it
+ was already closed and func returned. If OSMO_SOCK_F_BIND is not
+ set, then sfd = -1 */
+
+ /* figure out remote side of socket */
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ for (rp = remote; rp != NULL; rp = rp->ai_next) {
+ /* When called with AF_UNSPEC, family will set to IPv4 or IPv6 */
+ if (rp->ai_family != family)
+ continue;
+
+ if (sfd < 0) {
+ sfd = socket_helper(rp, flags);
+ if (sfd < 0)
+ continue;
+ }
+
+ rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
+ if (rc != 0 && errno != EINPROGRESS) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
+ remote_host, remote_port, strerror(errno));
+ /* We want to maintain the bind socket if bind was enabled */
+ if (!(flags & OSMO_SOCK_F_BIND)) {
+ close(sfd);
+ sfd = -1;
+ }
+ continue;
+ }
+ break;
+ }
+
+ freeaddrinfo(remote);
+ if (rp == NULL) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "no suitable remote addr found for: %s:%u\n",
+ remote_host, remote_port);
+ if (sfd >= 0)
+ close(sfd);
+ return -ENODEV;
+ }
+ }
+
+ rc = osmo_sock_init_tail(sfd, type, flags);
+ if (rc < 0) {
+ close(sfd);
+ sfd = -1;
+ }
+
+ return sfd;
+}
+
+#define _SOCKADDR_TO_STR(dest, sockaddr) do { \
+ if (osmo_sockaddr_str_from_sockaddr(dest, &sockaddr->u.sas)) \
+ osmo_strlcpy((dest)->ip, "Invalid IP", 11); \
+ } while (0)
+
+/*! Initialize a socket (including bind and/or connect)
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] local local address
+ * \param[in] remote remote address
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket file descriptor on success; negative on error
+ *
+ * This function creates a new socket of the
+ * \a type and \a proto and optionally binds it to the \a local
+ * as well as optionally connects it to the \a remote
+ * depending on the value * of \a flags parameter.
+ *
+ * As opposed to \ref osmo_sock_init(), this function allows to combine
+ * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This
+ * is useful if you want to connect to a remote host/port, but still
+ * want to bind that socket to either a specific local alias IP and/or a
+ * specific local source port.
+ *
+ * You must specify either \ref OSMO_SOCK_F_BIND, or \ref
+ * OSMO_SOCK_F_CONNECT, or both.
+ *
+ * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to
+ * non-blocking mode.
+ */
+int osmo_sock_init_osa(uint16_t type, uint8_t proto,
+ const struct osmo_sockaddr *local,
+ const struct osmo_sockaddr *remote,
+ unsigned int flags)
+{
+ int sfd = -1, rc, on = 1;
+ struct osmo_sockaddr_str _sastr = {};
+ struct osmo_sockaddr_str *sastr = &_sastr;
+
+ if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
+ "BIND or CONNECT flags\n");
+ return -EINVAL;
+ }
+
+ if ((flags & OSMO_SOCK_F_BIND) && !local) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "invalid argument. Cannot BIND when local is NULL\n");
+ return -EINVAL;
+ }
+
+ if ((flags & OSMO_SOCK_F_CONNECT) && !remote) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "invalid argument. Cannot CONNECT when remote is NULL\n");
+ return -EINVAL;
+ }
+
+ if ((flags & OSMO_SOCK_F_BIND) &&
+ (flags & OSMO_SOCK_F_CONNECT) &&
+ local->u.sa.sa_family != remote->u.sa.sa_family) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "invalid: the family for "
+ "local and remote endpoint must be same.\n");
+ return -EINVAL;
+ }
+
+ /* figure out local side of socket */
+ if (flags & OSMO_SOCK_F_BIND) {
+ sfd = socket_helper_osa(local, type, proto, flags);
+ if (sfd < 0) {
+ _SOCKADDR_TO_STR(sastr, local);
+ LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: " OSMO_SOCKADDR_STR_FMT "\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(sastr));
+ return -ENODEV;
+ }
+
+ if (proto != IPPROTO_UDP || (flags & OSMO_SOCK_F_UDP_REUSEADDR)) {
+ rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on));
+ if (rc < 0) {
+ int err = errno;
+ _SOCKADDR_TO_STR(sastr, local);
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt socket: " OSMO_SOCKADDR_STR_FMT ": %s\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(err));
+ close(sfd);
+ return rc;
+ }
+ }
+
+ if (bind(sfd, &local->u.sa, sizeof(struct osmo_sockaddr)) == -1) {
+ int err = errno;
+ _SOCKADDR_TO_STR(sastr, local);
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: " OSMO_SOCKADDR_STR_FMT ": %s\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(err));
+ close(sfd);
+ return -1;
+ }
+ }
+
+ /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it
+ was already closed and func returned. If OSMO_SOCK_F_BIND is not
+ set, then sfd = -1 */
+
+ /* figure out remote side of socket */
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ if (sfd < 0) {
+ sfd = socket_helper_osa(remote, type, proto, flags);
+ if (sfd < 0) {
+ return sfd;
+ }
+ }
+
+ rc = connect(sfd, &remote->u.sa, sizeof(struct osmo_sockaddr));
+ if (rc != 0 && errno != EINPROGRESS) {
+ int err = errno;
+ _SOCKADDR_TO_STR(sastr, remote);
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: " OSMO_SOCKADDR_STR_FMT ": %s\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(sastr), strerror(err));
+ close(sfd);
+ return rc;
+ }
+ }
+
+ rc = osmo_sock_init_tail(sfd, type, flags);
+ if (rc < 0) {
+ close(sfd);
+ sfd = -1;
+ }
+
+ return sfd;
+}
+
+#ifdef HAVE_LIBSCTP
+
+/* Check whether there's an addrinfo item in the addrinfo set with an IPv4 or IPv6 option */
+static void addrinfo_has_v4v6addr(const struct addrinfo **result, size_t result_count, bool *has_v4, bool *has_v6)
+{
+ size_t host_idx;
+ const struct addrinfo *rp;
+ *has_v4 = false;
+ *has_v6 = false;
+
+ for (host_idx = 0; host_idx < result_count; host_idx++) {
+ for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
+ if (result[host_idx]->ai_family == AF_INET)
+ *has_v4 = true;
+ else if (result[host_idx]->ai_family == AF_INET6)
+ *has_v6 = true;
+ }
+ }
+}
+
+/* Check whether there's an addrinfo item in the addrinfo set with only an IPv4 or IPv6 option */
+static void addrinfo_has_v4v6only_addr(const struct addrinfo **result, size_t result_count, bool *has_v4only, bool *has_v6only)
+{
+ size_t host_idx;
+ const struct addrinfo *rp;
+ *has_v4only = false;
+ *has_v6only = false;
+
+ for (host_idx = 0; host_idx < result_count; host_idx++) {
+ bool has_v4 = false;
+ bool has_v6 = false;
+ for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
+ if (rp->ai_family == AF_INET6)
+ has_v6 = true;
+ else
+ has_v4 = true;
+ }
+ if (has_v4 && !has_v6)
+ *has_v4only = true;
+ else if (has_v6 && !has_v4)
+ *has_v6only = true;
+ }
+}
+
+/* Check whether there's an IPv6 with IN6ADDR_ANY_INIT ("::") */
+static bool addrinfo_has_in6addr_any(const struct addrinfo **result, size_t result_count)
+{
+ size_t host_idx;
+ struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+ const struct addrinfo *rp;
+
+ for (host_idx = 0; host_idx < result_count; host_idx++) {
+ for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
+ if (rp->ai_family != AF_INET6)
+ continue;
+ if (memcmp(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr,
+ &in6addr_any, sizeof(in6addr_any)) == 0)
+ return true;
+ }
+ }
+ return false;
+}
+
+static int socket_helper_multiaddr(uint16_t family, uint16_t type, uint8_t proto, unsigned int flags)
+{
+ int sfd, rc;
+
+ sfd = socket(family, type, proto);
+ if (sfd == -1) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "Unable to create socket: %s\n", strerror(errno));
+ return sfd;
+ }
+
+ rc = socket_helper_tail(sfd, flags);
+ if (rc < 0)
+ return rc;
+
+ return sfd;
+}
+
+/* Build array of addresses taking first addrinfo result of the requested family
+ * for each host in addrs_buf. */
+static int addrinfo_to_sockaddr(uint16_t family, const struct addrinfo **result,
+ const char **hosts, unsigned int host_cont,
+ uint8_t *addrs_buf, size_t addrs_buf_len) {
+ size_t host_idx, offset = 0;
+ const struct addrinfo *rp;
+
+ for (host_idx = 0; host_idx < host_cont; host_idx++) {
+ /* Addresses are ordered based on RFC 3484, see man getaddrinfo */
+ for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
+ if (family == AF_UNSPEC || rp->ai_family == family)
+ break;
+ }
+ if (!rp && family == AF_INET6) {
+ /* See if we can find an AF_INET addr for the AF_INET6 socket instead: */
+ for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
+ if (rp->ai_family == AF_INET)
+ break;
+ }
+ }
+ if (!rp) { /* No addr could be bound for this host! */
+ LOGP(DLGLOBAL, LOGL_ERROR, "No suitable remote address found for host: %s\n",
+ hosts[host_idx]);
+ return -ENODEV;
+ }
+ if (offset + rp->ai_addrlen > addrs_buf_len) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Output buffer to small: %zu\n",
+ addrs_buf_len);
+ return -ENOSPC;
+ }
+ memcpy(addrs_buf + offset, rp->ai_addr, rp->ai_addrlen);
+ offset += rp->ai_addrlen;
+ }
+ return 0;
+}
+
+static int setsockopt_sctp_auth_supported(int fd, uint32_t val)
+{
+#ifdef SCTP_AUTH_SUPPORTED
+ struct sctp_assoc_value assoc_val = {
+ .assoc_id = SCTP_FUTURE_ASSOC,
+ .assoc_value = val,
+ };
+ return setsockopt(fd, IPPROTO_SCTP, SCTP_AUTH_SUPPORTED, &assoc_val, sizeof(assoc_val));
+#else
+#pragma message "setsockopt(SCTP_AUTH_SUPPORTED) not supported! some SCTP features may not be available!"
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Built without support for setsockopt(SCTP_AUTH_SUPPORTED), skipping\n");
+ return -ENOTSUP;
+#endif
+}
+
+static int setsockopt_sctp_asconf_supported(int fd, uint32_t val)
+{
+#ifdef SCTP_ASCONF_SUPPORTED
+ struct sctp_assoc_value assoc_val = {
+ .assoc_id = SCTP_FUTURE_ASSOC,
+ .assoc_value = val,
+ };
+ return setsockopt(fd, IPPROTO_SCTP, SCTP_ASCONF_SUPPORTED, &assoc_val, sizeof(assoc_val));
+#else
+#pragma message "setsockopt(SCTP_ASCONF_SUPPORTED) not supported! some SCTP features may not be available!"
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Built without support for setsockopt(SCTP_ASCONF_SUPPORTED), skipping\n");
+ return -ENOTSUP;
+#endif
+}
+
+static int setsockopt_sctp_initmsg(int fd, const struct osmo_sock_init2_multiaddr_pars *pars)
+{
+ if (!pars->sctp.sockopt_initmsg.num_ostreams_present &&
+ !pars->sctp.sockopt_initmsg.max_instreams_present &&
+ !pars->sctp.sockopt_initmsg.max_attempts_present &&
+ !pars->sctp.sockopt_initmsg.max_init_timeo_present)
+ return 0; /* nothing to set/do */
+
+#ifdef SCTP_INITMSG
+ struct sctp_initmsg si = {0};
+ socklen_t si_len = sizeof(si);
+ int rc;
+
+ /* If at least one field not present, obtain current value from kernel: */
+ if (!pars->sctp.sockopt_initmsg.num_ostreams_present ||
+ !pars->sctp.sockopt_initmsg.max_instreams_present ||
+ !pars->sctp.sockopt_initmsg.max_attempts_present ||
+ !pars->sctp.sockopt_initmsg.max_init_timeo_present) {
+ rc = getsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &si, &si_len);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (pars->sctp.sockopt_initmsg.num_ostreams_present)
+ si.sinit_num_ostreams = pars->sctp.sockopt_initmsg.num_ostreams_value;
+ if (pars->sctp.sockopt_initmsg.max_instreams_present)
+ si.sinit_max_instreams = pars->sctp.sockopt_initmsg.max_instreams_value;
+ if (pars->sctp.sockopt_initmsg.max_attempts_present)
+ si.sinit_max_attempts = pars->sctp.sockopt_initmsg.max_attempts_value;
+ if (pars->sctp.sockopt_initmsg.max_init_timeo_present)
+ si.sinit_max_init_timeo = pars->sctp.sockopt_initmsg.max_init_timeo_value;
+
+ return setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &si, sizeof(si));
+#else
+#pragma message "setsockopt(SCTP_INITMSG) not supported! some SCTP features may not be available!"
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Built without support for setsockopt(SCTP_INITMSG), skipping\n");
+ return -ENOTSUP
+#endif
+}
+
+/*! Initialize a socket (including bind and/or connect) with multiple local or remote addresses.
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] local_hosts array of char pointers (strings), each containing local host name or IP address in string form
+ * \param[in] local_hosts_cnt length of local_hosts (in items)
+ * \param[in] local_port local port number in host byte order
+ * \param[in] remote_host array of char pointers (strings), each containing remote host name or IP address in string form
+ * \param[in] remote_hosts_cnt length of remote_hosts (in items)
+ * \param[in] remote_port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket file descriptor on success; negative on error
+ *
+ * This function is similar to \ref osmo_sock_init2(), but can be passed an
+ * array of local or remote addresses for protocols supporting multiple
+ * addresses per socket, like SCTP (currently only one supported). This function
+ * should not be used by protocols not supporting this kind of features, but
+ * rather \ref osmo_sock_init2() should be used instead.
+ * See \ref osmo_sock_init2() for more information on flags and general behavior.
+ */
+int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
+ const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
+ const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port,
+ unsigned int flags)
+{
+ return osmo_sock_init2_multiaddr2(family, type, proto, local_hosts, local_hosts_cnt, local_port,
+ remote_hosts, remote_hosts_cnt, remote_port, flags, NULL);
+}
+
+/*! Initialize a socket (including bind and/or connect) with multiple local or remote addresses.
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] local_hosts array of char pointers (strings), each containing local host name or IP address in string form
+ * \param[in] local_hosts_cnt length of local_hosts (in items)
+ * \param[in] local_port local port number in host byte order
+ * \param[in] remote_host array of char pointers (strings), each containing remote host name or IP address in string form
+ * \param[in] remote_hosts_cnt length of remote_hosts (in items)
+ * \param[in] remote_port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \param[in] pars Extra parameters for multi-address specific protocols, such as SCTP. Can be NULL.
+ * \returns socket file descriptor on success; negative on error
+ *
+ * This function is similar to \ref osmo_sock_init2(), but can be passed an
+ * array of local or remote addresses for protocols supporting multiple
+ * addresses per socket, like SCTP (currently only one supported). This function
+ * should not be used by protocols not supporting this kind of features, but
+ * rather \ref osmo_sock_init2() should be used instead.
+ * See \ref osmo_sock_init2() for more information on flags and general behavior.
+ *
+ * pars: If "pars" parameter is passed to the function, sctp.version shall be set to 0.
+ */
+int osmo_sock_init2_multiaddr2(uint16_t family, uint16_t type, uint8_t proto,
+ const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
+ const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port,
+ unsigned int flags, struct osmo_sock_init2_multiaddr_pars *pars)
+
+{
+ struct addrinfo *res_loc[OSMO_SOCK_MAX_ADDRS], *res_rem[OSMO_SOCK_MAX_ADDRS];
+ int sfd = -1, rc, on = 1;
+ unsigned int i;
+ bool loc_has_v4addr = false, loc_has_v6addr = false;
+ bool rem_has_v4addr = false, rem_has_v6addr = false;
+ bool loc_has_v4only_addr, rem_has_v4only_addr;
+ bool loc_has_v6only_addr, rem_has_v6only_addr;
+ struct sockaddr_in6 addrs_buf[OSMO_SOCK_MAX_ADDRS];
+ char strbuf[512];
+
+ /* TODO: So far this function is only aimed for SCTP, but could be
+ reused in the future for other protocols with multi-addr support */
+ if (proto != IPPROTO_SCTP)
+ return -ENOTSUP;
+
+ if (pars && pars->sctp.version != 0)
+ return -EINVAL;
+
+ if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
+ "BIND or CONNECT flags\n");
+ return -EINVAL;
+ }
+
+ if (((flags & OSMO_SOCK_F_BIND) && !local_hosts_cnt) ||
+ ((flags & OSMO_SOCK_F_CONNECT) && !remote_hosts_cnt) ||
+ local_hosts_cnt > OSMO_SOCK_MAX_ADDRS ||
+ remote_hosts_cnt > OSMO_SOCK_MAX_ADDRS)
+ return -EINVAL;
+
+ /* figure out local side of socket */
+ if (flags & OSMO_SOCK_F_BIND) {
+ rc = addrinfo_helper_multi(res_loc, family, type, proto, local_hosts,
+ local_hosts_cnt, local_port, true);
+ if (rc < 0)
+ return -EINVAL;
+ /* Figure out if there's any IPv4 or IPv6 entry in the result set */
+ addrinfo_has_v4v6addr((const struct addrinfo **)res_loc, local_hosts_cnt,
+ &loc_has_v4addr, &loc_has_v6addr);
+ /* Figure out if there's any IPv4-only or IPv6-only addr in the result set */
+ addrinfo_has_v4v6only_addr((const struct addrinfo **)res_loc, local_hosts_cnt,
+ &loc_has_v4only_addr, &loc_has_v6only_addr);
+ if (family == AF_INET && loc_has_v6only_addr) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind an IPv6 address to an AF_INET socket\n");
+ rc = -EINVAL;
+ goto ret_freeaddrinfo_loc;
+ }
+ }
+ /* figure out remote side of socket */
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ rc = addrinfo_helper_multi(res_rem, family, type, proto, remote_hosts,
+ remote_hosts_cnt, remote_port, false);
+ if (rc < 0) {
+ rc = -EINVAL;
+ goto ret_freeaddrinfo_loc;
+ }
+ /* Figure out if there's any IPv4 or IPv6 entry in the result set */
+ addrinfo_has_v4v6addr((const struct addrinfo **)res_rem, remote_hosts_cnt,
+ &rem_has_v4addr, &rem_has_v6addr);
+ /* Figure out if there's any IPv4-only or IPv6-only addr in the result set */
+ addrinfo_has_v4v6only_addr((const struct addrinfo **)res_rem, remote_hosts_cnt,
+ &rem_has_v4only_addr, &rem_has_v6only_addr);
+ if (family == AF_INET && rem_has_v6only_addr) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Cannot connect to an IPv6 address in an AF_INET socket\n");
+ rc = -EINVAL;
+ goto ret_freeaddrinfo;
+ }
+ }
+
+ /* Find out the socket family now if not established by caller:
+ * Both are checked here through "or" here to account for "bind flag set,
+ * connect flag not set" and viceversa. */
+ if (family == AF_UNSPEC) {
+ if (!loc_has_v6addr && !rem_has_v6addr)
+ family = AF_INET;
+ else
+ family = AF_INET6;
+ }
+
+ /* if both sets are used, make sure there's at least 1 address of the
+ * same type on each set so that SCTP INIT/INIT-ACK can work. */
+ if (family == AF_INET6 && ((flags & OSMO_SOCK_F_BIND) && (flags & OSMO_SOCK_F_CONNECT)) &&
+ (loc_has_v4addr != rem_has_v4addr || loc_has_v6addr != rem_has_v6addr)) {
+ if (!addrinfo_has_in6addr_any((const struct addrinfo **)res_loc, local_hosts_cnt)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Invalid v4 vs v6 in local vs remote addresses: "
+ "local:%s%s remote:%s%s\n",
+ loc_has_v4addr ? " v4" : "", loc_has_v6addr ? " v6" : "",
+ rem_has_v4addr ? " v4" : "", rem_has_v6addr ? " v6" : "");
+ rc = -EINVAL;
+ goto ret_freeaddrinfo;
+ }
+ }
+
+ sfd = socket_helper_multiaddr(family, type, proto, flags);
+ if (sfd < 0) {
+ rc = sfd;
+ goto ret_freeaddrinfo;
+ }
+
+ if (pars) {
+ if (pars->sctp.sockopt_auth_supported.set) {
+ /* RFC 5061 4.2.7: ASCONF also requires AUTH feature. */
+ rc = setsockopt_sctp_auth_supported(sfd, pars->sctp.sockopt_auth_supported.value);
+ if (rc < 0) {
+ int err = errno;
+ multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt(SCTP_AUTH_SUPPORTED) socket: %s:%u: %s\n",
+ strbuf, local_port, strerror(err));
+ if (pars->sctp.sockopt_auth_supported.abort_on_failure)
+ goto ret_close;
+ /* do not fail, some features such as Peer Primary Address won't be available
+ * unless configured system-wide through sysctl */
+ }
+ }
+
+ if (pars->sctp.sockopt_asconf_supported.set) {
+ rc = setsockopt_sctp_asconf_supported(sfd, pars->sctp.sockopt_asconf_supported.value);
+ if (rc < 0) {
+ int err = errno;
+ multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt(SCTP_ASCONF_SUPPORTED) socket: %s:%u: %s\n",
+ strbuf, local_port, strerror(err));
+ if (pars->sctp.sockopt_asconf_supported.abort_on_failure)
+ goto ret_close;
+ /* do not fail, some features such as Peer Primary Address won't be available
+ * unless configured system-wide through sysctl */
+ }
+ }
+
+ if (pars->sctp.sockopt_initmsg.set) {
+ rc = setsockopt_sctp_initmsg(sfd, pars);
+ if (rc < 0) {
+ int err = errno;
+ multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt(SCTP_INITMSG) socket: %s:%u: %s\n",
+ strbuf, local_port, strerror(err));
+ if (pars->sctp.sockopt_initmsg.abort_on_failure)
+ goto ret_close;
+ /* do not fail, some parameters will be left as the global default */
+ }
+ }
+ }
+
+ if (flags & OSMO_SOCK_F_BIND) {
+ /* Since so far we only allow IPPROTO_SCTP in this function,
+ no need to check below for "proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR" */
+ rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on));
+ if (rc < 0) {
+ int err = errno;
+ multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt socket:"
+ " %s:%u: %s\n",
+ strbuf, local_port,
+ strerror(err));
+ goto ret_close;
+ }
+
+ /* Build array of addresses taking first entry for each host.
+ TODO: Ideally we should use backtracking storing last used
+ indexes and trying next combination if connect() fails .*/
+ /* We could alternatively use v4v6 mapped addresses and call sctp_bindx once with an array od sockaddr_in6 */
+ rc = addrinfo_to_sockaddr(family, (const struct addrinfo **)res_loc,
+ local_hosts, local_hosts_cnt,
+ (uint8_t*)addrs_buf, sizeof(addrs_buf));
+ if (rc < 0) {
+ rc = -ENODEV;
+ goto ret_close;
+ }
+
+ rc = sctp_bindx(sfd, (struct sockaddr *)addrs_buf, local_hosts_cnt, SCTP_BINDX_ADD_ADDR);
+ if (rc == -1) {
+ int err = errno;
+ multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
+ LOGP(DLGLOBAL, LOGL_NOTICE, "unable to bind socket: %s:%u: %s\n",
+ strbuf, local_port, strerror(err));
+ rc = -ENODEV;
+ goto ret_close;
+ }
+ }
+
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ /* Build array of addresses taking first of same family for each host.
+ TODO: Ideally we should use backtracking storing last used
+ indexes and trying next combination if connect() fails .*/
+ rc = addrinfo_to_sockaddr(family, (const struct addrinfo **)res_rem,
+ remote_hosts, remote_hosts_cnt,
+ (uint8_t*)addrs_buf, sizeof(addrs_buf));
+ if (rc < 0) {
+ rc = -ENODEV;
+ goto ret_close;
+ }
+
+ rc = sctp_connectx(sfd, (struct sockaddr *)addrs_buf, remote_hosts_cnt, NULL);
+ if (rc != 0 && errno != EINPROGRESS) {
+ int err = errno;
+ multiaddr_snprintf(strbuf, sizeof(strbuf), remote_hosts, remote_hosts_cnt);
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
+ strbuf, remote_port, strerror(err));
+ rc = -ENODEV;
+ goto ret_close;
+ }
+ }
+
+ rc = osmo_sock_init_tail(sfd, type, flags);
+ if (rc < 0) {
+ close(sfd);
+ sfd = -1;
+ }
+
+ rc = sfd;
+ goto ret_freeaddrinfo;
+
+ret_close:
+ if (sfd >= 0)
+ close(sfd);
+ret_freeaddrinfo:
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ for (i = 0; i < remote_hosts_cnt; i++)
+ freeaddrinfo(res_rem[i]);
+ }
+ret_freeaddrinfo_loc:
+ if (flags & OSMO_SOCK_F_BIND) {
+ for (i = 0; i < local_hosts_cnt; i++)
+ freeaddrinfo(res_loc[i]);
+ }
+ return rc;
+}
+#endif /* HAVE_LIBSCTP */
+
+/*! Initialize a socket (including bind/connect)
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] host remote host name or IP address in string form
+ * \param[in] port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket file descriptor on success; negative on error
+ *
+ * This function creates a new socket of the designated \a family, \a
+ * type and \a proto and optionally binds or connects it, depending on
+ * the value of \a flags parameter.
+ */
+int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
+ const char *host, uint16_t port, unsigned int flags)
+{
+ struct addrinfo *result, *rp;
+ int sfd = -1; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */
+ int on = 1;
+ int rc;
+
+ if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
+ (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:"
+ " %s:%u\n", host, port);
+ return -EINVAL;
+ }
+
+ result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND);
+ if (!result)
+ return -EINVAL;
+
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ sfd = socket_helper(rp, flags);
+ if (sfd == -1)
+ continue;
+
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
+ if (rc != 0 && errno != EINPROGRESS) {
+ close(sfd);
+ continue;
+ }
+ } else {
+ if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) {
+ rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on));
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt socket:"
+ " %s:%u: %s\n",
+ host, port, strerror(errno));
+ close(sfd);
+ continue;
+ }
+ }
+ if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket:"
+ "%s:%u: %s\n",
+ host, port, strerror(errno));
+ close(sfd);
+ continue;
+ }
+ }
+ break;
+ }
+ freeaddrinfo(result);
+
+ if (rp == NULL) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "no suitable addr found for: %s:%u\n",
+ host, port);
+ return -ENODEV;
+ }
+
+ if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) {
+ rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR,
+ "cannot setsockopt socket: %s:%u: %s\n", host,
+ port, strerror(errno));
+ close(sfd);
+ sfd = -1;
+ }
+ }
+
+ rc = osmo_sock_init_tail(sfd, type, flags);
+ if (rc < 0) {
+ close(sfd);
+ sfd = -1;
+ }
+
+ return sfd;
+}
+
+/*! fill \ref osmo_fd for a give sfd
+ * \param[out] ofd file descriptor (will be filled in)
+ * \param[in] sfd socket file descriptor
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
+ *
+ * This function fills the \a ofd structure.
+ */
+static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd, unsigned int flags)
+{
+ int rc;
+
+ if (sfd < 0)
+ return sfd;
+
+ ofd->fd = sfd;
+ ofd->when = OSMO_FD_READ;
+
+ /* if we're doing a non-blocking connect, the completion will be signaled
+ * by marking the fd as WRITE-able. So in this exceptional case, we're
+ * also interested in when the socket becomes write-able */
+ if ((flags & (OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK)) ==
+ (OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK)) {
+ ofd->when |= OSMO_FD_WRITE;
+ }
+
+ rc = osmo_fd_register(ofd);
+ if (rc < 0) {
+ close(sfd);
+ return rc;
+ }
+
+ return sfd;
+}
+
+/*! Initialize a socket and fill \ref osmo_fd
+ * \param[out] ofd file descriptor (will be filled in)
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] host remote host name or IP address in string form
+ * \param[in] port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
+ *
+ * This function creates (and optionall binds/connects) a socket using
+ * \ref osmo_sock_init, but also fills the \a ofd structure.
+ */
+int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
+ const char *host, uint16_t port, unsigned int flags)
+{
+ return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags), flags);
+}
+
+/*! Initialize a socket and fill \ref osmo_fd
+ * \param[out] ofd file descriptor (will be filled in)
+ * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] local_host local host name or IP address in string form
+ * \param[in] local_port local port number in host byte order
+ * \param[in] remote_host remote host name or IP address in string form
+ * \param[in] remote_port remote port number in host byte order
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
+ *
+ * This function creates (and optionall binds/connects) a socket using
+ * \ref osmo_sock_init2, but also fills the \a ofd structure.
+ */
+int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
+ const char *local_host, uint16_t local_port,
+ const char *remote_host, uint16_t remote_port, unsigned int flags)
+{
+ return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
+ local_port, remote_host, remote_port, flags), flags);
+}
+
+int osmo_sock_init_osa_ofd(struct osmo_fd *ofd, int type, int proto,
+ const struct osmo_sockaddr *local,
+ const struct osmo_sockaddr *remote, unsigned int flags)
+{
+ return osmo_fd_init_ofd(ofd, osmo_sock_init_osa(type, proto, local, remote, flags), flags);
+}
+
+/*! Initialize a socket and fill \ref sockaddr
+ * \param[out] ss socket address (will be filled in)
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
+ *
+ * This function creates (and optionall binds/connects) a socket using
+ * \ref osmo_sock_init, but also fills the \a ss structure.
+ */
+int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
+ uint8_t proto, unsigned int flags)
+{
+ char host[NI_MAXHOST];
+ uint16_t port;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int s, sa_len;
+
+ /* determine port and host from ss */
+ switch (ss->sa_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *) ss;
+ sa_len = sizeof(struct sockaddr_in);
+ port = ntohs(sin->sin_port);
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) ss;
+ sa_len = sizeof(struct sockaddr_in6);
+ port = ntohs(sin6->sin6_port);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
+ NULL, 0, NI_NUMERICHOST);
+ if (s != 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:"
+ " %s\n", strerror(errno));
+ return s;
+ }
+
+ return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
+}
+
+#ifdef HAVE_LIBSCTP
+/*! Add addresses to the multi-address (SCTP) socket active binding set
+ * \param[in] sfd The multi-address (SCTP) socket
+ * \param[in] addrs array of char pointers (strings), each containing local host name or IP address in string form
+ * \param[in] addrs_cnt length of addrs_hosts (in items)
+ * \returns 0 on success; negative on error
+ *
+ * This function only supports SCTP sockets so far, and hence it should be
+ * called only on socket file descriptions referencing that kind of sockets.
+ */
+int osmo_sock_multiaddr_add_local_addr(int sfd, const char **addrs, size_t addrs_cnt)
+{
+ struct osmo_sockaddr osa;
+ socklen_t slen = sizeof(osa);
+ uint16_t sfd_family;
+ uint16_t type = SOCK_STREAM ;/* Fixme: we assume fd is SOCK_STREAM */
+ uint8_t proto = IPPROTO_SCTP; /* Fixme: we assume fd is IPPROTO_SCTP */
+ struct addrinfo *res[OSMO_SOCK_MAX_ADDRS];
+ uint16_t port;
+ struct sockaddr_in6 addrs_buf[OSMO_SOCK_MAX_ADDRS];
+ unsigned int i;
+ int rc;
+ bool res_has_v4addr = false, res_has_v6addr = false;
+
+ rc = getsockname(sfd, &osa.u.sa, &slen);
+ if (rc < 0)
+ return rc; /* TODO: log error? */
+ sfd_family = osa.u.sa.sa_family;
+ port = osmo_sockaddr_port(&osa.u.sa);
+
+ if (sfd_family != AF_INET && sfd_family != AF_INET6)
+ return -EINVAL;
+
+ rc = addrinfo_helper_multi(res, AF_UNSPEC, type, proto, addrs,
+ addrs_cnt, port, true);
+ if (rc < 0)
+ return -EINVAL;
+
+ addrinfo_has_v4v6addr((const struct addrinfo **)res, addrs_cnt,
+ &res_has_v4addr, &res_has_v6addr);
+ if (sfd_family == AF_INET && !res_has_v4addr) {
+ rc = -EINVAL;
+ goto ret_free;
+ }
+
+ uint16_t new_addr_family;
+ if (sfd_family == AF_INET)
+ new_addr_family = AF_INET;
+ else if (sfd_family == AF_INET6 && !res_has_v4addr)
+ new_addr_family = AF_INET6;
+ else
+ new_addr_family = AF_UNSPEC;
+ rc = addrinfo_to_sockaddr(new_addr_family, (const struct addrinfo **)res,
+ addrs, addrs_cnt,
+ (uint8_t *)addrs_buf, sizeof(addrs_buf));
+ if (rc < 0) {
+ rc = -ENODEV;
+ goto ret_free;
+ }
+
+ rc = sctp_bindx(sfd, (struct sockaddr *)addrs_buf, addrs_cnt, SCTP_BINDX_ADD_ADDR);
+ if (rc == -1) {
+ int err = errno;
+ char strbuf[512];
+ multiaddr_snprintf(strbuf, sizeof(strbuf), addrs, addrs_cnt);
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Unable to bind socket to new addresses: %s:%u: %s\n",
+ strbuf, port, strerror(err));
+ rc = -ENODEV;
+ goto ret_free;
+ }
+
+ret_free:
+ for (i = 0; i < addrs_cnt; i++)
+ freeaddrinfo(res[i]);
+ return rc;
+}
+
+/*! Remove addresses from the multi-address (SCTP) socket active binding set
+ * \param[in] sfd The multi-address (SCTP) socket
+ * \param[in] addrs array of char pointers (strings), each containing local host name or IP address in string form
+ * \param[in] addrs_cnt length of addrs_hosts (in items)
+ * \returns 0 on success; negative on error
+ *
+ * This function only supports SCTP sockets so far, and hence it should be
+ * called only on socket file descriptions referencing that kind of sockets.
+ */
+int osmo_sock_multiaddr_del_local_addr(int sfd, const char **addrs, size_t addrs_cnt)
+{
+ struct osmo_sockaddr osa;
+ socklen_t slen = sizeof(osa);
+ uint16_t sfd_family;
+ uint16_t type = SOCK_STREAM ;/* Fixme: we assume fd is SOCK_STREAM */
+ uint8_t proto = IPPROTO_SCTP; /* Fixme: we assume fd is IPPROTO_SCTP */
+ struct addrinfo *res[OSMO_SOCK_MAX_ADDRS];
+ uint16_t port;
+ struct sockaddr_in6 addrs_buf[OSMO_SOCK_MAX_ADDRS];
+ unsigned int i;
+ int rc;
+ bool res_has_v4addr = false, res_has_v6addr = false;
+
+ rc = getsockname(sfd, &osa.u.sa, &slen);
+ if (rc < 0)
+ return rc; /* TODO: log error? */
+ sfd_family = osa.u.sa.sa_family;
+ port = osmo_sockaddr_port(&osa.u.sa);
+
+ if (sfd_family != AF_INET && sfd_family != AF_INET6)
+ return -EINVAL;
+
+ rc = addrinfo_helper_multi(res, AF_UNSPEC, type, proto, addrs,
+ addrs_cnt, port, true);
+ if (rc < 0)
+ return -EINVAL;
+
+ addrinfo_has_v4v6addr((const struct addrinfo **)res, addrs_cnt,
+ &res_has_v4addr, &res_has_v6addr);
+ if (sfd_family == AF_INET && !res_has_v4addr) {
+ rc = -EINVAL;
+ goto ret_free;
+ }
+
+ uint16_t del_addr_family;
+ if (sfd_family == AF_INET)
+ del_addr_family = AF_INET;
+ else if (sfd_family == AF_INET6 && !res_has_v4addr)
+ del_addr_family = AF_INET6;
+ else
+ del_addr_family = AF_UNSPEC;
+ rc = addrinfo_to_sockaddr(del_addr_family, (const struct addrinfo **)res,
+ addrs, addrs_cnt,
+ (uint8_t *)addrs_buf, sizeof(addrs_buf));
+ if (rc < 0) {
+ rc = -ENODEV;
+ goto ret_free;
+ }
+
+ rc = sctp_bindx(sfd, (struct sockaddr *)addrs_buf, addrs_cnt, SCTP_BINDX_REM_ADDR);
+ if (rc == -1) {
+ int err = errno;
+ char strbuf[512];
+ multiaddr_snprintf(strbuf, sizeof(strbuf), addrs, addrs_cnt);
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Unable to unbind socket from addresses: %s:%u: %s\n",
+ strbuf, port, strerror(err));
+ rc = -ENODEV;
+ goto ret_free;
+ }
+
+ret_free:
+ for (i = 0; i < addrs_cnt; i++)
+ freeaddrinfo(res[i]);
+ return rc;
+}
+#endif /* HAVE_LIBSCTP */
+
+static int sockaddr_equal(const struct sockaddr *a,
+ const struct sockaddr *b, unsigned int len)
+{
+ struct sockaddr_in *sin_a, *sin_b;
+ struct sockaddr_in6 *sin6_a, *sin6_b;
+
+ if (a->sa_family != b->sa_family)
+ return 0;
+
+ switch (a->sa_family) {
+ case AF_INET:
+ sin_a = (struct sockaddr_in *)a;
+ sin_b = (struct sockaddr_in *)b;
+ if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
+ sizeof(struct in_addr)))
+ return 1;
+ break;
+ case AF_INET6:
+ sin6_a = (struct sockaddr_in6 *)a;
+ sin6_b = (struct sockaddr_in6 *)b;
+ if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
+ sizeof(struct in6_addr)))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+/* linux has a default route:
+local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
+*/
+static int sockaddr_is_local_routed(const struct sockaddr *a)
+{
+#if __linux__
+ if (a->sa_family != AF_INET)
+ return 0;
+
+ uint32_t address = ((struct sockaddr_in *)a)->sin_addr.s_addr; /* already BE */
+ uint32_t eightmask = htonl(0xff000000); /* /8 mask */
+ uint32_t local_prefix_127 = htonl(0x7f000000); /* 127.0.0.0 */
+
+ if ((address & eightmask) == local_prefix_127)
+ return 1;
+#endif
+ return 0;
+}
+
+/*! Determine if the given address is a local address
+ * \param[in] addr Socket Address
+ * \param[in] addrlen Length of socket address in bytes
+ * \returns 1 if address is local, 0 otherwise.
+ */
+int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
+{
+ struct ifaddrs *ifaddr, *ifa;
+
+ if (sockaddr_is_local_routed(addr))
+ return 1;
+
+ if (getifaddrs(&ifaddr) == -1) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
+ " %s\n", strerror(errno));
+ return -EIO;
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
+ if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) {
+ freeifaddrs(ifaddr);
+ return 1;
+ }
+ }
+
+ freeifaddrs(ifaddr);
+ return 0;
+}
+
+/*! Determine if the given address is an ANY address ("0.0.0.0", "::"). Port is not checked.
+ * \param[in] addr Socket Address
+ * \param[in] addrlen Length of socket address in bytes
+ * \returns 1 if address is ANY, 0 otherwise. -1 is address family not supported/detected.
+ */
+int osmo_sockaddr_is_any(const struct osmo_sockaddr *addr)
+{
+ switch (addr->u.sa.sa_family) {
+ case AF_INET6: {
+ struct in6_addr ip6_any = IN6ADDR_ANY_INIT;
+ return memcmp(&addr->u.sin6.sin6_addr,
+ &ip6_any, sizeof(ip6_any)) == 0;
+ }
+ case AF_INET:
+ return addr->u.sin.sin_addr.s_addr == INADDR_ANY;
+ default:
+ return -1;
+ }
+}
+
+/*! Convert sockaddr_in to IP address as char string and port as uint16_t.
+ * \param[out] addr String buffer to write IP address to, or NULL.
+ * \param[out] addr_len Size of \a addr.
+ * \param[out] port Pointer to uint16_t to write the port number to, or NULL.
+ * \param[in] sin Sockaddr to convert.
+ * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL.
+ */
+size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
+ const struct sockaddr_in *sin)
+{
+ if (port)
+ *port = ntohs(sin->sin_port);
+
+ if (addr)
+ return osmo_strlcpy(addr, inet_ntoa(sin->sin_addr), addr_len);
+
+ return 0;
+}
+
+/*! Convert sockaddr to IP address as char string and port as uint16_t.
+ * \param[out] addr String buffer to write IP address to, or NULL.
+ * \param[out] addr_len Size of \a addr.
+ * \param[out] port Pointer to uint16_t to write the port number to, or NULL.
+ * \param[in] sa Sockaddr to convert.
+ * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL.
+ */
+unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
+ const struct sockaddr *sa)
+{
+
+ const struct sockaddr_in6 *sin6;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ return osmo_sockaddr_in_to_str_and_uint(addr, addr_len, port,
+ (const struct sockaddr_in *)sa);
+ case AF_INET6:
+ sin6 = (const struct sockaddr_in6 *)sa;
+ if (port)
+ *port = ntohs(sin6->sin6_port);
+ if (addr && inet_ntop(sa->sa_family, &sin6->sin6_addr, addr, addr_len))
+ return strlen(addr);
+ break;
+ }
+ return 0;
+}
+
+/*! inet_ntop() wrapper for a struct sockaddr.
+ * \param[in] sa source sockaddr to get the address from.
+ * \param[out] dst string buffer of at least INET6_ADDRSTRLEN size.
+ * \returns returns a non-null pointer to dst. NULL is returned if there was an
+ * error, with errno set to indicate the error.
+ */
+const char *osmo_sockaddr_ntop(const struct sockaddr *sa, char *dst)
+{
+ const struct osmo_sockaddr *osa = (const struct osmo_sockaddr *)sa;
+ return inet_ntop(osa->u.sa.sa_family,
+ osa->u.sa.sa_family == AF_INET6 ?
+ (const void *)&osa->u.sin6.sin6_addr :
+ (const void *)&osa->u.sin.sin_addr,
+ dst, INET6_ADDRSTRLEN);
+}
+
+/*! Get sockaddr port content (in host byte order)
+ * \param[in] sa source sockaddr to get the port from.
+ * \returns returns the sockaddr port in host byte order
+ */
+uint16_t osmo_sockaddr_port(const struct sockaddr *sa)
+{
+ const struct osmo_sockaddr *osa = (const struct osmo_sockaddr *)sa;
+ switch (osa->u.sa.sa_family) {
+ case AF_INET6:
+ return ntohs(osa->u.sin6.sin6_port);
+ case AF_INET:
+ return ntohs(osa->u.sin.sin_port);
+ }
+ return 0;
+}
+
+/*! Set sockaddr port content (to network byte order).
+ * \param[out] sa sockaddr to set the port of.
+ * \param[in] port port nr to set.
+ */
+void osmo_sockaddr_set_port(struct sockaddr *sa, uint16_t port)
+{
+ struct osmo_sockaddr *osa = (struct osmo_sockaddr *)sa;
+ switch (osa->u.sa.sa_family) {
+ case AF_INET6:
+ osa->u.sin6.sin6_port = htons(port);
+ return;
+ case AF_INET:
+ osa->u.sin.sin_port = htons(port);
+ return;
+ }
+}
+
+static unsigned int in6_addr_netmask_to_prefixlen(const struct in6_addr *netmask)
+{
+ #if defined(__linux__)
+ #define ADDRFIELD(i) s6_addr32[i]
+ #else
+ #define ADDRFIELD(i) __u6_addr.__u6_addr32[i]
+ #endif
+
+ unsigned int i, j, prefix = 0;
+
+ for (j = 0; j < 4; j++) {
+ uint32_t bits = netmask->ADDRFIELD(j);
+ uint8_t *b = (uint8_t *)&bits;
+ for (i = 0; i < 4; i++) {
+ while (b[i] & 0x80) {
+ prefix++;
+ b[i] = b[i] << 1;
+ }
+ }
+ }
+
+ #undef ADDRFIELD
+
+ return prefix;
+}
+
+static unsigned int in_addr_netmask_to_prefixlen(const struct in_addr *netmask)
+{
+ uint32_t bits = netmask->s_addr;
+ uint8_t *b = (uint8_t *)&bits;
+ unsigned int i, prefix = 0;
+
+ for (i = 0; i < 4; i++) {
+ while (b[i] & 0x80) {
+ prefix++;
+ b[i] = b[i] << 1;
+ }
+ }
+ return prefix;
+}
+
+/*! Convert netmask to prefix length representation
+ * \param[in] netmask sockaddr containing a netmask (consecutive list of 1-bit followed by consecutive list of 0-bit)
+ * \returns prefix length representation of the netmask (count of 1-bit from the start of the netmask), negative on error.
+ */
+int osmo_sockaddr_netmask_to_prefixlen(const struct osmo_sockaddr *netmask)
+{
+ switch (netmask->u.sa.sa_family) {
+ case AF_INET6:
+ return in6_addr_netmask_to_prefixlen(&netmask->u.sin6.sin6_addr);
+ case AF_INET:
+ return in_addr_netmask_to_prefixlen(&netmask->u.sin.sin_addr);
+ default:
+ return -ENOTSUP;
+ }
+}
+
+/*! Convert an IP address string (and port number) into a 'struct osmo_sockaddr'.
+ * \param[out] osa_out caller-allocated osmo_sockaddr storage
+ * \param[in] ipstr IP[v4,v6] address in string format
+ * \param[in] port port number (host byte order)
+ * \returns 0 on success; negative on error. */
+int osmo_sockaddr_from_str_and_uint(struct osmo_sockaddr *osa_out, const char *ipstr, uint16_t port)
+{
+ struct addrinfo *ai = addrinfo_helper(AF_UNSPEC, 0, 0, ipstr, port, true);
+
+ if (!ai)
+ return -EIO;
+
+ if (ai->ai_addrlen > sizeof(*osa_out))
+ return -ENOSPC;
+
+ memcpy(&osa_out->u.sa, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+
+ return 0;
+}
+
+/*! Initialize a unix domain socket (including bind/connect)
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] socket_path path to identify the socket
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
+ *
+ * This function creates a new unix domain socket, \a
+ * type and \a proto and optionally binds or connects it, depending on
+ * the value of \a flags parameter.
+ */
+#if defined(__clang__) && defined(SUN_LEN)
+__attribute__((no_sanitize("undefined")))
+#endif
+int osmo_sock_unix_init(uint16_t type, uint8_t proto,
+ const char *socket_path, unsigned int flags)
+{
+ struct sockaddr_un local;
+ int sfd, rc;
+ unsigned int namelen;
+
+ if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
+ (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
+ return -EINVAL;
+
+ local.sun_family = AF_UNIX;
+ /* When an AF_UNIX socket is bound, sun_path should be NUL-terminated. See unix(7) man page. */
+ if (osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path)) >= sizeof(local.sun_path)) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Socket path exceeds maximum length of %zd bytes: %s\n",
+ sizeof(local.sun_path), socket_path);
+ return -ENOSPC;
+ }
+
+#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
+
+ sfd = socket(AF_UNIX, type, proto);
+ if (sfd < 0)
+ return -errno;
+
+ if (flags & OSMO_SOCK_F_CONNECT) {
+ rc = connect(sfd, (struct sockaddr *)&local, namelen);
+ if (rc < 0)
+ goto err;
+ } else {
+ unlink(local.sun_path);
+ rc = bind(sfd, (struct sockaddr *)&local, namelen);
+ if (rc < 0)
+ goto err;
+ }
+
+ rc = socket_helper_tail(sfd, flags);
+ if (rc < 0)
+ return rc;
+
+ rc = osmo_sock_init_tail(sfd, type, flags);
+ if (rc < 0) {
+ close(sfd);
+ sfd = rc;
+ }
+
+ return sfd;
+err:
+ close(sfd);
+ return -errno;
+}
+
+/*! Initialize a unix domain socket and fill \ref osmo_fd
+ * \param[out] ofd file descriptor (will be filled in)
+ * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
+ * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
+ * \param[in] socket_path path to identify the socket
+ * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
+ * \returns socket fd on success; negative on error
+ *
+ * This function creates (and optionally binds/connects) a socket
+ * using osmo_sock_unix_init, but also fills the ofd structure.
+ */
+int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
+ const char *socket_path, unsigned int flags)
+{
+ return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags), flags);
+}
+
+/*! Get the IP and/or port number on socket in separate string buffers.
+ * \param[in] fd file descriptor of socket
+ * \param[out] ip IP address (will be filled in when not NULL)
+ * \param[in] ip_len length of the ip buffer
+ * \param[out] port number (will be filled in when not NULL)
+ * \param[in] port_len length of the port buffer
+ * \param[in] local (true) or remote (false) name will get looked at
+ * \returns 0 on success; negative otherwise
+ */
+int osmo_sock_get_ip_and_port(int fd, char *ip, size_t ip_len, char *port, size_t port_len, bool local)
+{
+ struct sockaddr_storage sa;
+ socklen_t len = sizeof(sa);
+ char ipbuf[INET6_ADDRSTRLEN], portbuf[6];
+ int rc;
+
+ rc = local ? getsockname(fd, (struct sockaddr*)&sa, &len) : getpeername(fd, (struct sockaddr*)&sa, &len);
+ if (rc < 0)
+ return rc;
+
+ rc = getnameinfo((const struct sockaddr*)&sa, len, ipbuf, sizeof(ipbuf),
+ portbuf, sizeof(portbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc < 0)
+ return rc;
+
+ if (ip)
+ strncpy(ip, ipbuf, ip_len);
+ if (port)
+ strncpy(port, portbuf, port_len);
+ return 0;
+}
+
+#ifdef HAVE_LIBSCTP
+/*! Get multiple IP addresses and/or port number on socket in separate string buffers
+ * \param[in] fd file descriptor of socket.
+ * \param[out] ip_proto IPPROTO of the socket, eg: IPPROTO_SCTP.
+ * \param[out] ip Pointer to memory holding consecutive buffers of size ip_len.
+ * \param[out] ip_cnt length ip array pointer. on return it contains the number of addresses found.
+ * \param[in] ip_len length of each of the string buffer in the the ip array.
+ * \param[out] port number (will be filled in when not NULL).
+ * \param[in] port_len length of the port buffer.
+ * \param[in] local (true) or remote (false) name will get looked at.
+ * \returns 0 on success; negative otherwise.
+ *
+ * Upon return, ip_cnt can be set to a higher value than the one set by the
+ * caller. This can be used by the caller to find out the required array length
+ * and then obtaining by calling the function twice. Only up to ip_cnt addresses
+ * are filed in, as per the value provided by the caller.
+ *
+ * Usage example retrieving all (up to OSMO_SOCK_MAX_ADDRS, 32) bound IP addresses and bound port:
+ * char hostbuf[OSMO_SOCK_MAX_ADDRS][INET6_ADDRSTRLEN];
+ * size_t num_hostbuf = ARRAY_SIZE(hostbuf);
+ * char portbuf[6];
+ * rc = osmo_sock_multiaddr_get_ip_and_port(fd, IPPROTO_SCTP, &hostbuf[0][0], &num_hostbuf,
+ * sizeof(hostbuf[0]), portbuf, sizeof(portbuf), true);
+ * if (rc < 0)
+ * goto error;
+ * if (num_hostbuf > ARRAY_SIZE(hostbuf))
+ * goto not_enough_buffers;
+ */
+int osmo_sock_multiaddr_get_ip_and_port(int fd, int ip_proto, char *ip, size_t *ip_cnt, size_t ip_len,
+ char *port, size_t port_len, bool local)
+{
+ struct sockaddr *addrs = NULL;
+ unsigned int n_addrs, i;
+ void *addr_buf;
+ int rc;
+
+ switch (ip_proto) {
+ case IPPROTO_SCTP:
+ break; /* continue below */
+ default:
+ if (*ip_cnt == 0) {
+ *ip_cnt = 1;
+ return 0;
+ }
+ *ip_cnt = 1;
+ return osmo_sock_get_ip_and_port(fd, ip, ip_len, port, port_len, local);
+ }
+
+ rc = local ? sctp_getladdrs(fd, 0, &addrs) : sctp_getpaddrs(fd, 0, &addrs);
+ if (rc < 0)
+ return rc;
+ if (rc == 0)
+ return -ENOTCONN;
+
+ n_addrs = rc;
+ addr_buf = (void *)addrs;
+ for (i = 0; i < n_addrs; i++) {
+ struct sockaddr *sa_addr = (struct sockaddr *)addr_buf;
+ size_t addrlen;
+
+ if (i >= *ip_cnt)
+ break;
+
+ switch (sa_addr->sa_family) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ rc = -EINVAL;
+ goto free_addrs_ret;
+ }
+
+ rc = getnameinfo(sa_addr, addrlen, &ip[i * ip_len], ip_len,
+ port, port_len,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc < 0)
+ goto free_addrs_ret;
+ addr_buf += addrlen;
+ }
+
+ *ip_cnt = n_addrs;
+ rc = 0;
+free_addrs_ret:
+ local ? sctp_freeladdrs(addrs) : sctp_freepaddrs(addrs);
+ return rc;
+}
+#endif
+
+/*! Get local IP address on socket
+ * \param[in] fd file descriptor of socket
+ * \param[out] ip IP address (will be filled in)
+ * \param[in] len length of the output buffer
+ * \returns 0 on success; negative otherwise
+ */
+int osmo_sock_get_local_ip(int fd, char *ip, size_t len)
+{
+ return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, true);
+}
+
+/*! Get local port on socket
+ * \param[in] fd file descriptor of socket
+ * \param[out] port number (will be filled in)
+ * \param[in] len length of the output buffer
+ * \returns 0 on success; negative otherwise
+ */
+int osmo_sock_get_local_ip_port(int fd, char *port, size_t len)
+{
+ return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, true);
+}
+
+/*! Get remote IP address on socket
+ * \param[in] fd file descriptor of socket
+ * \param[out] ip IP address (will be filled in)
+ * \param[in] len length of the output buffer
+ * \returns 0 on success; negative otherwise
+ */
+int osmo_sock_get_remote_ip(int fd, char *ip, size_t len)
+{
+ return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, false);
+}
+
+/*! Get remote port on socket
+ * \param[in] fd file descriptor of socket
+ * \param[out] port number (will be filled in)
+ * \param[in] len length of the output buffer
+ * \returns 0 on success; negative otherwise
+ */
+int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len)
+{
+ return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, false);
+}
+
+/*! Get address/port information on socket in dyn-alloc string like "(r=1.2.3.4:5<->l=6.7.8.9:10)".
+ * Usually, it is better to use osmo_sock_get_name2() for a static string buffer or osmo_sock_get_name_buf() for a
+ * caller provided string buffer, to avoid the dynamic talloc allocation.
+ * \param[in] ctx talloc context from which to allocate string buffer
+ * \param[in] fd file descriptor of socket
+ * \returns string identifying the connection of this socket, talloc'd from ctx.
+ */
+char *osmo_sock_get_name(const void *ctx, int fd)
+{
+ char str[OSMO_SOCK_NAME_MAXLEN];
+ int rc;
+ rc = osmo_sock_get_name_buf(str, sizeof(str), fd);
+ if (rc <= 0)
+ return NULL;
+ return talloc_asprintf(ctx, "(%s)", str);
+}
+
+#ifdef HAVE_LIBSCTP
+/*! Format multiple IP addresses and/or port number into a combined string buffer
+ * \param[out] str Destination string buffer.
+ * \param[in] str_len sizeof(str), usually OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN.
+ * \param[out] ip Pointer to memory holding ip_cnt consecutive buffers of size ip_len.
+ * \param[out] ip_cnt length ip array pointer. on return it contains the number of addresses found.
+ * \param[in] ip_len length of each of the string buffer in the the ip array.
+ * \param[out] port number (will be printed in when not NULL).
+ * \return String length as returned by snprintf(), or negative on error.
+ *
+ * This API expects an ip array as the one filled in by
+ * osmo_sock_multiaddr_get_ip_and_port(), and hence it's a good companion for
+ * that API.
+ */
+int osmo_multiaddr_ip_and_port_snprintf(char *str, size_t str_len,
+ const char *ip, size_t ip_cnt, size_t ip_len,
+ const char *portbuf)
+{
+ struct osmo_strbuf sb = { .buf = str, .len = str_len };
+ bool is_v6 = false;
+ unsigned int i;
+
+ if (ip_cnt == 0) {
+ OSMO_STRBUF_PRINTF(sb, "NULL:%s", portbuf);
+ return sb.chars_needed;
+ }
+ if (ip_cnt > 1)
+ OSMO_STRBUF_PRINTF(sb, "(");
+ else if ((is_v6 = !!strchr(&ip[0], ':'))) /* IPv6, add [] to separate from port. */
+ OSMO_STRBUF_PRINTF(sb, "[");
+
+ for (i = 0; i < ip_cnt - 1; i++)
+ OSMO_STRBUF_PRINTF(sb, "%s|", &ip[i * ip_len]);
+ OSMO_STRBUF_PRINTF(sb, "%s", &ip[i * ip_len]);
+
+ if (ip_cnt > 1)
+ OSMO_STRBUF_PRINTF(sb, ")");
+ else if (is_v6)
+ OSMO_STRBUF_PRINTF(sb, "]");
+ if (portbuf)
+ OSMO_STRBUF_PRINTF(sb, ":%s", portbuf);
+
+ return sb.chars_needed;
+}
+
+/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10".
+ * This does not include braces like osmo_sock_get_name().
+ * \param[out] str Destination string buffer.
+ * \param[in] str_len sizeof(str), usually OSMO_SOCK_MULTIADDR_NAME_MAXLEN.
+ * \param[in] fd File descriptor of socket.
+ * \param[in] fd IPPROTO of the socket, eg: IPPROTO_SCTP.
+ * \return String length as returned by snprintf(), or negative on error.
+ */
+int osmo_sock_multiaddr_get_name_buf(char *str, size_t str_len, int fd, int sk_proto)
+{
+ char hostbuf[OSMO_SOCK_MAX_ADDRS][INET6_ADDRSTRLEN];
+ size_t num_hostbuf = ARRAY_SIZE(hostbuf);
+ char portbuf[6];
+ struct osmo_strbuf sb = { .buf = str, .len = str_len };
+
+ if (fd < 0) {
+ osmo_strlcpy(str, "<error-bad-fd>", str_len);
+ return sb.chars_needed;
+ }
+
+ switch (sk_proto) {
+ case IPPROTO_SCTP:
+ break; /* continue below */
+ default:
+ return osmo_sock_get_name_buf(str, str_len, fd);
+ }
+
+ /* get remote */
+ OSMO_STRBUF_PRINTF(sb, "r=");
+ if (osmo_sock_multiaddr_get_ip_and_port(fd, sk_proto, &hostbuf[0][0], &num_hostbuf,
+ sizeof(hostbuf[0]), portbuf, sizeof(portbuf), false) != 0) {
+ OSMO_STRBUF_PRINTF(sb, "NULL");
+ } else {
+ const bool need_more_bufs = num_hostbuf > ARRAY_SIZE(hostbuf);
+ if (need_more_bufs)
+ num_hostbuf = ARRAY_SIZE(hostbuf);
+ OSMO_STRBUF_APPEND(sb, osmo_multiaddr_ip_and_port_snprintf,
+ &hostbuf[0][0], num_hostbuf, sizeof(hostbuf[0]), portbuf);
+ if (need_more_bufs)
+ OSMO_STRBUF_PRINTF(sb, "<need-more-bufs!>");
+ }
+
+ OSMO_STRBUF_PRINTF(sb, "<->l=");
+
+ /* get local */
+ num_hostbuf = ARRAY_SIZE(hostbuf);
+ if (osmo_sock_multiaddr_get_ip_and_port(fd, sk_proto, &hostbuf[0][0], &num_hostbuf,
+ sizeof(hostbuf[0]), portbuf, sizeof(portbuf), true) != 0) {
+ OSMO_STRBUF_PRINTF(sb, "NULL");
+ } else {
+ const bool need_more_bufs = num_hostbuf > ARRAY_SIZE(hostbuf);
+ if (need_more_bufs)
+ num_hostbuf = ARRAY_SIZE(hostbuf);
+ OSMO_STRBUF_APPEND(sb, osmo_multiaddr_ip_and_port_snprintf,
+ &hostbuf[0][0], num_hostbuf, sizeof(hostbuf[0]), portbuf);
+ if (need_more_bufs)
+ OSMO_STRBUF_PRINTF(sb, "<need-more-bufs!>");
+ }
+
+ return sb.chars_needed;
+}
+#endif
+
+/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10".
+ * This does not include braces like osmo_sock_get_name().
+ * \param[out] str Destination string buffer.
+ * \param[in] str_len sizeof(str).
+ * \param[in] fd File descriptor of socket.
+ * \return String length as returned by snprintf(), or negative on error.
+ */
+int osmo_sock_get_name_buf(char *str, size_t str_len, int fd)
+{
+ struct osmo_strbuf sb = { .buf = str, .len = str_len };
+ struct osmo_sockaddr osa;
+ struct sockaddr_un *sun;
+ socklen_t len;
+ int rc;
+
+ if (fd < 0) {
+ osmo_strlcpy(str, "<error-bad-fd>", str_len);
+ return -EBADF;
+ }
+
+
+ len = sizeof(osa.u.sas);
+ rc = getsockname(fd, &osa.u.sa, &len);
+ if (rc < 0) {
+ osmo_strlcpy(str, "<error-in-getsockname>", str_len);
+ return rc;
+ }
+
+ switch (osa.u.sa.sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ {
+ char hostbuf_l[INET6_ADDRSTRLEN], hostbuf_r[INET6_ADDRSTRLEN];
+ char portbuf_l[6], portbuf_r[6];
+
+ len = sizeof(osa.u.sas);
+ rc = getnameinfo(&osa.u.sa, len, hostbuf_l, sizeof(hostbuf_l),
+ portbuf_l, sizeof(portbuf_l),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc < 0) {
+ osmo_strlcpy(str, "<error-in-getnameinfo>", str_len);
+ return rc;
+ }
+ /* Now attempt to get remote: */
+ len = sizeof(osa.u.sas);
+ rc = getpeername(fd, &osa.u.sa, &len);
+ if (rc < 0) {
+ OSMO_STRBUF_PRINTF(sb, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l);
+ return sb.chars_needed;
+ }
+ len = sizeof(osa.u.sas);
+ rc = getnameinfo(&osa.u.sa, len, hostbuf_r, sizeof(hostbuf_r),
+ portbuf_r, sizeof(portbuf_r),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (rc < 0) {
+ OSMO_STRBUF_PRINTF(sb, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l);
+ return sb.chars_needed;
+ }
+ OSMO_STRBUF_PRINTF(sb, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l);
+ return sb.chars_needed;
+ }
+ case AF_UNIX:
+ {
+ unsigned long long remote_pid;
+ bool have_remote_pid;
+#if defined(SO_PEERCRED)
+ struct ucred ucred;
+ len = sizeof(struct ucred);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
+ have_remote_pid = false;
+ } else {
+ have_remote_pid = true;
+ remote_pid = (unsigned long long)ucred.pid;
+ }
+#else
+ #pragma message "SO_PEERCRED not available"
+ have_remote_pid = false;
+#endif
+ /* Make sure sun_path is NULL terminated: */
+ sun = (struct sockaddr_un *)&osa.u.sa;
+ sun->sun_path[sizeof(sun->sun_path) - 1] = '\0';
+ if (have_remote_pid)
+ OSMO_STRBUF_PRINTF(sb, "r=%llu<->", remote_pid);
+ else
+ OSMO_STRBUF_PRINTF(sb, "r=NULL<->");
+ OSMO_STRBUF_PRINTF(sb, "l=%s:%d", sun->sun_path, fd);
+ return sb.chars_needed;
+ }
+ default:
+ osmo_strlcpy(str, "<socket-family-no-supported>", str_len);
+ return -ENOTSUP;
+ }
+}
+
+/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
+ * This does not include braces like osmo_sock_get_name().
+ * \param[in] fd File descriptor of socket.
+ * \return Static string buffer containing the result.
+ */
+const char *osmo_sock_get_name2(int fd)
+{
+ static __thread char str[OSMO_SOCK_NAME_MAXLEN];
+ osmo_sock_get_name_buf(str, sizeof(str), fd);
+ return str;
+}
+
+/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
+ * This does not include braces like osmo_sock_get_name().
+ * \param[in] fd File descriptor of socket.
+ * \return Static string buffer containing the result.
+ */
+char *osmo_sock_get_name2_c(const void *ctx, int fd)
+{
+ char *str = talloc_size(ctx, OSMO_SOCK_NAME_MAXLEN);
+ if (!str)
+ return NULL;
+ osmo_sock_get_name_buf(str, OSMO_SOCK_NAME_MAXLEN, fd);
+ return str;
+}
+
+static int sock_get_domain(int fd)
+{
+ int domain;
+#ifdef SO_DOMAIN
+ socklen_t dom_len = sizeof(domain);
+ int rc;
+
+ rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
+ if (rc < 0)
+ return rc;
+#else
+ /* This of course sucks, but what shall we do on OSs like
+ * FreeBSD that don't seem to expose a method by which one can
+ * learn the address family of a socket? */
+ domain = AF_INET;
+#endif
+ return domain;
+}
+
+
+/*! Activate or de-activate local loop-back of transmitted multicast packets
+ * \param[in] fd file descriptor of related socket
+ * \param[in] enable Enable (true) or disable (false) loop-back
+ * \returns 0 on success; negative otherwise */
+int osmo_sock_mcast_loop_set(int fd, bool enable)
+{
+ int domain, loop = 0;
+
+ if (enable)
+ loop = 1;
+
+ domain = sock_get_domain(fd);
+ if (domain < 0)
+ return domain;
+
+ switch (domain) {
+ case AF_INET:
+ return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
+ case AF_INET6:
+ return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
+ default:
+ return -EINVAL;
+ }
+}
+
+/*! Set the TTL of outbound multicast packets
+ * \param[in] fd file descriptor of related socket
+ * \param[in] ttl TTL of to-be-sent multicast packets
+ * \returns 0 on success; negative otherwise */
+int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
+{
+ int domain, ttli = ttl;
+
+ domain = sock_get_domain(fd);
+ if (domain < 0)
+ return domain;
+
+ switch (domain) {
+ case AF_INET:
+ return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli));
+ case AF_INET6:
+ return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
+ default:
+ return -EINVAL;
+ }
+}
+
+/*! Set the network device to which we should bind the multicast socket
+ * \param[in] fd file descriptor of related socket
+ * \param[in] ifname name of network interface to user for multicast
+ * \returns 0 on success; negative otherwise */
+int osmo_sock_mcast_iface_set(int fd, const char *ifname)
+{
+ unsigned int ifindex;
+ struct ip_mreqn mr;
+
+ /* first, resolve interface name to ifindex */
+ ifindex = if_nametoindex(ifname);
+ if (ifindex == 0)
+ return -errno;
+
+ /* next, configure kernel to use that ifindex for this sockets multicast traffic */
+ memset(&mr, 0, sizeof(mr));
+ mr.imr_ifindex = ifindex;
+ return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mr, sizeof(mr));
+}
+
+
+/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
+ * \param[in] fd file descriptor of related socket
+ * \param[in] enable Enable or Disable receiving of all packets
+ * \returns 0 on success; negative otherwise */
+int osmo_sock_mcast_all_set(int fd, bool enable)
+{
+ int domain, all = 0;
+
+ if (enable)
+ all = 1;
+
+ domain = sock_get_domain(fd);
+ if (domain < 0)
+ return domain;
+
+ switch (domain) {
+ case AF_INET:
+#ifdef IP_MULTICAST_ALL
+ return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
+#endif
+ case AF_INET6:
+ /* there seems no equivalent ?!? */
+ default:
+ return -EINVAL;
+ }
+}
+
+/* FreeBSD calls the socket option differently */
+#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#endif
+
+/*! Subscribe to the given IP multicast group
+ * \param[in] fd file descriptor of related scoket
+ * \param[in] grp_addr ASCII representation of the multicast group address
+ * \returns 0 on success; negative otherwise */
+int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
+{
+ int rc, domain;
+ struct ip_mreq mreq;
+ struct ipv6_mreq mreq6;
+ struct in6_addr i6a;
+
+ domain = sock_get_domain(fd);
+ if (domain < 0)
+ return domain;
+
+ switch (domain) {
+ case AF_INET:
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+#ifdef IPV6_ADD_MEMBERSHIP
+ case AF_INET6:
+ memset(&mreq6, 0, sizeof(mreq6));
+ rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
+ if (rc < 0)
+ return -EINVAL;
+ mreq6.ipv6mr_multiaddr = i6a;
+ return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
+#endif
+ default:
+ return -EINVAL;
+ }
+}
+
+/*! Determine the matching local IP-address for a given remote IP-Address.
+ * \param[out] local_ip caller provided memory for resulting local IP-address
+ * \param[in] remote_ip remote IP-address
+ * \returns 0 on success; negative otherwise
+ *
+ * The function accepts IPv4 and IPv6 address strings. The caller must provide
+ * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as
+ * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */
+int osmo_sock_local_ip(char *local_ip, const char *remote_ip)
+{
+ int sfd;
+ int rc;
+ struct addrinfo addrinfo_hint;
+ struct addrinfo *addrinfo = NULL;
+ struct sockaddr_storage local_addr;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ socklen_t local_addr_len;
+ uint16_t family;
+
+ /* Find out the address family (AF_INET or AF_INET6?) */
+ memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint));
+ addrinfo_hint.ai_family = AF_UNSPEC;
+ addrinfo_hint.ai_flags = AI_NUMERICHOST;
+ rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo);
+ if (rc)
+ return -EINVAL;
+ family = addrinfo->ai_family;
+ freeaddrinfo(addrinfo);
+
+ /* Connect a dummy socket to trick the kernel into determining the
+ * ip-address of the interface that would be used if we would send
+ * out an actual packet */
+ sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT);
+ if (sfd < 0)
+ return -EINVAL;
+
+ /* Request the IP address of the interface that the kernel has
+ * actually choosen. */
+ memset(&local_addr, 0, sizeof(local_addr));
+ local_addr_len = sizeof(local_addr);
+ rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len);
+ close(sfd);
+ if (rc < 0)
+ return -EINVAL;
+
+ switch (local_addr.ss_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in*)&local_addr;
+ if (!inet_ntop(AF_INET, &sin->sin_addr, local_ip, INET_ADDRSTRLEN))
+ return -EINVAL;
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6*)&local_addr;
+ if (!inet_ntop(AF_INET6, &sin6->sin6_addr, local_ip, INET6_ADDRSTRLEN))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*! Determine the matching local address for a given remote address.
+ * \param[out] local_ip caller provided memory for resulting local address
+ * \param[in] remote_ip remote address
+ * \returns 0 on success; negative otherwise
+ */
+int osmo_sockaddr_local_ip(struct osmo_sockaddr *local_ip, const struct osmo_sockaddr *remote_ip)
+{
+ int sfd;
+ int rc;
+ socklen_t local_ip_len;
+
+ sfd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, NULL, remote_ip, OSMO_SOCK_F_CONNECT);
+ if (sfd < 0)
+ return -EINVAL;
+
+ memset(local_ip, 0, sizeof(*local_ip));
+ local_ip_len = sizeof(*local_ip);
+ rc = getsockname(sfd, (struct sockaddr *)local_ip, &local_ip_len);
+ close(sfd);
+
+ return rc;
+}
+
+/*! Copy the addr part, the IP address octets in network byte order, to a buffer.
+ * Useful for encoding network protocols.
+ * \param[out] dst Write octets to this buffer.
+ * \param[in] dst_maxlen Space available in buffer.
+ * \param[in] os Sockaddr to copy IP of.
+ * \return nr of octets written on success, negative on error.
+ */
+int osmo_sockaddr_to_octets(uint8_t *dst, size_t dst_maxlen, const struct osmo_sockaddr *os)
+{
+ const void *addr;
+ size_t len;
+ switch (os->u.sa.sa_family) {
+ case AF_INET:
+ addr = &os->u.sin.sin_addr;
+ len = sizeof(os->u.sin.sin_addr);
+ break;
+ case AF_INET6:
+ addr = &os->u.sin6.sin6_addr;
+ len = sizeof(os->u.sin6.sin6_addr);
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ if (dst_maxlen < len)
+ return -ENOSPC;
+ memcpy(dst, addr, len);
+ return len;
+}
+
+/*! Copy the addr part, the IP address octets in network byte order, from a buffer.
+ * Useful for decoding network protocols.
+ * \param[out] os Write IP address to this sockaddr.
+ * \param[in] src Source buffer to read IP address octets from.
+ * \param[in] src_len Number of octets to copy.
+ * \return number of octets read on success, negative on error.
+ */
+int osmo_sockaddr_from_octets(struct osmo_sockaddr *os, const void *src, size_t src_len)
+{
+ void *addr;
+ size_t len;
+ *os = (struct osmo_sockaddr){0};
+ switch (src_len) {
+ case sizeof(os->u.sin.sin_addr):
+ os->u.sa.sa_family = AF_INET;
+ addr = &os->u.sin.sin_addr;
+ len = sizeof(os->u.sin.sin_addr);
+ break;
+ case sizeof(os->u.sin6.sin6_addr):
+ os->u.sin6.sin6_family = AF_INET6;
+ addr = &os->u.sin6.sin6_addr;
+ len = sizeof(os->u.sin6.sin6_addr);
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ memcpy(addr, src, len);
+ return len;
+}
+
+/*! Compare two osmo_sockaddr.
+ * \param[in] a
+ * \param[in] b
+ * \return 0 if a and b are equal. Otherwise it follows memcmp()
+ */
+int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
+ const struct osmo_sockaddr *b)
+{
+ if (a == b)
+ return 0;
+ if (!a)
+ return 1;
+ if (!b)
+ return -1;
+
+ if (a->u.sa.sa_family != b->u.sa.sa_family) {
+ return OSMO_CMP(a->u.sa.sa_family, b->u.sa.sa_family);
+ }
+
+ switch (a->u.sa.sa_family) {
+ case AF_INET:
+ return memcmp(&a->u.sin, &b->u.sin, sizeof(struct sockaddr_in));
+ case AF_INET6:
+ return memcmp(&a->u.sin6, &b->u.sin6, sizeof(struct sockaddr_in6));
+ default:
+ /* fallback to memcmp for remaining AF over the full osmo_sockaddr length */
+ return memcmp(a, b, sizeof(struct osmo_sockaddr));
+ }
+}
+
+/*! string-format a given osmo_sockaddr address
+ * \param[in] sockaddr the osmo_sockaddr to print
+ * \return pointer to the string on success; NULL on error
+ */
+const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *sockaddr)
+{
+ /* INET6_ADDRSTRLEN contains already a null termination,
+ * adding '[' ']' ':' '16 bit port' */
+ static __thread char buf[INET6_ADDRSTRLEN + 8];
+ return osmo_sockaddr_to_str_buf(buf, sizeof(buf), sockaddr);
+}
+
+/*! string-format a given osmo_sockaddr address into a user-supplied buffer.
+ * Same as osmo_sockaddr_to_str_buf() but returns a would-be length in snprintf() style.
+ * \param[in] buf user-supplied output buffer
+ * \param[in] buf_len size of the user-supplied output buffer in bytes
+ * \param[in] sockaddr the osmo_sockaddr to print
+ * \return number of characters that would be written if the buffer is large enough, like snprintf().
+ */
+int osmo_sockaddr_to_str_buf2(char *buf, size_t buf_len, const struct osmo_sockaddr *sockaddr)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buf_len };
+ uint16_t port = 0;
+
+ if (!sockaddr) {
+ OSMO_STRBUF_PRINTF(sb, "NULL");
+ return sb.chars_needed;
+ }
+
+ switch (sockaddr->u.sa.sa_family) {
+ case AF_INET:
+ OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_and_uint, &port, &sockaddr->u.sa);
+ if (port)
+ OSMO_STRBUF_PRINTF(sb, ":%u", port);
+ break;
+ case AF_INET6:
+ OSMO_STRBUF_PRINTF(sb, "[");
+ OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_and_uint, &port, &sockaddr->u.sa);
+ OSMO_STRBUF_PRINTF(sb, "]");
+ if (port)
+ OSMO_STRBUF_PRINTF(sb, ":%u", port);
+ break;
+ default:
+ OSMO_STRBUF_PRINTF(sb, "unsupported family %d", sockaddr->u.sa.sa_family);
+ break;
+ }
+
+ return sb.chars_needed;
+}
+
+/*! string-format a given osmo_sockaddr address into a talloc allocated buffer.
+ * Like osmo_sockaddr_to_str_buf2() but returns a talloc allocated string.
+ * \param[in] ctx talloc context to allocate from, e.g. OTC_SELECT.
+ * \param[in] sockaddr the osmo_sockaddr to print.
+ * \return human readable string.
+ */
+char *osmo_sockaddr_to_str_c(void *ctx, const struct osmo_sockaddr *sockaddr)
+{
+ OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_sockaddr_to_str_buf2, sockaddr)
+}
+
+/*! string-format a given osmo_sockaddr address into a user-supplied buffer.
+ * Like osmo_sockaddr_to_str_buf2() but returns buf, or NULL if too short.
+ * \param[in] buf user-supplied output buffer
+ * \param[in] buf_len size of the user-supplied output buffer in bytes
+ * \param[in] sockaddr the osmo_sockaddr to print
+ * \return pointer to the string on success; NULL on error
+ */
+char *osmo_sockaddr_to_str_buf(char *buf, size_t buf_len,
+ const struct osmo_sockaddr *sockaddr)
+{
+ int chars_needed = osmo_sockaddr_to_str_buf2(buf, buf_len, sockaddr);
+ if (chars_needed >= buf_len)
+ return NULL;
+ return buf;
+}
+
+/*! Set the DSCP (differentiated services code point) of a socket.
+ * \param[in] dscp DSCP value in range 0..63
+ * \returns 0 on success; negative on error. */
+int osmo_sock_set_dscp(int fd, uint8_t dscp)
+{
+ struct sockaddr_storage local_addr;
+ socklen_t local_addr_len = sizeof(local_addr);
+ uint8_t tos;
+ socklen_t tos_len = sizeof(tos);
+ int tclass;
+ socklen_t tclass_len = sizeof(tclass);
+ int rc;
+
+ /* DSCP is a 6-bit value stored in the upper 6 bits of the 8-bit TOS */
+ if (dscp > 63)
+ return -EINVAL;
+
+ rc = getsockname(fd, (struct sockaddr *)&local_addr, &local_addr_len);
+ if (rc < 0)
+ return rc;
+
+ switch (local_addr.ss_family) {
+ case AF_INET:
+ /* read the original value */
+ rc = getsockopt(fd, IPPROTO_IP, IP_TOS, &tos, &tos_len);
+ if (rc < 0)
+ return rc;
+ /* mask-in the DSCP into the upper 6 bits */
+ tos &= 0x03;
+ tos |= dscp << 2;
+ /* and write it back to the kernel */
+ rc = setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+ break;
+ case AF_INET6:
+ /* read the original value */
+ rc = getsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, &tclass_len);
+ if (rc < 0)
+ return rc;
+ /* mask-in the DSCP into the upper 6 bits */
+ tclass &= 0x03;
+ tclass |= dscp << 2;
+ /* and write it back to the kernel */
+ rc = setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof(tclass));
+ break;
+ case AF_UNSPEC:
+ default:
+ LOGP(DLGLOBAL, LOGL_ERROR, "No DSCP support for socket family %u\n",
+ local_addr.ss_family);
+ rc = -1;
+ break;
+ }
+
+ return rc;
+}
+
+/*! Set the priority value of a socket.
+ * \param[in] prio priority value. Values outside 0..6 require CAP_NET_ADMIN.
+ * \returns 0 on success; negative on error. */
+int osmo_sock_set_priority(int fd, int prio)
+{
+ /* and write it back to the kernel */
+ return setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio));
+}
+
+#ifdef HAVE_LIBSCTP
+/*! Fill in array of struct sctp_paddrinfo with each of the remote addresses of an SCTP socket
+ * \param[in] fd file descriptor of SCTP socket
+ * \param[out] pinfo Pointer to memory holding an array of struct sctp_paddrinfo (pinfo_cnt length).
+ * \param[in,out] pinfo_cnt length of pinfo array (in elements). On return it contains the number of addresses found.
+ * \returns 0 on success; negative otherwise
+ *
+ * Upon return, pinfo_cnt can be set to a higher value than the one set by the
+ * caller. This can be used by the caller to find out the required array length
+ * and then obtaining by calling the function twice. Only up to pinfo_cnt addresses
+ * are filled in, as per the value provided by the caller.
+ *
+ * Usage example retrieving struct sctp_paddrinfo for all (up to OSMO_SOCK_MAX_ADDRS, 32) remote IP addresses:
+ * struct sctp_paddrinfo pinfo[OSMO_SOCK_MAX_ADDRS];
+ * size_t pinfo_cnt = ARRAY_SIZE(pinfo);
+ * rc = osmo_sock_sctp_get_peer_addr_info(fd, &pinfo[0], &num_hostbuf, pinfo_cnt);
+ * if (rc < 0)
+ * goto error;
+ * if (pinfo_cnt > ARRAY_SIZE(hostbuf))
+ * goto not_enough_buffers;
+ */
+int osmo_sock_sctp_get_peer_addr_info(int fd, struct sctp_paddrinfo *pinfo, size_t *pinfo_cnt)
+{
+ struct sockaddr *addrs = NULL;
+ unsigned int n_addrs, i;
+ void *addr_buf;
+ int rc;
+ socklen_t optlen;
+
+ rc = sctp_getpaddrs(fd, 0, &addrs);
+
+ if (rc < 0)
+ return rc;
+ if (rc == 0)
+ return -ENOTCONN;
+
+ n_addrs = rc;
+ addr_buf = (void *)addrs;
+ for (i = 0; i < n_addrs; i++) {
+ struct sockaddr *sa_addr = (struct sockaddr *)addr_buf;
+ size_t addrlen;
+
+ switch (sa_addr->sa_family) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ rc = -EINVAL;
+ goto free_addrs_ret;
+ }
+
+ if (i >= *pinfo_cnt) {
+ addr_buf += addrlen;
+ continue;
+ }
+
+ memset(&pinfo[i], 0, sizeof(pinfo[0]));
+ memcpy(&pinfo[i].spinfo_address, sa_addr, addrlen);
+ optlen = sizeof(pinfo[0]);
+ rc = getsockopt(fd, SOL_SCTP, SCTP_GET_PEER_ADDR_INFO, &pinfo[i], &optlen);
+ if (rc < 0)
+ goto free_addrs_ret;
+
+ addr_buf += addrlen;
+ }
+
+ *pinfo_cnt = n_addrs;
+ rc = 0;
+free_addrs_ret:
+ sctp_freepaddrs(addrs);
+ return rc;
+}
+#endif
+
+#endif /* HAVE_SYS_SOCKET_H */
+
+/*! @} */
diff --git a/src/core/soft_uart.c b/src/core/soft_uart.c
new file mode 100644
index 00000000..f969ab70
--- /dev/null
+++ b/src/core/soft_uart.c
@@ -0,0 +1,518 @@
+/*! \file soft_uart.c
+ * Software UART implementation. */
+/*
+ * (C) 2022 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/soft_uart.h>
+
+/*! Rx/Tx flow state of a soft-UART */
+enum suart_flow_state {
+ SUART_FLOW_ST_IDLE, /*!< waiting for a start bit or Tx data */
+ SUART_FLOW_ST_DATA, /*!< receiving/transmitting data bits */
+ SUART_FLOW_ST_PARITY, /*!< receiving/transmitting parity bits */
+ SUART_FLOW_ST_STOP, /*!< receiving/transmitting stop bits */
+};
+
+/*! Internal state of a soft-UART */
+struct osmo_soft_uart {
+ struct osmo_soft_uart_cfg cfg;
+ const char *name;
+ /* modem status (bitmask of OSMO_SUART_STATUS_F_*) */
+ unsigned int status;
+ struct {
+ bool running;
+ uint8_t bit_count;
+ uint8_t shift_reg;
+ struct msgb *msg;
+ ubit_t parity_bit; /* 0 (even) / 1 (odd) */
+ unsigned int flags;
+ struct osmo_timer_list timer;
+ enum suart_flow_state flow_state;
+ } rx;
+ struct {
+ bool running;
+ uint8_t bit_count;
+ uint8_t shift_reg;
+ ubit_t parity_bit; /* 0 (even) / 1 (odd) */
+ enum suart_flow_state flow_state;
+ } tx;
+};
+
+/*! Default soft-UART configuration (8-N-1) */
+const struct osmo_soft_uart_cfg osmo_soft_uart_default_cfg = {
+ .num_data_bits = 8,
+ .num_stop_bits = 1,
+ .parity_mode = OSMO_SUART_PARITY_NONE,
+ .rx_buf_size = 1024,
+ .rx_timeout_ms = 100,
+ .flow_ctrl_mode = OSMO_SUART_FLOW_CTRL_NONE,
+};
+
+/*************************************************************************
+ * Receiver
+ *************************************************************************/
+
+/*! Flush the receive buffer, passing ownership of the msgb to the .rx_cb().
+ * \param[in] suart soft-UART instance holding the receive buffer. */
+void osmo_soft_uart_flush_rx(struct osmo_soft_uart *suart)
+{
+ if (suart->rx.msg && msgb_length(suart->rx.msg)) {
+ osmo_timer_del(&suart->rx.timer);
+ if (suart->cfg.rx_cb) {
+ suart->cfg.rx_cb(suart->cfg.priv, suart->rx.msg, suart->rx.flags);
+ /* call-back has taken ownership of msgb, no need to free() here */
+ suart->rx.msg = msgb_alloc_c(suart, suart->cfg.rx_buf_size, "soft_uart_rx");
+ } else {
+ msgb_reset(suart->rx.msg);
+ }
+ }
+}
+
+/* one character was received; add to receive buffer and notify user, if needed */
+static void suart_rx_ch(struct osmo_soft_uart *suart, uint8_t ch)
+{
+ unsigned int msg_len;
+
+ OSMO_ASSERT(suart->rx.msg);
+ msgb_put_u8(suart->rx.msg, ch);
+ msg_len = msgb_length(suart->rx.msg);
+
+ if (msg_len >= suart->cfg.rx_buf_size || suart->rx.flags) {
+ /* either the buffer is full, or we hit a parity and/or a framing error */
+ osmo_soft_uart_flush_rx(suart);
+ } else if (msg_len == 1) {
+ /* first character in new message: start timer */
+ osmo_timer_schedule(&suart->rx.timer, suart->cfg.rx_timeout_ms / 1000,
+ (suart->cfg.rx_timeout_ms % 1000) * 1000);
+ }
+}
+
+/* receive a single bit */
+static inline void suart_rx_bit(struct osmo_soft_uart *suart, const ubit_t bit)
+{
+ switch (suart->rx.flow_state) {
+ case SUART_FLOW_ST_IDLE:
+ if (bit == 0) { /* start bit condition */
+ suart->rx.flow_state = SUART_FLOW_ST_DATA;
+ suart->rx.flags = 0x00;
+ suart->rx.shift_reg = 0;
+ suart->rx.bit_count = 0;
+ suart->rx.parity_bit = 0;
+ }
+ break;
+ case SUART_FLOW_ST_DATA:
+ suart->rx.bit_count++;
+ suart->rx.shift_reg >>= 1;
+ if (bit != 0) {
+ suart->rx.parity_bit = !suart->rx.parity_bit; /* flip */
+ suart->rx.shift_reg |= 0x80;
+ }
+ if (suart->rx.bit_count >= suart->cfg.num_data_bits) {
+ /* we have accumulated enough data bits */
+ if (suart->cfg.parity_mode != OSMO_SUART_PARITY_NONE)
+ suart->rx.flow_state = SUART_FLOW_ST_PARITY;
+ else
+ suart->rx.flow_state = SUART_FLOW_ST_STOP;
+ /* align the register if needed */
+ if (suart->cfg.num_data_bits < 8)
+ suart->rx.shift_reg >>= (8 - suart->cfg.num_data_bits);
+ }
+ break;
+ case SUART_FLOW_ST_PARITY:
+ switch (suart->cfg.parity_mode) {
+ case OSMO_SUART_PARITY_EVEN:
+ /* number of 1-bits (in both data and parity) shall be even */
+ if (suart->rx.parity_bit != bit)
+ suart->rx.flags |= OSMO_SUART_F_PARITY_ERROR;
+ break;
+ case OSMO_SUART_PARITY_ODD:
+ /* number of 1-bits (in both data and parity) shall be odd */
+ if (suart->rx.parity_bit == bit)
+ suart->rx.flags |= OSMO_SUART_F_PARITY_ERROR;
+ break;
+ case OSMO_SUART_PARITY_MARK:
+ /* parity bit must always be 1 */
+ if (bit != 1)
+ suart->rx.flags |= OSMO_SUART_F_PARITY_ERROR;
+ break;
+ case OSMO_SUART_PARITY_SPACE:
+ /* parity bit must always be 0 */
+ if (bit != 0)
+ suart->rx.flags |= OSMO_SUART_F_PARITY_ERROR;
+ break;
+ case OSMO_SUART_PARITY_NONE: /* shall not happen */
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ suart->rx.flow_state = SUART_FLOW_ST_STOP;
+ break;
+ case SUART_FLOW_ST_STOP:
+ suart->rx.bit_count++;
+ if (bit != 1)
+ suart->rx.flags |= OSMO_SUART_F_FRAMING_ERROR;
+
+ if (suart->rx.bit_count >= (suart->cfg.num_data_bits + suart->cfg.num_stop_bits)) {
+ /* we have accumulated enough stop bits */
+ suart_rx_ch(suart, suart->rx.shift_reg);
+ suart->rx.flow_state = SUART_FLOW_ST_IDLE;
+ }
+ break;
+ }
+}
+
+/* receive timer expiration: flush rx-buffer to user call-back */
+static void suart_rx_timer_cb(void *data)
+{
+ struct osmo_soft_uart *suart = data;
+ osmo_soft_uart_flush_rx(suart);
+}
+
+/*! Feed a number of unpacked bits into the soft-UART receiver.
+ * \param[in] suart soft-UART instance to feed bits into.
+ * \param[in] ubits pointer to the unpacked bits.
+ * \param[in] n_ubits number of unpacked bits to be fed.
+ * \returns 0 on success; negative on error.
+ * -EAGAIN indicates that the receiver is disabled. */
+int osmo_soft_uart_rx_ubits(struct osmo_soft_uart *suart, const ubit_t *ubits, size_t n_ubits)
+{
+ if (!suart->rx.running)
+ return -EAGAIN;
+ for (size_t i = 0; i < n_ubits; i++)
+ suart_rx_bit(suart, ubits[i]);
+ return 0;
+}
+
+/*************************************************************************
+ * Transmitter
+ *************************************************************************/
+
+/* pull a single bit out of the UART transmitter */
+static inline ubit_t suart_tx_bit(struct osmo_soft_uart *suart, struct msgb *msg)
+{
+ ubit_t tx_bit = 1;
+
+ switch (suart->tx.flow_state) {
+ case SUART_FLOW_ST_IDLE:
+ if (msg && msgb_length(msg) > 0) { /* if we have pending data */
+ suart->tx.shift_reg = msgb_pull_u8(msg);
+ suart->tx.flow_state = SUART_FLOW_ST_DATA;
+ suart->tx.bit_count = 0;
+ suart->tx.parity_bit = 0;
+ tx_bit = 0;
+ }
+ break;
+ case SUART_FLOW_ST_DATA:
+ tx_bit = suart->tx.shift_reg & 1;
+ suart->tx.parity_bit ^= tx_bit;
+ suart->tx.shift_reg >>= 1;
+ suart->tx.bit_count++;
+ if (suart->tx.bit_count >= suart->cfg.num_data_bits) {
+ /* we have transmitted all data bits */
+ if (suart->cfg.parity_mode != OSMO_SUART_PARITY_NONE)
+ suart->tx.flow_state = SUART_FLOW_ST_PARITY;
+ else
+ suart->tx.flow_state = SUART_FLOW_ST_STOP;
+ }
+ break;
+ case SUART_FLOW_ST_PARITY:
+ switch (suart->cfg.parity_mode) {
+ case OSMO_SUART_PARITY_EVEN:
+ /* number of 1-bits (in both data and parity) shall be even */
+ tx_bit = suart->tx.parity_bit;
+ break;
+ case OSMO_SUART_PARITY_ODD:
+ /* number of 1-bits (in both data and parity) shall be odd */
+ tx_bit = !suart->tx.parity_bit;
+ break;
+ case OSMO_SUART_PARITY_MARK:
+ /* parity bit must always be 1 */
+ tx_bit = 1;
+ break;
+ case OSMO_SUART_PARITY_SPACE:
+ /* parity bit must always be 0 */
+ tx_bit = 0;
+ break;
+ case OSMO_SUART_PARITY_NONE:
+ default: /* shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ suart->tx.flow_state = SUART_FLOW_ST_STOP;
+ break;
+ case SUART_FLOW_ST_STOP:
+ suart->tx.bit_count++;
+ if (suart->tx.bit_count >= (suart->cfg.num_data_bits + suart->cfg.num_stop_bits)) {
+ /* we have transmitted all stop bits, we're done */
+ suart->tx.flow_state = SUART_FLOW_ST_IDLE;
+ }
+ break;
+ }
+
+ return tx_bit;
+}
+
+/* pull pending bits out of the UART */
+static size_t suart_tx_pending(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits)
+{
+ size_t i;
+
+ for (i = 0; i < n_ubits; i++) {
+ if (suart->tx.flow_state == SUART_FLOW_ST_IDLE)
+ break;
+ ubits[i] = suart_tx_bit(suart, NULL);
+ }
+
+ return i;
+}
+
+/*! Pull a number of unpacked bits out of the soft-UART transmitter.
+ * \param[in] suart soft-UART instance to pull the bits from.
+ * \param[out] ubits pointer to a buffer where to store pulled bits.
+ * \param[in] n_ubits number of unpacked bits to be pulled.
+ * \returns number of bits pulled (may be less than n_ubits); negative on error.
+ * -EAGAIN indicates that the transmitter is disabled. */
+int osmo_soft_uart_tx_ubits(struct osmo_soft_uart *suart, ubit_t *ubits, size_t n_ubits)
+{
+ const struct osmo_soft_uart_cfg *cfg = &suart->cfg;
+ size_t n_frame_bits, n_chars;
+ struct msgb *msg = NULL;
+
+ if (OSMO_UNLIKELY(n_ubits == 0))
+ return -EINVAL;
+
+ if (!suart->tx.running)
+ return -EAGAIN;
+
+ switch (suart->cfg.flow_ctrl_mode) {
+ case OSMO_SUART_FLOW_CTRL_DTR_DSR:
+ /* if DSR is de-asserted, Tx pending bits and suspend */
+ if (~suart->status & OSMO_SUART_STATUS_F_DSR)
+ return suart_tx_pending(suart, ubits, n_ubits);
+ /* else: keep transmitting as usual */
+ break;
+ case OSMO_SUART_FLOW_CTRL_RTS_CTS:
+ /* if CTS is de-asserted, Tx pending bits and suspend */
+ if (~suart->status & OSMO_SUART_STATUS_F_CTS)
+ return suart_tx_pending(suart, ubits, n_ubits);
+ /* else: keep transmitting as usual */
+ break;
+ case OSMO_SUART_FLOW_CTRL_NONE:
+ default:
+ break;
+ }
+
+ /* calculate UART frame size for the effective config */
+ n_frame_bits = 1 + cfg->num_data_bits + cfg->num_stop_bits;
+ if (cfg->parity_mode != OSMO_SUART_PARITY_NONE)
+ n_frame_bits += 1;
+
+ /* calculate the number of characters we can fit into n_ubits */
+ n_chars = n_ubits / n_frame_bits;
+ if (n_chars == 0) {
+ /* we can transmit at least one character */
+ if (suart->tx.flow_state == SUART_FLOW_ST_IDLE)
+ n_chars = 1;
+ }
+
+ if (n_chars > 0) {
+ /* allocate a Tx buffer msgb */
+ msg = msgb_alloc_c(suart, n_chars, "soft_uart_tx");
+ OSMO_ASSERT(msg != NULL);
+
+ /* call the .tx_cb() to populate the Tx buffer */
+ OSMO_ASSERT(cfg->tx_cb != NULL);
+ suart->cfg.tx_cb(cfg->priv, msg);
+ }
+
+ for (size_t i = 0; i < n_ubits; i++)
+ ubits[i] = suart_tx_bit(suart, msg);
+ msgb_free(msg);
+
+ return n_ubits;
+}
+
+/*! Get the modem status bitmask of the given soft-UART.
+ * \param[in] suart soft-UART instance to get the modem status.
+ * \returns bitmask of OSMO_SUART_STATUS_F_*. */
+unsigned int osmo_soft_uart_get_status(const struct osmo_soft_uart *suart)
+{
+ return suart->status;
+}
+
+/*! Set the modem status bitmask of the given soft-UART.
+ * \param[in] suart soft-UART instance to set the modem status.
+ * \param[in] status bitmask of OSMO_SUART_STATUS_F_*.
+ * \returns 0 on success; negative on error. */
+int osmo_soft_uart_set_status(struct osmo_soft_uart *suart, unsigned int status)
+{
+ const struct osmo_soft_uart_cfg *cfg = &suart->cfg;
+
+ if (cfg->status_change_cb != NULL) {
+ if (suart->status != status)
+ cfg->status_change_cb(cfg->priv, status);
+ }
+
+ suart->status = status;
+ return 0;
+}
+
+/*! Activate/deactivate a modem status line of the given soft-UART.
+ * \param[in] suart soft-UART instance to update the modem status.
+ * \param[in] line a modem status line, one of OSMO_SUART_STATUS_F_*.
+ * \param[in] active activate (true) or deactivate (false) the line. */
+void osmo_soft_uart_set_status_line(struct osmo_soft_uart *suart,
+ enum osmo_soft_uart_status line,
+ bool active)
+{
+ unsigned int status = suart->status;
+
+ if (active) /* assert the given line */
+ status |= line;
+ else /* de-assert the given line */
+ status &= ~line;
+
+ osmo_soft_uart_set_status(suart, status);
+}
+
+
+/*************************************************************************
+ * Management / Initialization
+ *************************************************************************/
+
+/*! Allocate a soft-UART instance.
+ * \param[in] ctx parent talloc context.
+ * \param[in] name name of the soft-UART instance.
+ * \param[in] cfg initial configuration of the soft-UART instance.
+ * \returns pointer to allocated soft-UART instance; NULL on error. */
+struct osmo_soft_uart *osmo_soft_uart_alloc(void *ctx, const char *name,
+ const struct osmo_soft_uart_cfg *cfg)
+{
+ struct osmo_soft_uart *suart = talloc_zero(ctx, struct osmo_soft_uart);
+ if (!suart)
+ return NULL;
+ suart->name = talloc_strdup(suart, name);
+
+ OSMO_ASSERT(cfg != NULL);
+ suart->cfg = *cfg;
+
+ return suart;
+}
+
+/*! Release memory taken by the given soft-UART.
+ * \param[in] suart soft-UART instance to be free()d. */
+void osmo_soft_uart_free(struct osmo_soft_uart *suart)
+{
+ if (suart == NULL)
+ return;
+
+ osmo_timer_del(&suart->rx.timer);
+ msgb_free(suart->rx.msg);
+
+ talloc_free((void *)suart->name);
+ talloc_free(suart);
+}
+
+/*! Change soft-UART configuration to the user-provided config.
+ * \param[in] suart soft-UART instance to be re-configured.
+ * \param[in] cfg the user-provided config to be applied.
+ * \returns 0 on success; negative on error. */
+int osmo_soft_uart_configure(struct osmo_soft_uart *suart, const struct osmo_soft_uart_cfg *cfg)
+{
+ /* consistency checks on the configuration */
+ if (cfg->num_data_bits > 8 || cfg->num_data_bits == 0)
+ return -EINVAL;
+ if (cfg->num_stop_bits == 0)
+ return -EINVAL;
+ if (cfg->parity_mode < 0 || cfg->parity_mode >= _OSMO_SUART_PARITY_NUM)
+ return -EINVAL;
+ if (cfg->rx_buf_size == 0)
+ return -EINVAL;
+
+ if (suart->cfg.rx_buf_size > cfg->rx_buf_size ||
+ suart->cfg.rx_timeout_ms > cfg->rx_timeout_ms) {
+ osmo_soft_uart_flush_rx(suart);
+ }
+
+ suart->cfg = *cfg;
+
+ osmo_timer_setup(&suart->rx.timer, suart_rx_timer_cb, suart);
+
+ return 0;
+}
+
+/*! Get a name for the given soft-UART instance.
+ * \param[in] suart soft-UART instance to get the name from.
+ * \returns name of the given soft-UART instance. */
+const char *osmo_soft_uart_get_name(const struct osmo_soft_uart *suart)
+{
+ return suart->name;
+}
+
+/*! Set a new name for the given soft-UART instance.
+ * \param[in] suart soft-UART instance to set the name for.
+ * \param[in] name the new name. */
+void osmo_soft_uart_set_name(struct osmo_soft_uart *suart, const char *name)
+{
+ osmo_talloc_replace_string(suart, (char **)&suart->name, name);
+}
+
+/*! Enable/disable receiver of the given soft-UART.
+ * \param[in] suart soft-UART instance to be re-configured.
+ * \param[in] enable enable/disable state of the receiver.
+ * \returns 0 on success; negative on error. */
+int osmo_soft_uart_set_rx(struct osmo_soft_uart *suart, bool enable)
+{
+ if (!enable && suart->rx.running) {
+ osmo_soft_uart_flush_rx(suart);
+ suart->rx.running = false;
+ suart->rx.flow_state = SUART_FLOW_ST_IDLE;
+ } else if (enable && !suart->rx.running) {
+ if (!suart->rx.msg)
+ suart->rx.msg = msgb_alloc_c(suart, suart->cfg.rx_buf_size, "soft_uart_rx");
+ suart->rx.running = true;
+ suart->rx.flow_state = SUART_FLOW_ST_IDLE;
+ }
+
+ return 0;
+}
+
+/*! Enable/disable transmitter of the given soft-UART.
+ * \param[in] suart soft-UART instance to be re-configured.
+ * \param[in] enable enable/disable state of the transmitter.
+ * \returns 0 on success; negative on error. */
+int osmo_soft_uart_set_tx(struct osmo_soft_uart *suart, bool enable)
+{
+ if (!enable && suart->tx.running) {
+ suart->tx.running = false;
+ suart->tx.flow_state = SUART_FLOW_ST_IDLE;
+ } else if (enable && !suart->tx.running) {
+ suart->tx.running = true;
+ suart->tx.flow_state = SUART_FLOW_ST_IDLE;
+ }
+
+ return 0;
+}
diff --git a/src/core/stat_item.c b/src/core/stat_item.c
new file mode 100644
index 00000000..804972bf
--- /dev/null
+++ b/src/core/stat_item.c
@@ -0,0 +1,463 @@
+/*! \file stat_item.c
+ * utility routines for keeping statistical values */
+/*
+ * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2015 by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*! \addtogroup osmo_stat_item
+ * @{
+ *
+ * This osmo_stat_item module adds instrumentation capabilities to
+ * gather measurement and statistical values in a similar fashion to
+ * what we have as \ref osmo_counter_group.
+ *
+ * As opposed to counters, osmo_stat_item do not increment but consist
+ * of a configurable-sized FIFO, which can store not only the current
+ * (most recent) value, but also historic values.
+ *
+ * The only supported value type is an int32_t.
+ *
+ * Getting values from osmo_stat_item is usually done at a high level
+ * through the stats API (stats.c). It uses item->stats_next_id to
+ * store what has been sent to all enabled reporters. It is also
+ * possible to read from osmo_stat_item directly, without modifying
+ * its state, by storing next_id outside of osmo_stat_item.
+ *
+ * Each value stored in the FIFO of an osmo_stat_item has an associated
+ * value_id. The value_id is increased with each value, so (until the
+ * counter wraps) more recent values will have higher values.
+ *
+ * When a new value is set, the oldest value in the FIFO gets silently
+ * overwritten. Lost values are skipped when getting values from the
+ * item.
+ *
+ */
+
+/* Struct overview:
+ *
+ * Group and item descriptions:
+ * Each group description exists once as osmo_stat_item_group_desc,
+ * each such group description lists N osmo_stat_item_desc, i.e. describes N stat items.
+ *
+ * Actual stats:
+ * The global osmo_stat_item_groups llist contains all group instances, each points at a group description.
+ * This list mixes all types of groups in a single llist, where each instance points at its group desc and has an index.
+ * There are one or more instances of each group, each storing stats for a distinct object (for example, one description
+ * for a BTS group, and any number of BTS instances with independent stats). A group is identified by a group index nr
+ * and possibly also a given name for that particular index (e.g. in osmo-mgw, a group instance is named
+ * "virtual-trunk-0" and can be looked up by that name instead of its more or less arbitrary group index number).
+ *
+ * Each group instance contains one osmo_stat_item instance per global stat item description.
+ * Each osmo_stat_item keeps track of the values for the current reporting period (min, last, max, sum, n),
+ * and also stores the set of values reported at the end of the previous reporting period.
+ *
+ * const osmo_stat_item_group_desc foo
+ * +-- group_name_prefix = "foo"
+ * +-- item_desc[] (array of osmo_stat_item_desc)
+ * +-- osmo_stat_item_desc bar
+ * | +-- name = "bar"
+ * | +-- description
+ * | +-- unit
+ * | +-- default_value
+ * |
+ * +-- osmo_stat_item_desc: baz
+ * +-- ...
+ *
+ * const osmo_stat_item_group_desc moo
+ * +-- group_name_prefix = "moo"
+ * +-- item_desc[]
+ * +-- osmo_stat_item_desc goo
+ * | +-- name = "goo"
+ * | +-- description
+ * | +-- unit
+ * | +-- default_value
+ * |
+ * +-- osmo_stat_item_desc: loo
+ * +-- ...
+ *
+ * osmo_stat_item_groups (llist of osmo_stat_item_group)
+ * |
+ * +-- group: foo[0]
+ * | +-- desc --> osmo_stat_item_group_desc foo
+ * | +-- idx = 0
+ * | +-- name = NULL (no given name for this group instance)
+ * | +-- items[]
+ * | |
+ * | [0] --> osmo_stat_item instance for "bar"
+ * | | +-- desc --> osmo_stat_item_desc bar (see above)
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | |
+ * | [1] --> osmo_stat_item instance for "baz"
+ * | | +-- desc --> osmo_stat_item_desc baz
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | .
+ * | :
+ * |
+ * +-- group: foo[1]
+ * | +-- desc --> osmo_stat_item_group_desc foo
+ * | +-- idx = 1
+ * | +-- name = "special-foo" (instance can be looked up by this index-name)
+ * | +-- items[]
+ * | |
+ * | [0] --> osmo_stat_item instance for "bar"
+ * | | +-- desc --> osmo_stat_item_desc bar
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | |
+ * | [1] --> osmo_stat_item instance for "baz"
+ * | | +-- desc --> osmo_stat_item_desc baz
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | .
+ * | :
+ * |
+ * +-- group: moo[0]
+ * | +-- desc --> osmo_stat_item_group_desc moo
+ * | +-- idx = 0
+ * | +-- name = NULL
+ * | +-- items[]
+ * | |
+ * | [0] --> osmo_stat_item instance for "goo"
+ * | | +-- desc --> osmo_stat_item_desc goo
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | |
+ * | [1] --> osmo_stat_item instance for "loo"
+ * | | +-- desc --> osmo_stat_item_desc loo
+ * | | +-- value.{min, last, max, n, sum}
+ * | | +-- reported.{min, last, max, n, sum}
+ * | .
+ * | :
+ * .
+ * :
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/stat_item.h>
+
+#include <stat_item_internal.h>
+
+/*! global list of stat_item groups */
+static LLIST_HEAD(osmo_stat_item_groups);
+
+/*! talloc context from which we allocate */
+static void *tall_stat_item_ctx;
+
+/*! Allocate a new group of counters according to description.
+ * Allocate a group of stat items described in \a desc from talloc context \a ctx,
+ * giving the new group the index \a idx.
+ * \param[in] ctx \ref talloc context
+ * \param[in] desc Statistics item group description
+ * \param[in] idx Index of new stat item group
+ */
+struct osmo_stat_item_group *osmo_stat_item_group_alloc(void *ctx,
+ const struct osmo_stat_item_group_desc *group_desc,
+ unsigned int idx)
+{
+ unsigned int group_size;
+ unsigned int item_idx;
+ struct osmo_stat_item *items;
+
+ struct osmo_stat_item_group *group;
+
+ group_size = sizeof(struct osmo_stat_item_group) +
+ group_desc->num_items * sizeof(struct osmo_stat_item *);
+
+ if (!ctx)
+ ctx = tall_stat_item_ctx;
+
+ group = talloc_zero_size(ctx, group_size);
+ if (!group)
+ return NULL;
+
+ group->desc = group_desc;
+ group->idx = idx;
+
+ items = talloc_array(group, struct osmo_stat_item, group_desc->num_items);
+ OSMO_ASSERT(items);
+ for (item_idx = 0; item_idx < group_desc->num_items; item_idx++) {
+ struct osmo_stat_item *item = &items[item_idx];
+ const struct osmo_stat_item_desc *item_desc = &group_desc->item_desc[item_idx];
+ group->items[item_idx] = item;
+ *item = (struct osmo_stat_item){
+ .desc = item_desc,
+ .value = {
+ .n = 0,
+ .last = item_desc->default_value,
+ .min = item_desc->default_value,
+ .max = item_desc->default_value,
+ .sum = 0,
+ },
+ };
+ }
+
+ llist_add(&group->list, &osmo_stat_item_groups);
+ return group;
+}
+
+/*! Free the memory for the specified group of stat items */
+void osmo_stat_item_group_free(struct osmo_stat_item_group *grp)
+{
+ if (!grp)
+ return;
+
+ llist_del(&grp->list);
+ talloc_free(grp);
+}
+
+/*! Get statistics item from group, identified by index idx
+ * \param[in] grp Rate counter group
+ * \param[in] idx Index of the counter to retrieve
+ * \returns rate counter requested
+ */
+struct osmo_stat_item *osmo_stat_item_group_get_item(struct osmo_stat_item_group *grp, unsigned int idx)
+{
+ return grp->items[idx];
+}
+
+/*! Set a name for the statistics item group to be used instead of index value
+ at report time.
+ * \param[in] statg Statistics item group
+ * \param[in] name Name identifier to assign to the statistics item group
+ */
+void osmo_stat_item_group_set_name(struct osmo_stat_item_group *statg, const char *name)
+{
+ osmo_talloc_replace_string(statg, &statg->name, name);
+}
+
+/*! Increase the stat_item to the given value.
+ * This function adds a new value for the given stat_item at the end of
+ * the FIFO.
+ * \param[in] item The stat_item whose \a value we want to set
+ * \param[in] value The numeric value we want to store at end of FIFO
+ */
+void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value)
+{
+ osmo_stat_item_set(item, item->value.last + value);
+}
+
+/*! Descrease the stat_item to the given value.
+ * This function adds a new value for the given stat_item at the end of
+ * the FIFO.
+ * \param[in] item The stat_item whose \a value we want to set
+ * \param[in] value The numeric value we want to store at end of FIFO
+ */
+void osmo_stat_item_dec(struct osmo_stat_item *item, int32_t value)
+{
+ osmo_stat_item_set(item, item->value.last - value);
+}
+
+/*! Set the a given stat_item to the given value.
+ * This function adds a new value for the given stat_item at the end of
+ * the FIFO.
+ * \param[in] item The stat_item whose \a value we want to set
+ * \param[in] value The numeric value we want to store at end of FIFO
+ */
+void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)
+{
+ item->value.last = value;
+ if (item->value.n == 0) {
+ /* No values recorded yet, clamp min and max to this first value. */
+ item->value.min = item->value.max = value;
+ /* Overwrite any cruft remaining in value.sum */
+ item->value.sum = value;
+ item->value.n = 1;
+ } else {
+ item->value.min = OSMO_MIN(item->value.min, value);
+ item->value.max = OSMO_MAX(item->value.max, value);
+ item->value.sum += value;
+ item->value.n++;
+ }
+}
+
+/*! Indicate that a reporting period has elapsed, and prepare the stat item for a new period of collecting min/max/avg.
+ * \param item Stat item to flush.
+ */
+void osmo_stat_item_flush(struct osmo_stat_item *item)
+{
+ item->reported = item->value;
+
+ /* Indicate a new reporting period: no values have been received, but the previous value.last remains unchanged
+ * for the case that an entire period elapses without a new value appearing. */
+ item->value.n = 0;
+ item->value.sum = 0;
+
+ /* Also for the case that an entire period elapses without any osmo_stat_item_set(), put the min and max to the
+ * last value. As soon as one osmo_stat_item_set() occurs, these are both set to the new value (when n is still
+ * zero from above). */
+ item->value.min = item->value.max = item->value.last;
+}
+
+/*! Initialize the stat item module. Call this once from your program.
+ * \param[in] tall_ctx Talloc context from which this module allocates */
+int osmo_stat_item_init(void *tall_ctx)
+{
+ tall_stat_item_ctx = tall_ctx;
+
+ return 0;
+}
+
+/*! Search for item group based on group name and index
+ * \param[in] name Name of stats_item_group we want to find
+ * \param[in] idx Index of the group we want to find
+ * \returns pointer to group, if found; NULL otherwise */
+struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
+ const char *name, const unsigned int idx)
+{
+ struct osmo_stat_item_group *statg;
+
+ llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
+ if (!statg->desc)
+ continue;
+
+ if (!strcmp(statg->desc->group_name_prefix, name) &&
+ statg->idx == idx)
+ return statg;
+ }
+ return NULL;
+}
+
+/*! Search for item group based on group name and index's name.
+ * \param[in] name Name of stats_item_group we want to find.
+ * \param[in] idx_name Index of the group we want to find, by the index's name (osmo_stat_item_group->name).
+ * \returns pointer to group, if found; NULL otherwise. */
+struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idxname(const char *group_name, const char *idx_name)
+{
+ struct osmo_stat_item_group *statg;
+
+ llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
+ if (!statg->desc || !statg->name)
+ continue;
+ if (strcmp(statg->desc->group_name_prefix, group_name))
+ continue;
+ if (strcmp(statg->name, idx_name))
+ continue;
+ return statg;
+ }
+ return NULL;
+}
+
+/*! Search for item based on group + item name
+ * \param[in] statg group in which to search for the item
+ * \param[in] name name of item to search within \a statg
+ * \returns pointer to item, if found; NULL otherwise */
+const struct osmo_stat_item *osmo_stat_item_get_by_name(
+ const struct osmo_stat_item_group *statg, const char *name)
+{
+ int i;
+ const struct osmo_stat_item_desc *item_desc;
+
+ if (!statg->desc)
+ return NULL;
+
+ for (i = 0; i < statg->desc->num_items; i++) {
+ item_desc = &statg->desc->item_desc[i];
+
+ if (!strcmp(item_desc->name, name)) {
+ return statg->items[i];
+ }
+ }
+ return NULL;
+}
+
+/*! Iterate over all items in group, call user-supplied function on each
+ * \param[in] statg stat_item group over whose items to iterate
+ * \param[in] handle_item Call-back function, aborts if rc < 0
+ * \param[in] data Private data handed through to \a handle_item
+ */
+int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
+ osmo_stat_item_handler_t handle_item, void *data)
+{
+ int rc = 0;
+ int i;
+
+ for (i = 0; i < statg->desc->num_items; i++) {
+ struct osmo_stat_item *item = statg->items[i];
+ rc = handle_item(statg, item, data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
+/*! Iterate over all stat_item groups in system, call user-supplied function on each
+ * \param[in] handle_group Call-back function, aborts if rc < 0
+ * \param[in] data Private data handed through to \a handle_group
+ */
+int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data)
+{
+ struct osmo_stat_item_group *statg;
+ int rc = 0;
+
+ llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
+ rc = handle_group(statg, data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
+/*! Get the last (freshest) value. */
+int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item)
+{
+ return item->value.last;
+}
+
+/*! Remove all values of a stat item
+ * \param[in] item stat item to reset
+ */
+void osmo_stat_item_reset(struct osmo_stat_item *item)
+{
+ item->value.sum = 0;
+ item->value.n = 0;
+ item->value.last = item->value.min = item->value.max = item->desc->default_value;
+}
+
+/*! Reset all osmo stat items in a group
+ * \param[in] statg stat item group to reset
+ */
+void osmo_stat_item_group_reset(struct osmo_stat_item_group *statg)
+{
+ int i;
+
+ for (i = 0; i < statg->desc->num_items; i++) {
+ struct osmo_stat_item *item = statg->items[i];
+ osmo_stat_item_reset(item);
+ }
+}
+
+/*! Return the description for an osmo_stat_item. */
+const struct osmo_stat_item_desc *osmo_stat_item_get_desc(struct osmo_stat_item *item)
+{
+ return item->desc;
+}
+
+/*! @} */
diff --git a/src/core/stat_item_internal.h b/src/core/stat_item_internal.h
new file mode 100644
index 00000000..9ede8c41
--- /dev/null
+++ b/src/core/stat_item_internal.h
@@ -0,0 +1,35 @@
+/*! \file stat_item_internal.h
+ * internal definitions for the osmo_stat_item API */
+#pragma once
+
+/*! \addtogroup osmo_stat_item
+ * @{
+ */
+
+struct osmo_stat_item_period {
+ /*! Number of osmo_stat_item_set() that occurred during the reporting period, zero if none. */
+ uint32_t n;
+ /*! Smallest value seen in a reporting period. */
+ int32_t min;
+ /*! Most recent value passed to osmo_stat_item_set(), or the item->desc->default_value if none. */
+ int32_t last;
+ /*! Largest value seen in a reporting period. */
+ int32_t max;
+ /*! Sum of all values passed to osmo_stat_item_set() in the reporting period. */
+ int64_t sum;
+};
+
+/*! data we keep for each actual item */
+struct osmo_stat_item {
+ /*! back-reference to the item description */
+ const struct osmo_stat_item_desc *desc;
+
+ /*! Current reporting period / current value. */
+ struct osmo_stat_item_period value;
+
+ /*! The results of the previous reporting period. According to these, the stats reporter decides whether to
+ * re-send values or omit an unchanged value from a report. */
+ struct osmo_stat_item_period reported;
+};
+
+/*! @} */
diff --git a/src/stats.c b/src/core/stats.c
index b5adbf29..16e6f620 100644
--- a/src/stats.c
+++ b/src/core/stats.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup stats
@@ -74,6 +70,7 @@
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
+#include <inttypes.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
@@ -85,24 +82,37 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stat_item.h>
-#include <osmocom/core/timer.h>
+#include <osmocom/core/select.h>
#include <osmocom/core/counter.h>
#include <osmocom/core/msgb.h>
+#include <osmocom/core/stats_tcp.h>
+
+#ifdef HAVE_SYSTEMTAP
+/* include the generated probes header and put markers in code */
+#include "probes.h"
+#define TRACE(probe) probe
+#define TRACE_ENABLED(probe) probe ## _ENABLED()
+#else
+/* Wrap the probe to allow it to be removed when no systemtap available */
+#define TRACE(probe)
+#define TRACE_ENABLED(probe) (0)
+#endif /* HAVE_SYSTEMTAP */
+
+#include <stat_item_internal.h>
#define STATS_DEFAULT_INTERVAL 5 /* secs */
#define STATS_DEFAULT_BUFLEN 256
-static LLIST_HEAD(osmo_stats_reporter_list);
+LLIST_HEAD(osmo_stats_reporter_list);
static void *osmo_stats_ctx = NULL;
static int is_initialised = 0;
-static int32_t current_stat_item_index = 0;
static struct osmo_stats_config s_stats_config = {
.interval = STATS_DEFAULT_INTERVAL,
};
struct osmo_stats_config *osmo_stats_config = &s_stats_config;
-static struct osmo_timer_list osmo_stats_timer;
+static struct osmo_fd osmo_stats_timer = { .fd = -1 };
static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep,
const struct rate_ctr_group *ctrg,
@@ -140,23 +150,61 @@ static int update_srep_config(struct osmo_stats_reporter *srep)
return rc;
}
-static void osmo_stats_timer_cb(void *data)
+static int osmo_stats_timer_cb(struct osmo_fd *ofd, unsigned int what)
{
- int interval = osmo_stats_config->interval;
+ uint64_t expire_count;
+ int rc;
+
+ /* check that the timer has actually expired */
+ if (!(what & OSMO_FD_READ))
+ return 0;
+
+ /* read from timerfd: number of expirations of periodic timer */
+ rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count));
+ if (rc < 0 && errno == EAGAIN)
+ return 0;
+ OSMO_ASSERT(rc == sizeof(expire_count));
+
+ if (expire_count > 1)
+ LOGP(DLSTATS, LOGL_NOTICE, "Stats timer expire_count=%" PRIu64 ": We missed %" PRIu64 " timers\n",
+ expire_count, expire_count-1);
if (!llist_empty(&osmo_stats_reporter_list))
osmo_stats_report();
- osmo_timer_schedule(&osmo_stats_timer, interval, 0);
+ return 0;
}
-static int start_timer()
+static int start_timer(void)
{
+ int rc;
+ int interval = osmo_stats_config->interval;
+
if (!is_initialised)
return -ESRCH;
- osmo_timer_setup(&osmo_stats_timer, osmo_stats_timer_cb, NULL);
- osmo_timer_schedule(&osmo_stats_timer, 0, 1);
+ struct timespec ts_first = {.tv_sec=0, .tv_nsec=1000};
+ struct timespec ts_interval = {.tv_sec=interval, .tv_nsec=0};
+
+ rc = osmo_timerfd_setup(&osmo_stats_timer, osmo_stats_timer_cb, NULL);
+ if (rc < 0)
+ LOGP(DLSTATS, LOGL_ERROR, "Failed to setup the timer with error code %d (fd=%d)\n",
+ rc, osmo_stats_timer.fd);
+
+ if (interval == 0) {
+ rc = osmo_timerfd_disable(&osmo_stats_timer);
+ if (rc < 0)
+ LOGP(DLSTATS, LOGL_ERROR, "Failed to disable the timer with error code %d (fd=%d)\n",
+ rc, osmo_stats_timer.fd);
+ } else {
+
+ rc = osmo_timerfd_schedule(&osmo_stats_timer, &ts_first, &ts_interval);
+ if (rc < 0)
+ LOGP(DLSTATS, LOGL_ERROR, "Failed to schedule the timer with error code %d (fd=%d, interval %d sec)\n",
+ rc, osmo_stats_timer.fd, interval);
+
+ LOGP(DLSTATS, LOGL_INFO, "Stats timer started with interval %d sec\n", interval);
+ }
return 0;
}
@@ -166,13 +214,15 @@ struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_t
{
struct osmo_stats_reporter *srep;
srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter);
- OSMO_ASSERT(srep);
+ if (!srep)
+ return NULL;
+
srep->type = type;
if (name)
srep->name = talloc_strdup(srep, name);
srep->fd = -1;
- llist_add(&srep->list, &osmo_stats_reporter_list);
+ llist_add_tail(&srep->list, &osmo_stats_reporter_list);
return srep;
}
@@ -186,15 +236,17 @@ void osmo_stats_reporter_free(struct osmo_stats_reporter *srep)
talloc_free(srep);
}
-/*! Initilize the stats reporting module; call this once in your program
+/*! Initialize the stats reporting module; call this once in your program.
* \param[in] ctx Talloc context from which stats related memory is allocated */
void osmo_stats_init(void *ctx)
{
osmo_stats_ctx = ctx;
- osmo_stat_item_discard_all(&current_stat_item_index);
-
is_initialised = 1;
start_timer();
+
+ /* Make sure that the tcp-stats interval timer also runs at its
+ * preconfigured rate. The vty might change this setting later. */
+ osmo_stats_tcp_set_interval(osmo_tcp_stats_config->interval);
}
/*! Find a stats_reporter of given \a type and \a name.
@@ -326,13 +378,12 @@ int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep,
return 0;
}
-/*! Set the reporting interval of a given stats_reporter (in seconds).
- * \param[in] srep stats_reporter whose remote address is to be set
+/*! Set the reporting interval (common for all reporters)
* \param[in] interval Reporting interval in seconds
* \returns 0 on success; negative on error */
int osmo_stats_set_interval(int interval)
{
- if (interval <= 0)
+ if (interval < 0)
return -EINVAL;
osmo_stats_config->interval = interval;
@@ -342,9 +393,28 @@ int osmo_stats_set_interval(int interval)
return 0;
}
+/*! Set the regular flush period for a given stats_reporter
+ *
+ * Send all stats even if they have not changed (i.e. force the flush)
+ * every N-th reporting interval. Set to 0 to disable regular flush,
+ * set to 1 to flush every time, set to 2 to flush every 2nd time, etc.
+ * \param[in] srep stats_reporter to set flush period for
+ * \param[in] period Reporting interval in seconds
+ * \returns 0 on success; negative on error */
+int osmo_stats_reporter_set_flush_period(struct osmo_stats_reporter *srep, unsigned int period)
+{
+ srep->flush_period = period;
+ srep->flush_period_counter = 0;
+ /* force the flush now if it's not disabled by period=0 */
+ if (period > 0)
+ srep->force_single_flush = 1;
+
+ return 0;
+}
+
/*! Set the name prefix of a given stats_reporter.
* \param[in] srep stats_reporter whose name prefix is to be set
- * \param[in] prefix NAme perfix to pre-pend for any reported value
+ * \param[in] prefix Name prefix to pre-pend for any reported value
* \returns 0 on success; negative on error */
int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix)
{
@@ -418,6 +488,8 @@ int osmo_stats_reporter_udp_open(struct osmo_stats_reporter *srep)
}
srep->buffer = msgb_alloc(buffer_size, "stats buffer");
+ if (!srep->buffer)
+ goto failed;
return 0;
@@ -501,6 +573,8 @@ struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name)
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name);
+ if (!srep)
+ return NULL;
srep->have_net_config = 0;
@@ -626,36 +700,28 @@ static int osmo_stat_item_handler(
struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)
{
struct osmo_stats_reporter *srep;
- int32_t idx = current_stat_item_index;
- int32_t value;
- int have_value;
-
- have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
- if (!have_value)
- /* Send the last value in case a flush is requested */
- value = osmo_stat_item_get_last(item);
-
- do {
- llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
- if (!srep->running)
- continue;
+ int32_t prev_reported_value = item->reported.max;
+ int32_t new_value = item->value.max;
- if (!have_value && !srep->force_single_flush)
- continue;
+ llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {
+ if (!srep->running)
+ continue;
- if (!osmo_stats_reporter_check_config(srep,
- statg->idx, statg->desc->class_id))
- continue;
+ /* If the previously reported value is the same as the current value, skip resending the value.
+ * However, if the stats reporter is set to resend all values, do resend the current value regardless of
+ * repetitions.
+ */
+ if (new_value == prev_reported_value && !srep->force_single_flush)
+ continue;
- osmo_stats_reporter_send_item(srep, statg,
- item->desc, value);
- }
+ if (!osmo_stats_reporter_check_config(srep,
+ statg->idx, statg->desc->class_id))
+ continue;
- if (!have_value)
- break;
+ osmo_stats_reporter_send_item(srep, statg, item->desc, new_value);
+ }
- have_value = osmo_stat_item_get_next(item, &idx, &value) > 0;
- } while (have_value);
+ osmo_stat_item_flush(item);
return 0;
}
@@ -698,7 +764,7 @@ static int handle_counter(struct osmo_counter *counter, void *sctx_)
/*** main reporting function ***/
-static void flush_all_reporters()
+static void flush_all_reporters(void)
{
struct osmo_stats_reporter *srep;
@@ -707,20 +773,31 @@ static void flush_all_reporters()
continue;
osmo_stats_reporter_send_buffer(srep);
+
+ /* reset force_single_flush first */
srep->force_single_flush = 0;
+ /* and schedule a new flush if it's time for it */
+ if (srep->flush_period > 0) {
+ srep->flush_period_counter++;
+ if (srep->flush_period_counter >= srep->flush_period) {
+ srep->force_single_flush = 1;
+ srep->flush_period_counter = 0;
+ }
+ }
}
}
-int osmo_stats_report()
+int osmo_stats_report(void)
{
/* per group actions */
+ TRACE(LIBOSMOCORE_STATS_START());
osmo_counters_for_each(handle_counter, NULL);
rate_ctr_for_each_group(rate_ctr_group_handler, NULL);
osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);
/* global actions */
- osmo_stat_item_discard_all(&current_stat_item_index);
flush_all_reporters();
+ TRACE(LIBOSMOCORE_STATS_DONE());
return 0;
}
diff --git a/src/stats_statsd.c b/src/core/stats_statsd.c
index c3f739e2..b27baff8 100644
--- a/src/stats_statsd.c
+++ b/src/core/stats_statsd.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup stats
@@ -32,6 +28,7 @@
#include <string.h>
#include <stdint.h>
+#include <inttypes.h>
#include <errno.h>
#include <osmocom/core/utils.h>
@@ -57,6 +54,8 @@ struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name)
{
struct osmo_stats_reporter *srep;
srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_STATSD, name);
+ if (!srep)
+ return NULL;
srep->have_net_config = 1;
@@ -88,7 +87,7 @@ static void osmo_stats_reporter_sanitize_name(char *buf)
}
static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
- const char *name1, unsigned int index1, const char *name2, int64_t value,
+ const char *name1, const char *index1, const char *name2, int64_t value,
const char *unit)
{
char *buf;
@@ -99,24 +98,16 @@ static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep,
int old_len = msgb_length(srep->buffer);
if (prefix) {
- if (name1) {
- if (index1 != 0)
- fmt = "%1$s.%2$s.%6$u.%3$s:%4$d|%5$s";
- else
- fmt = "%1$s.%2$s.%3$s:%4$d|%5$s";
- } else {
- fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s";
- }
+ if (name1)
+ fmt = "%1$s.%2$s.%6$s.%3$s:%4$" PRId64 "|%5$s";
+ else
+ fmt = "%1$s.%2$0.0s%3$s:%4$" PRId64 "|%5$s";
} else {
prefix = "";
- if (name1) {
- if (index1 != 0)
- fmt = "%1$s%2$s.%6$u.%3$s:%4$d|%5$s";
- else
- fmt = "%1$s%2$s.%3$s:%4$d|%5$s";
- } else {
- fmt = "%1$s%2$0.0s%3$s:%4$d|%5$s";
- }
+ if (name1)
+ fmt = "%1$s%2$s.%6$s.%3$s:%4$" PRId64 "|%5$s";
+ else
+ fmt = "%1$s%2$0.0s%3$s:%4$" PRId64 "|%5$s";
}
if (srep->agg_enabled) {
@@ -169,32 +160,42 @@ static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *s
const struct rate_ctr_desc *desc,
int64_t value, int64_t delta)
{
- if (ctrg)
- return osmo_stats_reporter_statsd_send(srep,
- ctrg->desc->group_name_prefix,
- ctrg->idx,
- desc->name, delta, "c");
- else
- return osmo_stats_reporter_statsd_send(srep,
- NULL, 0,
- desc->name, delta, "c");
+ char buf_idx[64];
+ const char *idx_name = buf_idx;
+ const char *prefix;
+
+ if (ctrg) {
+ prefix = ctrg->desc->group_name_prefix;
+ if (ctrg->name)
+ idx_name = ctrg->name;
+ else
+ snprintf(buf_idx, sizeof(buf_idx), "%u", ctrg->idx);
+ } else {
+ prefix = NULL;
+ buf_idx[0] = '0';
+ buf_idx[1] = '\n';
+ }
+ return osmo_stats_reporter_statsd_send(srep, prefix, idx_name, desc->name, delta, "c");
}
static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep,
const struct osmo_stat_item_group *statg,
const struct osmo_stat_item_desc *desc, int64_t value)
{
- if (value < 0) {
- return osmo_stats_reporter_statsd_send(srep,
- statg->desc->group_name_prefix,
- statg->idx,
- desc->name, 0, "g");
- } else {
- return osmo_stats_reporter_statsd_send(srep,
- statg->desc->group_name_prefix,
- statg->idx,
- desc->name, value, "g");
+ char buf_idx[64];
+ char *idx_name;
+ if (statg->name)
+ idx_name = statg->name;
+ else {
+ snprintf(buf_idx, sizeof(buf_idx), "%u", statg->idx);
+ idx_name = buf_idx;
}
+
+ if (value < 0)
+ value = 0;
+
+ return osmo_stats_reporter_statsd_send(srep, statg->desc->group_name_prefix,
+ idx_name, desc->name, value, "g");
}
#endif /* !EMBEDDED */
diff --git a/src/core/stats_tcp.c b/src/core/stats_tcp.c
new file mode 100644
index 00000000..c6459fe8
--- /dev/null
+++ b/src/core/stats_tcp.c
@@ -0,0 +1,327 @@
+/*
+ * (C) 2021 by sysmocom - s.f.m.c. GmbH
+ * Author: Philipp Maier <pmaier@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*! \addtogroup stats
+ * @{
+ * \file stats_tcp.c */
+
+#include "config.h"
+#if !defined(EMBEDDED)
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <linux/tcp.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/stats_tcp.h>
+
+static struct osmo_tcp_stats_config s_tcp_stats_config = {
+ .interval = TCP_STATS_DEFAULT_INTERVAL,
+};
+
+struct osmo_tcp_stats_config *osmo_tcp_stats_config = &s_tcp_stats_config;
+
+static struct osmo_timer_list stats_tcp_poll_timer;
+
+static LLIST_HEAD(stats_tcp);
+static struct stats_tcp_entry *stats_tcp_entry_cur;
+pthread_mutex_t stats_tcp_lock;
+
+struct stats_tcp_entry {
+ struct llist_head entry;
+ const struct osmo_fd *fd;
+ struct osmo_stat_item_group *stats_tcp;
+ const char *name;
+};
+
+enum {
+ STATS_TCP_UNACKED,
+ STATS_TCP_LOST,
+ STATS_TCP_RETRANS,
+ STATS_TCP_RTT,
+ STATS_TCP_RCV_RTT,
+ STATS_TCP_NOTSENT_BYTES,
+ STATS_TCP_RWND_LIMITED,
+ STATS_TCP_SNDBUF_LIMITED,
+ STATS_TCP_REORD_SEEN,
+};
+
+static struct osmo_stat_item_desc stats_tcp_item_desc[] = {
+ [STATS_TCP_UNACKED] = { "tcp:unacked", "unacknowledged packets", "", 60, 0 },
+ [STATS_TCP_LOST] = { "tcp:lost", "lost packets", "", 60, 0 },
+ [STATS_TCP_RETRANS] = { "tcp:retrans", "retransmitted packets", "", 60, 0 },
+ [STATS_TCP_RTT] = { "tcp:rtt", "roundtrip-time", "", 60, 0 },
+ [STATS_TCP_RCV_RTT] = { "tcp:rcv_rtt", "roundtrip-time (receive)", "", 60, 0 },
+ [STATS_TCP_NOTSENT_BYTES] = { "tcp:notsent_bytes", "bytes not yet sent", "", 60, 0 },
+ [STATS_TCP_RWND_LIMITED] = { "tcp:rwnd_limited", "time (usec) limited by receive window", "", 60, 0 },
+ [STATS_TCP_SNDBUF_LIMITED] = { "tcp:sndbuf_limited", "Time (usec) limited by send buffer", "", 60, 0 },
+ [STATS_TCP_REORD_SEEN] = { "tcp:reord_seen", "reordering events seen", "", 60, 0 },
+};
+
+static struct osmo_stat_item_group_desc stats_tcp_desc = {
+ .group_name_prefix = "tcp",
+ .group_description = "stats tcp",
+ .class_id = OSMO_STATS_CLASS_GLOBAL,
+ .num_items = ARRAY_SIZE(stats_tcp_item_desc),
+ .item_desc = stats_tcp_item_desc,
+};
+
+static void fill_stats(struct stats_tcp_entry *stats_tcp_entry)
+{
+ int rc;
+ struct tcp_info tcp_info;
+ socklen_t tcp_info_len = sizeof(tcp_info);
+ char stat_name[256];
+
+ /* Do not fill in anything before the socket is connected to a remote end */
+ if (osmo_sock_get_ip_and_port(stats_tcp_entry->fd->fd, NULL, 0, NULL, 0, false) != 0)
+ return;
+
+ /* Gather TCP statistics and update the stats items */
+ rc = getsockopt(stats_tcp_entry->fd->fd, IPPROTO_TCP, TCP_INFO, &tcp_info, &tcp_info_len);
+ if (rc < 0)
+ return;
+
+ /* Create stats items if they do not exist yet */
+ if (!stats_tcp_entry->stats_tcp) {
+ stats_tcp_entry->stats_tcp =
+ osmo_stat_item_group_alloc(stats_tcp_entry, &stats_tcp_desc, stats_tcp_entry->fd->fd);
+ OSMO_ASSERT(stats_tcp_entry->stats_tcp);
+ }
+
+ /* Update statistics */
+ if (stats_tcp_entry->name)
+ snprintf(stat_name, sizeof(stat_name), "%s", stats_tcp_entry->name);
+ else
+ snprintf(stat_name, sizeof(stat_name), "%s", osmo_sock_get_name2(stats_tcp_entry->fd->fd));
+ osmo_stat_item_group_set_name(stats_tcp_entry->stats_tcp, stat_name);
+
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_UNACKED),
+ tcp_info.tcpi_unacked);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_LOST),
+ tcp_info.tcpi_lost);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RETRANS),
+ tcp_info.tcpi_retrans);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RTT), tcp_info.tcpi_rtt);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RCV_RTT),
+ tcp_info.tcpi_rcv_rtt);
+#if HAVE_TCP_INFO_TCPI_NOTSENT_BYTES == 1
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_NOTSENT_BYTES),
+ tcp_info.tcpi_notsent_bytes);
+#else
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_NOTSENT_BYTES), -1);
+#endif
+
+#if HAVE_TCP_INFO_TCPI_RWND_LIMITED == 1
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RWND_LIMITED),
+ tcp_info.tcpi_rwnd_limited);
+#else
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RWND_LIMITED), -1);
+#endif
+
+#if STATS_TCP_SNDBUF_LIMITED == 1
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN),
+ tcp_info.tcpi_sndbuf_limited);
+#else
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN), -1);
+#endif
+
+#if HAVE_TCP_INFO_TCPI_REORD_SEEN == 1
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN),
+ tcp_info.tcpi_reord_seen);
+#else
+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN), -1);
+#endif
+
+}
+
+static bool is_tcp(const struct osmo_fd *fd)
+{
+ int rc;
+ struct stat fd_stat;
+ int so_protocol = 0;
+ socklen_t so_protocol_len = sizeof(so_protocol);
+
+ /* Is this a socket? */
+ rc = fstat(fd->fd, &fd_stat);
+ if (rc < 0)
+ return false;
+ if (!S_ISSOCK(fd_stat.st_mode))
+ return false;
+
+ /* Is it a TCP socket? */
+ rc = getsockopt(fd->fd, SOL_SOCKET, SO_PROTOCOL, &so_protocol, &so_protocol_len);
+ if (rc < 0)
+ return false;
+ if (so_protocol == IPPROTO_TCP)
+ return true;
+
+ return false;
+}
+
+/*! Register an osmo_fd for TCP stats monitoring.
+ * \param[in] fd osmocom file descriptor to be registered.
+ * \param[in] human readbla name that is used as prefix for the related stats item.
+ * \returns 0 on success; negative in case of error. */
+int osmo_stats_tcp_osmo_fd_register(const struct osmo_fd *fd, const char *name)
+{
+ struct stats_tcp_entry *stats_tcp_entry;
+
+ /* Only TCP sockets can be registered for monitoring, anything else will fall through. */
+ if (!is_tcp(fd))
+ return -EINVAL;
+
+ /* When the osmo_fd is registered and unregistered properly there shouldn't be any leftovers from already closed
+ * osmo_fds in the stats_tcp list. But lets proactively make sure that any leftovers are cleaned up. */
+ osmo_stats_tcp_osmo_fd_unregister(fd);
+
+ /* Make a new list object, attach the osmo_fd... */
+ stats_tcp_entry = talloc_zero(OTC_GLOBAL, struct stats_tcp_entry);
+ OSMO_ASSERT(stats_tcp_entry);
+ stats_tcp_entry->fd = fd;
+ stats_tcp_entry->name = talloc_strdup(stats_tcp_entry, name);
+
+ pthread_mutex_lock(&stats_tcp_lock);
+ llist_add_tail(&stats_tcp_entry->entry, &stats_tcp);
+ pthread_mutex_unlock(&stats_tcp_lock);
+
+ return 0;
+}
+
+static void next_stats_tcp_entry(void)
+{
+ struct stats_tcp_entry *last;
+
+ if (llist_empty(&stats_tcp)) {
+ stats_tcp_entry_cur = NULL;
+ return;
+ }
+
+ last = (struct stats_tcp_entry *)llist_last_entry(&stats_tcp, struct stats_tcp_entry, entry);
+
+ if (!stats_tcp_entry_cur || stats_tcp_entry_cur == last)
+ stats_tcp_entry_cur =
+ (struct stats_tcp_entry *)llist_first_entry(&stats_tcp, struct stats_tcp_entry, entry);
+ else
+ stats_tcp_entry_cur =
+ (struct stats_tcp_entry *)llist_entry(stats_tcp_entry_cur->entry.next, struct stats_tcp_entry,
+ entry);
+}
+
+/*! Register an osmo_fd for TCP stats monitoring.
+ * \param[in] fd osmocom file descriptor to be unregistered.
+ * \returns 0 on success; negative in case of error. */
+int osmo_stats_tcp_osmo_fd_unregister(const struct osmo_fd *fd)
+{
+ struct stats_tcp_entry *stats_tcp_entry;
+ int rc = -EINVAL;
+
+ pthread_mutex_lock(&stats_tcp_lock);
+ llist_for_each_entry(stats_tcp_entry, &stats_tcp, entry) {
+ if (fd->fd == stats_tcp_entry->fd->fd) {
+ /* In case we want to remove exactly that item which is also
+ * selected as the current item, we must designate either a
+ * different item or invalidate the current item.
+ */
+ if (stats_tcp_entry == stats_tcp_entry_cur) {
+ if (llist_count(&stats_tcp) > 2)
+ next_stats_tcp_entry();
+ else
+ stats_tcp_entry_cur = NULL;
+ }
+
+ /* Date item from list */
+ llist_del(&stats_tcp_entry->entry);
+ osmo_stat_item_group_free(stats_tcp_entry->stats_tcp);
+ talloc_free(stats_tcp_entry);
+ rc = 0;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&stats_tcp_lock);
+
+ return rc;
+}
+
+static void stats_tcp_poll_timer_cb(void *data)
+{
+ int i;
+ int batch_size;
+ int llist_size;
+
+ pthread_mutex_lock(&stats_tcp_lock);
+
+ /* Make sure we do not run over the same sockets multiple times if the
+ * configured llist_size is larger then the actual list */
+ batch_size = osmo_tcp_stats_config->batch_size;
+ llist_size = llist_count(&stats_tcp);
+ if (llist_size < batch_size)
+ batch_size = llist_size;
+
+ /* Process a batch of sockets */
+ for (i = 0; i < batch_size; i++) {
+ next_stats_tcp_entry();
+ if (stats_tcp_entry_cur)
+ fill_stats(stats_tcp_entry_cur);
+ }
+
+ pthread_mutex_unlock(&stats_tcp_lock);
+
+ if (osmo_tcp_stats_config->interval > 0)
+ osmo_timer_schedule(&stats_tcp_poll_timer, osmo_tcp_stats_config->interval, 0);
+}
+
+/*! Set the polling interval (common for all sockets)
+ * \param[in] interval Poll interval in seconds
+ * \returns 0 on success; negative on error */
+int osmo_stats_tcp_set_interval(int interval)
+{
+ osmo_tcp_stats_config->interval = interval;
+ if (osmo_tcp_stats_config->interval > 0)
+ osmo_timer_schedule(&stats_tcp_poll_timer, osmo_tcp_stats_config->interval, 0);
+ return 0;
+}
+
+static __attribute__((constructor))
+void on_dso_load_stats_tcp(void)
+{
+ stats_tcp_entry_cur = NULL;
+ pthread_mutex_init(&stats_tcp_lock, NULL);
+
+ osmo_tcp_stats_config->interval = TCP_STATS_DEFAULT_INTERVAL;
+ osmo_tcp_stats_config->batch_size = TCP_STATS_DEFAULT_BATCH_SIZE;
+
+ osmo_timer_setup(&stats_tcp_poll_timer, stats_tcp_poll_timer_cb, NULL);
+}
+
+#endif /* !EMBEDDED */
+
+/* @} */
diff --git a/src/strrb.c b/src/core/strrb.c
index 461fdecc..c5a5ed6e 100644
--- a/src/strrb.c
+++ b/src/core/strrb.c
@@ -31,10 +31,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup utils
@@ -56,12 +52,12 @@
* This function creates and initializes a ringbuffer.
* Note that the ringbuffer stores at most rb_size - 1 messages.
*/
-struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size)
+struct osmo_strrb *osmo_strrb_create(void *talloc_ctx, size_t rb_size)
{
struct osmo_strrb *rb = NULL;
unsigned int i;
- rb = talloc_zero(ctx, struct osmo_strrb);
+ rb = talloc_zero(talloc_ctx, struct osmo_strrb);
if (!rb)
goto alloc_error;
diff --git a/src/tdef.c b/src/core/tdef.c
index 71a33158..f0c0f2e8 100644
--- a/src/tdef.c
+++ b/src/core/tdef.c
@@ -52,18 +52,18 @@
* By keeping separate osmo_tdef arrays, several groups of timers can be kept
* separately. The VTY tests in tests/tdef/ showcase different schemes:
*
- * - \ref tests/vty/tdef_vty_test_config_root.c:
+ * - \ref tests/tdef/tdef_vty_config_root_test.c:
* Keep several timer definitions in separately named groups: showcase the
* osmo_tdef_vty_groups*() API. Each timer group exists exactly once.
*
- * - \ref tests/vty/tdef_vty_test_config_subnode.c:
+ * - \ref tests/tdef/tdef_vty_config_subnode_test.c:
* Keep a single list of timers without separate grouping.
* Put this list on a specific subnode below the CONFIG_NODE.
* There could be several separate subnodes with timers like this, i.e.
* continuing from this example, sets of timers could be separated by placing
* timers in specific config subnodes instead of using the global group name.
*
- * - \ref tests/vty/tdef_vty_test_dynamic.c:
+ * - \ref tests/tdef/tdef_vty_dynamic_test.c:
* Dynamically allocate timer definitions per each new created object.
* Thus there can be an arbitrary number of independent timer definitions, one
* per allocated object.
@@ -93,6 +93,17 @@ static unsigned long osmo_tdef_factor(enum osmo_tdef_unit a, enum osmo_tdef_unit
return 1;
switch (b) {
+ case OSMO_TDEF_US:
+ switch (a) {
+ case OSMO_TDEF_MS:
+ return 1000;
+ case OSMO_TDEF_S:
+ return 1000*1000;
+ case OSMO_TDEF_M:
+ return 60*1000*1000;
+ default:
+ return 0;
+ }
case OSMO_TDEF_MS:
switch (a) {
case OSMO_TDEF_S:
@@ -189,8 +200,10 @@ void osmo_tdefs_reset(struct osmo_tdef *tdefs)
* \param[in] tdefs Array of timer definitions, last entry must be fully zero initialized.
* \param[in] T Timer number to get the value for.
* \param[in] as_unit Return timeout value in this unit.
- * \param[in] val_if_not_present Fallback value to return if no timeout is defined.
+ * \param[in] val_if_not_present Fallback value to return if no timeout is defined; if this is a negative number, a
+ * missing T timer definition aborts the program via OSMO_ASSERT().
* \return Timeout value in the unit given by as_unit, rounded up if necessary, or val_if_not_present.
+ * If val_if_not_present is negative and no T timer is defined, trigger OSMO_ASSERT() and do not return.
*/
unsigned long osmo_tdef_get(const struct osmo_tdef *tdefs, int T, enum osmo_tdef_unit as_unit, long val_if_not_present)
{
@@ -320,30 +333,41 @@ const struct osmo_tdef_state_timeout *osmo_tdef_get_state_timeout(uint32_t state
*/
int _osmo_tdef_fsm_inst_state_chg(struct osmo_fsm_inst *fi, uint32_t state,
const struct osmo_tdef_state_timeout *timeouts_array,
- const struct osmo_tdef *tdefs, unsigned long default_timeout,
+ const struct osmo_tdef *tdefs, long default_timeout,
const char *file, int line)
{
const struct osmo_tdef_state_timeout *t = osmo_tdef_get_state_timeout(state, timeouts_array);
- unsigned long val = 0;
+ unsigned long val_ms = 0;
/* No timeout defined for this state? */
if (!t)
return _osmo_fsm_inst_state_chg(fi, state, 0, 0, file, line);
- if (t->T)
- val = osmo_tdef_get(tdefs, t->T, OSMO_TDEF_S, default_timeout);
+ if (t->T) {
+ const struct osmo_tdef *tdef = osmo_tdef_get_entry((struct osmo_tdef *)tdefs, t->T);
+ if (tdef == NULL) {
+ /* emulate the old behavior: treat default_timeout as OSMO_TDEF_S */
+ OSMO_ASSERT(default_timeout >= 0);
+ val_ms = default_timeout * 1000;
+ } else {
+ val_ms = osmo_tdef_round(tdef->val, tdef->unit, OSMO_TDEF_MS);
+ /* emulate the old behavior: treat OSMO_TDEF_CUSTOM as OSMO_TDEF_S */
+ if (tdef->unit == OSMO_TDEF_CUSTOM)
+ val_ms *= 1000;
+ }
+ }
if (t->keep_timer) {
if (t->T)
- return _osmo_fsm_inst_state_chg_keep_or_start_timer(fi, state, val, t->T, file, line);
+ return _osmo_fsm_inst_state_chg_keep_or_start_timer_ms(fi, state, val_ms, t->T, file, line);
else
return _osmo_fsm_inst_state_chg_keep_timer(fi, state, file, line);
}
- /* val is always initialized here, because if t->keep_timer is false, t->T must be != 0.
+ /* val_ms is always initialized here, because if t->keep_timer is false, t->T must be != 0.
* Otherwise osmo_tdef_get_state_timeout() would have returned NULL. */
OSMO_ASSERT(t->T);
- return _osmo_fsm_inst_state_chg(fi, state, val, t->T, file, line);
+ return _osmo_fsm_inst_state_chg_ms(fi, state, val_ms, t->T, file, line);
}
const struct value_string osmo_tdef_unit_names[] = {
@@ -351,6 +375,7 @@ const struct value_string osmo_tdef_unit_names[] = {
{ OSMO_TDEF_MS, "ms" },
{ OSMO_TDEF_M, "m" },
{ OSMO_TDEF_CUSTOM, "custom-unit" },
+ { OSMO_TDEF_US, "us" },
{}
};
diff --git a/src/core/thread.c b/src/core/thread.c
new file mode 100644
index 00000000..d9a98422
--- /dev/null
+++ b/src/core/thread.c
@@ -0,0 +1,56 @@
+/*
+ * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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.
+ *
+ */
+
+/*! \addtogroup thread
+ * @{
+ * \file thread.c
+ */
+
+/*! \file thread.c
+ */
+
+#include "config.h"
+
+/* If HAVE_GETTID, then "_GNU_SOURCE" may need to be defined to use gettid() */
+#if HAVE_GETTID
+#define _GNU_SOURCE
+#endif
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <osmocom/core/thread.h>
+
+/*! Wrapper around Linux's gettid() to make it easily accessible on different system versions.
+ * If the gettid() API cannot be found, it will use the syscall directly if
+ * available. If no syscall is found available, then getpid() is called as
+ * fallback. See 'man 2 gettid' for further and details information.
+ * \returns This call is always successful and returns returns the thread ID of
+ * the calling thread (or the process ID of the current process if
+ * gettid() or its syscall are unavailable in the system).
+ */
+pid_t osmo_gettid(void)
+{
+#if HAVE_GETTID
+ return gettid();
+#elif defined(LINUX) && defined(__NR_gettid)
+ return (pid_t) syscall(__NR_gettid);
+#else
+ #pragma message ("use pid as tid")
+ return getpid();
+#endif
+}
diff --git a/src/core/time_cc.c b/src/core/time_cc.c
new file mode 100644
index 00000000..0e6879e5
--- /dev/null
+++ b/src/core/time_cc.c
@@ -0,0 +1,228 @@
+/*! \file foo.c
+ * Report the cumulative counter of time for which a flag is true as rate counter.
+ */
+/* Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*! \addtogroup time_cc
+ *
+ * Report the cumulative counter of time for which a flag is true as rate counter.
+ *
+ * Useful for reporting cumulative time counters as defined in 3GPP TS 52.402, for example allAvailableSDCCHAllocated,
+ * allAvailableTCHAllocated, availablePDCHAllocatedTime.
+ *
+ * For a usage example, see the description of struct osmo_time_cc.
+ *
+ * @{
+ * \file time_cc.c
+ */
+#include "config.h"
+#ifdef HAVE_CLOCK_GETTIME
+
+#include <limits.h>
+#include <time.h>
+
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/time_cc.h>
+
+#define GRAN_USEC(TIME_CC) ((TIME_CC)->cfg.gran_usec ? : 1000000)
+#define ROUND_THRESHOLD_USEC(TIME_CC) ((TIME_CC)->cfg.round_threshold_usec ? \
+ OSMO_MIN((TIME_CC)->cfg.round_threshold_usec, GRAN_USEC(TIME_CC)) \
+ : (GRAN_USEC(TIME_CC) / 2))
+
+static uint64_t time_now_usec(void)
+{
+ struct timespec tp;
+ if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp))
+ return 0;
+ return (uint64_t)tp.tv_sec * 1000000 + tp.tv_nsec / 1000;
+}
+
+static void osmo_time_cc_forget_sum(struct osmo_time_cc *tc, uint64_t now);
+
+static void osmo_time_cc_update_from_tdef(struct osmo_time_cc *tc, uint64_t now)
+{
+ bool do_forget_sum = false;
+ if (!tc->cfg.T_defs)
+ return;
+ if (tc->cfg.T_gran) {
+ uint64_t was = GRAN_USEC(tc);
+ tc->cfg.gran_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_gran, OSMO_TDEF_US, -1);
+ if (was != GRAN_USEC(tc))
+ do_forget_sum = true;
+ }
+ if (tc->cfg.T_round_threshold)
+ tc->cfg.round_threshold_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_round_threshold,
+ OSMO_TDEF_US, -1);
+ if (tc->cfg.T_forget_sum) {
+ uint64_t was = tc->cfg.forget_sum_usec;
+ tc->cfg.forget_sum_usec = osmo_tdef_get(tc->cfg.T_defs, tc->cfg.T_forget_sum, OSMO_TDEF_US, -1);
+ if (tc->cfg.forget_sum_usec && was != tc->cfg.forget_sum_usec)
+ do_forget_sum = true;
+ }
+
+ if (do_forget_sum && tc->sum)
+ osmo_time_cc_forget_sum(tc, now);
+}
+
+static void osmo_time_cc_schedule_timer(struct osmo_time_cc *tc, uint64_t now);
+
+/*! Clear out osmo_timer and internal counting state of struct osmo_time_cc. The .cfg remains unaffected. After calling,
+ * the osmo_time_cc instance can be used again to accumulate state as if it had just been initialized. */
+void osmo_time_cc_cleanup(struct osmo_time_cc *tc)
+{
+ osmo_timer_del(&tc->timer);
+ *tc = (struct osmo_time_cc){
+ .cfg = tc->cfg,
+ };
+}
+
+static void osmo_time_cc_start(struct osmo_time_cc *tc, uint64_t now)
+{
+ osmo_time_cc_cleanup(tc);
+ tc->start_time = now;
+ tc->last_counted_time = now;
+ osmo_time_cc_update_from_tdef(tc, now);
+ osmo_time_cc_schedule_timer(tc, now);
+}
+
+static void osmo_time_cc_count_time(struct osmo_time_cc *tc, uint64_t now)
+{
+ uint64_t time_delta = now - tc->last_counted_time;
+ tc->last_counted_time = now;
+ if (!tc->flag_state)
+ return;
+ /* Flag is currently true, cumulate the elapsed time */
+ tc->total_sum += time_delta;
+ tc->sum += time_delta;
+}
+
+static void osmo_time_cc_report(struct osmo_time_cc *tc, uint64_t now)
+{
+ uint64_t delta;
+ uint64_t n;
+ /* We report a sum "rounded up", ahead of time. If the granularity period has not yet elapsed after the last
+ * reporting, do not report again yet. */
+ if (tc->reported_sum > tc->sum)
+ return;
+ delta = tc->sum - tc->reported_sum;
+ /* elapsed full periods */
+ n = delta / GRAN_USEC(tc);
+ /* If the delta has passed round_threshold (normally half of gran_usec), increment. */
+ delta -= n * GRAN_USEC(tc);
+ if (delta >= ROUND_THRESHOLD_USEC(tc))
+ n++;
+ if (!n)
+ return;
+
+ /* integer sanity, since rate_ctr_add() takes an int argument. */
+ if (n > INT_MAX)
+ n = INT_MAX;
+ if (tc->cfg.rate_ctr)
+ rate_ctr_add(tc->cfg.rate_ctr, n);
+ /* Store the increments of gran_usec that were counted. */
+ tc->reported_sum += n * GRAN_USEC(tc);
+}
+
+static void osmo_time_cc_forget_sum(struct osmo_time_cc *tc, uint64_t now)
+{
+ tc->reported_sum = 0;
+ tc->sum = 0;
+
+ if (tc->last_counted_time < now)
+ tc->last_counted_time = now;
+}
+
+/*! Initialize struct osmo_time_cc. Call this once before use, and before setting up the .cfg items. */
+void osmo_time_cc_init(struct osmo_time_cc *tc)
+{
+ *tc = (struct osmo_time_cc){0};
+}
+
+/*! Report state to be recorded by osmo_time_cc instance. Setting an unchanged state repeatedly has no effect. */
+void osmo_time_cc_set_flag(struct osmo_time_cc *tc, bool flag)
+{
+ uint64_t now = time_now_usec();
+ if (!tc->start_time)
+ osmo_time_cc_start(tc, now);
+ /* No flag change == no effect */
+ if (flag == tc->flag_state)
+ return;
+ /* Sum up elapsed time, report increments for that. */
+ osmo_time_cc_count_time(tc, now);
+ osmo_time_cc_report(tc, now);
+ tc->flag_state = flag;
+ osmo_time_cc_schedule_timer(tc, now);
+}
+
+static void osmo_time_cc_timer_cb(void *data)
+{
+ struct osmo_time_cc *tc = data;
+ uint64_t now = time_now_usec();
+
+ osmo_time_cc_update_from_tdef(tc, now);
+
+ if (tc->flag_state) {
+ osmo_time_cc_count_time(tc, now);
+ osmo_time_cc_report(tc, now);
+ } else if (tc->cfg.forget_sum_usec && tc->sum
+ && (now >= tc->last_counted_time + tc->cfg.forget_sum_usec)) {
+ osmo_time_cc_forget_sum(tc, now);
+ }
+ osmo_time_cc_schedule_timer(tc, now);
+}
+
+/*! Figure out the next time we should do anything, if the flag state remains unchanged. */
+static void osmo_time_cc_schedule_timer(struct osmo_time_cc *tc, uint64_t now)
+{
+ uint64_t next_event = UINT64_MAX;
+
+ osmo_time_cc_update_from_tdef(tc, now);
+
+ /* If it is required, when will the next forget_sum happen? */
+ if (tc->cfg.forget_sum_usec && !tc->flag_state && tc->sum > 0) {
+ uint64_t next_forget_time = tc->last_counted_time + tc->cfg.forget_sum_usec;
+ next_event = OSMO_MIN(next_event, next_forget_time);
+ }
+ /* Next rate_ctr increment? */
+ if (tc->flag_state) {
+ uint64_t next_inc = now + (tc->reported_sum - tc->sum) + ROUND_THRESHOLD_USEC(tc);
+ next_event = OSMO_MIN(next_event, next_inc);
+ }
+
+ /* No event coming up? */
+ if (next_event == UINT64_MAX)
+ return;
+
+ if (next_event <= now)
+ next_event = 0;
+ else
+ next_event -= now;
+
+ osmo_timer_setup(&tc->timer, osmo_time_cc_timer_cb, tc);
+ osmo_timer_del(&tc->timer);
+ osmo_timer_schedule(&tc->timer, next_event / 1000000, next_event % 1000000);
+}
+
+#endif /* HAVE_CLOCK_GETTIME */
+
+/*! @} */
diff --git a/src/timer.c b/src/core/timer.c
index 0b2e3dd3..067bd875 100644
--- a/src/timer.c
+++ b/src/core/timer.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -40,10 +36,10 @@
#include <osmocom/core/linuxlist.h>
/* These store the amount of time that we wait until next timer expires. */
-static struct timeval nearest;
-static struct timeval *nearest_p;
+static __thread struct timeval nearest;
+static __thread struct timeval *nearest_p;
-static struct rb_root timer_root = RB_ROOT;
+static __thread struct rb_root timer_root = RB_ROOT;
static void __add_timer(struct osmo_timer_list *timer)
{
@@ -135,7 +131,7 @@ void osmo_timer_del(struct osmo_timer_list *timer)
* This function can be used to determine whether a given timer
* has alredy expired (returns 0) or is still pending (returns 1)
*/
-int osmo_timer_pending(struct osmo_timer_list *timer)
+int osmo_timer_pending(const struct osmo_timer_list *timer)
{
return timer->active;
}
@@ -184,6 +180,31 @@ struct timeval *osmo_timers_nearest(void)
return nearest_p;
}
+/*! Determine time between now and the nearest timer in milliseconds
+ * \returns number of milliseconds until nearest timer expires; -1 if no timers pending
+ */
+int osmo_timers_nearest_ms(void)
+{
+ int nearest_ms;
+
+ if (!nearest_p)
+ return -1;
+
+ nearest_ms = nearest_p->tv_sec * 1000;
+#ifndef EMBEDDED
+ /* By adding 999 milliseconds, we ensure rounding up to the nearest
+ * whole millisecond. This approach prevents the return of 0 when the
+ * timer is still active, and it guarantees that the calling process
+ * does not wait for a duration shorter than the time remaining on the
+ * timer. */
+ nearest_ms += (nearest_p->tv_usec + 999) / 1000;
+#else
+ nearest_ms += nearest_p->tv_usec / 1000;
+#endif
+
+ return nearest_ms;
+}
+
static void update_nearest(struct timeval *cand, struct timeval *current)
{
if (cand->tv_sec != LONG_MAX) {
diff --git a/src/timer_clockgettime.c b/src/core/timer_clockgettime.c
index 7b17fd11..6112b8a5 100644
--- a/src/timer_clockgettime.c
+++ b/src/core/timer_clockgettime.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup timer
diff --git a/src/timer_gettimeofday.c b/src/core/timer_gettimeofday.c
index 34949465..e0212b5a 100644
--- a/src/timer_gettimeofday.c
+++ b/src/core/timer_gettimeofday.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup timer
diff --git a/src/core/tun.c b/src/core/tun.c
new file mode 100644
index 00000000..86128190
--- /dev/null
+++ b/src/core/tun.c
@@ -0,0 +1,577 @@
+
+/* TUN interface functions.
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "config.h"
+
+/*! \addtogroup tun
+ * @{
+ * tun network device (interface) convenience functions
+ *
+ * \file tundev.c
+ *
+ * Example lifecycle use of the API:
+ *
+ * struct osmo_sockaddr_str osa_str = {};
+ * struct osmo_sockaddr osa = {};
+ *
+ * // Allocate object:
+ * struct osmo_tundev *tundev = osmo_tundev_alloc(parent_talloc_ctx, name);
+ * OSMO_ASSERT(tundev);
+ *
+ * // Configure object (before opening):
+ * osmo_tundev_set_data_ind_cb(tun, tun_data_ind_cb);
+ * rc = osmo_tundev_set_dev_name(tun, "mytunnel0");
+ * rc = osmo_tundev_set_netns_name(tun, "some_netns_name_or_null");
+ *
+ * // Open the tundev object:
+ * rc = osmo_tundev_open(tundev);
+ * // The tunnel device is now created and an associatd netdev object
+ * // is available to operate the device:
+ * struct osmo_netdev *netdev = osmo_tundev_get_netdev(tundev);
+ * OSMO_ASSERT(netdev);
+ *
+ * // Add a local IPv4 address:
+ * rc = osmo_sockaddr_str_from_str2(&osa_str, "192.168.200.1");
+ * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
+ * rc = osmo_netdev_add_addr(netdev, &osa, 24);
+ *
+ * // Bring network interface up:
+ * rc = osmo_netdev_ifupdown(netdev, true);
+ *
+ * // Add default route (0.0.0.0/0):
+ * rc = osmo_sockaddr_str_from_str2(&osa_str, "0.0.0.0");
+ * rc = osmo_sockaddr_str_to_sockaddr(&osa_str, &osa.u.sas);
+ * rc = osmo_netdev_add_route(netdev, &osa, 0, NULL);
+ *
+ * // Close the tunnel (asssociated netdev object becomes unavailable)
+ * rc = osmo_tundev_close(tundev);
+ * // Free the object:
+ * osmo_tundev_free(tundev);
+ */
+
+#if (!EMBEDDED)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <net/if.h>
+
+#if defined(__linux__)
+#include <linux/if_tun.h>
+#else
+#error "Unknown platform!"
+#endif
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/netns.h>
+#include <osmocom/core/netdev.h>
+#include <osmocom/core/tun.h>
+
+#define TUN_DEV_PATH "/dev/net/tun"
+#define TUN_PACKET_MAX 8196
+
+#define LOGTUN(tun, lvl, fmt, args ...) \
+ LOGP(DLGLOBAL, lvl, "TUN(%s,if=%s/%u,ns=%s): " fmt, \
+ (tun)->name, (tun)->dev_name ? : "", \
+ (tun)->ifindex, (tun)->netns_name ? : "", ## args)
+
+struct osmo_tundev {
+ /* Name used to identify the osmo_tundev */
+ char *name;
+
+ /* netdev managing the tun interface: */
+ struct osmo_netdev *netdev;
+
+ /* ifindiex of the currently opened tunnel interface */
+ unsigned int ifindex;
+
+ /* Network interface name to use when setting up the tun device.
+ * NULL = let the system pick one. */
+ char *dev_name;
+ /* Whether dev_name is set by user or dynamically allocated by system */
+ bool dev_name_dynamic;
+
+ /* Write queue used since tun fd is set non-blocking */
+ struct osmo_wqueue wqueue;
+
+ /* netns name where the tun interface is created (NULL = default netns) */
+ char *netns_name;
+
+ /* API user private data */
+ void *priv_data;
+
+ /* Called by tundev each time a new packet is received on the tun interface. Can be NULL. */
+ osmo_tundev_data_ind_cb_t data_ind_cb;
+
+ /* Whether the tundev is in opened state (managing the tun interface) */
+ bool opened;
+};
+
+/* A new pkt arrived from the tun device, dispatch it to the API user */
+static int tundev_decaps(struct osmo_tundev *tundev)
+{
+ struct msgb *msg;
+ int rc;
+
+ msg = msgb_alloc(TUN_PACKET_MAX, "tundev_rx");
+
+ if ((rc = read(tundev->wqueue.bfd.fd, msgb_data(msg), TUN_PACKET_MAX)) <= 0) {
+ LOGTUN(tundev, LOGL_ERROR, "read() failed: %s (%d)\n", strerror(errno), errno);
+ msgb_free(msg);
+ return -1;
+ }
+ msgb_put(msg, rc);
+
+ if (tundev->data_ind_cb)
+ return tundev->data_ind_cb(tundev, msg);
+
+ msgb_free(msg);
+ return 0;
+}
+
+/* callback for tun device osmocom select loop integration */
+static int tundev_read_cb(struct osmo_fd *fd)
+{
+ struct osmo_tundev *tundev = fd->data;
+ return tundev_decaps(tundev);
+}
+
+/* callback for tun device osmocom select loop integration */
+static int tundev_write_cb(struct osmo_fd *fd, struct msgb *msg)
+{
+ struct osmo_tundev *tundev = fd->data;
+ size_t pkt_len = msgb_length(msg);
+
+ int rc;
+ rc = write(tundev->wqueue.bfd.fd, msgb_data(msg), pkt_len);
+ if (rc < 0)
+ LOGTUN(tundev, LOGL_ERROR, "write() failed: %s (%d)\n", strerror(errno), errno);
+ else if (rc < pkt_len)
+ LOGTUN(tundev, LOGL_ERROR, "short write() %d < %zu\n", rc, pkt_len);
+ return rc;
+}
+
+static int tundev_ifupdown_ind_cb(struct osmo_netdev *netdev, bool ifupdown)
+{
+ struct osmo_tundev *tundev = osmo_netdev_get_priv_data(netdev);
+ LOGTUN(tundev, LOGL_NOTICE, "Physical link state changed: %s\n",
+ ifupdown ? "UP" : "DOWN");
+
+ /* free any backlog, both on IFUP and IFDOWN. Keep the LMI, as it makes
+ * sense to get one out of the door ASAP. */
+ osmo_wqueue_clear(&tundev->wqueue);
+ return 0;
+}
+
+static int tundev_dev_name_chg_cb(struct osmo_netdev *netdev, const char *new_dev_name)
+{
+ struct osmo_tundev *tundev = osmo_netdev_get_priv_data(netdev);
+ LOGTUN(tundev, LOGL_NOTICE, "netdev changed name: %s -> %s\n",
+ osmo_netdev_get_dev_name(netdev), new_dev_name);
+
+ if (tundev->dev_name_dynamic) {
+ osmo_talloc_replace_string(tundev, &tundev->dev_name, new_dev_name);
+ } else {
+ /* TODO: in here we probably want to force the iface name back
+ * to tundev->dev_name one we have a osmo_netdev_set_ifname() API */
+ osmo_talloc_replace_string(tundev, &tundev->dev_name, new_dev_name);
+ }
+
+ return 0;
+}
+
+static int tundev_mtu_chg_cb(struct osmo_netdev *netdev, uint32_t new_mtu)
+{
+ struct osmo_tundev *tundev = osmo_netdev_get_priv_data(netdev);
+ LOGTUN(tundev, LOGL_NOTICE, "netdev changed MTU: %u\n", new_mtu);
+
+ return 0;
+}
+
+/*! Allocate a new tundev object.
+ * \param[in] ctx talloc context to use as a parent when allocating the tundev object
+ * \param[in] name A name providen to identify the tundev object
+ * \returns newly allocated tundev object on success; NULL on error
+ */
+struct osmo_tundev *osmo_tundev_alloc(void *ctx, const char *name)
+{
+ struct osmo_tundev *tundev;
+
+ tundev = talloc_zero(ctx, struct osmo_tundev);
+ if (!tundev)
+ return NULL;
+
+ tundev->netdev = osmo_netdev_alloc(tundev, name);
+ if (!tundev->netdev) {
+ talloc_free(tundev);
+ return NULL;
+ }
+ osmo_netdev_set_priv_data(tundev->netdev, tundev);
+ osmo_netdev_set_ifupdown_ind_cb(tundev->netdev, tundev_ifupdown_ind_cb);
+ osmo_netdev_set_dev_name_chg_cb(tundev->netdev, tundev_dev_name_chg_cb);
+ osmo_netdev_set_mtu_chg_cb(tundev->netdev, tundev_mtu_chg_cb);
+
+ tundev->name = talloc_strdup(tundev, name);
+ osmo_wqueue_init(&tundev->wqueue, 1000);
+ osmo_fd_setup(&tundev->wqueue.bfd, -1, OSMO_FD_READ, osmo_wqueue_bfd_cb, tundev, 0);
+ tundev->wqueue.read_cb = tundev_read_cb;
+ tundev->wqueue.write_cb = tundev_write_cb;
+
+ return tundev;
+}
+
+/*! Free an allocated tundev object.
+ * \param[in] tundev The tundev object to free
+ */
+void osmo_tundev_free(struct osmo_tundev *tundev)
+{
+ if (!tundev)
+ return;
+ osmo_tundev_close(tundev);
+ osmo_netdev_free(tundev->netdev);
+ talloc_free(tundev);
+}
+
+/*! Open and configure fd of the tunnel device.
+ * \param[in] tundev The tundev object whose tunnel interface to open
+ * \param[in] flags internal linux flags to pass when creating the device (not used yet)
+ * \returns 0 on success; negative on error
+ */
+static int tundev_open_fd(struct osmo_tundev *tundev, int flags)
+{
+ struct ifreq ifr;
+ int fd, rc;
+
+ fd = open(TUN_DEV_PATH, O_RDWR);
+ if (fd < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Cannot open " TUN_DEV_PATH ": %s\n", strerror(errno));
+ return fd;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TUN | IFF_NO_PI | flags;
+ if (tundev->dev_name) {
+ /* if a TUN interface name was specified, put it in the structure; otherwise,
+ the kernel will try to allocate the "next" device of the specified type */
+ osmo_strlcpy(ifr.ifr_name, tundev->dev_name, IFNAMSIZ);
+ }
+
+ /* try to create the device */
+ rc = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (rc < 0)
+ goto close_ret;
+
+ /* Read name back from device */
+ if (!tundev->dev_name) {
+ ifr.ifr_name[IFNAMSIZ - 1] = '\0';
+ tundev->dev_name = talloc_strdup(tundev, ifr.ifr_name);
+ tundev->dev_name_dynamic = true;
+ }
+
+ /* Store interface index:
+ * (Note: there's a potential race condition here between creating the
+ * iface with a given name above and attempting to retrieve its ifindex based
+ * on that name. Someone (ie udev) could have the iface renamed in
+ * between here. It's a pity that TUNSETIFF doesn't copy back to us the
+ * newly allocated ifinidex as it does with ifname)
+ */
+ tundev->ifindex = if_nametoindex(tundev->dev_name);
+ if (tundev->ifindex == 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Unable to find ifinidex for dev %s\n",
+ tundev->dev_name);
+ rc = -ENODEV;
+ goto close_ret;
+ }
+
+ LOGTUN(tundev, LOGL_INFO, "TUN device created\n");
+
+ /* set non-blocking: */
+ rc = fcntl(fd, F_GETFL);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "fcntl(F_GETFL) failed: %s (%d)\n",
+ strerror(errno), errno);
+ goto close_ret;
+ }
+ rc = fcntl(fd, F_SETFL, rc | O_NONBLOCK);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "fcntl(F_SETFL, O_NONBLOCK) failed: %s (%d)\n",
+ strerror(errno), errno);
+ goto close_ret;
+ }
+ return fd;
+
+close_ret:
+ close(fd);
+ return rc;
+}
+
+/*! Open the tunnel device owned by the tundev object.
+ * \param[in] tundev The tundev object to open
+ * \returns 0 on success; negative on error
+ */
+int osmo_tundev_open(struct osmo_tundev *tundev)
+{
+ struct osmo_netns_switch_state switch_state;
+ int rc;
+ int netns_fd = -1;
+
+ if (tundev->opened)
+ return -EALREADY;
+
+ /* temporarily switch to specified namespace to create tun device */
+ if (tundev->netns_name) {
+ LOGTUN(tundev, LOGL_INFO, "Open tun: Switch to netns '%s'\n",
+ tundev->netns_name);
+ netns_fd = osmo_netns_open_fd(tundev->netns_name);
+ if (netns_fd < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Open tun: Cannot switch to netns '%s': %s (%d)\n",
+ tundev->netns_name, strerror(errno), errno);
+ return netns_fd;
+ }
+ rc = osmo_netns_switch_enter(netns_fd, &switch_state);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Open tun: Cannot switch to netns '%s': %s (%d)\n",
+ tundev->netns_name, strerror(errno), errno);
+ goto err_close_netns_fd;
+ }
+ }
+
+ tundev->wqueue.bfd.fd = tundev_open_fd(tundev, 0);
+ if (tundev->wqueue.bfd.fd < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Cannot open TUN device: %s\n", strerror(errno));
+ rc = -ENODEV;
+ goto err_restore_ns;
+ }
+
+ /* switch back to default namespace */
+ if (tundev->netns_name) {
+ rc = osmo_netns_switch_exit(&switch_state);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Open tun: Cannot switch back from netns '%s': %s\n",
+ tundev->netns_name, strerror(errno));
+ goto err_close_tun;
+ }
+ LOGTUN(tundev, LOGL_INFO, "Open tun: Back from netns '%s'\n",
+ tundev->netns_name);
+ }
+
+ rc = osmo_netdev_set_netns_name(tundev->netdev, tundev->netns_name);
+ if (rc < 0)
+ goto err_close_tun;
+ rc = osmo_netdev_set_ifindex(tundev->netdev, tundev->ifindex);
+ if (rc < 0)
+ goto err_close_tun;
+
+ rc = osmo_netdev_register(tundev->netdev);
+ if (rc < 0)
+ goto err_close_tun;
+
+ rc = osmo_fd_register(&tundev->wqueue.bfd);
+ if (rc < 0)
+ goto err_unregister_netdev;
+
+ tundev->opened = true;
+ return 0;
+
+err_unregister_netdev:
+ osmo_netdev_unregister(tundev->netdev);
+err_close_tun:
+ close(tundev->wqueue.bfd.fd);
+ tundev->wqueue.bfd.fd = -1;
+err_restore_ns:
+ if (tundev->netns_name)
+ osmo_netns_switch_exit(&switch_state);
+err_close_netns_fd:
+ if (netns_fd >= 0)
+ close(netns_fd);
+ return rc;
+}
+
+/*! Close the tunnel device owned by the tundev object.
+ * \param[in] tundev The tundev object to close
+ * \returns 0 on success; negative on error
+ */
+int osmo_tundev_close(struct osmo_tundev *tundev)
+{
+ if (!tundev->opened)
+ return -EALREADY;
+
+ osmo_wqueue_clear(&tundev->wqueue);
+ if (tundev->wqueue.bfd.fd != -1) {
+ osmo_fd_unregister(&tundev->wqueue.bfd);
+ close(tundev->wqueue.bfd.fd);
+ tundev->wqueue.bfd.fd = -1;
+ }
+
+ osmo_netdev_unregister(tundev->netdev);
+ if (tundev->dev_name_dynamic) {
+ TALLOC_FREE(tundev->dev_name);
+ tundev->dev_name_dynamic = false;
+ }
+ tundev->opened = false;
+ return 0;
+}
+
+/*! Retrieve whether the tundev object is in "opened" state.
+ * \param[in] tundev The tundev object to check
+ * \returns true if in state "opened"; false otherwise
+ */
+bool osmo_tundev_is_open(struct osmo_tundev *tundev)
+{
+ return tundev->opened;
+}
+
+/*! Set private user data pointer on the tundev object.
+ * \param[in] tundev The tundev object where the field is set
+ */
+void osmo_tundev_set_priv_data(struct osmo_tundev *tundev, void *priv_data)
+{
+ tundev->priv_data = priv_data;
+}
+
+/*! Get private user data pointer from the tundev object.
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The current value of the priv_data field.
+ */
+void *osmo_tundev_get_priv_data(struct osmo_tundev *tundev)
+{
+ return tundev->priv_data;
+}
+
+/*! Set data_ind_cb callback, called when a new packet is received on the tun interface.
+ * \param[in] tundev The tundev object where the field is set
+ * \param[in] data_ind_cb the user provided function to be called when a new packet is received
+ */
+void osmo_tundev_set_data_ind_cb(struct osmo_tundev *tundev, osmo_tundev_data_ind_cb_t data_ind_cb)
+{
+ tundev->data_ind_cb = data_ind_cb;
+}
+
+/*! Get name used to identify the tundev object.
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The current value of the name used to identify the tundev object
+ */
+const char *osmo_tundev_get_name(const struct osmo_tundev *tundev)
+{
+ return tundev->name;
+}
+
+/*! Set name used to name the tunnel interface created by the tundev object.
+ * \param[in] tundev The tundev object where the field is set
+ * \param[in] dev_name The tunnel interface name to use
+ * \returns 0 on success; negative on error
+ *
+ * This is used during osmo_tundev_open() time, and hence shouldn't be changed
+ * when the tundev object is in "opened" state.
+ * If left as NULL (default), the system will pick a suitable name during
+ * osmo_tundev_open(), and the field will be updated to the system-selected
+ * name, which can be retrieved later with osmo_tundev_get_dev_name().
+ */
+int osmo_tundev_set_dev_name(struct osmo_tundev *tundev, const char *dev_name)
+{
+ if (tundev->opened)
+ return -EALREADY;
+ osmo_talloc_replace_string(tundev, &tundev->dev_name, dev_name);
+ tundev->dev_name_dynamic = false;
+ return 0;
+}
+
+/*! Get name used to name the tunnel interface created by the tundev object
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The current value of the configured tunnel interface name to use
+ */
+const char *osmo_tundev_get_dev_name(const struct osmo_tundev *tundev)
+{
+ return tundev->dev_name;
+}
+
+/*! Set name of the network namespace to use when opening the tunnel interface
+ * \param[in] tundev The tundev object where the field is set
+ * \param[in] netns_name The network namespace to use during tunnel interface creation
+ * \returns 0 on success; negative on error
+ *
+ * This is used during osmo_tundev_open() time, and hence shouldn't be changed
+ * when the tundev object is in "opened" state.
+ * If left as NULL (default), the system will stay in the current network namespace.
+ */
+int osmo_tundev_set_netns_name(struct osmo_tundev *tundev, const char *netns_name)
+{
+ if (tundev->opened)
+ return -EALREADY;
+ osmo_talloc_replace_string(tundev, &tundev->netns_name, netns_name);
+ return 0;
+}
+
+/*! Get name of network namespace used when opening the tunnel interface
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The current value of the configured network namespace
+ */
+const char *osmo_tundev_get_netns_name(const struct osmo_tundev *tundev)
+{
+ return tundev->netns_name;
+}
+
+/*! Get netdev managing the tunnel interface of tundev
+ * \param[in] tundev The tundev object from where to retrieve the field
+ * \returns The netdev objet managing the tun interface
+ */
+struct osmo_netdev *osmo_tundev_get_netdev(struct osmo_tundev *tundev)
+{
+ return tundev->netdev;
+}
+
+/*! Submit a packet to the tunnel device managed by the tundev object
+ * \param[in] tundev The tundev object owning the tunnel device where to inject the packet
+ * \param[in] msg The msgb containg the packet to transfer
+ * \returns The current value of the configured network namespace
+ *
+ * This function takes the ownership of msg in all cases.
+ */
+int osmo_tundev_send(struct osmo_tundev *tundev, struct msgb *msg)
+{
+ int rc = osmo_wqueue_enqueue(&tundev->wqueue, msg);
+ if (rc < 0) {
+ LOGTUN(tundev, LOGL_ERROR, "Failed to enqueue the packet\n");
+ msgb_free(msg);
+ return rc;
+ }
+ return rc;
+}
+
+
+#endif /* (!EMBEDDED) */
+
+/*! @} */
diff --git a/src/use_count.c b/src/core/use_count.c
index 453d2ae8..9714403d 100644
--- a/src/use_count.c
+++ b/src/core/use_count.c
@@ -19,10 +19,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <errno.h>
@@ -107,6 +103,19 @@ int32_t osmo_use_count_by(const struct osmo_use_count *uc, const char *use)
*/
const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc)
{
+ osmo_use_count_to_str_buf(buf, buf_len, uc);
+ return buf;
+}
+
+/*! Write a comprehensive listing of use counts to a string buffer.
+ * Reads like "12 (3*barring,fighting,8*kungfoo)".
+ * \param[inout] buf Destination buffer.
+ * \param[in] buf_len sizeof(buf).
+ * \param[in] uc Use counts to print.
+ * \return number of bytes that would be written, like snprintf().
+ */
+int osmo_use_count_to_str_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc)
+{
int32_t count = osmo_use_count_total(uc);
struct osmo_strbuf sb = { .buf = buf, .len = buf_len };
struct osmo_use_count_entry *e;
@@ -114,6 +123,9 @@ const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo
OSMO_STRBUF_PRINTF(sb, "%" PRId32 " (", count);
+ if (!uc->use_counts.next)
+ goto uninitialized;
+
first = true;
llist_for_each_entry(e, &uc->use_counts, entry) {
if (!e->count)
@@ -127,8 +139,21 @@ const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo
}
if (first)
OSMO_STRBUF_PRINTF(sb, "-");
+
+uninitialized:
OSMO_STRBUF_PRINTF(sb, ")");
- return buf;
+ return sb.chars_needed;
+}
+
+/*! Write a comprehensive listing of use counts to a talloc allocated string buffer.
+ * Reads like "12 (3*barring,fighting,8*kungfoo)".
+ * \param[in] ctx talloc pool to allocate from.
+ * \param[in] uc Use counts to print.
+ * \return buf, always nul-terminated.
+ */
+char *osmo_use_count_to_str_c(void *ctx, const struct osmo_use_count *uc)
+{
+ OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_use_count_to_str_buf, uc)
}
/* Return a use token's use count entry -- probably you want osmo_use_count_by() instead.
@@ -210,6 +235,8 @@ int _osmo_use_count_get_put(struct osmo_use_count *uc, const char *use, int32_t
{
struct osmo_use_count_entry *e;
int32_t old_use_count;
+ if (!uc)
+ return -EINVAL;
if (!change)
return 0;
diff --git a/src/utils.c b/src/core/utils.c
index ea1de0f5..1ec940d0 100644
--- a/src/utils.c
+++ b/src/core/utils.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -30,6 +26,7 @@
#include <errno.h>
#include <stdio.h>
#include <inttypes.h>
+#include <limits.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/bit64gen.h>
@@ -150,13 +147,15 @@ uint8_t osmo_char2bcd(char c)
*/
int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex)
{
- char *dst_end = dst + dst_size - 1;
+ char *dst_end;
int nibble_i;
int rc = 0;
- if (!dst || dst_size < 1)
+ if (!dst || dst_size < 1 || start_nibble < 0)
return -ENOMEM;
+ dst_end = dst + dst_size - 1;
+
for (nibble_i = start_nibble; nibble_i < end_nibble && dst < dst_end; nibble_i++, dst++) {
uint8_t nibble = bcd[nibble_i >> 1];
if ((nibble_i & 1))
@@ -175,13 +174,72 @@ int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibbl
return OSMO_MAX(0, end_nibble - start_nibble);
}
+/*! Convert string to BCD.
+ * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd[0] & 0x0f, nibble 1 is bcd[0] & 0xf0, nibble
+ * 3 is bcd[1] & 0x0f, etc..
+ * \param[out] dst Output BCD buffer.
+ * \param[in] dst_size sizeof() the output string buffer.
+ * \param[in] digits String containing decimal or hexadecimal digits in upper or lower case.
+ * \param[in] start_nibble Offset to start from, in nibbles, typically 1 to skip the first (MI type) nibble.
+ * \param[in] end_nibble Negative to write all digits found in str, followed by 0xf nibbles to fill any started octet.
+ * If >= 0, stop before this offset in nibbles, e.g. to get default behavior, pass
+ * start_nibble + strlen(str) + ((start_nibble + strlen(str)) & 1? 1 : 0) + 1.
+ * \param[in] allow_hex If false, return error if there are hexadecimal digits (A-F). If true, write those to
+ * BCD.
+ * \returns The buffer size in octets that is used to place all bcd digits (including the skipped nibbles
+ * from 'start_nibble' and rounded up to full octets); -EINVAL on invalid digits;
+ * -ENOMEM if dst is NULL, if dst_size is too small to contain all nibbles, or if start_nibble is negative.
+ */
+int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_nibble, int end_nibble, bool allow_hex)
+{
+ const char *digit = digits;
+ int nibble_i;
+
+ if (!dst || !dst_size || start_nibble < 0)
+ return -ENOMEM;
+
+ if (end_nibble < 0) {
+ end_nibble = start_nibble + strlen(digits);
+ /* If the last octet is not complete, add another filler nibble */
+ if (end_nibble & 1)
+ end_nibble++;
+ }
+ if ((unsigned int) (end_nibble / 2) > dst_size)
+ return -ENOMEM;
+
+ for (nibble_i = start_nibble; nibble_i < end_nibble; nibble_i++) {
+ uint8_t nibble = 0xf;
+ int octet = nibble_i >> 1;
+ if (*digit) {
+ char c = *digit;
+ digit++;
+ if (c >= '0' && c <= '9')
+ nibble = c - '0';
+ else if (allow_hex && c >= 'A' && c <= 'F')
+ nibble = 0xa + (c - 'A');
+ else if (allow_hex && c >= 'a' && c <= 'f')
+ nibble = 0xa + (c - 'a');
+ else
+ return -EINVAL;
+ }
+ nibble &= 0xf;
+ if ((nibble_i & 1))
+ dst[octet] = (nibble << 4) | (dst[octet] & 0x0f);
+ else
+ dst[octet] = (dst[octet] & 0xf0) | nibble;
+ }
+
+ /* floor(float(end_nibble) / 2) */
+ return end_nibble / 2;
+}
+
/*! Parse a string containing hexadecimal digits
* \param[in] str string containing ASCII encoded hexadecimal digits
* \param[out] b output buffer
* \param[in] max_len maximum space in output buffer
* \returns number of parsed octets, or -1 on error
*/
-int osmo_hexparse(const char *str, uint8_t *b, int max_len)
+int osmo_hexparse(const char *str, uint8_t *b, unsigned int max_len)
{
char c;
@@ -256,7 +314,7 @@ const char *osmo_hexdump_buf(char *out_buf, size_t out_buf_size, const unsigned
for (i = 0; i < len; i++) {
const char *delimp = delim;
int len_remain = out_buf_size - (cur - out_buf) - 1;
- if (len_remain < (2 + delim_len)
+ if (len_remain < (int) (2 + delim_len)
&& !(!delim_after_last && i == (len - 1) && len_remain >= 2))
break;
@@ -280,11 +338,11 @@ const char *osmo_hexdump_buf(char *out_buf, size_t out_buf_size, const unsigned
* \param[out] buf_len size of buf in bytes
* \param[in] bits A sequence of unpacked bits
* \param[in] len Length of bits
- * \returns string representation in static buffer.
+ * \return The output buffer (buf).
*/
char *osmo_ubit_dump_buf(char *buf, size_t buf_len, const uint8_t *bits, unsigned int len)
{
- int i;
+ unsigned int i;
if (len > buf_len-1)
len = buf_len-1;
@@ -347,9 +405,6 @@ char *osmo_hexdump(const unsigned char *buf, int len)
*
* This function will print a sequence of bytes as hexadecimal numbers,
* adding one space character between each byte (e.g. "1a ef d9")
- *
- * The maximum size of the output buffer is 4096 bytes, i.e. the maximum
- * number of input bytes that can be printed in one call is 1365!
*/
char *osmo_hexdump_c(const void *ctx, const unsigned char *buf, int len)
{
@@ -386,9 +441,6 @@ char *osmo_hexdump_nospc(const unsigned char *buf, int len)
*
* This function will print a sequence of bytes as hexadecimal numbers,
* without any space character between each byte (e.g. "1aefd9")
- *
- * The maximum size of the output buffer is 4096 bytes, i.e. the maximum
- * number of input bytes that can be printed in one call is 2048!
*/
char *osmo_hexdump_nospc_c(const void *ctx, const unsigned char *buf, int len)
{
@@ -409,7 +461,7 @@ char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len)
__attribute__((weak, alias("osmo_hexdump_nospc")));
#endif
-#include "../config.h"
+#include "config.h"
#ifdef HAVE_CTYPE_H
#include <ctype.h>
/*! Convert an entire string to lower case
@@ -486,7 +538,7 @@ uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len)
/*! Generic big-endian encoding of big endian number up to 64bit
* \param[in] value unsigned integer value to be stored
- * \param[in] data_len number of octets
+ * \param[in] data_len number of octets
* \returns static buffer containing big-endian stored value
*
* This is like osmo_store64be_ext, except that this returns a static buffer of
@@ -509,20 +561,44 @@ uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len)
* Copy at most \a siz bytes from \a src to \a dst, ensuring that the result is
* NUL terminated. The NUL character is included in \a siz, i.e. passing the
* actual sizeof(*dst) is correct.
+ *
+ * Note, a similar function that also limits the input buffer size is osmo_print_n().
*/
size_t osmo_strlcpy(char *dst, const char *src, size_t siz)
{
size_t ret = src ? strlen(src) : 0;
if (siz) {
- size_t len = (ret >= siz) ? siz - 1 : ret;
- if (src)
+ size_t len = OSMO_MIN(siz - 1, ret);
+ if (len)
memcpy(dst, src, len);
dst[len] = '\0';
}
return ret;
}
+/*! Find first occurence of a char in a size limited string.
+ * Like strchr() but with a buffer size limit.
+ * \param[in] str String buffer to examine.
+ * \param[in] str_size sizeof(str).
+ * \param[in] c Character to look for.
+ * \return Pointer to the matched char, or NULL if not found.
+ */
+const char *osmo_strnchr(const char *str, size_t str_size, char c)
+{
+ const char *end = str + str_size;
+ const char *pos;
+ if (!str)
+ return NULL;
+ for (pos = str; pos < end; pos++) {
+ if (c == *pos)
+ return pos;
+ if (!*pos)
+ return NULL;
+ }
+ return NULL;
+}
+
/*! Validate that a given string is a hex string within given size limits.
* Note that each hex digit amounts to a nibble, so if checking for a hex
* string to result in N bytes, pass amount of digits as 2*N.
@@ -603,7 +679,7 @@ bool osmo_identifier_valid(const char *str)
* To guarantee passing osmo_separated_identifiers_valid(), replace_with must not itself be an illegal character. If in
* doubt, use '-'.
* \param[inout] str Identifier to sanitize, must be nul terminated and in a writable buffer.
- * \param[in] sep_chars Additional characters that are allowed besides osmo_identifier_illegal_chars.
+ * \param[in] sep_chars Additional characters that are to be replaced besides osmo_identifier_illegal_chars.
* \param[in] replace_with Replace any illegal characters with this character.
*/
void osmo_identifier_sanitize_buf(char *str, const char *sep_chars, char replace_with)
@@ -668,13 +744,19 @@ int osmo_print_n(char *buf, size_t bufsize, const char *str, size_t n)
}
/*! Return the string with all non-printable characters escaped.
+ * This internal function is the implementation for all osmo_escape_str* and osmo_quote_str* API versions.
+ * It provides both the legacy (non C compatible) escaping, as well as C compatible string constant syntax,
+ * and it provides a return value of characters-needed, to allow producing un-truncated strings in all cases.
* \param[out] buf string buffer to write escaped characters to.
* \param[in] bufsize sizeof(buf).
* \param[in] str A string that may contain any characters.
* \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars).
+ * \param[in] legacy_format If false, return C compatible string constants ("\x0f"), if true the legacy
+ * escaping format ("\15"). The legacy format also escapes as "\a\b\f\v", while
+ * the non-legacy format also escapes those as "\xNN" sequences.
* \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()).
*/
-char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_len)
+static int _osmo_escape_str_buf(char *buf, size_t bufsize, const char *str, int in_len, bool legacy_format)
{
struct osmo_strbuf sb = { .buf = buf, .len = bufsize };
int in_pos = 0;
@@ -713,22 +795,56 @@ char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_le
BACKSLASH_CASE('\r', 'r');
BACKSLASH_CASE('\t', 't');
BACKSLASH_CASE('\0', '0');
- BACKSLASH_CASE('\a', 'a');
- BACKSLASH_CASE('\b', 'b');
- BACKSLASH_CASE('\v', 'v');
- BACKSLASH_CASE('\f', 'f');
BACKSLASH_CASE('\\', '\\');
BACKSLASH_CASE('"', '"');
-#undef BACKSLASH_CASE
default:
- OSMO_STRBUF_PRINTF(sb, "\\%u", (unsigned char)str[in_pos]);
+ if (legacy_format) {
+ switch (str[next_unprintable]) {
+ BACKSLASH_CASE('\a', 'a');
+ BACKSLASH_CASE('\b', 'b');
+ BACKSLASH_CASE('\v', 'v');
+ BACKSLASH_CASE('\f', 'f');
+ default:
+ OSMO_STRBUF_PRINTF(sb, "\\%u", (unsigned char)str[in_pos]);
+ break;
+ }
+ break;
+ }
+
+ OSMO_STRBUF_PRINTF(sb, "\\x%02x", (unsigned char)str[in_pos]);
break;
}
in_pos ++;
+#undef BACKSLASH_CASE
}
done:
+ return sb.chars_needed;
+}
+
+/*! Return the string with all non-printable characters escaped.
+ * \param[out] buf string buffer to write escaped characters to.
+ * \param[in] bufsize sizeof(buf).
+ * \param[in] str A string that may contain any characters.
+ * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars).
+ * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()).
+ */
+int osmo_escape_str_buf3(char *buf, size_t bufsize, const char *str, int in_len)
+{
+ return _osmo_escape_str_buf(buf, bufsize, str, in_len, false);
+}
+
+/*! Return the string with all non-printable characters escaped.
+ * \param[out] buf string buffer to write escaped characters to.
+ * \param[in] bufsize sizeof(buf).
+ * \param[in] str A string that may contain any characters.
+ * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars).
+ * \return The output buffer (buf).
+ */
+char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_len)
+{
+ _osmo_escape_str_buf(buf, bufsize, str, in_len, true);
return buf;
}
@@ -750,31 +866,63 @@ const char *osmo_escape_str(const char *str, int in_len)
*/
char *osmo_escape_str_c(const void *ctx, const char *str, int in_len)
{
- char *buf = talloc_size(ctx, in_len+1);
- if (!buf)
- return NULL;
- return osmo_escape_str_buf2(buf, in_len+1, str, in_len);
+ /* The string will be at least as long as in_len, but some characters might need escaping.
+ * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */
+ OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_escape_str_buf, str, in_len, true);
}
-/*! Like osmo_escape_str_buf2(), but returns double-quotes around a string, or "NULL" for a NULL string.
- * This allows passing any char* value and get its C representation as string.
- * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN().
+/*! Return a quoted and escaped representation of the string.
+ * This internal function is the implementation for all osmo_quote_str* API versions.
+ * It provides both the legacy (non C compatible) escaping, as well as C compatible string constant syntax,
+ * and it provides a return value of characters-needed, to allow producing un-truncated strings in all cases.
* \param[out] buf string buffer to write escaped characters to.
* \param[in] bufsize sizeof(buf).
* \param[in] str A string that may contain any characters.
- * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length.
+ * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars).
+ * \param[in] legacy_format If false, return C compatible string constants ("\x0f"), if true the legacy
+ * escaping format ("\15"). The legacy format also escapes as "\a\b\f\v", while
+ * the non-legacy format also escapes those as "\xNN" sequences.
* \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()).
*/
-char *osmo_quote_str_buf2(char *buf, size_t bufsize, const char *str, int in_len)
+static size_t _osmo_quote_str_buf(char *buf, size_t bufsize, const char *str, int in_len, bool legacy_format)
{
struct osmo_strbuf sb = { .buf = buf, .len = bufsize };
if (!str)
OSMO_STRBUF_PRINTF(sb, "NULL");
else {
OSMO_STRBUF_PRINTF(sb, "\"");
- OSMO_STRBUF_APPEND_NOLEN(sb, osmo_escape_str_buf2, str, in_len);
+ OSMO_STRBUF_APPEND(sb, _osmo_escape_str_buf, str, in_len, legacy_format);
OSMO_STRBUF_PRINTF(sb, "\"");
}
+ return sb.chars_needed;
+}
+
+/*! Like osmo_escape_str_buf3(), but returns double-quotes around a string, or "NULL" for a NULL string.
+ * This allows passing any char* value and get its C representation as string.
+ * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN().
+ * \param[out] buf string buffer to write escaped characters to.
+ * \param[in] bufsize sizeof(buf).
+ * \param[in] str A string that may contain any characters.
+ * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length.
+ * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()).
+ */
+int osmo_quote_str_buf3(char *buf, size_t bufsize, const char *str, int in_len)
+{
+ return _osmo_quote_str_buf(buf, bufsize, str, in_len, false);
+}
+
+/*! Like osmo_escape_str_buf2(), but returns double-quotes around a string, or "NULL" for a NULL string.
+ * This allows passing any char* value and get its C representation as string.
+ * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN().
+ * \param[out] buf string buffer to write escaped characters to.
+ * \param[in] bufsize sizeof(buf).
+ * \param[in] str A string that may contain any characters.
+ * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length.
+ * \return The output buffer (buf).
+ */
+char *osmo_quote_str_buf2(char *buf, size_t bufsize, const char *str, int in_len)
+{
+ _osmo_quote_str_buf(buf, bufsize, str, in_len, true);
return buf;
}
@@ -792,7 +940,7 @@ const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bu
return "NULL";
if (!buf || !bufsize)
return "(error)";
- osmo_quote_str_buf2(buf, bufsize, str, in_len);
+ _osmo_quote_str_buf(buf, bufsize, str, in_len, true);
return buf;
}
@@ -804,32 +952,78 @@ const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bu
*/
const char *osmo_quote_str(const char *str, int in_len)
{
- return osmo_quote_str_buf(str, in_len, namebuf, sizeof(namebuf));
+ _osmo_quote_str_buf(namebuf, sizeof(namebuf), str, in_len, true);
+ return namebuf;
}
/*! Like osmo_quote_str_buf() but returns the result in a dynamically-allocated buffer.
- * The static buffer is shared with get_value_string() and osmo_escape_str().
* \param[in] str A string that may contain any characters.
* \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length.
* \returns dynamically-allocated buffer containing a quoted and escaped representation.
*/
char *osmo_quote_str_c(const void *ctx, const char *str, int in_len)
{
- size_t len = in_len == -1 ? strlen(str) : in_len;
- char *buf;
+ /* The string will be at least as long as in_len, but some characters might need escaping.
+ * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */
+ OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_quote_str_buf, str, in_len, true);
+}
- /* account for two quote characters + terminating NUL */
- len += 3;
+/*! Return the string with all non-printable characters escaped.
+ * In contrast to osmo_escape_str_buf2(), this returns the needed buffer size suitable for OSMO_STRBUF_APPEND(), and
+ * this escapes characters in a way compatible with C string constant syntax.
+ * \param[out] buf string buffer to write escaped characters to.
+ * \param[in] bufsize sizeof(buf).
+ * \param[in] str A string that may contain any characters.
+ * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length (also past nul chars).
+ * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()).
+ */
+size_t osmo_escape_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len)
+{
+ return _osmo_escape_str_buf(buf, bufsize, str, in_len, false);
+}
- /* some minimum length for things like "NULL" or "(error)" */
- if (len < 32)
- len = 32;
+/*! Return the string with all non-printable characters escaped, in dynamically-allocated buffer.
+ * In contrast to osmo_escape_str_c(), this escapes characters in a way compatible with C string constant syntax, and
+ * allocates sufficient memory in all cases.
+ * \param[in] str A string that may contain any characters.
+ * \param[in] len Pass -1 to print until nul char, or >= 0 to force a length.
+ * \returns dynamically-allocated buffer, containing an escaped representation.
+ */
+char *osmo_escape_cstr_c(void *ctx, const char *str, int in_len)
+{
+ /* The string will be at least as long as in_len, but some characters might need escaping.
+ * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */
+ OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_escape_str_buf, str, in_len, false);
+}
- buf = talloc_size(ctx, len);
- if (!buf)
- return NULL;
+/*! Like osmo_escape_str_buf2(), but returns double-quotes around a string, or "NULL" for a NULL string.
+ * This allows passing any char* value and get its C representation as string.
+ * The function signature is suitable for OSMO_STRBUF_APPEND_NOLEN().
+ * In contrast to osmo_escape_str_buf2(), this returns the needed buffer size suitable for OSMO_STRBUF_APPEND(), and
+ * this escapes characters in a way compatible with C string constant syntax.
+ * \param[out] buf string buffer to write escaped characters to.
+ * \param[in] bufsize sizeof(buf).
+ * \param[in] str A string that may contain any characters.
+ * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length.
+ * \return Number of characters that would be written if bufsize were large enough excluding '\0' (like snprintf()).
+ */
+size_t osmo_quote_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len)
+{
+ return _osmo_quote_str_buf(buf, bufsize, str, in_len, false);
+}
- return osmo_quote_str_buf2(buf, len, str, in_len);
+/*! Return the string quoted and with all non-printable characters escaped, in dynamically-allocated buffer.
+ * In contrast to osmo_quote_str_c(), this escapes characters in a way compatible with C string constant syntax, and
+ * allocates sufficient memory in all cases.
+ * \param[in] str A string that may contain any characters.
+ * \param[in] len Pass -1 to print until nul char, or >= 0 to force a length.
+ * \returns dynamically-allocated buffer, containing a quoted and escaped representation.
+ */
+char *osmo_quote_cstr_c(void *ctx, const char *str, int in_len)
+{
+ /* The string will be at least as long as in_len plus two quotes, but some characters might need escaping.
+ * These extra bytes should catch most usual escaping situations, avoiding a second run in OSMO_NAME_C_IMPL. */
+ OSMO_NAME_C_IMPL(ctx, in_len + 16, "ERROR", _osmo_quote_str_buf, str, in_len, false);
}
/*! perform an integer square root operation on unsigned 32bit integer.
@@ -1019,6 +1213,51 @@ char osmo_luhn(const char* in, int in_len)
return (sum * 9) % 10 + '0';
}
+/*! Remove up to N chars from the end of an osmo_strbuf.
+ * |--char-count---| - - chars_needed - - |
+ * |<---------drop----------|
+ */
+void osmo_strbuf_drop_tail(struct osmo_strbuf *sb, size_t n_chars)
+{
+ size_t drop_n;
+ if (sb->pos <= sb->buf)
+ return;
+ drop_n = OSMO_MIN(sb->chars_needed, n_chars);
+ sb->chars_needed -= drop_n;
+ /* chars_needed was reduced by n_chars, which may have been entirely behind the end of a full buffer, within the
+ * hypothetical chars_needed. Modify the buffer tail pos only if the buffer is not or longer full now. */
+ if (sb->chars_needed >= OSMO_STRBUF_CHAR_COUNT(*sb))
+ return;
+ sb->pos = sb->buf + sb->chars_needed;
+ *sb->pos = '\0';
+}
+
+/*! Let osmo_strbuf know that n_chars characters (excluding nul) were written to the end of the buffer.
+ * If sb is nonempty, the n_chars are assumed to have been written to sb->pos. If sb is still empty and pos == NULL, the
+ * n_chars are assumed to have been written to the start of the buffer.
+ * Advance sb->pos and sb->chars_needed by at most n_chars, or up to sb->len - 1.
+ * Ensure nul termination. */
+void osmo_strbuf_added_tail(struct osmo_strbuf *sb, size_t n_chars)
+{
+ /* On init of an osmo_strbuf, sb->pos == NULL, which is defined as semantically identical to pointing at the
+ * start of the buffer. A caller may just write to the buffer and call osmo_strbuf_added_tail(), in which case
+ * still pos == NULL. pos != NULL happens as soon as the first OSMO_STRBUF_*() API has acted on the strbuf. */
+ if (!sb->pos)
+ sb->pos = sb->buf;
+ sb->chars_needed += n_chars;
+ /* first get remaining space, not counting trailing nul; but safeguard against empty buffer */
+ size_t n_added = OSMO_STRBUF_REMAIN(*sb);
+ if (n_added)
+ n_added--;
+ /* do not add more than fit in sb->len, still ensuring nul termination */
+ n_added = OSMO_MIN(n_added, n_chars);
+ if (n_added)
+ sb->pos += n_added;
+ /* when a strbuf is full, sb->pos may point after the final nul, so nul terminate only when pos is valid. */
+ if (sb->pos < sb->buf + sb->len)
+ *sb->pos = '\0';
+}
+
/*! Compare start of a string.
* This is an optimisation of 'strstr(str, startswith_str) == str' because it doesn't search through the entire string.
* \param str (Longer) string to compare.
@@ -1033,4 +1272,315 @@ bool osmo_str_startswith(const char *str, const char *startswith_str)
return strncmp(str, startswith_str, strlen(startswith_str)) == 0;
}
+/*! Convert a string of a floating point number to a signed int, with a decimal factor (fixed-point precision).
+ * For example, with precision=3, convert "-1.23" to -1230. In other words, the float value is multiplied by
+ * 10 to-the-power-of precision to obtain the returned integer.
+ * The usable range of digits is -INT64_MAX .. INT64_MAX -- note, not INT64_MIN! The value of INT64_MIN is excluded to
+ * reduce implementation complexity. See also utils_test.c.
+ * The advantage over using sscanf("%f") is guaranteed precision: float or double types may apply rounding in the
+ * conversion result. osmo_float_str_to_int() and osmo_int_to_float_str_buf() guarantee true results when converting
+ * back and forth between string and int.
+ * \param[out] val Returned integer value.
+ * \param[in] str String of a float, like '-12.345'.
+ * \param[in] precision Fixed-point precision, or * \returns 0 on success, negative on error.
+ */
+int osmo_float_str_to_int(int64_t *val, const char *str, unsigned int precision)
+{
+ const char *point;
+ char *endptr;
+ const char *p;
+ int64_t sign = 1;
+ int64_t integer = 0;
+ int64_t decimal = 0;
+ int64_t precision_factor;
+ int64_t integer_max;
+ int64_t decimal_max;
+ unsigned int i;
+
+ OSMO_ASSERT(val);
+ *val = 0;
+
+ if (!str)
+ return -EINVAL;
+ if (str[0] == '-') {
+ str = str + 1;
+ sign = -1;
+ } else if (str[0] == '+') {
+ str = str + 1;
+ }
+ if (!str[0])
+ return -EINVAL;
+
+ /* Validate entire string as purely digits and at most one decimal dot. If not doing this here in advance,
+ * parsing digits might stop early because of precision cut-off and miss validation of input data. */
+ point = NULL;
+ for (p = str; *p; p++) {
+ if (*p == '.') {
+ if (point)
+ return -EINVAL;
+ point = p;
+ } else if (!isdigit((unsigned char)*p))
+ return -EINVAL;
+ }
+
+ /* Parse integer part if there is one. If the string starts with a point, there's nothing to parse for the
+ * integer part. */
+ if (!point || point > str) {
+ errno = 0;
+ integer = strtoll(str, &endptr, 10);
+ if ((errno == ERANGE && (integer == LLONG_MAX || integer == LLONG_MIN))
+ || (errno != 0 && integer == 0))
+ return -ERANGE;
+
+ if ((point && endptr != point)
+ || (!point && *endptr))
+ return -EINVAL;
+ }
+
+ /* Parse the fractional part if there is any, and if the precision is nonzero (if we even care about fractional
+ * digits) */
+ if (precision && point && point[1] != '\0') {
+ /* limit the number of digits parsed to 'precision'.
+ * If 'precision' is larger than the 19 digits representable in int64_t, skip some, to pick up lower
+ * magnitude digits. */
+ unsigned int skip_digits = (precision < 20) ? 0 : precision - 20;
+ char decimal_str[precision + 1];
+ osmo_strlcpy(decimal_str, point+1, precision+1);
+
+ /* fill with zeros to make exactly 'precision' digits */
+ for (i = strlen(decimal_str); i < precision; i++)
+ decimal_str[i] = '0';
+ decimal_str[precision] = '\0';
+
+ for (i = 0; i < skip_digits; i++) {
+ /* When skipping digits because precision > nr-of-digits-in-int64_t, they must be zero;
+ * if there is a nonzero digit above the precision, it's -ERANGE. */
+ if (decimal_str[i] != '0')
+ return -ERANGE;
+ }
+ errno = 0;
+ decimal = strtoll(decimal_str + skip_digits, &endptr, 10);
+ if ((errno == ERANGE && (decimal == LLONG_MAX || decimal == LLONG_MIN))
+ || (errno != 0 && decimal == 0))
+ return -ERANGE;
+
+ if (*endptr)
+ return -EINVAL;
+ }
+
+ if (precision > 18) {
+ /* Special case of returning more digits than fit in int64_t range, e.g.
+ * osmo_float_str_to_int("0.0000000012345678901234567", precision=25) -> 12345678901234567. */
+ precision_factor = 0;
+ integer_max = 0;
+ decimal_max = INT64_MAX;
+ } else {
+ /* Do not surpass the resulting int64_t range. Depending on the amount of precision, the integer part
+ * and decimal part have specific ranges they must comply to. */
+ precision_factor = 1;
+ for (i = 0; i < precision; i++)
+ precision_factor *= 10;
+ integer_max = INT64_MAX / precision_factor;
+ if (integer == integer_max)
+ decimal_max = INT64_MAX % precision_factor;
+ else
+ decimal_max = INT64_MAX;
+ }
+
+ if (integer > integer_max)
+ return -ERANGE;
+ if (decimal > decimal_max)
+ return -ERANGE;
+
+ *val = sign * (integer * precision_factor + decimal);
+ return 0;
+}
+
+/*! Convert an integer to a floating point string using a decimal quotient (fixed-point precision).
+ * For example, with precision = 3, convert -1230 to "-1.23".
+ * The usable range of digits is -INT64_MAX .. INT64_MAX -- note, not INT64_MIN! The value of INT64_MIN is excluded to
+ * reduce implementation complexity. See also utils_test.c.
+ * The advantage over using printf("%.6g") is guaranteed precision: float or double types may apply rounding in the
+ * conversion result. osmo_float_str_to_int() and osmo_int_to_float_str_buf() guarantee true results when converting
+ * back and forth between string and int.
+ * The resulting string omits trailing zeros in the fractional part (like "%g" would) but never applies rounding.
+ * \param[out] buf Buffer to write string to.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] val Value to convert to float.
+ * \returns number of chars that would be written, like snprintf().
+ */
+int osmo_int_to_float_str_buf(char *buf, size_t buflen, int64_t val, unsigned int precision)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ unsigned int i;
+ unsigned int w;
+ int64_t precision_factor;
+ if (val < 0) {
+ OSMO_STRBUF_PRINTF(sb, "-");
+ if (val == INT64_MIN) {
+ OSMO_STRBUF_PRINTF(sb, "ERR");
+ return sb.chars_needed;
+ }
+ val = -val;
+ }
+
+ if (precision > 18) {
+ /* Special case of returning more digits than fit in int64_t range, e.g.
+ * osmo_int_to_float_str(12345678901234567, precision=25) -> "0.0000000012345678901234567". */
+ if (!val) {
+ OSMO_STRBUF_PRINTF(sb, "0");
+ return sb.chars_needed;
+ }
+ OSMO_STRBUF_PRINTF(sb, "0.");
+ for (i = 19; i < precision; i++)
+ OSMO_STRBUF_PRINTF(sb, "0");
+ precision = 19;
+ } else {
+ precision_factor = 1;
+ for (i = 0; i < precision; i++)
+ precision_factor *= 10;
+
+ OSMO_STRBUF_PRINTF(sb, "%" PRId64, val / precision_factor);
+ val %= precision_factor;
+ if (!val)
+ return sb.chars_needed;
+ OSMO_STRBUF_PRINTF(sb, ".");
+ }
+
+ /* print fractional part, skip trailing zeros */
+ w = precision;
+ while (!(val % 10)) {
+ val /= 10;
+ w--;
+ }
+ OSMO_STRBUF_PRINTF(sb, "%0*" PRId64, w, val);
+ return sb.chars_needed;
+}
+
+/*! Convert an integer with a factor of a million to a floating point string.
+ * For example, convert -1230000 to "-1.23".
+ * \param[in] ctx Talloc ctx to allocate string buffer from.
+ * \param[in] val Value to convert to float.
+ * \returns resulting string, dynamically allocated.
+ */
+char *osmo_int_to_float_str_c(void *ctx, int64_t val, unsigned int precision)
+{
+ OSMO_NAME_C_IMPL(ctx, 16, "ERROR", osmo_int_to_float_str_buf, val, precision)
+}
+
+/*! Convert a string of a number to int64_t, including all common strtoll() validity checks.
+ * It's not so trivial to call strtoll() and properly verify that the input string was indeed a valid number string.
+ * \param[out] result Buffer for the resulting integer number, or NULL if the caller is only interested in the
+ * validation result (returned rc).
+ * \param[in] str The string to convert.
+ * \param[in] base The integer base, i.e. 10 for decimal numbers or 16 for hexadecimal, as in strtoll().
+ * \param[in] min_val The smallest valid number expected in the string.
+ * \param[in] max_val The largest valid number expected in the string.
+ * \return 0 on success, -EOVERFLOW if the number in the string exceeds int64_t, -ENOTSUPP if the base is not supported,
+ * -ERANGE if the converted number exceeds the range [min_val..max_val] but is still within int64_t range, -E2BIG if
+ * surplus characters follow after the number, -EINVAL if the string does not contain a number. In case of -ERANGE and
+ * -E2BIG, the converted number is still accurately returned in result. In case of -EOVERFLOW, the returned value is
+ * clamped to INT64_MIN..INT64_MAX.
+ */
+int osmo_str_to_int64(int64_t *result, const char *str, int base, int64_t min_val, int64_t max_val)
+{
+ long long int val;
+ char *endptr;
+ if (result)
+ *result = 0;
+ if (!str || !*str)
+ return -EINVAL;
+ errno = 0;
+ val = strtoll(str, &endptr, base);
+ /* In case the number string exceeds long long int range, strtoll() clamps the returned value to LLONG_MIN or
+ * LLONG_MAX. Make sure of the same here with respect to int64_t. */
+ if (val < INT64_MIN) {
+ if (result)
+ *result = INT64_MIN;
+ return -ERANGE;
+ }
+ if (val > INT64_MAX) {
+ if (result)
+ *result = INT64_MAX;
+ return -ERANGE;
+ }
+ if (result)
+ *result = (int64_t)val;
+ switch (errno) {
+ case 0:
+ break;
+ case ERANGE:
+ return -EOVERFLOW;
+ default:
+ case EINVAL:
+ return -ENOTSUP;
+ }
+ if (!endptr || *endptr) {
+ /* No chars were converted */
+ if (endptr == str)
+ return -EINVAL;
+ /* Or there are surplus chars after the converted number */
+ return -E2BIG;
+ }
+ if (val < min_val || val > max_val)
+ return -ERANGE;
+ return 0;
+}
+
+/*! Convert a string of a number to int, including all common strtoll() validity checks.
+ * Same as osmo_str_to_int64() but using the plain int data type.
+ * \param[out] result Buffer for the resulting integer number, or NULL if the caller is only interested in the
+ * validation result (returned rc).
+ * \param[in] str The string to convert.
+ * \param[in] base The integer base, i.e. 10 for decimal numbers or 16 for hexadecimal, as in strtoll().
+ * \param[in] min_val The smallest valid number expected in the string.
+ * \param[in] max_val The largest valid number expected in the string.
+ * \return 0 on success, -EOVERFLOW if the number in the string exceeds int range, -ENOTSUPP if the base is not supported,
+ * -ERANGE if the converted number exceeds the range [min_val..max_val] but is still within int range, -E2BIG if
+ * surplus characters follow after the number, -EINVAL if the string does not contain a number. In case of -ERANGE and
+ * -E2BIG, the converted number is still accurately returned in result. In case of -EOVERFLOW, the returned value is
+ * clamped to INT_MIN..INT_MAX.
+ */
+int osmo_str_to_int(int *result, const char *str, int base, int min_val, int max_val)
+{
+ int64_t val;
+ int rc = osmo_str_to_int64(&val, str, base, min_val, max_val);
+ /* In case the number string exceeds long long int range, strtoll() clamps the returned value to LLONG_MIN or
+ * LLONG_MAX. Make sure of the same here with respect to int. */
+ if (val < INT_MIN) {
+ if (result)
+ *result = INT_MIN;
+ return -EOVERFLOW;
+ }
+ if (val > INT_MAX) {
+ if (result)
+ *result = INT_MAX;
+ return -EOVERFLOW;
+ }
+ if (result)
+ *result = (int)val;
+ return rc;
+}
+
+/*! Replace a string using talloc and release its prior content (if any).
+ * This is a format string capable equivalent of osmo_talloc_replace_string().
+ * \param[in] ctx Talloc context to use for allocation.
+ * \param[out] dst Pointer to string, will be updated with ptr to new string.
+ * \param[in] fmt Format string that will be copied to newly allocated string. */
+void osmo_talloc_replace_string_fmt(void *ctx, char **dst, const char *fmt, ...)
+{
+ char *name = NULL;
+
+ if (fmt != NULL) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ name = talloc_vasprintf(ctx, fmt, ap);
+ va_end(ap);
+ }
+
+ talloc_free(*dst);
+ *dst = name;
+}
+
/*! @} */
diff --git a/src/write_queue.c b/src/core/write_queue.c
index 3399b0f1..8fb73a67 100644
--- a/src/write_queue.c
+++ b/src/core/write_queue.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
@@ -64,16 +60,19 @@ int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what)
fd->when &= ~OSMO_FD_WRITE;
+ msg = msgb_dequeue_count(&queue->msg_queue, &queue->current_length);
/* the queue might have been emptied */
- if (!llist_empty(&queue->msg_queue)) {
- --queue->current_length;
-
- msg = msgb_dequeue(&queue->msg_queue);
+ if (msg) {
rc = queue->write_cb(fd, msg);
- msgb_free(msg);
-
- if (rc == -EBADF)
+ if (rc == -EBADF) {
+ msgb_free(msg);
goto err_badfd;
+ } else if (rc == -EAGAIN) {
+ /* re-enqueue the msgb to the head of the queue */
+ llist_add(&msg->list, &queue->msg_queue);
+ queue->current_length++;
+ } else
+ msgb_free(msg);
if (!llist_empty(&queue->msg_queue))
fd->when |= OSMO_FD_WRITE;
@@ -100,10 +99,26 @@ void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length)
INIT_LLIST_HEAD(&queue->msg_queue);
}
+/*! Enqueue a new \ref msgb into a write queue (without logging full queue events)
+ * \param[in] queue Write queue to be used
+ * \param[in] data to-be-enqueued message buffer
+ * \returns 0 on success; negative on error (MESSAGE NOT FREED IN CASE OF ERROR).
+ */
+int osmo_wqueue_enqueue_quiet(struct osmo_wqueue *queue, struct msgb *data)
+{
+ if (queue->current_length >= queue->max_length)
+ return -ENOSPC;
+
+ msgb_enqueue_count(&queue->msg_queue, data, &queue->current_length);
+ queue->bfd.when |= OSMO_FD_WRITE;
+
+ return 0;
+}
+
/*! Enqueue a new \ref msgb into a write queue
* \param[in] queue Write queue to be used
* \param[in] data to-be-enqueued message buffer
- * \returns 0 on success; negative on error
+ * \returns 0 on success; negative on error (MESSAGE NOT FREED IN CASE OF ERROR).
*/
int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data)
{
@@ -113,11 +128,7 @@ int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data)
return -ENOSPC;
}
- ++queue->current_length;
- msgb_enqueue(&queue->msg_queue, data);
- queue->bfd.when |= OSMO_FD_WRITE;
-
- return 0;
+ return osmo_wqueue_enqueue_quiet(queue, data);
}
/*! Clear a \ref osmo_wqueue
@@ -136,4 +147,24 @@ void osmo_wqueue_clear(struct osmo_wqueue *queue)
queue->bfd.when &= ~OSMO_FD_WRITE;
}
+/*! Update write queue length & drop excess messages.
+ * \param[in] queue linked list header of message queue
+ * \param[in] len new max. wqueue length
+ * \returns Number of messages dropped.
+ *
+ * Messages beyond the new maximum message queue size will be dropped.
+ */
+size_t osmo_wqueue_set_maxlen(struct osmo_wqueue *queue, unsigned int len)
+{
+ size_t dropped_msgs = 0;
+ struct msgb *msg;
+ queue->max_length = len;
+ while (queue->current_length > queue->max_length) {
+ msg = msgb_dequeue_count(&queue->msg_queue, &queue->current_length);
+ msgb_free(msg);
+ dropped_msgs++;
+ }
+ return dropped_msgs;
+}
+
/*! @} */
diff --git a/src/ctrl/Makefile.am b/src/ctrl/Makefile.am
index ca642869..f9e34333 100644
--- a/src/ctrl/Makefile.am
+++ b/src/ctrl/Makefile.am
@@ -1,9 +1,10 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
-LIBVERSION=4:0:4
+LIBVERSION=8:1:8
-AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
if ENABLE_CTRL
lib_LTLIBRARIES = libosmoctrl.la
@@ -12,7 +13,7 @@ libosmoctrl_la_SOURCES = control_cmd.c control_if.c fsm_ctrl_commands.c
libosmoctrl_la_LDFLAGS = $(LTLDFLAGS_OSMOCTRL) -version-info $(LIBVERSION) -no-undefined
libosmoctrl_la_LIBADD = $(TALLOC_LIBS) \
- $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/core/libosmocore.la \
$(top_builddir)/src/gsm/libosmogsm.la \
$(top_builddir)/src/vty/libosmovty.la
@@ -21,5 +22,6 @@ libosmoctrl_la_SOURCES += control_vty.c
endif
EXTRA_DIST = libosmoctrl.map
+EXTRA_libosmoctrl_la_DEPENDENCIES = libosmoctrl.map
endif
diff --git a/src/ctrl/control_cmd.c b/src/ctrl/control_cmd.c
index 33496bd8..db205510 100644
--- a/src/ctrl/control_cmd.c
+++ b/src/ctrl/control_cmd.c
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License 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>
@@ -35,6 +31,7 @@
#include <unistd.h>
#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/ctrl/control_if.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
@@ -207,13 +204,23 @@ failure:
}
/*! Install a given command definition at a given CTRL node.
- * \param[in] node CTRL node at whihc \a cmd is to be installed
+ * \param[in] node CTRL node at which \a cmd is to be installed
* \param[in] cmd command definition to be installed
* \returns 0 on success; negative on error */
int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd)
{
vector cmds_vec;
+ /* If this assert triggers, it means the program forgot to initialize
+ * the CTRL interface first by calling ctrl_handle_alloc(2)() directly
+ * or indirectly through ctrl_interface_setup_dynip(2)()
+ */
+ if (!ctrl_node_vec) {
+ LOGP(DLCTRL, LOGL_ERROR,
+ "ctrl_handle must be initialized prior to installing cmds.\n");
+ return -ENODEV;
+ }
+
cmds_vec = vector_lookup_ensure(ctrl_node_vec, node);
if (!cmds_vec) {
@@ -516,92 +523,61 @@ err:
* \returns callee-allocated message buffer containing the encoded \a cmd; NULL on error */
struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
{
- struct msgb *msg;
+ struct msgb *msg = NULL;
+ char *strbuf;
+ size_t len;
const char *type;
- char *tmp;
if (!cmd->id)
return NULL;
- msg = msgb_alloc_headroom(4096, 128, "ctrl command make");
- if (!msg)
- return NULL;
-
type = get_value_string(ctrl_type_vals, cmd->type);
switch (cmd->type) {
case CTRL_TYPE_GET:
if (!cmd->variable)
- goto err;
-
- tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable);
- if (!tmp) {
- LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
- goto err;
- }
-
- msg->l2h = msgb_put(msg, strlen(tmp));
- memcpy(msg->l2h, tmp, strlen(tmp));
- talloc_free(tmp);
+ return NULL;
+ strbuf = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable);
break;
case CTRL_TYPE_SET:
if (!cmd->variable || !cmd->value)
- goto err;
-
- tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
- cmd->value);
- if (!tmp) {
- LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
- goto err;
- }
-
- msg->l2h = msgb_put(msg, strlen(tmp));
- memcpy(msg->l2h, tmp, strlen(tmp));
- talloc_free(tmp);
+ return NULL;
+ strbuf = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id,
+ cmd->variable, cmd->value);
break;
case CTRL_TYPE_GET_REPLY:
case CTRL_TYPE_SET_REPLY:
case CTRL_TYPE_TRAP:
if (!cmd->variable || !cmd->reply)
- goto err;
-
- tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
- cmd->reply);
- if (!tmp) {
- LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
- goto err;
- }
-
- msg->l2h = msgb_put(msg, strlen(tmp));
- memcpy(msg->l2h, tmp, strlen(tmp));
- talloc_free(tmp);
+ return NULL;
+ strbuf = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id,
+ cmd->variable, cmd->reply);
break;
case CTRL_TYPE_ERROR:
if (!cmd->reply)
- goto err;
-
- tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id,
- cmd->reply);
- if (!tmp) {
- LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
- goto err;
- }
-
- msg->l2h = msgb_put(msg, strlen(tmp));
- memcpy(msg->l2h, tmp, strlen(tmp));
- talloc_free(tmp);
+ return NULL;
+ strbuf = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->reply);
break;
default:
LOGP(DLCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
- goto err;
- break;
+ return NULL;
}
- return msg;
+ if (!strbuf) {
+ LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
+ goto ret;
+ }
+ len = strlen(strbuf);
-err:
- msgb_free(msg);
- return NULL;
+ msg = msgb_alloc_headroom(len + 128, 128, "ctrl ERROR command make");
+ if (!msg)
+ goto ret;
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, strbuf, len);
+
+ret:
+ talloc_free(strbuf);
+ return msg;
}
/*! Build a deferred control command state and keep it the per-connection list of deferred commands.
@@ -672,7 +648,7 @@ int ctrl_cmd_def_send(struct ctrl_cmd_def *cd)
cmd->type = CTRL_TYPE_ERROR;
}
- rc = ctrl_cmd_send(&cmd->ccon->write_queue, cmd);
+ rc = ctrl_cmd_send2(cmd->ccon, cmd);
talloc_free(cmd);
llist_del(&cd->list);
diff --git a/src/ctrl/control_if.c b/src/ctrl/control_if.c
index ce2e3676..c265c3a9 100644
--- a/src/ctrl/control_if.c
+++ b/src/ctrl/control_if.c
@@ -20,10 +20,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
@@ -35,6 +31,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <limits.h>
#include <arpa/inet.h>
@@ -50,9 +47,11 @@
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/ctrl/control_if.h>
+#include <osmocom/ctrl/control_vty.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stat_item.h>
#include <osmocom/core/select.h>
#include <osmocom/core/counter.h>
#include <osmocom/core/talloc.h>
@@ -82,26 +81,22 @@ static LLIST_HEAD(ctrl_lookup_helpers);
* \returns 1 on success; 0 in case of error */
int ctrl_parse_get_num(vector vline, int i, long *num)
{
- char *token, *tmp;
+ char *token;
+ int64_t val;
if (i >= vector_active(vline))
return 0;
token = vector_slot(vline, i);
- errno = 0;
- if (token[0] == '\0')
- return 0;
-
- *num = strtol(token, &tmp, 10);
- if (tmp[0] != '\0' || errno != 0)
+ if (osmo_str_to_int64(&val, token, 10, LONG_MIN, LONG_MAX))
return 0;
-
+ *num = (long)val;
return 1;
}
/*! Send a CTRL command to all connections.
* \param[in] ctrl global control handle
- * \param[in] cmd command to send to all connections in \ctrl
+ * \param[in] cmd command to send to all connections in ctrl
* \returns number of times the command has been sent */
int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd)
{
@@ -111,18 +106,28 @@ int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd)
llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) {
if (ccon == cmd->ccon)
continue;
- if (ctrl_cmd_send(&ccon->write_queue, cmd))
+ if (ctrl_cmd_send2(ccon, cmd))
ret++;
}
return ret;
}
-/*! Encode a CTRL command and append it to the given write queue
+/*! Encode a CTRL command and append it to the given ctrl_connection
* \param[inout] queue write queue to which encoded \a cmd shall be appended
* \param[in] cmd decoded command representation
* \returns 0 in case of success; negative on error */
int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd)
{
+ struct ctrl_connection *ccon = container_of(queue, struct ctrl_connection, write_queue);
+ return ctrl_cmd_send2(ccon, cmd);
+}
+
+/*! Encode a CTRL command and append it to the given ctrl_connection
+ * \param[inout] queue write queue to which encoded \a cmd shall be appended
+ * \param[in] cmd decoded command representation
+ * \returns 0 in case of success; negative on error */
+int ctrl_cmd_send2(struct ctrl_connection *ccon, struct ctrl_cmd *cmd)
+{
int ret;
struct msgb *msg;
@@ -135,7 +140,7 @@ int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd)
ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
ipa_prepend_header(msg, IPAC_PROTO_OSMO);
- ret = osmo_wqueue_enqueue(queue, msg);
+ ret = osmo_wqueue_enqueue(&ccon->write_queue, msg);
if (ret != 0) {
LOGP(DLCTRL, LOGL_ERROR, "Failed to enqueue the command.\n");
msgb_free(msg);
@@ -221,6 +226,10 @@ int ctrl_cmd_handle(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd,
if (cmd->type == CTRL_TYPE_SET_REPLY ||
cmd->type == CTRL_TYPE_GET_REPLY) {
+ if (ctrl->reply_cb) {
+ ctrl->reply_cb(ctrl, cmd, data);
+ return CTRL_CMD_HANDLED;
+ }
if (strncmp(cmd->reply, "OK", 2) == 0) {
LOGP(DLCTRL, LOGL_DEBUG, "%s <%s> for %s is OK\n",
get_value_string(ctrl_type_vals, cmd->type),
@@ -465,7 +474,7 @@ int ctrl_handle_msg(struct ctrl_handle *ctrl, struct ctrl_connection *ccon, stru
send_reply:
/* There is a reply or error that should be reported back to the sender. */
- ctrl_cmd_send(&ccon->write_queue, cmd);
+ ctrl_cmd_send2(ccon, cmd);
just_free:
talloc_free(cmd);
return 0;
@@ -485,8 +494,14 @@ static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg)
control_close_conn(ccon);
return -EBADF;
}
- if (rc != msg->len)
+ if (rc < 0) {
LOGP(DLCTRL, LOGL_ERROR, "Failed to write message to the CTRL connection.\n");
+ return 0;
+ }
+ if (rc < msg->len) {
+ msgb_pull(msg, rc);
+ return -EAGAIN;
+ }
return 0;
}
@@ -510,6 +525,7 @@ struct ctrl_connection *osmo_ctrl_conn_alloc(void *ctx, void *data)
INIT_LLIST_HEAD(&ccon->def_cmds);
ccon->write_queue.bfd.data = data;
+ ccon->write_queue.bfd.fd = -1;
ccon->write_queue.write_cb = control_write_cb;
ccon->write_queue.read_cb = handle_control_read;
@@ -584,12 +600,12 @@ static uint64_t get_rate_ctr_value(const struct rate_ctr *ctr, int intv, const c
}
}
-static int get_rate_ctr_group_idx(const struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd)
+static int get_rate_ctr_group_idx(struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd)
{
unsigned int i;
for (i = 0; i < ctrg->desc->num_ctr; i++) {
ctrl_cmd_reply_printf(cmd, "%s %"PRIu64";", ctrg->desc->ctr_desc[i].name,
- get_rate_ctr_value(&ctrg->ctr[i], intv, ctrg->desc->group_name_prefix));
+ get_rate_ctr_value(rate_ctr_group_get_ctr(ctrg, i), intv, ctrg->desc->group_name_prefix));
if (!cmd->reply) {
cmd->reply = "OOM";
return CTRL_CMD_ERROR;
@@ -718,6 +734,100 @@ static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data)
return 0;
}
+/* Expose all stat_item groups on CTRL, as read-only variables of the form:
+ * stat_item.(last|...).group_name.N.item_name
+ * stat_item.(last|...).group_name.by_name.group_idx_name.item_name
+ */
+CTRL_CMD_DEFINE_RO(stat_item, "stat_item *");
+static int get_stat_item(struct ctrl_cmd *cmd, void *data)
+{
+ char *dup;
+ char *saveptr;
+ char *value_type;
+ char *group_name;
+ char *group_idx_str;
+ char *item_name;
+ char *tmp;
+ int32_t val;
+ struct osmo_stat_item_group *statg;
+ const struct osmo_stat_item *stat_item;
+
+ /* cmd will be freed in control_if.c after handling here, so no need to free the dup string. */
+ dup = talloc_strdup(cmd, cmd->variable);
+ if (!dup) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ /* Split off the first "stat_item." part */
+ tmp = strtok_r(dup, ".", &saveptr);
+ if (!tmp || strcmp(tmp, "stat_item") != 0)
+ goto format_error;
+
+ /* Split off the "last." part (validated further below) */
+ value_type = strtok_r(NULL, ".", &saveptr);
+ if (!value_type)
+ goto format_error;
+
+ /* Split off the "group_name." part */
+ group_name = strtok_r(NULL, ".", &saveptr);
+ if (!group_name)
+ goto format_error;
+
+ /* Split off the "N." part */
+ group_idx_str = strtok_r(NULL, ".", &saveptr);
+ if (!group_idx_str)
+ goto format_error;
+ if (strcmp(group_idx_str, "by_name") == 0) {
+ /* The index is not given by "N" but by "by_name.foo". Get the "foo" idx-name */
+ group_idx_str = strtok_r(NULL, ".", &saveptr);
+ statg = osmo_stat_item_get_group_by_name_idxname(group_name, group_idx_str);
+ } else {
+ int idx;
+ if (osmo_str_to_int(&idx, group_idx_str, 10, 0, INT_MAX))
+ goto format_error;
+ statg = osmo_stat_item_get_group_by_name_idx(group_name, idx);
+ }
+ if (!statg) {
+ cmd->reply = "Stat group with given name and index not found";
+ return CTRL_CMD_ERROR;
+ }
+
+ /* Split off the "item_name" part */
+ item_name = strtok_r(NULL, ".", &saveptr);
+ if (!item_name)
+ goto format_error;
+ stat_item = osmo_stat_item_get_by_name(statg, item_name);
+ if (!stat_item) {
+ cmd->reply = "No such stat item found";
+ return CTRL_CMD_ERROR;
+ }
+
+ tmp = strtok_r(NULL, "", &saveptr);
+ if (tmp) {
+ cmd->reply = "Garbage after stat item name";
+ return CTRL_CMD_ERROR;
+ }
+
+ if (!strcmp(value_type, "last"))
+ val = osmo_stat_item_get_last(stat_item);
+ else
+ goto format_error;
+
+ cmd->reply = talloc_asprintf(cmd, "%"PRIu32, val);
+ if (!cmd->reply) {
+ cmd->reply = "OOM";
+ return CTRL_CMD_ERROR;
+ }
+
+ return CTRL_CMD_REPLY;
+
+format_error:
+ cmd->reply = "Stat item must be of form stat_item.type.group_name.N.item_name,"
+ " e.g. 'stat_item.last.bsc.0.msc_num:connected'";
+ return CTRL_CMD_ERROR;
+}
+
/* counter */
CTRL_CMD_DEFINE(counter, "counter *");
static int get_counter(struct ctrl_cmd *cmd, void *data)
@@ -779,7 +889,7 @@ static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data)
struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port,
ctrl_cmd_lookup lookup)
{
- return ctrl_interface_setup_dynip(data, "127.0.0.1", port, lookup);
+ return ctrl_interface_setup2(data, port, lookup, 0);
}
static int ctrl_initialized = 0;
@@ -805,6 +915,9 @@ static int ctrl_init(unsigned int node_count)
ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr);
if (ret)
goto err_vec;
+ ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_stat_item);
+ if (ret)
+ goto err_vec;
ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter);
if (ret)
goto err_vec;
@@ -928,12 +1041,24 @@ struct ctrl_handle *ctrl_interface_setup_dynip(void *data,
return ctrl_interface_setup_dynip2(data, bind_addr, port, lookup, 0);
}
+/*! Initializes CTRL interface using the configured bind addr/port.
+ * \param[in] data Pointer which will be made available to each set_..() get_..() verify_..() control command function
+ * \param[in] default_port TCP port number to bind to if not explicitly configured
+ * \param[in] lookup Lookup function pointer, can be NULL
+ * \param[in] node_count Number of CTRL nodes to allocate, 0 for default.
+ */
+struct ctrl_handle *ctrl_interface_setup2(void *data, uint16_t default_port, ctrl_cmd_lookup lookup,
+ unsigned int node_count)
+{
+ return ctrl_interface_setup_dynip2(data, ctrl_vty_get_bind_addr(), ctrl_vty_get_bind_port(default_port), lookup,
+ node_count);
+}
/*! Install a lookup helper function for control nodes
* This function is used by e.g. library code to install lookup helpers
* for additional nodes in the control interface.
* \param[in] lookup The lookup helper function
- * \retuns - on success; negative on error.
+ * \returns - on success; negative on error.
*/
int ctrl_lookup_register(ctrl_cmd_lookup lookup)
{
diff --git a/src/ctrl/control_vty.c b/src/ctrl/control_vty.c
index ef988892..a7ebddc2 100644
--- a/src/ctrl/control_vty.c
+++ b/src/ctrl/control_vty.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
@@ -30,16 +26,20 @@
static void *ctrl_vty_ctx = NULL;
static const char *ctrl_vty_bind_addr = NULL;
+/* Port the CTRL should bind to: -1 means not configured */
+static int ctrl_bind_port = -1;
DEFUN(cfg_ctrl_bind_addr,
cfg_ctrl_bind_addr_cmd,
- "bind A.B.C.D",
+ "bind A.B.C.D [<0-65535>]",
"Set bind address to listen for Control connections\n"
- "Local IP address (default 127.0.0.1)\n")
+ "Local IP address (default 127.0.0.1)\n"
+ "Local TCP port number\n")
{
talloc_free((char*)ctrl_vty_bind_addr);
ctrl_vty_bind_addr = NULL;
ctrl_vty_bind_addr = talloc_strdup(ctrl_vty_ctx, argv[0]);
+ ctrl_bind_port = argc > 1 ? atoi(argv[1]) : -1;
return CMD_SUCCESS;
}
@@ -50,6 +50,11 @@ const char *ctrl_vty_get_bind_addr(void)
return ctrl_vty_bind_addr;
}
+uint16_t ctrl_vty_get_bind_port(uint16_t default_port)
+{
+ return ctrl_bind_port >= 0 ? ctrl_bind_port : default_port;
+}
+
static struct cmd_node ctrl_node = {
L_CTRL_NODE,
"%s(config-ctrl)# ",
@@ -82,10 +87,10 @@ static int config_write_ctrl(struct vty *vty)
int ctrl_vty_init(void *ctx)
{
ctrl_vty_ctx = ctx;
- install_element(CONFIG_NODE, &cfg_ctrl_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_ctrl_cmd);
install_node(&ctrl_node, config_write_ctrl);
- install_element(L_CTRL_NODE, &cfg_ctrl_bind_addr_cmd);
+ install_lib_element(L_CTRL_NODE, &cfg_ctrl_bind_addr_cmd);
return 0;
}
diff --git a/src/ctrl/libosmoctrl.map b/src/ctrl/libosmoctrl.map
index f995467b..3418e620 100644
--- a/src/ctrl/libosmoctrl.map
+++ b/src/ctrl/libosmoctrl.map
@@ -15,6 +15,7 @@ ctrl_cmd_parse;
ctrl_cmd_parse2;
ctrl_cmd_parse3;
ctrl_cmd_send;
+ctrl_cmd_send2;
ctrl_cmd_send_to_all;
ctrl_cmd_send_trap;
ctrl_cmd_trap;
@@ -22,12 +23,14 @@ ctrl_handle_alloc; /* could be removed? */
ctrl_handle_alloc2; /* could be removed? */
ctrl_handle_msg; /* only used in unit test */
ctrl_interface_setup;
+ctrl_interface_setup2;
ctrl_interface_setup_dynip;
ctrl_interface_setup_dynip2;
ctrl_lookup_register;
ctrl_parse_get_num;
ctrl_type_vals;
ctrl_vty_get_bind_addr;
+ctrl_vty_get_bind_port;
ctrl_vty_init;
osmo_ctrl_conn_alloc;
diff --git a/src/gb/Makefile.am b/src/gb/Makefile.am
index e14c11c1..2f7ad5eb 100644
--- a/src/gb/Makefile.am
+++ b/src/gb/Makefile.am
@@ -1,26 +1,43 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
-LIBVERSION=9:1:0
+LIBVERSION=16:0:2
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing $(TALLOC_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall -fno-strict-aliasing \
+ $(TALLOC_CFLAGS) \
+ $(NULL)
# FIXME: this should eventually go into a milenage/Makefile.am
-noinst_HEADERS = common_vty.h gb_internal.h
+noinst_HEADERS = common_vty.h gb_internal.h gprs_bssgp_internal.h gprs_ns2_internal.h
if ENABLE_GB
lib_LTLIBRARIES = libosmogb.la
-libosmogb_la_LDFLAGS = $(LTLDFLAGS_OSMOGB) -version-info $(LIBVERSION)
+libosmogb_la_LDFLAGS = \
+ $(LTLDFLAGS_OSMOGB) \
+ -version-info $(LIBVERSION) \
+ -no-undefined \
+ $(NULL)
libosmogb_la_LIBADD = $(TALLOC_LIBS) \
- $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/core/libosmocore.la \
$(top_builddir)/src/vty/libosmovty.la \
$(top_builddir)/src/gsm/libosmogsm.la
libosmogb_la_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c gprs_ns_sns.c \
- gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c \
- gprs_bssgp_bss.c common_vty.c
+ gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c gprs_bssgp_rim.c \
+ gprs_bssgp_bss.c \
+ gprs_ns2.c gprs_ns2_udp.c gprs_ns2_frgre.c gprs_ns2_fr.c gprs_ns2_vc_fsm.c gprs_ns2_sns.c \
+ gprs_ns2_message.c gprs_ns2_vty.c \
+ gprs_bssgp2.c bssgp_bvc_fsm.c \
+ common_vty.c frame_relay.c
+
+# convenience library for testing with access to all non-static symbols
+noinst_LTLIBRARIES = libosmogb-test.la
+libosmogb_test_la_LIBADD = $(libosmogb_la_LIBADD)
+libosmogb_test_la_SOURCES= $(libosmogb_la_SOURCES)
+
endif
EXTRA_DIST = libosmogb.map
+EXTRA_libosmogb_la_DEPENDENCIES = libosmogb.map
diff --git a/src/gb/bssgp_bvc_fsm.c b/src/gb/bssgp_bvc_fsm.c
new file mode 100644
index 00000000..3a36c7dc
--- /dev/null
+++ b/src/gb/bssgp_bvc_fsm.c
@@ -0,0 +1,857 @@
+/* BSSGP per-BVC Finite State Machine */
+
+/* (C) 2020 Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/tdef.h>
+
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/tlv.h>
+
+#include <osmocom/gprs/gprs_msgb.h>
+#include <osmocom/gprs/gprs_bssgp.h>
+#include <osmocom/gprs/gprs_bssgp2.h>
+#include <osmocom/gprs/bssgp_bvc_fsm.h>
+
+#include "common_vty.h"
+
+#define S(x) (1 << (x))
+
+/* TODO: Those are not made cofnigurable via a VTY yet */
+struct osmo_tdef bssgp_bvc_fsm_tdefs[] = {
+ {
+ .T = 1,
+ .default_val = 5,
+ .min_val = 1,
+ .max_val = 30,
+ .unit = OSMO_TDEF_S,
+ .desc = "Guards the BSSGP BVC (un)blocking procedure",
+ }, {
+ .T = 2,
+ .default_val = 10,
+ .min_val = 1,
+ .max_val = 120,
+ .unit = OSMO_TDEF_S,
+ .desc = "Guards the BSSGP BVC reset procedure",
+ }, {
+ .T = 3,
+ .default_val = 500,
+ .min_val = 100,
+ .max_val = 10000,
+ .unit = OSMO_TDEF_MS,
+ .desc = "Guards the BSSGP SUSPEND procedure",
+ }, {
+ .T = 4,
+ .default_val = 500,
+ .min_val = 100,
+ .max_val = 10000,
+ .unit = OSMO_TDEF_MS,
+ .desc = "Guards the BSSGP RESUME procedure",
+ }, {
+ .T = 5,
+ .default_val = 15,
+ .min_val = 1,
+ .max_val = 30,
+ .unit = OSMO_TDEF_S,
+ .desc = "Guards the BSSGP Radio Access Capability Update procedure",
+ },
+ {}
+};
+
+#define T1 1
+#define T2 2
+
+/* We cannot use osmo_tdef_fsm_* as it makes hard-coded assumptions that
+ * each new/target state will always use the same timer and timeout - or
+ * a timeout at all */
+#define T1_SECS osmo_tdef_get(bssgp_bvc_fsm_tdefs, 1, OSMO_TDEF_S, 5)
+#define T2_SECS osmo_tdef_get(bssgp_bvc_fsm_tdefs, 2, OSMO_TDEF_S, 10)
+
+/* forward declaration */
+static struct osmo_fsm bssgp_bvc_fsm;
+
+static const struct value_string ptp_bvc_event_names[] = {
+ { BSSGP_BVCFSM_E_RX_BLOCK, "RX-BVC-BLOCK" },
+ { BSSGP_BVCFSM_E_RX_BLOCK_ACK, "RX-BVC-BLOCK-ACK" },
+ { BSSGP_BVCFSM_E_RX_UNBLOCK, "RX-BVC-UNBLOCK" },
+ { BSSGP_BVCFSM_E_RX_UNBLOCK_ACK, "RX-BVC-UNBLOCK-ACK" },
+ { BSSGP_BVCFSM_E_RX_RESET, "RX-BVC-RESET" },
+ { BSSGP_BVCFSM_E_RX_RESET_ACK, "RX-BVC-RESET-ACK" },
+ { BSSGP_BVCFSM_E_RX_FC_BVC, "RX-FLOW-CONTROL-BVC" },
+ { BSSGP_BVCFSM_E_RX_FC_BVC_ACK, "RX-FLOW-CONTROL-BVC-ACK" },
+ { BSSGP_BVCFSM_E_REQ_BLOCK, "REQ-BLOCK" },
+ { BSSGP_BVCFSM_E_REQ_UNBLOCK, "REQ-UNBLOCK" },
+ { BSSGP_BVCFSM_E_REQ_RESET, "REQ-RESET" },
+ { BSSGP_BVCFSM_E_REQ_FC_BVC, "REQ-FLOW-CONTROL-BVC" },
+ { 0, NULL }
+};
+
+struct bvc_fsm_priv {
+ /* NS-instance; defining the scope for NSEI below */
+ struct gprs_ns2_inst *nsi;
+
+ /* NSEI of the underlying NS Entity */
+ uint16_t nsei;
+ /* Maximum size of the BSSGP PDU */
+ uint16_t max_pdu_len;
+
+ /* BVCI of this BVC */
+ uint16_t bvci;
+
+ /* are we the SGSN (true) or the BSS (false) */
+ bool role_sgsn;
+
+ /* BSS side: are we locally marked blocked? */
+ bool locally_blocked;
+ uint8_t block_cause;
+
+ /* cause value of the last outbound BVC-RESET (for re-transmissions) */
+ uint8_t last_reset_cause;
+
+ struct {
+ /* Bit 0..7: Features; Bit 8..15: Extended Features */
+ uint32_t advertised;
+ uint32_t received;
+ uint32_t negotiated;
+ /* only used if BSSGP_XFEAT_GBIT is negotiated */
+ enum bssgp_fc_granularity fc_granularity;
+ } features;
+
+ /* Cell Identification used by BSS when
+ * transmitting BVC-RESET / BVC-RESET-ACK, or those received
+ * from BSS in SGSN role */
+ struct gprs_ra_id ra_id;
+ uint16_t cell_id;
+
+ /* call-backs provided by the user */
+ const struct bssgp_bvc_fsm_ops *ops;
+ /* private data pointer passed to each call-back invocation */
+ void *ops_priv;
+};
+
+static int fi_tx_ptp(struct osmo_fsm_inst *fi, struct msgb *msg)
+{
+ struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+
+ LOGPFSM(fi, "Tx BSSGP %s\n", osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type));
+
+ return bssgp2_nsi_tx_ptp(bfp->nsi, bfp->nsei, bfp->bvci, msg, 0);
+}
+
+static int fi_tx_sig(struct osmo_fsm_inst *fi, struct msgb *msg)
+{
+ struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+
+ LOGPFSM(fi, "Tx BSSGP %s\n", osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type));
+
+ return bssgp2_nsi_tx_sig(bfp->nsi, bfp->nsei, msg, 0);
+}
+
+/* helper function to transmit BVC-RESET with right combination of conditional/optional IEs */
+static void _tx_bvc_reset(struct osmo_fsm_inst *fi, uint8_t cause)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+ const uint8_t *features = NULL;
+ const uint8_t *features_ext = NULL;
+ uint8_t _features[2] = {
+ (bfp->features.advertised >> 0) & 0xff,
+ (bfp->features.advertised >> 8) & 0xff,
+ };
+ struct msgb *tx;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+
+ /* transmit BVC-RESET to peer; RA-ID only present for PTP from BSS */
+ if (bfp->bvci == 0) {
+ features = &_features[0];
+ features_ext = &_features[1];
+ }
+ tx = bssgp2_enc_bvc_reset(bfp->bvci, cause,
+ bfp->bvci && !bfp->role_sgsn ? &bfp->ra_id : NULL,
+ bfp->cell_id, features, features_ext);
+ fi_tx_sig(fi, tx);
+}
+
+/* helper function to transmit BVC-RESET-ACK with right combination of conditional/optional IEs */
+static void _tx_bvc_reset_ack(struct osmo_fsm_inst *fi)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+ const uint8_t *features = NULL;
+ const uint8_t *features_ext = NULL;
+ uint8_t _features[2] = {
+ (bfp->features.advertised >> 0) & 0xff,
+ (bfp->features.advertised >> 8) & 0xff,
+ };
+ struct msgb *tx;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+
+ /* transmit BVC-RESET-ACK to peer; RA-ID only present for PTP from BSS -> SGSN */
+ if (bfp->bvci == 0) {
+ features = &_features[0];
+ features_ext = &_features[1];
+ }
+ tx = bssgp2_enc_bvc_reset_ack(bfp->bvci, bfp->bvci && !bfp->role_sgsn ? &bfp->ra_id : NULL,
+ bfp->cell_id, features, features_ext);
+ fi_tx_sig(fi, tx);
+}
+
+/* helper function to transmit BVC-STATUS with right combination of conditional/optional IEs */
+static void _tx_status(struct osmo_fsm_inst *fi, enum gprs_bssgp_cause cause, const struct msgb *rx)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+ struct msgb *tx;
+ uint16_t *bvci = NULL;
+
+ /* GSM 08.18, 10.4.14.1: The BVCI must be included if (and only if) the
+ * cause is either "BVCI blocked" or "BVCI unknown" */
+ if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED)
+ bvci = &bfp->bvci;
+
+ tx = bssgp2_enc_status(cause, bvci, rx, bfp->max_pdu_len);
+
+ if (msgb_bvci(rx) == 0)
+ fi_tx_sig(fi, tx);
+ else
+ fi_tx_ptp(fi, tx);
+}
+
+/* Update the features by bit-wise AND of advertised + received features */
+static void update_negotiated_features(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+
+ bfp->features.received = 0;
+
+ if (TLVP_PRES_LEN(tp, BSSGP_IE_FEATURE_BITMAP, 1))
+ bfp->features.received |= *TLVP_VAL(tp, BSSGP_IE_FEATURE_BITMAP);
+
+ if (TLVP_PRES_LEN(tp, BSSGP_IE_EXT_FEATURE_BITMAP, 1))
+ bfp->features.received |= (*TLVP_VAL(tp, BSSGP_IE_EXT_FEATURE_BITMAP) << 8);
+
+ bfp->features.negotiated = bfp->features.advertised & bfp->features.received;
+
+ LOGPFSML(fi, LOGL_NOTICE, "Updating features: Advertised 0x%04x, Received 0x%04x, Negotiated 0x%04x\n",
+ bfp->features.advertised, bfp->features.received, bfp->features.negotiated);
+}
+
+/* "tail" of each onenter() handler: Calling the state change notification call-back */
+static void _onenter_tail(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ if (prev_state == fi->state)
+ return;
+
+ if (bfp->ops && bfp->ops->state_chg_notification)
+ bfp->ops->state_chg_notification(bfp->nsei, bfp->bvci, prev_state, fi->state, bfp->ops_priv);
+}
+
+static void bssgp_bvc_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ /* we don't really expect anything in this state; all handled via allstate */
+ OSMO_ASSERT(0);
+}
+
+static void bssgp_bvc_fsm_blocked_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+ /* signaling BVC can never be blocked */
+ OSMO_ASSERT(bfp->bvci != 0);
+ _onenter_tail(fi, prev_state);
+}
+
+static void bssgp_bvc_fsm_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+ struct msgb *rx = NULL, *tx;
+ const struct tlv_parsed *tp = NULL;
+ uint8_t cause;
+
+ switch (event) {
+ case BSSGP_BVCFSM_E_RX_BLOCK_ACK:
+ rx = data;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ /* If a BVC-BLOCK-ACK PDU is received by a BSS for the signalling BVC, the PDU is ignored. */
+ if (bfp->bvci == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx BVC-BLOCK-ACK on BVCI=0 is illegal\n");
+ if (!bfp->role_sgsn)
+ break;
+ _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
+ break;
+ }
+ /* stop T1 timer */
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, 0, 0);
+ break;
+ case BSSGP_BVCFSM_E_RX_BLOCK:
+ rx = data;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ cause = *TLVP_VAL(tp, BSSGP_IE_CAUSE);
+ LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-BLOCK (cause=%s)\n", bssgp_cause_str(cause));
+ /* If a BVC-BLOCK PDU is received by an SGSN for a blocked BVC, a BVC-BLOCK-ACK
+ * PDU shall be returned. */
+ if (bfp->role_sgsn) {
+ /* If a BVC-BLOCK PDU is received by an SGSN for
+ * the signalling BVC, the PDU is ignored */
+ if (bfp->bvci == 0)
+ break;
+ tx = bssgp2_enc_bvc_block_ack(bfp->bvci);
+ fi_tx_sig(fi, tx);
+ }
+ break;
+ case BSSGP_BVCFSM_E_RX_UNBLOCK:
+ rx = data;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-UNBLOCK\n");
+ if (bfp->bvci == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK on BVCI=0 is illegal\n");
+ /* If BVC-UNBLOCK PDU is received by an SGSN for the signalling BVC, the PDU is ignored.*/
+ if (bfp->role_sgsn)
+ break;
+ _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
+ break;
+ }
+ if (!bfp->role_sgsn) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK on BSS is illegal\n");
+ _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
+ break;
+ }
+ tx = bssgp2_enc_bvc_unblock_ack(bfp->bvci);
+ fi_tx_sig(fi, tx);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, T1_SECS, T1);
+ break;
+ case BSSGP_BVCFSM_E_REQ_UNBLOCK:
+ if (bfp->role_sgsn) {
+ LOGPFSML(fi, LOGL_ERROR, "SGSN side cannot initiate BVC unblock\n");
+ break;
+ }
+ if (bfp->bvci == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "BVCI 0 cannot be unblocked\n");
+ break;
+ }
+ bfp->locally_blocked = false;
+ tx = bssgp2_enc_bvc_unblock(bfp->bvci);
+ fi_tx_sig(fi, tx);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
+ break;
+ }
+}
+
+/* Waiting for RESET-ACK: Receive PDUs but don't transmit */
+static void bssgp_bvc_fsm_wait_reset_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+ const struct tlv_parsed *tp = NULL;
+ struct msgb *rx = NULL, *tx;
+ uint8_t cause;
+
+ switch (event) {
+ case BSSGP_BVCFSM_E_RX_RESET:
+ /* 48.018 Section 8.4.3: If the BSS (or SGSN) has sent a BVC-RESET PDU for a BVCI to
+ * the SGSN (or BSS) and is awaiting a BVC-RESET-ACK PDU in response, but instead
+ * receives a BVC-RESET PDU indicating the same BVCI, then this shall be interpreted
+ * as a BVC-RESET ACK PDU and the T2 timer shall be stopped. */
+ /* fall-through */
+ case BSSGP_BVCFSM_E_RX_RESET_ACK:
+ rx = data;
+ cause = bfp->last_reset_cause;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ if (bfp->bvci == 0)
+ update_negotiated_features(fi, tp);
+ if (bfp->role_sgsn && bfp->bvci != 0)
+ bfp->cell_id = bssgp_parse_cell_id(&bfp->ra_id, TLVP_VAL(tp, BSSGP_IE_CELL_ID));
+ if (!bfp->role_sgsn && bfp->bvci != 0 && bfp->locally_blocked) {
+ /* initiate the blocking procedure */
+ /* transmit BVC-BLOCK, transition to BLOCKED state and start re-transmit timer */
+ tx = bssgp2_enc_bvc_block(bfp->bvci, bfp->block_cause);
+ fi_tx_sig(fi, tx);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
+ } else
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
+ if (bfp->ops && bfp->ops->reset_ack_notification)
+ bfp->ops->reset_ack_notification(bfp->nsei, bfp->bvci, &bfp->ra_id, bfp->cell_id, cause, bfp->ops_priv);
+ break;
+ }
+}
+
+static void bssgp_bvc_fsm_unblocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct bssgp2_flow_ctrl rx_fc, *tx_fc;
+ struct bvc_fsm_priv *bfp = fi->priv;
+ const struct tlv_parsed *tp = NULL;
+ struct msgb *rx = NULL, *tx;
+ int rc;
+
+ switch (event) {
+ case BSSGP_BVCFSM_E_RX_UNBLOCK_ACK:
+ rx = data;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ /* If BVC-UNBLOCK-ACK PDU is received by an BSS for the signalling BVC, the PDU is ignored. */
+ if (bfp->bvci == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK-ACK on BVCI=0 is illegal\n");
+ if (!bfp->role_sgsn)
+ break;
+ _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
+ break;
+ }
+ /* stop T1 timer */
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
+ break;
+ case BSSGP_BVCFSM_E_RX_UNBLOCK:
+ rx = data;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ /* If a BVC-UNBLOCK PDU is received by an SGSN for a blocked BVC, a BVC-UNBLOCK-ACK
+ * PDU shall be returned. */
+ if (bfp->role_sgsn) {
+ /* If a BVC-UNBLOCK PDU is received by an SGSN for
+ * the signalling BVC, the PDU is ignored */
+ if (bfp->bvci == 0)
+ break;
+ bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, bfp->nsei, bfp->bvci, 0);
+ }
+ break;
+ case BSSGP_BVCFSM_E_RX_BLOCK:
+ rx = data;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-BLOCK (cause=%s)\n",
+ bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
+ /* If a BVC-BLOCK PDU is received by an SGSN for the signalling BVC, the PDU is ignored */
+ if (bfp->bvci == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx BVC-BLOCK on BVCI=0 is illegal\n");
+ if (bfp->role_sgsn)
+ break;
+ _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
+ break;
+ }
+ if (!bfp->role_sgsn) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx BVC-BLOCK on BSS is illegal\n");
+ _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
+ break;
+ }
+ /* transmit BVC-BLOCK-ACK, transition to BLOCKED state */
+ tx = bssgp2_enc_bvc_block_ack(bfp->bvci);
+ fi_tx_sig(fi, tx);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, 0, 0);
+ break;
+ case BSSGP_BVCFSM_E_REQ_BLOCK:
+ if (bfp->role_sgsn) {
+ LOGPFSML(fi, LOGL_ERROR, "SGSN may not initiate BVC-BLOCK\n");
+ break;
+ }
+ if (bfp->bvci == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "BVCI 0 cannot be blocked\n");
+ break;
+ }
+ bfp->locally_blocked = true;
+ bfp->block_cause = *(uint8_t *)data;
+ /* transmit BVC-BLOCK, transition to BLOCKED state and start re-transmit timer */
+ tx = bssgp2_enc_bvc_block(bfp->bvci, bfp->block_cause);
+ fi_tx_sig(fi, tx);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
+ break;
+ case BSSGP_BVCFSM_E_RX_FC_BVC:
+ rx = data;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ /* we assume osmo_tlv_prot_* has been used before calling here to ensure this */
+ OSMO_ASSERT(bfp->role_sgsn);
+ rc = bssgp2_dec_fc_bvc(&rx_fc, tp);
+ if (rc < 0) {
+ _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
+ break;
+ }
+ if (bfp->ops->rx_fc_bvc)
+ bfp->ops->rx_fc_bvc(bfp->nsei, bfp->bvci, &rx_fc, bfp->ops_priv);
+ tx = bssgp2_enc_fc_bvc_ack(rx_fc.tag);
+ fi_tx_ptp(fi, tx);
+ break;
+ case BSSGP_BVCFSM_E_RX_FC_BVC_ACK:
+ rx = data;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ /* we assume osmo_tlv_prot_* has been used before calling here to ensure this */
+ OSMO_ASSERT(!bfp->role_sgsn);
+ break;
+ case BSSGP_BVCFSM_E_REQ_FC_BVC:
+ tx_fc = data;
+ tx = bssgp2_enc_fc_bvc(tx_fc, bfp->features.negotiated & (BSSGP_XFEAT_GBIT << 8) ?
+ &bfp->features.fc_granularity : NULL);
+ fi_tx_ptp(fi, tx);
+ break;
+ }
+}
+
+static void bssgp_bvc_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+ uint8_t cause;
+ const struct tlv_parsed *tp = NULL;
+ struct msgb *rx = NULL;
+
+ switch (event) {
+ case BSSGP_BVCFSM_E_REQ_RESET:
+ bfp->locally_blocked = false;
+ cause = bfp->last_reset_cause = *(uint8_t *) data;
+ _tx_bvc_reset(fi, cause);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_WAIT_RESET_ACK, T2_SECS, T2);
+#if 0 /* not sure if we really should notify the application if itself has requested the reset? */
+ if (bfp->ops && bfp->ops->reset_notification)
+ bfp->ops->reset_notification(bfp->nsei, bfp->bvci, NULL, 0, cause, bfp->ops_priv);
+#endif
+ break;
+ case BSSGP_BVCFSM_E_RX_RESET:
+ rx = data;
+ tp = (const struct tlv_parsed *) msgb_bcid(rx);
+ cause = *TLVP_VAL(tp, BSSGP_IE_CAUSE);
+ if (bfp->role_sgsn && bfp->bvci != 0)
+ bfp->cell_id = bssgp_parse_cell_id(&bfp->ra_id, TLVP_VAL(tp, BSSGP_IE_CELL_ID));
+ LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-RESET (cause=%s)\n", bssgp_cause_str(cause));
+ if (bfp->bvci == 0)
+ update_negotiated_features(fi, tp);
+ _tx_bvc_reset_ack(fi);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
+ if (bfp->ops && bfp->ops->reset_notification) {
+ bfp->ops->reset_notification(bfp->nsei, bfp->bvci, &bfp->ra_id, bfp->cell_id,
+ cause, bfp->ops_priv);
+ }
+ break;
+ }
+}
+
+static int bssgp_bvc_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+ struct msgb *tx;
+
+ switch (fi->T) {
+ case T1:
+ switch (fi->state) {
+ case BSSGP_BVCFSM_S_BLOCKED:
+ /* re-transmit BVC-BLOCK */
+ tx = bssgp2_enc_bvc_block(bfp->bvci, bfp->block_cause);
+ fi_tx_sig(fi, tx);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
+ break;
+ case BSSGP_BVCFSM_S_UNBLOCKED:
+ /* re-transmit BVC-UNBLOCK */
+ tx = bssgp2_enc_bvc_unblock(bfp->bvci);
+ fi_tx_sig(fi, tx);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, T1_SECS, T1);
+ break;
+ }
+ break;
+ case T2:
+ switch (fi->state) {
+ case BSSGP_BVCFSM_S_WAIT_RESET_ACK:
+ /* re-transmit BVC-RESET */
+ _tx_bvc_reset(fi, bfp->last_reset_cause);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_WAIT_RESET_ACK, T2_SECS, T2);
+ break;
+ case BSSGP_BVCFSM_S_UNBLOCKED:
+ /* re-transmit BVC-RESET-ACK */
+ _tx_bvc_reset_ack(fi);
+ osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, T2_SECS, T2);
+ break;
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ break;
+ }
+ return 0;
+}
+
+
+
+static const struct osmo_fsm_state bssgp_bvc_fsm_states[] = {
+ [BSSGP_BVCFSM_S_NULL] = {
+ /* initial state from which we must do a RESET */
+ .name = "NULL",
+ .in_event_mask = 0,
+ .out_state_mask = S(BSSGP_BVCFSM_S_WAIT_RESET_ACK) |
+ S(BSSGP_BVCFSM_S_UNBLOCKED),
+ .action = bssgp_bvc_fsm_null,
+ },
+ [BSSGP_BVCFSM_S_BLOCKED] = {
+ .name = "BLOCKED",
+ .in_event_mask = S(BSSGP_BVCFSM_E_RX_UNBLOCK) |
+ S(BSSGP_BVCFSM_E_RX_BLOCK) |
+ S(BSSGP_BVCFSM_E_RX_BLOCK_ACK) |
+ S(BSSGP_BVCFSM_E_REQ_UNBLOCK),
+ .out_state_mask = S(BSSGP_BVCFSM_S_WAIT_RESET_ACK) |
+ S(BSSGP_BVCFSM_S_UNBLOCKED) |
+ S(BSSGP_BVCFSM_S_BLOCKED),
+ .action = bssgp_bvc_fsm_blocked,
+ .onenter = bssgp_bvc_fsm_blocked_onenter,
+ },
+ [BSSGP_BVCFSM_S_WAIT_RESET_ACK]= {
+ .name = "WAIT_RESET_ACK",
+ .in_event_mask = S(BSSGP_BVCFSM_E_RX_RESET_ACK) |
+ S(BSSGP_BVCFSM_E_RX_RESET),
+ .out_state_mask = S(BSSGP_BVCFSM_S_UNBLOCKED) |
+ S(BSSGP_BVCFSM_S_BLOCKED) |
+ S(BSSGP_BVCFSM_S_WAIT_RESET_ACK),
+ .action = bssgp_bvc_fsm_wait_reset_ack,
+ .onenter = _onenter_tail,
+ },
+
+ [BSSGP_BVCFSM_S_UNBLOCKED] = {
+ .name = "UNBLOCKED",
+ .in_event_mask = S(BSSGP_BVCFSM_E_RX_BLOCK) |
+ S(BSSGP_BVCFSM_E_RX_UNBLOCK) |
+ S(BSSGP_BVCFSM_E_RX_UNBLOCK_ACK) |
+ S(BSSGP_BVCFSM_E_REQ_BLOCK) |
+ S(BSSGP_BVCFSM_E_RX_FC_BVC) |
+ S(BSSGP_BVCFSM_E_RX_FC_BVC_ACK) |
+ S(BSSGP_BVCFSM_E_REQ_FC_BVC),
+ .out_state_mask = S(BSSGP_BVCFSM_S_BLOCKED) |
+ S(BSSGP_BVCFSM_S_WAIT_RESET_ACK) |
+ S(BSSGP_BVCFSM_S_UNBLOCKED),
+ .action = bssgp_bvc_fsm_unblocked,
+ .onenter = _onenter_tail,
+ },
+};
+
+static struct osmo_fsm bssgp_bvc_fsm = {
+ .name = "BSSGP-BVC",
+ .states = bssgp_bvc_fsm_states,
+ .num_states = ARRAY_SIZE(bssgp_bvc_fsm_states),
+ .allstate_event_mask = S(BSSGP_BVCFSM_E_REQ_RESET) |
+ S(BSSGP_BVCFSM_E_RX_RESET),
+ .allstate_action = bssgp_bvc_fsm_allstate,
+ .timer_cb = bssgp_bvc_fsm_timer_cb,
+ .log_subsys = DLBSSGP,
+ .event_names = ptp_bvc_event_names,
+};
+
+static struct osmo_fsm_inst *
+_bvc_fsm_alloc(void *ctx, struct gprs_ns2_inst *nsi, bool role_sgsn, uint16_t nsei, uint16_t bvci)
+{
+ struct osmo_fsm_inst *fi;
+ struct bvc_fsm_priv *bfp;
+ char idbuf[64];
+
+ /* TODO: encode our role in the id string? */
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-BVC%05u", nsei, bvci);
+
+ fi = osmo_fsm_inst_alloc(&bssgp_bvc_fsm, ctx, NULL, LOGL_INFO, idbuf);
+ if (!fi)
+ return NULL;
+
+ bfp = talloc_zero(fi, struct bvc_fsm_priv);
+ if (!bfp) {
+ osmo_fsm_inst_free(fi);
+ return NULL;
+ }
+ fi->priv = bfp;
+
+ bfp->nsi = nsi;
+ bfp->role_sgsn = role_sgsn;
+ bfp->nsei = nsei;
+ bfp->bvci = bvci;
+ bfp->max_pdu_len = UINT16_MAX;
+
+ return fi;
+}
+
+/*! Allocate a SIGNALING-BVC FSM for the BSS role (facing a remote SGSN).
+ * \param[in] ctx talloc context from which to allocate
+ * \param[in] nsi NS Instance on which this BVC operates
+ * \param[in] nsei NS Entity Identifier on which this BVC operates
+ * \param[in] features Feature [byte 0] and Extended Feature [byte 1] bitmap
+ * \returns newly-allocated FSM Instance; NULL in case of error */
+struct osmo_fsm_inst *
+bssgp_bvc_fsm_alloc_sig_bss(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint32_t features)
+{
+ struct osmo_fsm_inst *fi = _bvc_fsm_alloc(ctx, nsi, false, nsei, 0);
+ struct bvc_fsm_priv *bfp;
+
+ if (!fi)
+ return NULL;
+
+ bfp = fi->priv;
+ bfp->features.advertised = features;
+
+ return fi;
+}
+
+/*! Allocate a PTP-BVC FSM for the BSS role (facing a remote SGSN).
+ * \param[in] ctx talloc context from which to allocate
+ * \param[in] nsi NS Instance on which this BVC operates
+ * \param[in] nsei NS Entity Identifier on which this BVC operates
+ * \param[in] bvci BVCI of this FSM
+ * \param[in] ra_id Routing Area Identity of the cell (reported to SGSN)
+ * \param[in] cell_id Cell Identifier of the cell (reported to SGSN)
+ * \returns newly-allocated FSM Instance; NULL in case of error */
+struct osmo_fsm_inst *
+bssgp_bvc_fsm_alloc_ptp_bss(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei,
+ uint16_t bvci, const struct gprs_ra_id *ra_id, uint16_t cell_id)
+{
+ struct osmo_fsm_inst *fi;
+ struct bvc_fsm_priv *bfp;
+
+ OSMO_ASSERT(bvci >= 2);
+ OSMO_ASSERT(ra_id);
+
+ fi = _bvc_fsm_alloc(ctx, nsi, false, nsei, bvci);
+ if (!fi)
+ return NULL;
+
+ bfp = fi->priv;
+ bfp->ra_id = *ra_id;
+ bfp->cell_id = cell_id;
+
+ return fi;
+}
+
+/*! Allocate a SIGNALING-BVC FSM for the SGSN role (facing a remote BSS).
+ * \param[in] ctx talloc context from which to allocate
+ * \param[in] nsi NS Instance on which this BVC operates
+ * \param[in] nsei NS Entity Identifier on which this BVC operates
+ * \param[in] features Feature [byte 0] and Extended Feature [byte 1] bitmap
+ * \returns newly-allocated FSM Instance; NULL in case of error */
+struct osmo_fsm_inst *
+bssgp_bvc_fsm_alloc_sig_sgsn(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint32_t features)
+{
+ struct osmo_fsm_inst *fi = _bvc_fsm_alloc(ctx, nsi, true, nsei, 0);
+ struct bvc_fsm_priv *bfp;
+
+ if (!fi)
+ return NULL;
+
+ bfp = fi->priv;
+ bfp->features.advertised = features;
+
+ return fi;
+}
+
+/*! Allocate a PTP-BVC FSM for the SGSN role (facing a remote BSS).
+ * \param[in] ctx talloc context from which to allocate
+ * \param[in] nsi NS Instance on which this BVC operates
+ * \param[in] nsei NS Entity Identifier on which this BVC operates
+ * \param[in] bvci BVCI of this FSM
+ * \returns newly-allocated FSM Instance; NULL in case of error */
+struct osmo_fsm_inst *
+bssgp_bvc_fsm_alloc_ptp_sgsn(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint16_t bvci)
+{
+ struct osmo_fsm_inst *fi;
+
+ OSMO_ASSERT(bvci >= 2);
+
+ fi = _bvc_fsm_alloc(ctx, nsi, true, nsei, bvci);
+ if (!fi)
+ return NULL;
+
+ return fi;
+}
+
+/*! Set the 'operations' callbacks + private data.
+ * \param[in] fi FSM instance for which the data shall be set
+ * \param[in] ops BSSGP BVC FSM operations (call-back functions) to register
+ * \param[in] ops_priv opaque/private data pointer passed through to call-backs */
+void bssgp_bvc_fsm_set_ops(struct osmo_fsm_inst *fi, const struct bssgp_bvc_fsm_ops *ops, void *ops_priv)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+
+ bfp->ops = ops;
+ bfp->ops_priv = ops_priv;
+}
+
+/*! Return if the given BVC FSM is in UNBLOCKED state. */
+bool bssgp_bvc_fsm_is_unblocked(struct osmo_fsm_inst *fi)
+{
+ return fi->state == BSSGP_BVCFSM_S_UNBLOCKED;
+}
+
+/*! Determine the cause value why given BVC FSM is blocked. */
+uint8_t bssgp_bvc_fsm_get_block_cause(struct osmo_fsm_inst *fi)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+ return bfp->block_cause;
+}
+
+/*! Return the advertised features / extended features. */
+uint32_t bssgp_bvc_fsm_get_features_advertised(struct osmo_fsm_inst *fi)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+ return bfp->features.advertised;
+}
+
+/*! Return the received features / extended features. */
+uint32_t bssgp_bvc_fsm_get_features_received(struct osmo_fsm_inst *fi)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+ return bfp->features.received;
+}
+
+/*! Return the negotiated features / extended features. */
+uint32_t bssgp_bvc_fsm_get_features_negotiated(struct osmo_fsm_inst *fi)
+{
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+ return bfp->features.negotiated;
+}
+
+/*! Set the maximum size of a BSSGP PDU.
+ *! On the NS layer this corresponds to the size of an NS SDU in NS-UNITDATA (3GPP TS 48.016 Ch. 9.2.10) */
+void bssgp_bvc_fsm_set_max_pdu_len(struct osmo_fsm_inst *fi, uint16_t max_pdu_len) {
+ struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+ bfp->max_pdu_len = max_pdu_len;
+}
+
+/*! Return the maximum size of a BSSGP PDU
+ *! On the NS layer this corresponds to the size of an NS SDU in NS-UNITDATA (3GPP TS 48.016 Ch. 9.2.10) */
+uint16_t bssgp_bvc_fsm_get_max_pdu_len(const struct osmo_fsm_inst *fi)
+{
+ const struct bvc_fsm_priv *bfp = fi->priv;
+
+ OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
+ return bfp->max_pdu_len;
+}
+
+
+static __attribute__((constructor)) void on_dso_load_bvc_fsm(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&bssgp_bvc_fsm) == 0);
+}
diff --git a/src/gb/common_vty.c b/src/gb/common_vty.c
index a47294b5..ad3dea23 100644
--- a/src/gb/common_vty.c
+++ b/src/gb/common_vty.c
@@ -40,15 +40,21 @@
int gprs_log_filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
- const struct gprs_nsvc *nsvc = ctx->ctx[LOG_CTX_GB_NSVC];
- const struct gprs_bvc *bvc = ctx->ctx[LOG_CTX_GB_BVC];
+ const void *nse = ctx->ctx[LOG_CTX_GB_NSE];
+ const void *nsvc = ctx->ctx[LOG_CTX_GB_NSVC];
+ const void *bvc = ctx->ctx[LOG_CTX_GB_BVC];
+
+ /* Filter on the NS Entity */
+ if ((tar->filter_map & (1 << LOG_FLT_GB_NSE)) != 0
+ && nse && (nse == tar->filter_data[LOG_FLT_GB_NSE]))
+ return 1;
/* Filter on the NS Virtual Connection */
if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0
&& nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC]))
return 1;
- /* Filter on the NS Virtual Connection */
+ /* Filter on the BSSGP Virtual Connection */
if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0
&& bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC]))
return 1;
@@ -57,4 +63,4 @@ int gprs_log_filter_fn(const struct log_context *ctx,
}
-int DNS, DBSSGP;
+int DNS;
diff --git a/src/gb/common_vty.h b/src/gb/common_vty.h
index 801d2dab..8e883319 100644
--- a/src/gb/common_vty.h
+++ b/src/gb/common_vty.h
@@ -3,5 +3,5 @@
#include <osmocom/vty/command.h>
#include <osmocom/core/logging.h>
-extern int DNS, DBSSGP;
+extern int DNS;
diff --git a/src/gb/frame_relay.c b/src/gb/frame_relay.c
new file mode 100644
index 00000000..e973b915
--- /dev/null
+++ b/src/gb/frame_relay.c
@@ -0,0 +1,1051 @@
+/*! \file frame_relay.c
+ * Implement frame relay/PVC by Q.933
+ */
+/* (C) 2020 Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <osmocom/gprs/frame_relay.h>
+#include <osmocom/core/endian.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/tlv.h>
+
+#define LOGPFRL(frl, lvl, fmt, args ...) \
+ LOGP(DFR, lvl, "%s: " fmt, (frl)->name, ## args)
+
+#define DFR DLNS
+
+/* Table 4-2/Q.931 */
+enum q931_msgtype {
+ /* Call establishment message */
+ Q931_MSGT_ALERTING = 0x01,
+ Q931_MSGT_CALL_PROCEEDING = 0x02,
+ Q931_MSGT_CONNECT = 0x07,
+ Q931_MSGT_CONNECT_ACK = 0x0f,
+ Q931_MSGT_PROGRESS = 0x03,
+ Q931_MSGT_SETUP = 0x05,
+ Q931_MSGT_SETUP_ACK = 0x0d,
+ /* Call information phase message */
+ Q931_MSGT_RESUME = 0x26,
+ Q931_MSGT_RESUME_ACK = 0x2e,
+ Q931_MSGT_RESUME_REJ = 0x22,
+ Q931_MSGT_SUSPEND = 0x25,
+ Q931_MSGT_SUSPEND_ACK = 0x2d,
+ Q931_MSGT_USER_INFO = 0x20,
+ /* Call clearing message */
+ Q931_MSGT_DISCONNECT = 0x45,
+ Q931_MSGT_RELEASE = 0x4d,
+ Q931_MSGT_RELEASE_COMPLETE = 0x5a,
+ Q931_MSGT_RESTART = 0x46,
+ Q931_MSGT_RESTART_ACK = 0x4e,
+ /* Miscellaneous messages */
+ Q931_MSGT_SEGMENT = 0x60,
+ Q931_MSGT_CONGESTION_CONTROL = 0x79,
+ Q931_MSGT_IFORMATION = 0x7b,
+ Q931_MSGT_NOTIFY = 0x6e,
+ Q931_MSGT_STATUS = 0x7d,
+ Q931_MSGT_STATUS_ENQUIRY = 0x75,
+};
+
+
+/* Figure A.1/Q.933 Report type information element */
+enum q933_type_of_report {
+ Q933_REPT_FULL_STATUS = 0x00,
+ Q933_REPT_LINK_INTEGRITY_VERIF = 0x01,
+ Q933_REPT_SINGLE_PVC_ASYNC_STS = 0x02,
+};
+
+/* Q.933 Section A.3 */
+enum q933_iei {
+ Q933_IEI_REPORT_TYPE = 0x51,
+ Q933_IEI_LINK_INT_VERIF = 0x53,
+ Q933_IEI_PVC_STATUS = 0x57,
+};
+
+/* Q.933 Section A.3.3 */
+enum q933_pvc_status {
+ Q933_PVC_STATUS_DLC_ACTIVE = 0x02,
+ Q933_PVC_STATUS_DLC_DELETE = 0x04,
+ Q933_PVC_STATUS_DLC_NEW = 0x08,
+};
+
+
+
+#define LAPF_UI 0x03 /* UI control word */
+#define Q931_PDISC_CC 0x08 /* protocol discriminator */
+#define LMI_Q933A_CALLREF 0x00 /* NULL call-ref */
+
+/* LMI DLCI values */
+#define LMI_Q933A_DLCI 0 /* Q.933A DLCI */
+#define LMI_CISCO_DLCI 1023 /* Cisco DLCI */
+
+/* maximum of supported */
+#define MAX_SUPPORTED_PVC 10
+
+/* TODO: add counters since good connection */
+
+/* Message header of the L3 payload of a Q.933 Annex A message */
+struct q933_a_hdr {
+ uint8_t prot_disc;
+ uint8_t call_ref;
+ uint8_t msg_type;
+} __attribute__((packed));
+
+/* Value part of the Q.933 Annex A.3.3 IE */
+struct q933_a_pvc_sts {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t dlci_msb:6,
+ spare:1,
+ ext0:1;
+ uint8_t space1:3,
+ dlci_lsb:4,
+ ext1:1;
+ uint8_t reserved:1,
+ active:1,
+ delete:1,
+ new:1,
+ spare2:3,
+ ext2:1;
+
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t ext0:1, spare:1, dlci_msb:6;
+ uint8_t ext1:1, dlci_lsb:4, space1:3;
+ uint8_t ext2:1, spare2:3, new:1, delete:1, active:1, reserved:1;
+#endif
+} __attribute__((packed));
+
+/* RX Message: 14 [ 00 01 03 08 00 75 95 01 01 00 03 02 01 00 ] */
+/* RX Message: 13 [ 00 01 03 08 00 75 51 01 00 53 02 01 00 ] */
+
+const struct value_string osmo_fr_role_names[] = {
+ { FR_ROLE_USER_EQUIPMENT, "USER" },
+ { FR_ROLE_NETWORK_EQUIPMENT, "NETWORK" },
+ { 0, NULL }
+};
+
+/* Table A.4/Q.933 */
+struct osmo_tdef fr_tdefs[] = {
+ {
+ .T=391,
+ .default_val = 10,
+ .min_val = 5,
+ .max_val = 30,
+ .desc = "Link integrity verification polling timer",
+ .unit = OSMO_TDEF_S,
+ }, {
+ .T=392,
+ .default_val = 15,
+ .min_val = 5,
+ .max_val = 30,
+ .desc = "Polling verification timer",
+ .unit = OSMO_TDEF_S,
+ },
+ {}
+};
+
+static const struct tlv_definition q933_att_tlvdef = {
+ .def = {
+ [Q933_IEI_REPORT_TYPE] = { TLV_TYPE_TLV },
+ [Q933_IEI_LINK_INT_VERIF] = { TLV_TYPE_TLV },
+ [Q933_IEI_PVC_STATUS] = { TLV_TYPE_TLV },
+ },
+};
+
+static void check_link_state(struct osmo_fr_link *link, bool valid);
+
+static inline uint16_t q922_to_dlci(const uint8_t *hdr)
+{
+ return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
+}
+
+
+static inline void dlci_to_q922(uint8_t *hdr, uint16_t dlci)
+{
+ hdr[0] = (dlci >> 2) & 0xFC;
+ hdr[1] = ((dlci << 4) & 0xF0) | 0x01;
+}
+
+static void dlc_set_active(struct osmo_fr_dlc *dlc, bool active)
+{
+ if (active == dlc->active)
+ return;
+
+ dlc->active = active;
+
+ LOGPFRL(dlc->link, LOGL_NOTICE, "DLCI %u became %s\n", dlc->dlci, active ? "active" : "inactive");
+ if (dlc->status_cb)
+ dlc->status_cb(dlc, dlc->cb_data, active);
+}
+
+/* allocate a message buffer and put Q.933 Annex A headers (L2 + L3) */
+static struct msgb *q933_msgb_alloc(uint16_t dlci, uint8_t prot_disc, uint8_t msg_type)
+{
+ struct msgb *msg = msgb_alloc_headroom(1600+64, 64, "FR Q.933 Tx");
+ struct q933_a_hdr *qh;
+
+ if (!msg)
+ return NULL;
+
+ msg->l1h = msgb_put(msg, 2);
+ dlci_to_q922(msg->l1h, dlci);
+
+ /* LAPF UI control */
+ msg->l2h = msgb_put(msg, 1);
+ *msg->l2h = LAPF_UI;
+
+ msg->l3h = msgb_put(msg, sizeof(*qh));
+ qh = (struct q933_a_hdr *) msg->l3h;
+ qh->prot_disc = prot_disc;
+ qh->call_ref = LMI_Q933A_CALLREF;
+ qh->msg_type = msg_type;
+
+ return msg;
+}
+
+/* obtain the [next] transmit sequence number */
+static uint8_t link_get_tx_seq(struct osmo_fr_link *link)
+{
+ /* The {user equipment, network} increments the send sequence
+ * counter using modulo 256. The value zero is skipped. */
+ link->last_tx_seq++;
+ if (link->last_tx_seq == 0)
+ link->last_tx_seq++;
+
+ return link->last_tx_seq;
+}
+
+/* Append PVC Status IE according to Q.933 A.3.2 */
+static void msgb_put_link_int_verif(struct msgb *msg, struct osmo_fr_link *link)
+{
+ uint8_t link_int_tx[2];
+ link_int_tx[0] = link_get_tx_seq(link);
+ link_int_tx[1] = link->last_rx_seq;
+ msgb_tlv_put(msg, Q933_IEI_LINK_INT_VERIF, 2, link_int_tx);
+}
+
+static void dlc_destroy(struct osmo_fr_dlc *dlc)
+{
+ llist_del(&dlc->list);
+ talloc_free(dlc);
+}
+
+/* Append PVC Status IE according to Q.933 A.3.3 */
+static void msgb_put_pvc_status(struct msgb *msg, struct osmo_fr_dlc *dlc)
+{
+ uint8_t ie[3];
+
+ ie[0] = (dlc->dlci >> 4) & 0x3f;
+ /* extension bits */
+ ie[1] = 0x80 | ((dlc->dlci & 0xf) << 3);
+ /* extension bits */
+ ie[2] = 0x80;
+
+ /* FIXME: validate: this status should be added as long it's not yet acked by the remote */
+ if (dlc->active)
+ ie[2] |= Q933_PVC_STATUS_DLC_ACTIVE;
+
+ if (dlc->add) {
+ ie[2] |= Q933_PVC_STATUS_DLC_NEW;
+ /* we've reported it as new once, reset the status */
+ }
+
+ if (dlc->del) {
+ ie[2] |= Q933_PVC_STATUS_DLC_DELETE;
+ /* we've reported it as deleted once, destroy it */
+ dlc_destroy(dlc);
+ }
+
+ msgb_tlv_put(msg, Q933_IEI_PVC_STATUS, 3, ie);
+}
+
+/* Send a Q.933 STATUS ENQUIRY given type over given link */
+static int tx_lmi_q933_status_enq(struct osmo_fr_link *link, uint8_t rep_type)
+{
+ struct msgb *resp;
+
+ resp = q933_msgb_alloc(0, Q931_PDISC_CC, Q931_MSGT_STATUS_ENQUIRY);
+ if (!resp)
+ return -1;
+ resp->dst = link;
+ link->expected_rep = rep_type;
+
+ /* Table A.2/Q.933 */
+ msgb_tlv_put(resp, Q933_IEI_REPORT_TYPE, 1, &rep_type);
+ msgb_put_link_int_verif(resp, link);
+
+ return link->tx_cb(link->cb_data, resp);
+}
+
+/* Send a Q.933 STATUS of given type over given link */
+static int tx_lmi_q933_status(struct osmo_fr_link *link, uint8_t rep_type)
+{
+ struct osmo_fr_dlc *dlc;
+ struct msgb *resp;
+
+ resp = q933_msgb_alloc(0, Q931_PDISC_CC, Q931_MSGT_STATUS);
+ if (!resp)
+ return -1;
+
+ resp->dst = link;
+
+ /* Table A.1/Q.933 */
+ msgb_tlv_put(resp, Q933_IEI_REPORT_TYPE, 1, &rep_type);
+ switch (rep_type) {
+ case Q933_REPT_FULL_STATUS:
+ msgb_put_link_int_verif(resp, link);
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (dlc->add || dlc->del)
+ dlc->state_send = true;
+
+ msgb_put_pvc_status(resp, dlc);
+ }
+ break;
+ case Q933_REPT_LINK_INTEGRITY_VERIF:
+ msgb_put_link_int_verif(resp, link);
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (dlc->add || dlc->del) {
+ msgb_put_pvc_status(resp, dlc);
+ dlc->state_send = true;
+ }
+ }
+ break;
+ case Q933_REPT_SINGLE_PVC_ASYNC_STS:
+ llist_for_each_entry(dlc, &link->dlc_list, list)
+ msgb_put_pvc_status(resp, dlc);
+ break;
+ }
+
+ return link->tx_cb(link->cb_data, resp);
+}
+
+
+static void link_set_failed(struct osmo_fr_link *link)
+{
+ struct osmo_fr_dlc *dlc;
+
+ LOGPFRL(link, LOGL_NOTICE, "Link failed\n");
+ link->state = false;
+ if (link->status_cb)
+ link->status_cb(link, link->cb_data, link->state);
+
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ dlc_set_active(dlc, false);
+ }
+}
+
+/* Q.933 */
+static int rx_lmi_q933_status_enq(struct msgb *msg, struct tlv_parsed *tp)
+{
+ struct osmo_fr_link *link = msg->dst;
+ struct osmo_fr_dlc *dlc;
+ const uint8_t *link_int_rx;
+ uint8_t rep_type;
+
+ OSMO_ASSERT(link);
+
+ if (link->role == FR_ROLE_USER_EQUIPMENT) {
+ LOGPFRL(link, LOGL_ERROR, "STATUS-ENQ aren't supported in role user\n");
+ return -1;
+ }
+
+ /* check for mandatory IEs */
+ if (!TLVP_PRES_LEN(tp, Q933_IEI_REPORT_TYPE, 1) ||
+ !TLVP_PRES_LEN(tp, Q933_IEI_LINK_INT_VERIF, 2))
+ return -1;
+
+ rep_type = *TLVP_VAL(tp, Q933_IEI_REPORT_TYPE);
+
+ link_int_rx = TLVP_VAL(tp, Q933_IEI_LINK_INT_VERIF);
+ link->last_rx_seq = link_int_rx[0];
+
+ /* this is a bit of a hack. Q.933 explicitly forbids either side from ever
+ * sending a sequence number of '0'. Values start from '1' and are modulo 256,
+ * but '0' is always skipped. So if the peer is sending us a "last received
+ * sequence number of '0' it means it has not yet received any packets from us,
+ * which in turn can only mean that it has just been restarted. Let's treat
+ * this as "service affecting condition" and notify upper layers. This helps
+ * particularly in recovering from rapidly re-starting peers, where the Q.933
+ * nor NS have time to actually detect the connection was lost. Se OS#4974 */
+ if (link_int_rx[1] == 0) {
+ link_set_failed(link);
+ /* the network checks the receive sequence number received from
+ * the user equipment against its send sequence counter */
+ } else if (link_int_rx[1] != link->last_tx_seq) {
+ check_link_state(link, false);
+ link->err_count++;
+ } else {
+ check_link_state(link, true);
+ /* confirm DLC state changes */
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (!dlc->state_send)
+ continue;
+
+ if (dlc->add) {
+ dlc_set_active(dlc, link->state);
+ dlc->add = false;
+ }
+
+ if (dlc->del) {
+ dlc->del = false;
+ }
+
+ dlc->state_send = false;
+ }
+ }
+
+
+ /* The network responds to each STATUS ENQUIRY message with a
+ * STATUS message and resets the T392 timer */
+ osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);
+
+ return tx_lmi_q933_status(link, rep_type);
+}
+
+/* check if the link become active.
+ * The link becomes active when enough times a STATUS/STATUS ENQUIRY arrives without any loss.
+ * Look at the last N393 STATUS/STATUS ENQUIRY PDUs. The link is valid if at least N392
+ * got received.
+ * param[in] valid contains the status of the last packet */
+static void check_link_state(struct osmo_fr_link *link, bool valid)
+{
+ unsigned int last, i;
+ unsigned int carry = 0;
+ struct osmo_fr_dlc *dlc;
+
+ link->succeed <<= 1;
+ if (valid)
+ link->succeed |= 1;
+
+ /* count the bits */
+ last = link->succeed & ((1 << link->net->n393) - 1);
+ for (i = 0; i < link->net->n393; i++)
+ if (last & (1 << i))
+ carry++;
+
+ if (link->net->n393 - carry >= link->net->n392) {
+ /* failing link */
+ if (!link->state)
+ return;
+
+ link_set_failed(link);
+ } else {
+ /* good link */
+ if (link->state)
+ return;
+
+ LOGPFRL(link, LOGL_NOTICE, "Link recovered\n");
+ link->state = true;
+ if (link->status_cb)
+ link->status_cb(link, link->cb_data, link->state);
+
+ if (link->role == FR_ROLE_USER_EQUIPMENT) {
+ /* make sure the next STATUS ENQUIRY is for a full
+ * status report to get the configred DLCs ASAP */
+ link->polling_count = 0;
+ /* we must not proceed further below if we're in user role,
+ * as otherwise link recovery would set all DLCs as active */
+ return;
+ }
+
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (!dlc->add && !dlc->del)
+ dlc_set_active(dlc, true);
+ }
+ }
+}
+
+static int validate_pvc_status(struct tlv_parsed *tp, size_t tp_len)
+{
+ size_t i;
+ uint16_t len = 0;
+
+ for (i = 0; i < tp_len; i++) {
+ if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))
+ continue;
+
+ /* PVC status can be 2 or 3 bytes. If the PVC is bigger
+ * ignore this to be compatible to future extensions. */
+ len = TLVP_LEN(&tp[i], Q933_IEI_PVC_STATUS);
+ if (len <= 1) {
+ return -EINVAL;
+ }
+ /* FIXME: validate correct flags: are some flags invalid at the same time? */
+ }
+
+ return 0;
+}
+
+static int parse_full_pvc_status(struct osmo_fr_link *link, struct tlv_parsed *tp, size_t tp_len)
+{
+ size_t i;
+ int err = 0;
+ struct osmo_fr_dlc *dlc, *tmp;
+ struct q933_a_pvc_sts *pvc;
+ uint16_t dlci = 0;
+ uint16_t *dlcis = talloc_zero_array(link, uint16_t, tp_len);
+ if (!dlcis)
+ return -ENOMEM;
+
+ /* first run validate all PVCs */
+ err = validate_pvc_status(tp, tp_len);
+ if (err < 0)
+ goto out;
+
+ for (i = 0; i < tp_len; i++) {
+ if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))
+ continue;
+
+ /* parse only 3 byte PVCs */
+ pvc = (struct q933_a_pvc_sts *) TLVP_VAL_MINLEN(
+ &tp[i],
+ Q933_IEI_PVC_STATUS,
+ sizeof(struct q933_a_pvc_sts));
+ if (!pvc)
+ continue;
+
+ dlci = ((pvc->dlci_msb & 0x3f) << 4) | (pvc->dlci_lsb & 0xf);
+ dlcis[i] = dlci;
+ dlc = osmo_fr_dlc_by_dlci(link, dlci);
+ if (!dlc) {
+ dlc = osmo_fr_dlc_alloc(link, dlci);
+ if (!dlc) {
+ LOGPFRL(link, LOGL_ERROR, "Could not create DLC %d\n", dlci);
+ continue;
+ }
+ }
+
+ /* Figure A.3/Q.933: The delete bit is only applicable for timely notification
+ * using the optional single PVC asynchronous status report.
+ * Ignoring the delete. */
+ dlc->add = pvc->new;
+ dlc_set_active(dlc, pvc->active);
+ dlc->del = 0;
+ }
+
+ /* check if all dlc are present in PVC Status */
+ llist_for_each_entry_safe(dlc, tmp, &link->dlc_list, list) {
+ bool found = false;
+ for (i = 0; i < tp_len; i++) {
+ if (dlcis[i] == dlc->dlci) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ dlc_set_active(dlc, false);
+ dlc->del = true;
+ }
+ }
+
+ return 0;
+out:
+ talloc_free(dlcis);
+ return err;
+}
+
+static int parse_link_pvc_status(struct osmo_fr_link *link, struct tlv_parsed *tp, size_t tp_len)
+{
+ int err;
+ size_t i;
+ struct q933_a_pvc_sts *pvc;
+ struct osmo_fr_dlc *dlc;
+ uint16_t dlci = 0;
+
+ err = validate_pvc_status(tp, tp_len);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < tp_len; i++) {
+ if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))
+ continue;
+
+ /* parse only 3 byte PVCs */
+ pvc = (struct q933_a_pvc_sts *) TLVP_VAL_MINLEN(
+ &tp[i],
+ Q933_IEI_PVC_STATUS,
+ sizeof(struct q933_a_pvc_sts));
+ if (!pvc)
+ continue;
+
+ dlci = ((pvc->dlci_msb & 0x3f) << 4) | (pvc->dlci_lsb & 0xf);
+ dlc = osmo_fr_dlc_by_dlci(link, dlci);
+ if (!dlc) {
+ /* don't create dlc's for the ones which are about to be deleted. */
+ if (pvc->delete)
+ continue;
+
+ dlc = osmo_fr_dlc_alloc(link, dlci);
+ if (!dlc) {
+ LOGPFRL(link, LOGL_ERROR, "Rx STATUS: Could not create DLC %d\n", dlci);
+ continue;
+ }
+ }
+
+ if (pvc->delete) {
+ dlc->del = 1;
+ } else {
+ dlc->add = pvc->new;
+ dlc_set_active(dlc, pvc->active);
+ dlc->del = 0;
+ }
+ }
+
+ return 0;
+}
+
+static size_t count_pvc_status(struct tlv_parsed *tp, size_t tp_len)
+{
+ size_t i, count = 0;
+ for (i = 0; i < tp_len; i++) {
+ if (!TLVP_PRESENT(&tp[i], Q933_IEI_PVC_STATUS))
+ continue;
+ count++;
+ }
+
+ return count;
+}
+
+static int rx_lmi_q933_status(struct msgb *msg, struct tlv_parsed *tp)
+{
+ struct osmo_fr_link *link = msg->dst;
+ const uint8_t *link_int_rx;
+ uint8_t rep_type;
+
+ OSMO_ASSERT(link);
+
+ if (link->role == FR_ROLE_NETWORK_EQUIPMENT) {
+ LOGPFRL(link, LOGL_ERROR, "Rx STATUS: STATUS aren't supported in role network\n");
+ return -1;
+ }
+
+ /* check for mandatory IEs */
+ if (!TLVP_PRES_LEN(tp, Q933_IEI_REPORT_TYPE, 1)) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx STATUS: Missing TLV Q933 Report Type\n");
+ return -1;
+ }
+
+ rep_type = *TLVP_VAL(tp, Q933_IEI_REPORT_TYPE);
+
+ switch (rep_type) {
+ case Q933_REPT_FULL_STATUS:
+ case Q933_REPT_LINK_INTEGRITY_VERIF:
+ if (rep_type != link->expected_rep) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx STATUS: Unexpected Q933 report type (got 0x%x != exp 0x%x)\n",
+ rep_type, link->expected_rep);
+ return -1;
+ }
+
+ if (!TLVP_PRES_LEN(tp, Q933_IEI_LINK_INT_VERIF, 2)) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx STATUS: Missing TLV Q933 Link Integrety Verification\n");
+ return -1;
+ }
+ link_int_rx = TLVP_VAL(tp, Q933_IEI_LINK_INT_VERIF);
+ link->last_rx_seq = link_int_rx[0];
+ /* The received receive sequence number is not valid if
+ * it is not equal to the last transmitted send sequence
+ * number. Ignore messages containing this error. As a
+ * result, timer T391 expires and the user then
+ * increments the error count. */
+ if (link_int_rx[1] != link->last_tx_seq)
+ return 0;
+ break;
+ case Q933_REPT_SINGLE_PVC_ASYNC_STS:
+ default:
+ return -1;
+ }
+
+ check_link_state(link, true);
+ if (count_pvc_status(tp, MAX_SUPPORTED_PVC + 1) > MAX_SUPPORTED_PVC) {
+ LOGPFRL(link, LOGL_ERROR, "Rx STATUS: Too many PVC! Only %d are supported!\n", MAX_SUPPORTED_PVC);
+ }
+
+ switch (rep_type) {
+ case Q933_REPT_FULL_STATUS:
+ parse_full_pvc_status(link, tp, MAX_SUPPORTED_PVC);
+ break;
+ case Q933_REPT_LINK_INTEGRITY_VERIF:
+ parse_link_pvc_status(link, tp, MAX_SUPPORTED_PVC);
+ break;
+ default:
+ break;
+ }
+
+ /* The network responds to each STATUS ENQUIRY message with a
+ * STATUS message and resets the T392 timer */
+ osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);
+
+ return 0;
+}
+
+static int rx_lmi_q922(struct msgb *msg)
+{
+ struct osmo_fr_link *link = msg->dst;
+ struct q933_a_hdr *qh;
+ /* the + 1 is used to detect more than MAX_SUPPORTED_PVC */
+ struct tlv_parsed tp[MAX_SUPPORTED_PVC + 1];
+ uint8_t *lapf;
+ int rc;
+
+ OSMO_ASSERT(link);
+
+ if (msgb_l2len(msg) < 1)
+ return -1;
+ lapf = msgb_l2(msg);
+
+ /* we only support LAPF UI frames */
+ if (lapf[0] != LAPF_UI)
+ return -1;
+
+ msg->l3h = msg->l2h + 1;
+ if (msgb_l3len(msg) < 3)
+ return -1;
+
+ qh = (struct q933_a_hdr *) msgb_l3(msg);
+ if (qh->prot_disc != Q931_PDISC_CC) {
+ LOGPFRL(link, LOGL_NOTICE,
+ "Rx unsupported LMI protocol discriminator %u\n", qh->prot_disc);
+ return -1;
+ }
+
+ rc = tlv_parse2(tp, MAX_SUPPORTED_PVC + 1, &q933_att_tlvdef,
+ msgb_l3(msg) + sizeof(*qh),
+ msgb_l3len(msg) - sizeof(*qh), 0, 0);
+ if (rc < 0) {
+ LOGPFRL(link, LOGL_NOTICE,
+ "Failed to parse TLVs in LMI message type %u\n", qh->msg_type);
+ return rc;
+ }
+
+ switch (qh->msg_type) {
+ case Q931_MSGT_STATUS_ENQUIRY:
+ rc = rx_lmi_q933_status_enq(msg, tp);
+ break;
+ case Q931_MSGT_STATUS:
+ rc = rx_lmi_q933_status(msg, tp);
+ break;
+ default:
+ LOGPFRL(link, LOGL_NOTICE,
+ "Rx unsupported LMI message type %u\n", qh->msg_type);
+ rc = -1;
+ break;
+ }
+ msgb_free(msg);
+
+ return rc;
+}
+
+int osmo_fr_rx(struct msgb *msg)
+{
+ int rc = 0;
+ uint8_t *frh;
+ uint16_t dlci;
+ struct osmo_fr_dlc *dlc;
+ struct osmo_fr_link *link = msg->dst;
+
+ OSMO_ASSERT(link);
+
+ if (msgb_length(msg) < 2) {
+ LOGPFRL(link, LOGL_ERROR, "Rx short FR header: %u bytes\n", msgb_length(msg));
+ rc = -1;
+ goto out;
+ }
+
+ frh = msg->l1h = msgb_data(msg);
+ if (frh[0] & 0x01) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx Unsupported single-byte FR address\n");
+ rc = -1;
+ goto out;
+ }
+ if ((frh[1] & 0x0f) != 0x01) {
+ LOGPFRL(link, LOGL_NOTICE, "Rx Unknown second FR octet 0x%02x\n", frh[1]);
+ rc = -1;
+ goto out;
+ }
+ dlci = q922_to_dlci(frh);
+ msg->l2h = frh + 2;
+
+ switch (dlci) {
+ case LMI_Q933A_DLCI:
+ return rx_lmi_q922(msg);
+ case LMI_CISCO_DLCI:
+ LOGPFRL(link, LOGL_ERROR, "Rx Unsupported FR DLCI %u\n", dlci);
+ goto out;
+ }
+
+ if (!link->state) {
+ LOGPFRL(link, LOGL_NOTICE, "Link is not reliable. Discarding Rx PDU on DLCI %d\n", dlci);
+ goto out;
+ }
+
+ dlc = osmo_fr_dlc_by_dlci(link, dlci);
+ if (dlc) {
+ if (dlc->active) {
+ /* dispatch to handler of respective DLC */
+ msg->dst = dlc;
+ return dlc->rx_cb(dlc->cb_data, msg);
+ } else {
+ LOGPFRL(link, LOGL_NOTICE, "DLCI %u not yet active. Discarding Rx PDU\n", dlci);
+ }
+ } else {
+ if (link->unknown_dlc_rx_cb)
+ return link->unknown_dlc_rx_cb(link->unknown_dlc_rx_cb_data, msg);
+ else
+ LOGPFRL(link, LOGL_NOTICE, "DLCI %u doesn't exist. Discarding Rx PDU\n", dlci);
+ }
+
+out:
+ msgb_free(msg);
+
+ return rc;
+}
+
+int osmo_fr_tx_dlc(struct msgb *msg)
+{
+ uint8_t *frh;
+ struct osmo_fr_dlc *dlc = msg->dst;
+ struct osmo_fr_link *link = dlc->link;
+
+ OSMO_ASSERT(dlc);
+ OSMO_ASSERT(link);
+
+ if (!link->state) {
+ LOGPFRL(link, LOGL_NOTICE, "Link is not reliable (yet), discarding Tx\n");
+ msgb_free(msg);
+ return -1;
+ }
+ if (!dlc->active) {
+ LOGPFRL(link, LOGL_NOTICE, "DLCI %u is not active (yet), discarding Tx\n", dlc->dlci);
+ msgb_free(msg);
+ return -1;
+ }
+ LOGPFRL(link, LOGL_DEBUG, "DLCI %u is active, sending message\n", dlc->dlci);
+
+ if (msgb_headroom(msg) < 2) {
+ msgb_free(msg);
+ return -ENOSPC;
+ }
+
+ frh = msgb_push(msg, 2);
+ dlci_to_q922(frh, dlc->dlci);
+
+ msg->dst = link;
+ return link->tx_cb(link->cb_data, msg);
+}
+
+/* Every T391 seconds, the user equipment sends a STATUS ENQUIRY
+ * message to the network and resets its polling timer (T391). */
+static void fr_t391_cb(void *data)
+{
+ struct osmo_fr_link *link = data;
+
+ OSMO_ASSERT(link);
+
+ if (link->polling_count % link->net->n391 == 0)
+ tx_lmi_q933_status_enq(link, Q933_REPT_FULL_STATUS);
+ else
+ tx_lmi_q933_status_enq(link, Q933_REPT_LINK_INTEGRITY_VERIF);
+ link->polling_count++;
+ osmo_timer_schedule(&link->t391, osmo_tdef_get(link->net->T_defs, 391, OSMO_TDEF_S, 10), 0);
+}
+
+static void fr_t392_cb(void *data)
+{
+ struct osmo_fr_link *link = data;
+
+ OSMO_ASSERT(link);
+
+ /* A.5 The network increments the error count .. Non-receipt of
+ * a STATUS ENQUIRY within T392, which results in restarting
+ * T392 */
+ link->err_count++;
+ check_link_state(link, false);
+ osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);
+}
+
+/* allocate a frame relay network */
+struct osmo_fr_network *osmo_fr_network_alloc(void *ctx)
+{
+ struct osmo_fr_network *net = talloc_zero(ctx, struct osmo_fr_network);
+ if (!net)
+ return NULL;
+
+ INIT_LLIST_HEAD(&net->links);
+ net->T_defs = fr_tdefs;
+ osmo_tdefs_reset(net->T_defs);
+ net->n391 = 6;
+ net->n392 = 3;
+ net->n393 = 4;
+
+ return net;
+}
+
+void osmo_fr_network_free(struct osmo_fr_network *net)
+{
+ struct osmo_fr_link *link, *tmp;
+
+ if (!net)
+ return;
+
+ llist_for_each_entry_safe(link, tmp, &net->links, list) {
+ osmo_fr_link_free(link);
+ }
+}
+
+/* allocate a frame relay link in a given network */
+struct osmo_fr_link *osmo_fr_link_alloc(struct osmo_fr_network *net, enum osmo_fr_role role, const char *name)
+{
+ struct osmo_fr_link *link = talloc_zero(net, struct osmo_fr_link);
+ if (!link)
+ return NULL;
+ link->role = role;
+ link->net = net;
+ link->name = talloc_strdup(link, name);
+ INIT_LLIST_HEAD(&link->dlc_list);
+ llist_add_tail(&link->list, &net->links);
+
+ osmo_timer_setup(&link->t391, fr_t391_cb, link);
+ osmo_timer_setup(&link->t392, fr_t392_cb, link);
+
+ switch (role) {
+ case FR_ROLE_USER_EQUIPMENT:
+ osmo_timer_schedule(&link->t391, osmo_tdef_get(link->net->T_defs, 391, OSMO_TDEF_S, 15), 0);
+ break;
+ case FR_ROLE_NETWORK_EQUIPMENT:
+ osmo_timer_schedule(&link->t392, osmo_tdef_get(link->net->T_defs, 392, OSMO_TDEF_S, 15), 0);
+ break;
+ }
+
+ LOGPFRL(link, LOGL_INFO, "Creating frame relay link with role %s\n", osmo_fr_role_str(role));
+
+ return link;
+}
+
+void osmo_fr_link_free(struct osmo_fr_link *link)
+{
+ struct osmo_fr_dlc *dlc, *tmp;
+
+ if (!link)
+ return;
+
+ osmo_timer_del(&link->t391);
+ osmo_timer_del(&link->t392);
+
+ llist_for_each_entry_safe(dlc, tmp, &link->dlc_list, list) {
+ osmo_fr_dlc_free(dlc);
+ }
+
+ llist_del(&link->list);
+ talloc_free(link);
+}
+
+/* allocate a data link connectoin on a given framerelay link */
+struct osmo_fr_dlc *osmo_fr_dlc_alloc(struct osmo_fr_link *link, uint16_t dlci)
+{
+ struct osmo_fr_dlc *dlc = talloc_zero(link, struct osmo_fr_dlc);
+ if (!dlc)
+ return NULL;
+
+ dlc->link = link;
+ dlc->dlci = dlci;
+ dlc->active = false;
+
+ llist_add_tail(&dlc->list, &link->dlc_list);
+
+ dlc->add = true;
+ tx_lmi_q933_status(link, Q933_REPT_SINGLE_PVC_ASYNC_STS);
+
+ return dlc;
+}
+
+void osmo_fr_dlc_free(struct osmo_fr_dlc *dlc)
+{
+ llist_del(&dlc->list);
+ talloc_free(dlc);
+}
+
+/* TODO: rework osmo_fr_dlc_alloc/free with handling it's own memory.
+ * For network role: The dlc have to created by the application (e.g. vty).
+ * The dlc shouldn't free'd directly. It should be communicated to the
+ * other side and wait until it's confirmed OR the link go off and free it afterwards.
+ * For user equpment role: The dlc can be created by the application or the dlc will be created
+ * by the frame relay because the network is configuring the dlc.
+ * The dlc shouldn't be free'd. Only the handler should be set to NULL.
+ */
+
+struct osmo_fr_dlc *osmo_fr_dlc_by_dlci(struct osmo_fr_link *link, uint16_t dlci)
+{
+ struct osmo_fr_dlc *dlc;
+
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ if (dlc->dlci == dlci)
+ return dlc;
+ }
+ return NULL;
+}
+
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/tdef_vty.h>
+
+static void fr_dlc_dump_vty(struct vty *vty, const struct osmo_fr_dlc *dlc)
+{
+ vty_out(vty, " FR DLC %05u: %s%s%s%s", dlc->dlci,
+ dlc->active ? "ACTIVE" : "INACTIVE",
+ dlc->add ? " ADDED" : "", dlc->del ? " DELETED" : "", VTY_NEWLINE);
+}
+
+static void fr_link_dump_vty(struct vty *vty, const struct osmo_fr_link *link)
+{
+ const struct osmo_fr_dlc *dlc;
+
+ vty_out(vty, "FR Link '%s': Role %s, LastRxSeq %u, LastTxSeq %u%s",
+ link->name, link->role == FR_ROLE_USER_EQUIPMENT ? "USER" : "NETWORK",
+ link->last_rx_seq, link->last_tx_seq, VTY_NEWLINE);
+ llist_for_each_entry(dlc, &link->dlc_list, list) {
+ fr_dlc_dump_vty(vty, dlc);
+ }
+}
+
+void osmo_fr_network_dump_vty(struct vty *vty, const struct osmo_fr_network *net)
+{
+ struct osmo_fr_link *link;
+
+ vty_out(vty, "FR Network: N391 %u, N392 %u, N393 %u%s",
+ net->n391, net->n392, net->n393, VTY_NEWLINE);
+ osmo_tdef_vty_out_all(vty, net->T_defs, " ");
+ llist_for_each_entry(link, &net->links, list) {
+ fr_link_dump_vty(vty, link);
+ }
+}
diff --git a/src/gb/gprs_bssgp.c b/src/gb/gprs_bssgp.c
index 896f1c5a..7abef804 100644
--- a/src/gb/gprs_bssgp.c
+++ b/src/gb/gprs_bssgp.c
@@ -40,10 +40,16 @@
#include <osmocom/gprs/gprs_bssgp_bss.h>
#include <osmocom/gprs/gprs_ns.h>
-#include "common_vty.h"
+#include "osmocom/gsm/gsm48.h"
+#include "gprs_bssgp_internal.h"
void *bssgp_tall_ctx = NULL;
+static int _gprs_ns_sendmsg(void *ctx, struct msgb *msg);
+
+bssgp_bvc_send bssgp_ns_send = _gprs_ns_sendmsg;
+void *bssgp_ns_send_data = NULL;
+
static const struct rate_ctr_desc bssgp_ctr_description[] = {
{ "packets:in", "Packets at BSSGP Level ( In)" },
{ "packets:out","Packets at BSSGP Level (Out)" },
@@ -67,6 +73,14 @@ LLIST_HEAD(bssgp_bvc_ctxts);
static int _bssgp_tx_dl_ud(struct bssgp_flow_control *fc, struct msgb *msg,
uint32_t llc_pdu_len, void *priv);
+
+/* callback to be backward compatible with old users which do not set the bssgp_ns_send function */
+static int _gprs_ns_sendmsg(void *ctx, struct msgb *msg)
+{
+ OSMO_ASSERT(bssgp_nsi);
+ return gprs_ns_sendmsg(bssgp_nsi, msg);
+}
+
/* Find a BTS Context based on parsed RA ID and Cell ID */
struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid)
{
@@ -80,6 +94,75 @@ struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t
return NULL;
}
+/* Transmit a BVC-RESET or BVC-RESET-ACK with a given nsei and bvci (Chapter 10.4.12)
+ * \param[in] pdu Either BSSGP_PDUT_BVC_RESET or BSSGP_PDUT_BVC_RESET_ACK
+ * \param[in] nsei The NSEI to transmit over
+ * \param[in] bvci BVCI of the BVC to reset
+ * \param[in] cause The cause of the reset only valid for BSSGP_PDUT_BVC_RESET.
+ * \param[in] ra_id Pointer to the ra_id to include. If NULL no cell information will be included
+ * \param[in] cell_id The cell_id to include (if ra_id is not NULL)
+ * returns >= 0 on success, on error < 0.
+ */
+static int tx_bvc_reset_nsei_bvci(enum bssgp_pdu_type pdu, uint16_t nsei, uint16_t bvci,
+ enum gprs_bssgp_cause cause, const struct gprs_ra_id *ra_id, uint16_t cell_id)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph =
+ (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ uint16_t _bvci = osmo_htons(bvci);
+
+ OSMO_ASSERT(pdu == BSSGP_PDUT_BVC_RESET || pdu == BSSGP_PDUT_BVC_RESET_ACK);
+
+ msgb_nsei(msg) = nsei;
+ msgb_bvci(msg) = BVCI_SIGNALLING;
+ bgph->pdu_type = pdu;
+
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+
+ if (pdu == BSSGP_PDUT_BVC_RESET) {
+ msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, (uint8_t *) &cause);
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-RESET "
+ "CAUSE=%s\n", bvci, bssgp_cause_str(cause));
+ } else {
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-RESET-ACK\n", bvci);
+ }
+
+ if (ra_id) {
+ uint8_t bssgp_cid[8];
+ bssgp_create_cell_id(bssgp_cid, ra_id, cell_id);
+ msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
+ }
+
+ /* Optional: Feature Bitmap */
+
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
+}
+
+/*! Transmit a BVC-RESET message with a given nsei and bvci (Chapter 10.4.12)
+ * \param[in] nsei The NSEI to transmit over
+ * \param[in] bvci BVCI of the BVC to reset
+ * \param[in] cause The cause of the reset
+ * \param[in] ra_id Pointer to the ra_id to include. If NULL no cell information will be included
+ * \param[in] cell_id The cell_id to include (if ra_id is not NULL)
+ * returns >= 0 on success, on error < 0.
+ */
+int bssgp_tx_bvc_reset_nsei_bvci(uint16_t nsei, uint16_t bvci, enum gprs_bssgp_cause cause, const struct gprs_ra_id *ra_id, uint16_t cell_id)
+{
+ return tx_bvc_reset_nsei_bvci(BSSGP_PDUT_BVC_RESET, nsei, bvci, cause, ra_id, cell_id);
+}
+
+/*! Transmit a BVC-RESET-ACK message with a given nsei and bvci (Chapter 10.4.12)
+ * \param[in] nsei The NSEI to transmit over
+ * \param[in] bvci BVCI of the BVC to reset
+ * \param[in] ra_id Pointer to the ra_id to include. If NULL no cell information will be included
+ * \param[in] cell_id The cell_id to include (if ra_id is not NULL)
+ * returns >= 0 on success, on error < 0.
+ */
+int bssgp_tx_bvc_reset_ack_nsei_bvci(uint16_t nsei, uint16_t bvci, const struct gprs_ra_id *ra_id, uint16_t cell_id)
+{
+ return tx_bvc_reset_nsei_bvci(BSSGP_PDUT_BVC_RESET_ACK, nsei, bvci, 0, ra_id, cell_id);
+}
+
/*! Initiate reset procedure for all PTP BVC on a given NSEI.
*
* This function initiates reset procedure for all PTP BVC with a given cause.
@@ -94,7 +177,7 @@ int bssgp_tx_bvc_ptp_reset(uint16_t nsei, enum gprs_bssgp_cause cause)
llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
if (bctx->nsei == nsei && bctx->bvci != BVCI_SIGNALLING) {
- LOGP(DBSSGP, LOGL_DEBUG, "NSEI=%u/BVCI=%u RESET due to %s\n",
+ LOGP(DLBSSGP, LOGL_DEBUG, "NSEI=%u/BVCI=%u RESET due to %s\n",
nsei, bctx->bvci, bssgp_cause_str(cause));
rc = bssgp_tx_bvc_reset(bctx, bctx->bvci, cause);
if (rc < 0)
@@ -117,6 +200,12 @@ struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei)
return NULL;
}
+void bssgp_set_bssgp_callback(bssgp_bvc_send ns_send, void *data)
+{
+ bssgp_ns_send = ns_send;
+ bssgp_ns_send_data = data;
+}
+
struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei)
{
struct bssgp_bvc_ctx *ctx;
@@ -126,25 +215,36 @@ struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei)
return NULL;
ctx->bvci = bvci;
ctx->nsei = nsei;
+ ctx->is_sgsn = true;
/* FIXME: BVCI is not unique, only BVCI+NSEI ?!? */
ctx->ctrg = rate_ctr_group_alloc(ctx, &bssgp_ctrg_desc, bvci);
- if (!ctx->ctrg) {
- talloc_free(ctx);
- return NULL;
- }
+ if (!ctx->ctrg)
+ goto err_ctrg;
+
ctx->fc = talloc_zero(ctx, struct bssgp_flow_control);
+ if (!ctx->fc)
+ goto err_fc;
+
/* cofigure for 2Mbit, 30 packets in queue */
bssgp_fc_init(ctx->fc, 100000, 2*1024*1024/8, 30, &_bssgp_tx_dl_ud);
llist_add(&ctx->list, &bssgp_bvc_ctxts);
return ctx;
+
+err_fc:
+ rate_ctr_group_free(ctx->ctrg);
+err_ctrg:
+ talloc_free(ctx);
+ return NULL;
}
void bssgp_bvc_ctx_free(struct bssgp_bvc_ctx *ctx)
{
if (!ctx)
return;
+
+ osmo_timer_del(&ctx->fc->timer);
rate_ctr_group_free(ctx->ctrg);
llist_del(&ctx->list);
talloc_free(ctx);
@@ -163,7 +263,7 @@ static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_t ns_bvci)
bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK;
msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* 10.3.7 SUSPEND-ACK PDU */
@@ -182,7 +282,7 @@ int bssgp_tx_suspend_ack(uint16_t nsei, uint32_t tlli,
bssgp_msgb_ra_put(msg, ra_id);
msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* 10.3.8 SUSPEND-NACK PDU */
@@ -204,7 +304,7 @@ int bssgp_tx_suspend_nack(uint16_t nsei, uint32_t tlli,
if (cause)
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* 10.3.10 RESUME-ACK PDU */
@@ -222,7 +322,7 @@ int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli,
bssgp_msgb_tlli_put(msg, tlli);
bssgp_msgb_ra_put(msg, ra_id);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* 10.3.11 RESUME-NACK PDU */
@@ -243,7 +343,7 @@ int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli,
if (cause)
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf)
@@ -266,7 +366,7 @@ int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
}
/* Chapter 8.4 BVC-Reset Procedure */
-static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
+static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
uint16_t ns_bvci)
{
struct osmo_bssgp_prim nmp;
@@ -275,7 +375,7 @@ static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
uint16_t bvci;
bvci = tlvp_val16be(tp, BSSGP_IE_BVCI);
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci,
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci,
bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
/* look-up or create the BTS context for this BVC */
@@ -288,19 +388,26 @@ static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
/* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
* informs us about its RAC + Cell ID, so we can create a mapping */
- if (bvci != 0 && bvci != 1) {
- if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESET "
+ if (bctx->is_sgsn && bvci != BVCI_SIGNALLING && bvci != BVCI_PTM) {
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_CELL_ID, 8)) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESET "
"missing mandatory IE\n", bvci);
return -EINVAL;
}
/* actually extract RAC / CID */
bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
TLVP_VAL(tp, BSSGP_IE_CELL_ID));
- LOGP(DBSSGP, LOGL_NOTICE, "Cell %s CI %u on BVCI %u\n",
+ LOGP(DLBSSGP, LOGL_NOTICE, "Cell %s CI %u on BVCI %u\n",
osmo_rai_name(&bctx->ra_id), bctx->cell_id, bvci);
}
+ /* Acknowledge the RESET to the BTS */
+ if (bvci == BVCI_SIGNALLING || bvci == BVCI_PTM || bctx->is_sgsn)
+ bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
+ nsei, bvci, ns_bvci);
+ else
+ bssgp_tx_bvc_reset_ack_nsei_bvci(nsei, bvci, &bctx->ra_id, bctx->cell_id);
+
/* Send NM_BVC_RESET.ind to NM */
memset(&nmp, 0, sizeof(nmp));
nmp.nsei = nsei;
@@ -310,10 +417,6 @@ static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_RESET,
PRIM_OP_INDICATION, msg);
bssgp_prim_cb(&nmp.oph, NULL);
-
- /* Acknowledge the RESET to the BTS */
- bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
- nsei, bvci, ns_bvci);
return 0;
}
@@ -326,20 +429,20 @@ static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp)
bvci = tlvp_val16be(tp, BSSGP_IE_BVCI);
if (bvci == BVCI_SIGNALLING) {
/* 8.3.2: Signalling BVC shall never be blocked */
- LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
+ LOGP(DLBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
"received block for signalling BVC!?!\n",
nsei, msgb_bvci(msg));
return 0;
}
- LOGP(DBSSGP, LOGL_INFO, "BSSGP Rx BVCI=%u BVC-BLOCK\n", bvci);
+ LOGP(DLBSSGP, LOGL_INFO, "BSSGP Rx BVCI=%u BVC-BLOCK\n", bvci);
ptp_ctx = btsctx_by_bvci_nsei(bvci, nsei);
if (!ptp_ctx)
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
ptp_ctx->state |= BVC_S_BLOCKED;
- rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ptp_ctx->ctrg, BSSGP_CTR_BLOCKED));
/* Send NM_BVC_BLOCK.ind to NM */
memset(&nmp, 0, sizeof(nmp));
@@ -364,13 +467,13 @@ static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp)
bvci = tlvp_val16be(tp, BSSGP_IE_BVCI);
if (bvci == BVCI_SIGNALLING) {
/* 8.3.2: Signalling BVC shall never be blocked */
- LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
+ LOGP(DLBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
"received unblock for signalling BVC!?!\n",
nsei, msgb_bvci(msg));
return 0;
}
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci);
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci);
ptp_ctx = btsctx_by_bvci_nsei(bvci, nsei);
if (!ptp_ctx)
@@ -402,12 +505,12 @@ static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
/* extract TLLI and parse TLV IEs */
msgb_tlli(msg) = osmo_ntohl(budh->tlli);
- DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x Rx UPLINK-UNITDATA\n", msgb_tlli(msg));
+ DEBUGP(DLBSSGP, "BSSGP TLLI=0x%08x Rx UPLINK-UNITDATA\n", msgb_tlli(msg));
/* Cell ID and LLC_PDU are the only mandatory IE */
- if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) ||
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_CELL_ID, 8) ||
!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
"missing mandatory IE\n", msgb_tlli(msg));
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
@@ -435,16 +538,16 @@ static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp)
uint16_t ns_bvci = msgb_bvci(msg), nsei = msgb_nsei(msg);
int rc;
- if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_TLLI, 4) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_ROUTEING_AREA, 6)) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
"missing mandatory IE\n", ns_bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
tlli = tlvp_val32be(tp, BSSGP_IE_TLLI);
- DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
ns_bvci, tlli);
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
@@ -476,10 +579,10 @@ static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp)
uint16_t ns_bvci = msgb_bvci(msg), nsei = msgb_nsei(msg);
int rc;
- if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
- !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_TLLI, 4 ) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_ROUTEING_AREA, 6) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_SUSPEND_REF_NR, 1)) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
"missing mandatory IE\n", ns_bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
@@ -487,7 +590,7 @@ static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp)
tlli = tlvp_val32be(tp, BSSGP_IE_TLLI);
suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR);
- DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ns_bvci, tlli);
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ns_bvci, tlli);
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
@@ -518,21 +621,21 @@ static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
uint32_t tlli = 0;
uint16_t nsei = msgb_nsei(msg);
- if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) ||
- !TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_TLLI, 4) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_LLC_FRAMES_DISCARDED, 1) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_NUM_OCT_AFF, 3)) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
"missing mandatory IE\n", ctx->bvci);
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
- if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
- tlli = tlvp_val32be(tp, BSSGP_IE_TLLI);
+ tlli = tlvp_val32be(tp, BSSGP_IE_TLLI);
- DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x Rx LLC DISCARDED\n",
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u TLLI=%08x Rx LLC DISCARDED\n",
ctx->bvci, tlli);
- rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctx->ctrg, BSSGP_CTR_DISCARDED));
/* send NM_LLC_DISCARDED to NM */
memset(&nmp, 0, sizeof(nmp));
@@ -553,27 +656,27 @@ int bssgp_rx_status(struct msgb *msg, struct tlv_parsed *tp,
struct osmo_bssgp_prim nmp;
enum gprs_bssgp_cause cause;
- if (!TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx STATUS "
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_CAUSE, 1)) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx STATUS "
"missing mandatory IE\n", bvci);
cause = BSSGP_CAUSE_PROTO_ERR_UNSPEC;
} else {
cause = *TLVP_VAL(tp, BSSGP_IE_CAUSE);
}
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx BVC STATUS, cause=%s\n",
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx BVC STATUS, cause=%s\n",
bvci, bssgp_cause_str(cause));
if (cause == BSSGP_CAUSE_BVCI_BLOCKED || cause == BSSGP_CAUSE_UNKNOWN_BVCI) {
- if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI))
- LOGP(DBSSGP, LOGL_ERROR,
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2))
+ LOGP(DLBSSGP, LOGL_ERROR,
"BSSGP BVCI=%u Rx STATUS cause=%s "
"missing conditional BVCI IE\n",
bvci, bssgp_cause_str(cause));
}
if (bctx)
- rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_STATUS]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_STATUS));
/* send NM_STATUS to NM */
memset(&nmp, 0, sizeof(nmp));
@@ -586,7 +689,6 @@ int bssgp_rx_status(struct msgb *msg, struct tlv_parsed *tp,
return bssgp_prim_cb(&nmp.oph, NULL);
}
-
/* One element (msgb) in a BSSGP Flow Control queue */
struct bssgp_fc_queue_element {
/* linked list of queue elements */
@@ -618,7 +720,7 @@ static void fc_timer_cb(void *data)
list);
if (bssgp_fc_needs_queueing(fc, fcqe->llc_pdu_len)) {
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP-FC: fc_timer_cb() but still "
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP-FC: fc_timer_cb() but still "
"not able to send PDU of %u bytes\n", fcqe->llc_pdu_len);
/* make sure we re-start the timer */
fc_queue_timer_cfg(fc);
@@ -744,7 +846,7 @@ static int bssgp_fc_needs_queueing(struct bssgp_flow_control *fc, uint32_t pdu_l
static int _bssgp_tx_dl_ud(struct bssgp_flow_control *fc, struct msgb *msg,
uint32_t llc_pdu_len, void *priv)
{
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* input function of the flow control implementation, called first
@@ -756,7 +858,7 @@ int bssgp_fc_in(struct bssgp_flow_control *fc, struct msgb *msg,
struct timeval time_now;
if (llc_pdu_len > fc->bucket_size_max) {
- LOGP(DBSSGP, LOGL_NOTICE, "Single PDU (size=%u) is larger "
+ LOGP(DLBSSGP, LOGL_NOTICE, "Single PDU (size=%u) is larger "
"than maximum bucket size (%u)!\n", llc_pdu_len,
fc->bucket_size_max);
msgb_free(msg);
@@ -817,15 +919,15 @@ static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
uint32_t old_leak_rate = bctx->fc->bucket_leak_rate;
uint32_t old_r_def_ms = bctx->r_default_ms;
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
bctx->bvci);
- if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) ||
- !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) ||
- !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) ||
- !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) ||
- !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_TAG, 1) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_BVC_BUCKET_SIZE, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_BUCKET_LEAK_RATE, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_BMAX_DEFAULT_MS, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_R_DEFAULT_MS,2)) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
"missing mandatory IE\n", bctx->bvci);
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
@@ -840,17 +942,17 @@ static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
bctx->r_default_ms = 100 * tlvp_val16be(tp, BSSGP_IE_R_DEFAULT_MS) / 8;
if (old_leak_rate != 0 && bctx->fc->bucket_leak_rate == 0)
- LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak "
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak "
"rate of 0, stopping all DL GPRS!\n");
else if (old_leak_rate == 0 && bctx->fc->bucket_leak_rate != 0)
- LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak "
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak "
"rate of != 0, restarting all DL GPRS!\n");
if (old_r_def_ms != 0 && bctx->r_default_ms == 0)
- LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to MS default "
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSS instructs us to MS default "
"bucket leak rate of 0, stopping DL GPRS!\n");
else if (old_r_def_ms == 0 && bctx->r_default_ms != 0)
- LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to MS default "
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSS instructs us to MS default "
"bucket leak rate != 0, restarting DL GPRS!\n");
/* reconfigure the timer for flow control based on new values */
@@ -887,13 +989,13 @@ static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
break;
case BSSGP_PDUT_RA_CAPABILITY:
/* BSS requests RA capability or IMSI */
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n",
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n",
bctx->bvci);
/* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */
/* FIXME: send RA_CAPA_UPDATE_ACK */
break;
case BSSGP_PDUT_RADIO_STATUS:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci);
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci);
/* BSS informs us of some exception */
/* FIXME: send GMM_RADIO_STATUS.ind to GMM */
break;
@@ -903,7 +1005,7 @@ static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
break;
case BSSGP_PDUT_FLOW_CONTROL_MS:
/* BSS informs us of available bandwidth to one MS */
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n",
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n",
bctx->bvci);
/* FIXME: actually implement flow control */
/* FIXME: Send FLOW_CONTROL_MS_ACK */
@@ -911,12 +1013,15 @@ static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
case BSSGP_PDUT_STATUS:
/* This is already handled in bssgp_rcvmsg() */
break;
+ case BSSGP_PDUT_BVC_RESET:
+ rc = bssgp_rx_bvc_reset(msg, tp, bctx->bvci);
+ break;
case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
case BSSGP_PDUT_MODIFY_BSS_PFC:
case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type %s not [yet] "
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx PDU type %s not [yet] "
"implemented\n", bctx->bvci, bssgp_pdu_str(pdu_type));
rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
break;
@@ -927,13 +1032,13 @@ static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type %s only exists in DL\n",
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u PDU type %s only exists in DL\n",
bctx->bvci, bssgp_pdu_str(pdu_type));
bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
rc = -EINVAL;
break;
default:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type %s unknown\n",
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u PDU type %s unknown\n",
bctx->bvci, bssgp_pdu_str(pdu_type));
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
@@ -964,13 +1069,13 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
break;
case BSSGP_PDUT_FLUSH_LL_ACK:
/* BSS informs us it has performed LL FLUSH */
- DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bvci);
+ DEBUGP(DLBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bvci);
/* FIXME: send NM_FLUSH_LL.res to NM */
break;
case BSSGP_PDUT_LLC_DISCARD:
/* BSS informs that some LLC PDU's have been discarded */
if (!bctx) {
- LOGP(DBSSGP, LOGL_ERROR,
+ LOGP(DLBSSGP, LOGL_ERROR,
"BSSGP Rx LLC-DISCARD missing mandatory BVCI\n");
goto err_mand_ie;
}
@@ -978,9 +1083,9 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
break;
case BSSGP_PDUT_BVC_BLOCK:
/* BSS tells us that BVC shall be blocked */
- if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_CAUSE, 1)) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
"missing mandatory IE\n");
goto err_mand_ie;
}
@@ -988,21 +1093,21 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
break;
case BSSGP_PDUT_BVC_UNBLOCK:
/* BSS tells us that BVC shall be unblocked */
- if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2)) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
"missing mandatory IE\n");
goto err_mand_ie;
}
rc = bssgp_rx_bvc_unblock(msg, tp);
break;
case BSSGP_PDUT_BVC_RESET_ACK:
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx BVC-RESET-ACK\n", bvci);
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx BVC-RESET-ACK\n", bvci);
break;
case BSSGP_PDUT_BVC_RESET:
- /* BSS tells us that BVC init is required */
- if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
- !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
+ /* SGSN or BSS tells us that BVC init is required */
+ if (!TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2) ||
+ !TLVP_PRES_LEN(tp, BSSGP_IE_CAUSE, 1)) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
"missing mandatory IE\n");
goto err_mand_ie;
}
@@ -1011,6 +1116,15 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
case BSSGP_PDUT_STATUS:
/* This is already handled in bssgp_rcvmsg() */
break;
+
+ case BSSGP_PDUT_RAN_INFO:
+ case BSSGP_PDUT_RAN_INFO_REQ:
+ case BSSGP_PDUT_RAN_INFO_ACK:
+ case BSSGP_PDUT_RAN_INFO_ERROR:
+ case BSSGP_PDUT_RAN_INFO_APP_ERROR:
+ rc = bssgp_rx_rim(msg, tp, bvci);
+ break;
+
/* those only exist in the SGSN -> BSS direction */
case BSSGP_PDUT_PAGING_PS:
case BSSGP_PDUT_PAGING_CS:
@@ -1022,13 +1136,13 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
case BSSGP_PDUT_BVC_BLOCK_ACK:
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type %s only exists in DL\n",
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx PDU type %s only exists in DL\n",
bvci, bssgp_pdu_str(pdu_type));
bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
rc = -EINVAL;
break;
default:
- DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type %s unknown\n",
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx PDU type %s unknown\n",
bvci, bssgp_pdu_str(pdu_type));
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
@@ -1066,14 +1180,14 @@ int bssgp_rcvmsg(struct msgb *msg)
rc = bssgp_tlv_parse(&tp, budh->data, data_len);
}
if (rc < 0) {
- LOGP(DBSSGP, LOGL_ERROR, "Failed to parse BSSGP %s message. Invalid message was: %s\n",
+ LOGP(DLBSSGP, LOGL_ERROR, "Failed to parse BSSGP %s message. Invalid message was: %s\n",
bssgp_pdu_str(pdu_type), msgb_hexdump(msg));
if (pdu_type != BSSGP_PDUT_STATUS)
return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
return rc;
}
- if (bvci == BVCI_SIGNALLING && TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
+ if (bvci == BVCI_SIGNALLING && TLVP_PRES_LEN(&tp, BSSGP_IE_BVCI, 2))
bvci = tlvp_val16be(&tp, BSSGP_IE_BVCI);
/* look-up or create the BTS context for this BVC */
@@ -1081,8 +1195,8 @@ int bssgp_rcvmsg(struct msgb *msg)
if (bctx) {
log_set_context(LOG_CTX_GB_BVC, bctx);
- rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
- rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN],
+ rate_ctr_inc(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_PKTS_IN));
+ rate_ctr_add(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_BYTES_IN),
msgb_bssgp_len(msg));
}
@@ -1096,7 +1210,7 @@ int bssgp_rcvmsg(struct msgb *msg)
* registered if a BVCI is given. */
if (!bctx && bvci != BVCI_SIGNALLING &&
pdu_type != BSSGP_PDUT_BVC_RESET) {
- LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU type %s for unknown BVCI\n", nsei, bvci,
+ LOGP(DLBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU type %s for unknown BVCI\n", nsei, bvci,
bssgp_pdu_str(pdu_type));
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
}
@@ -1108,7 +1222,7 @@ int bssgp_rcvmsg(struct msgb *msg)
else if (bctx)
rc = bssgp_rx_ptp(msg, &tp, bctx);
else
- LOGP(DBSSGP, LOGL_NOTICE,
+ LOGP(DLBSSGP, LOGL_NOTICE,
"NSEI=%u/BVCI=%u Cannot handle PDU type %s for unknown BVCI, NS BVCI %u\n", nsei, bvci,
bssgp_pdu_str(pdu_type), ns_bvci);
@@ -1132,7 +1246,7 @@ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
/* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
if (bvci <= BVCI_PTM ) {
- LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
+ LOGP(DLBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
bvci);
msgb_free(msg);
return -EINVAL;
@@ -1140,7 +1254,7 @@ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
bctx = btsctx_by_bvci_nsei(bvci, nsei);
if (!bctx) {
- LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to unknown BVCI %u\n",
+ LOGP(DLBSSGP, LOGL_ERROR, "Cannot send DL-UD to unknown BVCI %u\n",
bvci);
msgb_free(msg);
return -ENODEV;
@@ -1178,6 +1292,7 @@ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
int imsi_len = gsm48_generate_mid_from_imsi(mi, dup->imsi);
+ OSMO_ASSERT(imsi_len <= GSM48_MID_MAX_SIZE);
if (imsi_len > 2)
msgb_tvlv_push(msg, BSSGP_IE_IMSI,
imsi_len-2, mi+2);
@@ -1205,8 +1320,8 @@ int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
budh->tlli = osmo_htonl(msgb_tlli(msg));
budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
- rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
- rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_PKTS_OUT));
+ rate_ctr_add(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_BYTES_OUT), msg->len);
/* Identifiers down: BVCI, NSEI (in msgb->cb) */
@@ -1247,6 +1362,7 @@ int bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
* mi[131], which is wrong */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
+ OSMO_ASSERT(imsi_len <= GSM48_MID_MAX_SIZE);
msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2);
#pragma GCC diagnostic pop
/* DRX Parameters */
@@ -1284,12 +1400,14 @@ int bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi);
}
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
void bssgp_set_log_ss(int ss)
{
- DBSSGP = ss;
+ /* BSSGP has moved from DGPRS to DLGPRS, please update your code if it's
+ * still calling this function
+ */
}
/*!
@@ -1310,7 +1428,7 @@ void bssgp_fc_flush_queue(struct bssgp_flow_control *fc)
/*!
* \brief Flush the queues of all BSSGP contexts.
*/
-void bssgp_flush_all_queues()
+void bssgp_flush_all_queues(void)
{
struct bssgp_bvc_ctx *bctx;
diff --git a/src/gb/gprs_bssgp2.c b/src/gb/gprs_bssgp2.c
new file mode 100644
index 00000000..104fe08e
--- /dev/null
+++ b/src/gb/gprs_bssgp2.c
@@ -0,0 +1,486 @@
+/* BSSGP2 - second generation of BSSGP library */
+
+/* (C) 2020 Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/byteswap.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/tlv.h>
+
+#include <osmocom/gprs/gprs_ns2.h>
+#include <osmocom/gprs/gprs_bssgp.h>
+#include <osmocom/gprs/gprs_bssgp2.h>
+
+
+/*! transmit BSSGP PDU over NS (PTP BVC)
+ * \param[in] nsi NS Instance through which to transmit
+ * \param[in] nsei NSEI of NSE through which to transmit
+ * \param[in] bvci BVCI through which to transmit
+ * \param[in] msg BSSGP PDU to transmit
+ * \returns 0 on success; negative on error */
+int bssgp2_nsi_tx_ptp(struct gprs_ns2_inst *nsi, uint16_t nsei, uint16_t bvci,
+ struct msgb *msg, uint32_t lsp)
+{
+ struct osmo_gprs_ns2_prim nsp = {};
+ int rc;
+
+ if (!msg)
+ return 0;
+
+ nsp.bvci = bvci;
+ nsp.nsei = nsei;
+ nsp.u.unitdata.link_selector = lsp;
+
+ osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg);
+ rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
+
+ return rc;
+}
+
+/*! transmit BSSGP PDU over NS (SIGNALING BVC)
+ * \param[in] nsi NS Instance through which to transmit
+ * \param[in] nsei NSEI of NSE through which to transmit
+ * \param[in] msg BSSGP PDU to transmit
+ * \returns 0 on success; negative on error */
+int bssgp2_nsi_tx_sig(struct gprs_ns2_inst *nsi, uint16_t nsei, struct msgb *msg, uint32_t lsp)
+{
+ return bssgp2_nsi_tx_ptp(nsi, nsei, 0, msg, lsp);
+}
+
+/*! Encode BSSGP BVC-BLOCK PDU as per TS 48.018 Section 10.4.8. */
+struct msgb *bssgp2_enc_bvc_block(uint16_t bvci, enum gprs_bssgp_cause cause)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+ uint16_t _bvci = osmo_htons(bvci);
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_BVC_BLOCK;
+
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+ msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, (uint8_t *) &cause);
+
+ return msg;
+}
+
+/*! Encode BSSGP BVC-BLOCK-ACK PDU as per TS 48.018 Section 10.4.9. */
+struct msgb *bssgp2_enc_bvc_block_ack(uint16_t bvci)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+ uint16_t _bvci = osmo_htons(bvci);
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_BVC_BLOCK_ACK;
+
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+
+ return msg;
+}
+
+/*! Encode BSSGP BVC-UNBLOCK PDU as per TS 48.018 Section 10.4.10. */
+struct msgb *bssgp2_enc_bvc_unblock(uint16_t bvci)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+ uint16_t _bvci = osmo_htons(bvci);
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_BVC_UNBLOCK;
+
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+
+ return msg;
+}
+
+/*! Encode BSSGP BVC-UNBLOCK-ACK PDU as per TS 48.018 Section 10.4.11. */
+struct msgb *bssgp2_enc_bvc_unblock_ack(uint16_t bvci)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+ uint16_t _bvci = osmo_htons(bvci);
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_BVC_UNBLOCK_ACK;
+
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+
+ return msg;
+}
+
+/*! Encode BSSGP BVC-RESET PDU as per TS 48.018 Section 10.4.12.
+ * \param[in] bvci PTP BVCI to encode into the BVCI IE
+ * \param[in] cause BSSGP Cause value (reason for reset)
+ * \param[in] ra_id Routing Area ID to be encoded to CELL_ID IE (optional)
+ * \param[in] cell_id Cell ID to be encoded to CELL_ID IE (only if ra_id is non-NULL)
+ * \param[in] feat_bm Feature Bitmap (optional)
+ * \param[in] ext_feat_bm Extended Feature Bitmap (optional) */
+struct msgb *bssgp2_enc_bvc_reset(uint16_t bvci, enum gprs_bssgp_cause cause,
+ const struct gprs_ra_id *ra_id, uint16_t cell_id,
+ const uint8_t *feat_bm, const uint8_t *ext_feat_bm)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+ uint16_t _bvci = osmo_htons(bvci);
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_BVC_RESET;
+
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+ msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, (uint8_t *) &cause);
+ if (ra_id) {
+ uint8_t bssgp_cid[8];
+ bssgp_create_cell_id(bssgp_cid, ra_id, cell_id);
+ msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
+ }
+
+ if (feat_bm)
+ msgb_tvlv_put(msg, BSSGP_IE_FEATURE_BITMAP, 1, feat_bm);
+
+ if (ext_feat_bm)
+ msgb_tvlv_put(msg, BSSGP_IE_EXT_FEATURE_BITMAP, 1, feat_bm);
+
+ return msg;
+}
+
+/*! Encode BSSGP BVC-RESET-ACK PDU as per TS 48.018 Section 10.4.13.
+ * \param[in] bvci PTP BVCI to encode into the BVCI IE
+ * \param[in] ra_id Routing Area ID to be encoded to CELL_ID IE (optional)
+ * \param[in] cell_id Cell ID to be encoded to CELL_ID IE (only if ra_id is non-NULL)
+ * \param[in] feat_bm Feature Bitmap (optional)
+ * \param[in] ext_feat_bm Extended Feature Bitmap (optional) */
+struct msgb *bssgp2_enc_bvc_reset_ack(uint16_t bvci, const struct gprs_ra_id *ra_id, uint16_t cell_id,
+ const uint8_t *feat_bm, const uint8_t *ext_feat_bm)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+ uint16_t _bvci = osmo_htons(bvci);
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_BVC_RESET_ACK;
+
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+ if (ra_id) {
+ uint8_t bssgp_cid[8];
+ bssgp_create_cell_id(bssgp_cid, ra_id, cell_id);
+ msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
+ }
+
+ if (feat_bm)
+ msgb_tvlv_put(msg, BSSGP_IE_FEATURE_BITMAP, 1, feat_bm);
+
+ if (ext_feat_bm)
+ msgb_tvlv_put(msg, BSSGP_IE_EXT_FEATURE_BITMAP, 1, feat_bm);
+
+ return msg;
+}
+
+/*! Encode BSSGP STATUS PDU as per TS 48.018 Section 10.4.14.
+ * \param[in] cause BSSGP Cause value
+ * \param[in] bvci optional BVCI - only encoded if non-NULL
+ * \param[in] msg optional message buffer containing PDU in error - only encoded if non-NULL
+ * \param[in] max_pdu_len Maximum BSSGP PDU size the NS layer accepts */
+struct msgb *bssgp2_enc_status(uint8_t cause, const uint16_t *bvci, const struct msgb *orig_msg, uint16_t max_pdu_len)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_STATUS;
+ msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
+ /* FIXME: Require/encode BVCI only if cause is BVCI unknown/blocked
+ * See 3GPP TS 48.018 Ch. 10.4.14 */
+ if (bvci) {
+ uint16_t _bvci = osmo_htons(*bvci);
+ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
+ }
+ if (orig_msg) {
+ uint32_t orig_len, max_orig_len;
+ /* Calculate how big the reply would be: the BSSGP msg so far + size of the PDU IN ERROR including tvl */
+ orig_len = msgb_bssgp_len(orig_msg);
+ max_orig_len = msgb_length(msg) + TVLV_GROSS_LEN(orig_len);
+ /* Truncate the difference between max_orig_len and mtu */
+ if (max_orig_len > max_pdu_len)
+ orig_len -= max_orig_len - max_pdu_len;
+ msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR, orig_len, msgb_bssgph(orig_msg));
+ }
+
+ return msg;
+}
+
+static const unsigned int bssgp_fc_gran_tbl[] = {
+ [BSSGP_FC_GRAN_100] = 100,
+ [BSSGP_FC_GRAN_1000] = 1000,
+ [BSSGP_FC_GRAN_10000] = 10000,
+ [BSSGP_FC_GRAN_100000] = 100000,
+};
+
+/*! Decode a FLOW-CONTROL-BVC PDU as per TS 48.018 Section 10.4.4.
+ * \param[out] fc caller-allocated memory for parsed output
+ * \param[in] tp pre-parsed TLVs; caller must ensure mandatory IE presence/length
+ * \returns 0 on success; negative in case of error */
+int bssgp2_dec_fc_bvc(struct bssgp2_flow_ctrl *fc, const struct tlv_parsed *tp)
+{
+ unsigned int granularity = 100;
+
+ /* optional "Flow Control Granularity IE" (11.3.102); applies to
+ * bucket_size_max, bucket_leak_rate and PFC FC params IE */
+ if (TLVP_PRESENT(tp, BSSGP_IE_FLOW_CTRL_GRANULARITY)) {
+ uint8_t gran = *TLVP_VAL(tp, BSSGP_IE_FLOW_CTRL_GRANULARITY);
+ granularity = bssgp_fc_gran_tbl[gran & 3];
+ }
+
+ /* mandatory IEs */
+ fc->tag = *TLVP_VAL(tp, BSSGP_IE_TAG);
+ fc->bucket_size_max = granularity * tlvp_val16be(tp, BSSGP_IE_BVC_BUCKET_SIZE);
+ fc->bucket_leak_rate = (granularity * tlvp_val16be(tp, BSSGP_IE_BUCKET_LEAK_RATE)) / 8;
+ fc->u.bvc.bmax_default_ms = granularity * tlvp_val16be(tp, BSSGP_IE_BMAX_DEFAULT_MS);
+ fc->u.bvc.r_default_ms = (granularity * tlvp_val16be(tp, BSSGP_IE_R_DEFAULT_MS)) / 8;
+
+ /* optional / conditional */
+ if (TLVP_PRESENT(tp, BSSGP_IE_BUCKET_FULL_RATIO)) {
+ fc->bucket_full_ratio_present = true;
+ fc->bucket_full_ratio = *TLVP_VAL(tp, BSSGP_IE_BUCKET_FULL_RATIO);
+ } else {
+ fc->bucket_full_ratio_present = false;
+ }
+
+ if (TLVP_PRESENT(tp, BSSGP_IE_BVC_MEASUREMENT)) {
+ uint16_t val = tlvp_val16be(tp, BSSGP_IE_BVC_MEASUREMENT);
+ fc->u.bvc.measurement_present = true;
+ /* convert from centi-seconds to milli-seconds */
+ if (val == 0xffff)
+ fc->u.bvc.measurement = 0xffffffff;
+ else
+ fc->u.bvc.measurement = val * 10;
+ } else {
+ fc->u.bvc.measurement_present = false;
+ }
+
+ return 0;
+
+}
+
+/*! Encode a FLOW-CONTROL-BVC PDU as per TS 48.018 Section 10.4.4.
+ * \param[in] fc structure describing to-be-encoded FC parameters
+ * \param[in] gran if non-NULL: Encode using specified unit granularity
+ * \returns encoded PDU or NULL in case of error */
+struct msgb *bssgp2_enc_fc_bvc(const struct bssgp2_flow_ctrl *fc, enum bssgp_fc_granularity *gran)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+ unsigned int granularity = 100;
+
+ if (gran)
+ granularity = bssgp_fc_gran_tbl[*gran & 3];
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC;
+
+ msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &fc->tag);
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BVC_BUCKET_SIZE, fc->bucket_size_max / granularity);
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BUCKET_LEAK_RATE, fc->bucket_leak_rate * 8 / granularity);
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BMAX_DEFAULT_MS, fc->u.bvc.bmax_default_ms / granularity);
+ msgb_tvlv_put_16be(msg, BSSGP_IE_R_DEFAULT_MS, fc->u.bvc.r_default_ms * 8 / granularity);
+
+ if (fc->bucket_full_ratio_present)
+ msgb_tvlv_put(msg, BSSGP_IE_BUCKET_FULL_RATIO, 1, &fc->bucket_full_ratio);
+
+ if (fc->u.bvc.measurement_present) {
+ uint16_t val;
+ /* convert from ms to cs */
+ if (fc->u.bvc.measurement == 0xffffffff)
+ val = 0xffff;
+ else
+ val = fc->u.bvc.measurement / 10;
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BVC_MEASUREMENT, val);
+ }
+
+ if (gran) {
+ uint8_t val = *gran & 3;
+ msgb_tvlv_put(msg, BSSGP_IE_FLOW_CTRL_GRANULARITY, 1, &val);
+ }
+
+ return msg;
+}
+
+/*! Encode BSSGP FLUSH-LL PDU as per TS 48.018 Section 10.4.1.
+ * \param[in] tlli - the TLLI of the MS
+ * \param[in] old_bvci BVCI
+ * \param[in] new_bvci2 optional BVCI - only encoded if non-NULL
+ * \param[in] nsei optional - only encoded if non-NULL
+ * \returns encoded PDU or NULL in case of error */
+struct msgb *bssgp2_enc_flush_ll(uint32_t tlli, uint16_t old_bvci,
+ const uint16_t *new_bvci, const uint16_t *nsei)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_FLUSH_LL;
+
+ msgb_tvlv_put_32be(msg, BSSGP_IE_TLLI, tlli);
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BVCI, old_bvci);
+ if (new_bvci)
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BVCI, *new_bvci);
+
+ if (nsei)
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BVCI, *nsei);
+
+ return msg;
+}
+
+/*! Encode a FLOW-CONTROL-BVC-ACK PDU as per TS 48.018 Section 10.4.4.
+ * \param[in] tag the tag IE value to encode
+ * \returns encoded PDU or NULL in case of error */
+struct msgb *bssgp2_enc_fc_bvc_ack(uint8_t tag)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK;
+
+ msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
+
+ return msg;
+}
+
+/*! Decode a FLOW-CONTROL-MS PDU as per TS 48.018 Section 10.4.6.
+ * \param[out] fc caller-allocated memory for parsed output
+ * \param[in] tp pre-parsed TLVs; caller must ensure mandatory IE presence/length
+ * \returns 0 on success; negative in case of error */
+int bssgp2_dec_fc_ms(struct bssgp2_flow_ctrl *fc, struct tlv_parsed *tp)
+{
+ unsigned int granularity = 100;
+
+ /* optional "Flow Control Granularity IE" (11.3.102); applies to
+ * bucket_size_max, bucket_leak_rate and PFC FC params IE */
+ if (TLVP_PRESENT(tp, BSSGP_IE_FLOW_CTRL_GRANULARITY)) {
+ uint8_t gran = *TLVP_VAL(tp, BSSGP_IE_FLOW_CTRL_GRANULARITY);
+ granularity = bssgp_fc_gran_tbl[gran & 3];
+ }
+
+ /* mandatory IEs */
+ fc->u.ms.tlli = tlvp_val32be(tp, BSSGP_IE_TLLI);
+ fc->tag = *TLVP_VAL(tp, BSSGP_IE_TAG);
+ fc->bucket_size_max = granularity * tlvp_val16be(tp, BSSGP_IE_MS_BUCKET_SIZE);
+ fc->bucket_leak_rate = (granularity * tlvp_val16be(tp, BSSGP_IE_BUCKET_LEAK_RATE)) / 8;
+
+ /* optional / conditional */
+ if (TLVP_PRESENT(tp, BSSGP_IE_BUCKET_FULL_RATIO)) {
+ fc->bucket_full_ratio_present = true;
+ fc->bucket_full_ratio = *TLVP_VAL(tp, BSSGP_IE_BUCKET_FULL_RATIO);
+ } else {
+ fc->bucket_full_ratio_present = false;
+ }
+
+ return 0;
+}
+
+/*! Encode a FLOW-CONTROL-MS PDU as per TS 48.018 Section 10.4.6.
+ * \param[in] fc structure describing to-be-encoded FC parameters
+ * \param[in] gran if non-NULL: Encode using specified unit granularity
+ * \returns encoded PDU or NULL in case of error */
+struct msgb *bssgp2_enc_fc_ms(const struct bssgp2_flow_ctrl *fc, enum bssgp_fc_granularity *gran)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+ unsigned int granularity = 100;
+
+ if (gran)
+ granularity = bssgp_fc_gran_tbl[*gran & 3];
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_MS;
+
+ msgb_tvlv_put_32be(msg, BSSGP_IE_TLLI, fc->u.ms.tlli);
+ msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &fc->tag);
+ msgb_tvlv_put_16be(msg, BSSGP_IE_MS_BUCKET_SIZE, fc->bucket_size_max / granularity);
+ msgb_tvlv_put_16be(msg, BSSGP_IE_BUCKET_LEAK_RATE, fc->bucket_leak_rate * 8 / granularity);
+
+ if (fc->bucket_full_ratio_present)
+ msgb_tvlv_put(msg, BSSGP_IE_BUCKET_FULL_RATIO, 1, &fc->bucket_full_ratio);
+
+ if (gran) {
+ uint8_t val = *gran & 3;
+ msgb_tvlv_put(msg, BSSGP_IE_FLOW_CTRL_GRANULARITY, 1, &val);
+ }
+
+ return msg;
+}
+
+/*! Encode a FLOW-CONTROL-BVC-ACK PDU as per TS 48.018 Section 10.4.7.
+ * \param[in] tlli the TLLI IE value to encode
+ * \param[in] tag the tag IE value to encode
+ * \returns encoded PDU or NULL in case of error */
+struct msgb *bssgp2_enc_fc_ms_ack(uint32_t tlli, uint8_t tag)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+
+ if (!msg)
+ return NULL;
+
+ bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
+ bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_MS_ACK;
+
+ msgb_tvlv_put_32be(msg, BSSGP_IE_TLLI, tlli);
+ msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
+
+ return msg;
+}
diff --git a/src/gb/gprs_bssgp_bss.c b/src/gb/gprs_bssgp_bss.c
index f06c403f..8230d871 100644
--- a/src/gb/gprs_bssgp_bss.c
+++ b/src/gb/gprs_bssgp_bss.c
@@ -34,7 +34,7 @@
#include <osmocom/gprs/gprs_bssgp_bss.h>
#include <osmocom/gprs/gprs_ns.h>
-#include "common_vty.h"
+#include "gprs_bssgp_internal.h"
#define GSM_IMSI_LENGTH 17
@@ -60,7 +60,7 @@ int bssgp_tx_suspend(uint16_t nsei, uint32_t tlli,
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=0) Tx SUSPEND (TLLI=0x%04x)\n",
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP (BVCI=0) Tx SUSPEND (TLLI=0x%04x)\n",
tlli);
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = 0; /* Signalling */
@@ -69,7 +69,7 @@ int bssgp_tx_suspend(uint16_t nsei, uint32_t tlli,
bssgp_msgb_tlli_put(msg, tlli);
bssgp_msgb_ra_put(msg, ra_id);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/*! GMM-RESUME.req (Chapter 10.3.9) */
@@ -80,7 +80,7 @@ int bssgp_tx_resume(uint16_t nsei, uint32_t tlli,
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=0) Tx RESUME (TLLI=0x%04x)\n",
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP (BVCI=0) Tx RESUME (TLLI=0x%04x)\n",
tlli);
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = 0; /* Signalling */
@@ -91,7 +91,7 @@ int bssgp_tx_resume(uint16_t nsei, uint32_t tlli,
msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/*! Transmit RA-CAPABILITY-UPDATE (10.3.3) */
@@ -101,7 +101,7 @@ int bssgp_tx_ra_capa_upd(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t tag)
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx RA-CAPA-UPD (TLLI=0x%04x)\n",
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx RA-CAPA-UPD (TLLI=0x%04x)\n",
bctx->bvci, tlli);
/* set NSEI and BVCI in msgb cb */
@@ -113,7 +113,7 @@ int bssgp_tx_ra_capa_upd(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t tag)
msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* first common part of RADIO-STATUS */
@@ -123,7 +123,7 @@ static struct msgb *common_tx_radio_status(struct bssgp_bvc_ctx *bctx)
struct bssgp_normal_hdr *bgph =
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx RADIO-STATUS ",
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx RADIO-STATUS ",
bctx->bvci);
/* set NSEI and BVCI in msgb cb */
@@ -139,9 +139,9 @@ static struct msgb *common_tx_radio_status(struct bssgp_bvc_ctx *bctx)
static int common_tx_radio_status2(struct msgb *msg, uint8_t cause)
{
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
- LOGPC(DBSSGP, LOGL_NOTICE, "CAUSE=%s\n", bssgp_cause_str(cause));
+ LOGPC(DLBSSGP, LOGL_NOTICE, "CAUSE=%s\n", bssgp_cause_str(cause));
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/*! Transmit RADIO-STATUS for TLLI (10.3.5) */
@@ -153,7 +153,7 @@ int bssgp_tx_radio_status_tlli(struct bssgp_bvc_ctx *bctx, uint8_t cause,
if (!msg)
return -ENOMEM;
bssgp_msgb_tlli_put(msg, tlli);
- LOGPC(DBSSGP, LOGL_NOTICE, "TLLI=0x%08x ", tlli);
+ LOGPC(DLBSSGP, LOGL_NOTICE, "TLLI=0x%08x ", tlli);
return common_tx_radio_status2(msg, cause);
}
@@ -168,7 +168,7 @@ int bssgp_tx_radio_status_tmsi(struct bssgp_bvc_ctx *bctx, uint8_t cause,
if (!msg)
return -ENOMEM;
msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *)&_tmsi);
- LOGPC(DBSSGP, LOGL_NOTICE, "TMSI=0x%08x ", tmsi);
+ LOGPC(DLBSSGP, LOGL_NOTICE, "TMSI=0x%08x ", tmsi);
return common_tx_radio_status2(msg, cause);
}
@@ -189,11 +189,12 @@ int bssgp_tx_radio_status_imsi(struct bssgp_bvc_ctx *bctx, uint8_t cause,
* mi[131], which is wrong */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
+ OSMO_ASSERT(imsi_len <= GSM48_MID_MAX_SIZE);
/* strip the MI type and length values (2 bytes) */
if (imsi_len > 2)
msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2);
#pragma GCC diagnostic pop
- LOGPC(DBSSGP, LOGL_NOTICE, "IMSI=%s ", imsi);
+ LOGPC(DLBSSGP, LOGL_NOTICE, "IMSI=%s ", imsi);
return common_tx_radio_status2(msg, cause);
}
@@ -219,7 +220,7 @@ int bssgp_tx_flush_ll_ack(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci_new);
msgb_tvlv_put(msg, BSSGP_IE_NUM_OCT_AFF, 3, (uint8_t *) &_oct_aff);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/*! Transmit LLC-DISCARDED (Chapter 10.4.3) */
@@ -232,7 +233,7 @@ int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
uint16_t _bvci = osmo_htons(bctx->bvci);
uint32_t _oct_aff = osmo_htonl(num_octets & 0xFFFFFF);
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx LLC-DISCARDED "
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx LLC-DISCARDED "
"TLLI=0x%04x, FRAMES=%u, OCTETS=%u\n", bctx->bvci, tlli,
num_frames, num_octets);
msgb_nsei(msg) = bctx->nsei;
@@ -245,7 +246,7 @@ int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
msgb_tvlv_put(msg, BSSGP_IE_NUM_OCT_AFF, 3, ((uint8_t *) &_oct_aff) + 1);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/*! Transmit a BVC-BLOCK message (Chapter 10.4.8) */
@@ -256,7 +257,7 @@ int bssgp_tx_bvc_block(struct bssgp_bvc_ctx *bctx, uint8_t cause)
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint16_t _bvci = osmo_htons(bctx->bvci);
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-BLOCK "
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-BLOCK "
"CAUSE=%s\n", bctx->bvci, bssgp_cause_str(cause));
msgb_nsei(msg) = bctx->nsei;
@@ -266,7 +267,7 @@ int bssgp_tx_bvc_block(struct bssgp_bvc_ctx *bctx, uint8_t cause)
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/*! Transmit a BVC-UNBLOCK message (Chapter 10.4.10) */
@@ -277,7 +278,7 @@ int bssgp_tx_bvc_unblock(struct bssgp_bvc_ctx *bctx)
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
uint16_t _bvci = osmo_htons(bctx->bvci);
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-UNBLOCK\n", bctx->bvci);
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-UNBLOCK\n", bctx->bvci);
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = 0; /* Signalling */
@@ -285,34 +286,29 @@ int bssgp_tx_bvc_unblock(struct bssgp_bvc_ctx *bctx)
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/*! Transmit a BVC-RESET message (Chapter 10.4.12) */
+int bssgp_tx_bvc_reset2(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause, bool add_cell_id)
+{
+ if (add_cell_id)
+ return bssgp_tx_bvc_reset_nsei_bvci(bctx->nsei, bvci, cause, &bctx->ra_id, bctx->cell_id);
+ else
+ return bssgp_tx_bvc_reset_nsei_bvci(bctx->nsei, bvci, cause, NULL, 0);
+}
int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause)
{
- struct msgb *msg = bssgp_msgb_alloc();
- struct bssgp_normal_hdr *bgph =
- (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
- uint16_t _bvci = osmo_htons(bvci);
-
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-RESET "
- "CAUSE=%s\n", bvci, bssgp_cause_str(cause));
-
- msgb_nsei(msg) = bctx->nsei;
- msgb_bvci(msg) = 0; /* Signalling */
- bgph->pdu_type = BSSGP_PDUT_BVC_RESET;
-
- msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
- msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
- if (bvci != BVCI_PTM) {
- uint8_t bssgp_cid[8];
- bssgp_create_cell_id(bssgp_cid, &bctx->ra_id, bctx->cell_id);
- msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
+ /* The Cell Identifier IE is mandatory in the BVC-RESET PDU sent from BSS to SGSN in order to reset a
+ * BVC corresponding to a PTP functional entity. The Cell Identifier IE shall not be used in any other
+ * BVC-RESET PDU. */
+ switch (bvci) {
+ case BVCI_SIGNALLING:
+ case BVCI_PTM:
+ return bssgp_tx_bvc_reset2(bctx, bvci, cause, false);
+ default:
+ return bssgp_tx_bvc_reset2(bctx, bvci, cause, true);
}
- /* Optional: Feature Bitmap */
-
- return gprs_ns_sendmsg(bssgp_nsi, msg);
}
/*! Transmit a FLOW_CONTROL-BVC (Chapter 10.4.4)
@@ -384,7 +380,7 @@ int bssgp_tx_fc_bvc(struct bssgp_bvc_ctx *bctx, uint8_t tag,
sizeof(e_queue_delay),
(uint8_t *) &e_queue_delay);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/*! Transmit a FLOW_CONTROL-MS (Chapter 10.4.6)
@@ -427,7 +423,7 @@ int bssgp_tx_fc_ms(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t tag,
msgb_tvlv_put(msg, BSSGP_IE_BUCKET_FULL_RATIO,
1, bucket_full_ratio);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/*! RL-UL-UNITDATA.req (Chapter 10.2.2)
@@ -470,10 +466,10 @@ int bssgp_tx_ul_ud(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
msgb_nsei(msg) = bctx->nsei;
msgb_bvci(msg) = bctx->bvci;
- rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
- rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
+ rate_ctr_inc(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_PKTS_OUT));
+ rate_ctr_add(rate_ctr_group_get_ctr(bctx->ctrg, BSSGP_CTR_BYTES_OUT), msg->len);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* Parse a single GMM-PAGING.req to a given NSEI/NS-BVCI */
@@ -514,24 +510,24 @@ int bssgp_rx_paging(struct bssgp_paging_info *pinfo,
TLVP_LEN(&tp, BSSGP_IE_IMSI));
/* DRX Parameters */
- if (!TLVP_PRESENT(&tp, BSSGP_IE_DRX_PARAMS))
+ if (!TLVP_PRES_LEN(&tp, BSSGP_IE_DRX_PARAMS, 2))
goto err_mand_ie;
pinfo->drx_params = tlvp_val16be(&tp, BSSGP_IE_DRX_PARAMS);
/* Scope */
- if (TLVP_PRESENT(&tp, BSSGP_IE_BSS_AREA_ID)) {
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_BSS_AREA_ID, 1)) {
pinfo->scope = BSSGP_PAGING_BSS_AREA;
- } else if (TLVP_PRESENT(&tp, BSSGP_IE_LOCATION_AREA)) {
+ } else if (TLVP_PRES_LEN(&tp, BSSGP_IE_LOCATION_AREA, 5)) {
pinfo->scope = BSSGP_PAGING_LOCATION_AREA;
memcpy(ra, TLVP_VAL(&tp, BSSGP_IE_LOCATION_AREA),
TLVP_LEN(&tp, BSSGP_IE_LOCATION_AREA));
gsm48_parse_ra(&pinfo->raid, ra);
- } else if (TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) {
+ } else if (TLVP_PRES_LEN(&tp, BSSGP_IE_ROUTEING_AREA, 6)) {
pinfo->scope = BSSGP_PAGING_ROUTEING_AREA;
memcpy(ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA),
TLVP_LEN(&tp, BSSGP_IE_ROUTEING_AREA));
gsm48_parse_ra(&pinfo->raid, ra);
- } else if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) {
+ } else if (TLVP_PRES_LEN(&tp, BSSGP_IE_BVCI, 2)) {
pinfo->scope = BSSGP_PAGING_BVCI;
pinfo->bvci = tlvp_val16be(&tp, BSSGP_IE_BVCI);
} else
@@ -549,8 +545,7 @@ int bssgp_rx_paging(struct bssgp_paging_info *pinfo,
}
/* Optional (P-)TMSI */
- if (TLVP_PRESENT(&tp, BSSGP_IE_TMSI) &&
- TLVP_LEN(&tp, BSSGP_IE_TMSI) >= 4) {
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_TMSI, 4)) {
if (!pinfo->ptmsi)
pinfo->ptmsi = talloc_zero_size(pinfo, sizeof(uint32_t));
*(pinfo->ptmsi) = osmo_load32be(TLVP_VAL(&tp, BSSGP_IE_TMSI));
diff --git a/src/gb/gprs_bssgp_internal.h b/src/gb/gprs_bssgp_internal.h
new file mode 100644
index 00000000..5022d32d
--- /dev/null
+++ b/src/gb/gprs_bssgp_internal.h
@@ -0,0 +1,9 @@
+
+#pragma once
+
+#include <osmocom/gprs/gprs_bssgp.h>
+
+extern bssgp_bvc_send bssgp_ns_send;
+extern void *bssgp_ns_send_data;
+
+int bssgp_rx_rim(struct msgb *msg, struct tlv_parsed *tp, uint16_t bvci);
diff --git a/src/gb/gprs_bssgp_rim.c b/src/gb/gprs_bssgp_rim.c
new file mode 100644
index 00000000..9c09e1ef
--- /dev/null
+++ b/src/gb/gprs_bssgp_rim.c
@@ -0,0 +1,1261 @@
+/*! \file gprs_bssgp.c
+ * GPRS BSSGP RIM protocol implementation as per 3GPP TS 48.018. */
+/*
+ * (C) 2020-2021 by sysmocom - s.f.m.c. GmbH
+ * Author: Philipp Maier <pmaier@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <osmocom/gprs/gprs_bssgp.h>
+#include <osmocom/gprs/gprs_bssgp_rim.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+#include "gprs_bssgp_internal.h"
+
+/* TVLV IEs use a variable length field. To be sure we will do all buffer
+ * length checks with the maximum possible header length, which is
+ * 1 octet tag + 2 octets length = 3 */
+#define TVLV_HDR_MAXLEN 3
+
+/* Usually RIM application containers and their surrounding RIM containers
+ * are not likely to exceed 128 octets, so the usual header length will be 2 */
+#define TVLV_HDR_LEN 2
+
+/* The reporting cell identifier is encoded as a cell identifier IE
+ * (3GPP TS 48.018, sub-clause 11.3.9) but without IE and length octets. */
+#define REP_CELL_ID_LEN 8
+
+const struct value_string bssgp_rim_routing_info_discr_strs[] = {
+ { BSSGP_RIM_ROUTING_INFO_GERAN, "GERAN-cell" },
+ { BSSGP_RIM_ROUTING_INFO_UTRAN, "UTRAN-RNC" },
+ { BSSGP_RIM_ROUTING_INFO_EUTRAN, "E-UTRAN-eNodeB/HeNB" },
+ { 0, NULL }
+};
+
+/*! Parse a RIM Routing address IE (3GPP TS 29.060, chapter 7.7.57 and 7.7.77).
+ * \param[out] ri user provided memory to store the parsed results.
+ * \param[in] buf input buffer of the value part of the RIM Routing address IE.
+ * \discr[in] discr value part (one byte) of the RIM Routing Address Discriminator IE.
+ * \returns length of parsed octets, -EINVAL on error. */
+int bssgp_parse_rim_ra(struct bssgp_rim_routing_info *ri, const uint8_t *buf,
+ unsigned int len, uint8_t discr)
+{
+ struct gprs_ra_id raid_temp;
+
+ memset(ri, 0, sizeof(*ri));
+ if (len < 2)
+ return -EINVAL;
+
+ ri->discr = discr;
+
+ switch (ri->discr) {
+ case BSSGP_RIM_ROUTING_INFO_GERAN:
+ if (len < 8)
+ return -EINVAL;
+ ri->geran.cid = bssgp_parse_cell_id(&ri->geran.raid, buf);
+ return 8;
+ case BSSGP_RIM_ROUTING_INFO_UTRAN:
+ if (len < 8)
+ return -EINVAL;
+ gsm48_parse_ra(&ri->utran.raid, buf);
+ ri->utran.rncid = osmo_load16be(buf + 6);
+ return 8;
+ case BSSGP_RIM_ROUTING_INFO_EUTRAN:
+ if (len < 6 || len > 13)
+ return -EINVAL;
+ /* Note: 3GPP TS 24.301 Figure 9.9.3.32.1 and 3GPP TS 24.008
+ * Figure 10.5.130 specify MCC/MNC encoding in the same way,
+ * so we can re-use gsm48_parse_ra() for that. */
+ gsm48_parse_ra(&raid_temp, buf);
+ ri->eutran.tai.mcc = raid_temp.mcc;
+ ri->eutran.tai.mnc = raid_temp.mnc;
+ ri->eutran.tai.mnc_3_digits = raid_temp.mnc_3_digits;
+ ri->eutran.tai.tac = osmo_load16be(buf + 3);
+ memcpy(ri->eutran.global_enb_id, buf + 5, len - 5);
+ ri->eutran.global_enb_id_len = len - 5;
+ return len;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*! Parse a RIM Routing information IE (3GPP TS 48.018, chapter 11.3.70).
+ * \param[out] ri user provided memory to store the parsed results.
+ * \param[in] buf input buffer of the value part of the IE.
+ * \returns length of parsed octets, -EINVAL on error. */
+int bssgp_parse_rim_ri(struct bssgp_rim_routing_info *ri, const uint8_t *buf,
+ unsigned int len)
+{
+ uint8_t discr;
+ int rc;
+
+ if (len < 1)
+ return -EINVAL;
+
+ discr = buf[0] & 0x0f;
+
+ rc = bssgp_parse_rim_ra(ri, buf + 1, len - 1, discr);
+ if (rc < 0)
+ return rc;
+ return rc + 1;
+}
+
+/*! Encode a RIM Routing information IE (3GPP TS 48.018, chapter 11.3.70).
+ * \param[out] buf user provided memory (at least 14 byte) for the generated value part of the IE.
+ * \param[in] ri user provided input data struct.
+ * \returns length of encoded octets, -EINVAL on error. */
+int bssgp_create_rim_ri(uint8_t *buf, const struct bssgp_rim_routing_info *ri)
+{
+ int rc;
+ struct gprs_ra_id raid_temp;
+ int len;
+
+ buf[0] = ri->discr & 0x0f;
+ buf++;
+
+ switch (ri->discr) {
+ case BSSGP_RIM_ROUTING_INFO_GERAN:
+ rc = bssgp_create_cell_id(buf, &ri->geran.raid, ri->geran.cid);
+ if (rc < 0)
+ return -EINVAL;
+ len = rc + 1;
+ break;
+ case BSSGP_RIM_ROUTING_INFO_UTRAN:
+ gsm48_encode_ra((struct gsm48_ra_id *)buf, &ri->utran.raid);
+ osmo_store16be(ri->utran.rncid, buf + 6);
+ len = 9;
+ break;
+ case BSSGP_RIM_ROUTING_INFO_EUTRAN:
+ /* Note: 3GPP TS 24.301 Figure 9.9.3.32.1 and 3GPP TS 24.008
+ * Figure 10.5.130 specify MCC/MNC encoding in the same way,
+ * so we can re-use gsm48_encode_ra() for that. */
+ raid_temp = (struct gprs_ra_id) {
+ .mcc = ri->eutran.tai.mcc,
+ .mnc = ri->eutran.tai.mnc,
+ .mnc_3_digits = ri->eutran.tai.mnc_3_digits,
+ };
+
+ gsm48_encode_ra((struct gsm48_ra_id *)buf, &raid_temp);
+ osmo_store16be(ri->eutran.tai.tac, buf + 3);
+ OSMO_ASSERT(ri->eutran.global_enb_id_len <=
+ sizeof(ri->eutran.global_enb_id));
+ memcpy(buf + 5, ri->eutran.global_enb_id,
+ ri->eutran.global_enb_id_len);
+ len = ri->eutran.global_enb_id_len + 6;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(len <= BSSGP_RIM_ROUTING_INFO_MAXLEN);
+ return len;
+}
+
+/*! Encode a RIM Routing information into a human readable string.
+ * \param[buf] user provided string buffer to store the resulting string.
+ * \param[buf_len] maximum length of string buffer.
+ * \param[in] ri user provided input data struct.
+ * \returns pointer to the beginning of the resulting string stored in string buffer. */
+char *bssgp_rim_ri_name_buf(char *buf, size_t buf_len, const struct bssgp_rim_routing_info *ri)
+{
+ char plmn_str[16];
+ char enb_id_str[16];
+ char g_id_ps_str[32];
+ struct osmo_plmn_id plmn;
+ struct osmo_cell_global_id_ps g_id_ps;
+
+ if (!ri)
+ return NULL;
+
+ switch (ri->discr) {
+ case BSSGP_RIM_ROUTING_INFO_GERAN:
+ g_id_ps.rai.rac = ri->geran.raid.rac;
+ g_id_ps.rai.lac.lac = ri->geran.raid.lac;
+ g_id_ps.rai.lac.plmn.mcc = ri->geran.raid.mcc;
+ g_id_ps.rai.lac.plmn.mnc_3_digits = ri->geran.raid.mnc_3_digits;
+ g_id_ps.rai.lac.plmn.mnc = ri->geran.raid.mnc;
+ g_id_ps.cell_identity = ri->geran.cid;
+ snprintf(buf, buf_len, "%s-%s", bssgp_rim_routing_info_discr_str(ri->discr),
+ osmo_cgi_ps_name_buf(g_id_ps_str, sizeof(g_id_ps_str), &g_id_ps));
+ break;
+ case BSSGP_RIM_ROUTING_INFO_UTRAN:
+ g_id_ps.rai.rac = ri->utran.raid.rac;
+ g_id_ps.rai.lac.lac = ri->utran.raid.lac;
+ g_id_ps.rai.lac.plmn.mcc = ri->utran.raid.mcc;
+ g_id_ps.rai.lac.plmn.mnc_3_digits = ri->utran.raid.mnc_3_digits;
+ g_id_ps.rai.lac.plmn.mnc = ri->utran.raid.mnc;
+ g_id_ps.cell_identity = ri->utran.rncid;
+ snprintf(buf, buf_len, "%s-%s", bssgp_rim_routing_info_discr_str(ri->discr),
+ osmo_cgi_ps_name_buf(g_id_ps_str, sizeof(g_id_ps_str), &g_id_ps));
+ break;
+ case BSSGP_RIM_ROUTING_INFO_EUTRAN:
+ plmn.mcc = ri->eutran.tai.mcc;
+ plmn.mnc = ri->eutran.tai.mnc;
+ plmn.mnc_3_digits = ri->eutran.tai.mnc_3_digits;
+ snprintf(buf, buf_len, "%s-%s-%u-%s", bssgp_rim_routing_info_discr_str(ri->discr),
+ osmo_plmn_name_buf(plmn_str, sizeof(plmn_str), &plmn), ri->eutran.tai.tac,
+ osmo_hexdump_buf(enb_id_str, sizeof(enb_id_str), ri->eutran.global_enb_id,
+ ri->eutran.global_enb_id_len, "", false));
+ break;
+ default:
+ snprintf(buf, buf_len, "invalid");
+ }
+
+ return buf;
+}
+
+/*! Encode a RIM Routing information into a human readable string.
+ * \param[in] ri user provided input data struct.
+ * \returns pointer to the resulting string. */
+const char *bssgp_rim_ri_name(const struct bssgp_rim_routing_info *ri)
+{
+ static __thread char rim_ri_buf[64];
+ return bssgp_rim_ri_name_buf(rim_ri_buf, sizeof(rim_ri_buf), ri);
+}
+
+/*! Decode a RAN Information Request Application Container for NACC (3GPP TS 48.018, section 11.3.63.1.1).
+ * \param[out] user provided memory for decoded data struct.
+ * \param[in] buf user provided memory with the encoded value data of the IE.
+ * \returns 0 on success, -EINVAL on error. */
+int bssgp_dec_ran_inf_req_app_cont_nacc(struct bssgp_ran_inf_req_app_cont_nacc *cont, const uint8_t *buf, size_t len)
+{
+ int rc;
+
+ if (len < REP_CELL_ID_LEN)
+ return -EINVAL;
+
+ rc = gsm0808_decode_cell_id_u((union gsm0808_cell_id_u*)&cont->reprt_cell,
+ CELL_IDENT_WHOLE_GLOBAL_PS, buf, len);
+ if (rc < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*! Encode a RAN Information Request Application Container for NACC (3GPP TS 48.018, section 11.3.63.1.1).
+ * \param[out] buf user provided memory for the generated value part of the IE.
+ * \param[in] cont user provided input data struct.
+ * \returns length of encoded octets, -EINVAL on error. */
+int bssgp_enc_ran_inf_req_app_cont_nacc(uint8_t *buf, size_t len, const struct bssgp_ran_inf_req_app_cont_nacc *cont)
+{
+ int rc;
+ struct gprs_ra_id *raid;
+
+ if (len < REP_CELL_ID_LEN)
+ return -EINVAL;
+
+ raid = (struct gprs_ra_id *)&cont->reprt_cell.rai;
+ rc = bssgp_create_cell_id(buf, raid, cont->reprt_cell.cell_identity);
+ if (rc < 0)
+ return -EINVAL;
+ return rc;
+}
+
+/*! Decode a RAN Information Application Container (3GPP TS 48.018, section 11.3.63.2.1).
+ * \param[out] user provided memory for decoded data struct.
+ * \param[in] buf user provided memory with the encoded value data of the IE.
+ * \returns 0 on success, -EINVAL on error. */
+int bssgp_dec_ran_inf_app_cont_nacc(struct bssgp_ran_inf_app_cont_nacc *cont, const uint8_t *buf, size_t len)
+{
+ unsigned int i;
+ int remaining_buf_len;
+ int rc;
+
+ /* The given buffer must at least contain a reporting cell identifer
+ * plus one octet that defines number/type of attached sysinfo messages. */
+ if (len < REP_CELL_ID_LEN + 1)
+ return -EINVAL;
+
+ rc = gsm0808_decode_cell_id_u((union gsm0808_cell_id_u*)&cont->reprt_cell,
+ CELL_IDENT_WHOLE_GLOBAL_PS, buf, len);
+ if (rc < 0)
+ return -EINVAL;
+
+ buf += REP_CELL_ID_LEN;
+
+ cont->type_psi = buf[0] & 1;
+ cont->num_si = buf[0] >> 1;
+ buf++;
+
+ /* The number of sysinfo messages may be zero */
+ if (cont->num_si == 0)
+ return 0;
+
+ /* Check if the prospected system information messages fit in the
+ * remaining buffer space */
+ remaining_buf_len = len - REP_CELL_ID_LEN - 1;
+ if (remaining_buf_len <= 0)
+ return -EINVAL;
+ if (cont->type_psi && remaining_buf_len / BSSGP_RIM_PSI_LEN < cont->num_si)
+ return -EINVAL;
+ else if (remaining_buf_len / BSSGP_RIM_SI_LEN < cont->num_si)
+ return -EINVAL;
+
+ for (i = 0; i < cont->num_si; i++) {
+ cont->si[i] = buf;
+ if (cont->type_psi)
+ buf += BSSGP_RIM_PSI_LEN;
+ else
+ buf += BSSGP_RIM_SI_LEN;
+ }
+
+ return 0;
+}
+
+/*! Encode a RAN Information Application Container (3GPP TS 48.018, section 11.3.63.2.1).
+ * \param[out] buf user provided memory for the generated value part of the IE.
+ * \param[in] cont user provided input data struct.
+ * \returns length of encoded octets, -EINVAL on error. */
+int bssgp_enc_ran_inf_app_cont_nacc(uint8_t *buf, size_t len, const struct bssgp_ran_inf_app_cont_nacc *cont)
+{
+ uint8_t *buf_ptr = buf;
+ int rc;
+ unsigned int silen;
+ unsigned int i;
+ struct gprs_ra_id *raid;
+
+ if (cont->type_psi)
+ silen = BSSGP_RIM_PSI_LEN;
+ else
+ silen = BSSGP_RIM_SI_LEN;
+
+ /* The buffer must accept the reporting cell id, plus 1 byte to define
+ * the type and number of sysinfo messages. */
+ if (len < REP_CELL_ID_LEN + 1 + silen * cont->num_si)
+ return -EINVAL;
+
+ raid = (struct gprs_ra_id *)&cont->reprt_cell.rai;
+ rc = bssgp_create_cell_id(buf_ptr, raid, cont->reprt_cell.cell_identity);
+ if (rc < 0)
+ return -EINVAL;
+ buf_ptr += rc;
+
+ buf_ptr[0] = 0x00;
+ if (cont->type_psi)
+ buf_ptr[0] |= 0x01;
+ buf_ptr[0] |= (cont->num_si << 1);
+ buf_ptr++;
+
+ for (i = 0; i < cont->num_si; i++) {
+ memcpy(buf_ptr, cont->si[i], silen);
+ buf_ptr += silen;
+ }
+
+ return (int)(buf_ptr - buf);
+}
+
+/* 3GPP TS 48.018, table 11.3.64.1.b, NACC Cause coding */
+const struct value_string bssgp_nacc_cause_strs[] = {
+ { BSSGP_NACC_CAUSE_UNSPEC, "unspecified error" },
+ { BSSGP_NACC_CAUSE_SYNTAX_ERR, "syntax error in app container" },
+ { BSSGP_NACC_CAUSE_RPRT_CELL_MISSMTCH, "reporting cell id mismatch" },
+ { BSSGP_NACC_CAUSE_SIPSI_TYPE_ERR, "SI/PSI type error" },
+ { BSSGP_NACC_CAUSE_SIPSI_LEN_ERR, "SI/PSI inconsistent length" },
+ { BSSGP_NACC_CAUSE_SIPSI_SET_ERR, "inconsistent set of msg" },
+ { 0, NULL }
+};
+
+/*! Decode a Application Error Container for NACC (3GPP TS 48.018, section 11.3.64.1).
+ * \param[out] user provided memory for decoded data struct.
+ * \param[in] buf user provided memory with the encoded value data of the IE.
+ * \returns 0 on success, -EINVAL on error. */
+int bssgp_dec_app_err_cont_nacc(struct bssgp_app_err_cont_nacc *cont, const uint8_t *buf, size_t len)
+{
+ /* The buffer must at least contain the NACC cause code, it should also
+ * contain the application container, but we won't error if it is missing. */
+ if (len < 1)
+ return -EINVAL;
+
+ cont->nacc_cause = buf[0];
+
+ if (len > 1) {
+ cont->err_app_cont = buf + 1;
+ cont->err_app_cont_len = len - 1;
+ } else {
+ cont->err_app_cont = NULL;
+ cont->err_app_cont_len = 0;
+ }
+
+ return 0;
+}
+
+/*! Encode Application Error Container for NACC (3GPP TS 48.018, section 11.3.64.1).
+ * \param[out] buf user provided memory for the generated value part of the IE.
+ * \param[in] cont user provided input data struct.
+ * \returns length of encoded octets, -EINVAL on error. */
+int bssgp_enc_app_err_cont_nacc(uint8_t *buf, size_t len, const struct bssgp_app_err_cont_nacc *cont)
+{
+ uint8_t *buf_ptr = buf;
+
+ /* The buffer must accept the length of the application container and the NACC
+ * cause code, which is one octet in length. */
+ if (len < cont->err_app_cont_len + 1)
+ return -EINVAL;
+
+ buf_ptr[0] = cont->nacc_cause;
+ buf_ptr++;
+
+ memcpy(buf_ptr, cont->err_app_cont, cont->err_app_cont_len);
+ buf_ptr += cont->err_app_cont_len;
+
+ return (int)(buf_ptr - buf);
+}
+
+/* The structs bssgp_ran_inf_req_rim_cont, bssgp_ran_inf_rim_cont and bssgp_ran_inf_app_err_rim_cont *cont
+ * share four common fields at the beginning, we use the following struct as parameter type for the common
+ * encoder/decoder functions. (See also 3GPP TS 48.018 table 11.3.62a.1.b, table 11.3.62a.2.b, and
+ * table 11.3.62a.5.b) */
+struct bssgp_ran_inf_x_cont {
+ enum bssgp_ran_inf_app_id app_id;
+ uint32_t seq_num;
+ struct bssgp_rim_pdu_ind pdu_ind;
+ uint8_t prot_ver;
+};
+
+static int dec_rim_cont_common(struct bssgp_ran_inf_x_cont *cont, struct tlv_parsed *tp)
+{
+ if (TLVP_PRES_LEN(tp, BSSGP_IE_RIM_APP_IDENTITY, sizeof(uint8_t)))
+ cont->app_id = TLVP_VAL(tp, BSSGP_IE_RIM_APP_IDENTITY)[0];
+ else
+ return -EINVAL;
+
+ if (TLVP_PRES_LEN(tp, BSSGP_IE_RIM_SEQ_NR, sizeof(cont->seq_num)))
+ cont->seq_num = tlvp_val32be(tp, BSSGP_IE_RIM_SEQ_NR);
+ else
+ return -EINVAL;
+
+ if (TLVP_PRES_LEN(tp, BSSGP_IE_RIM_PDU_INDICATIONS, sizeof(cont->pdu_ind)))
+ memcpy(&cont->pdu_ind, TLVP_VAL(tp, BSSGP_IE_RIM_PDU_INDICATIONS), sizeof(cont->pdu_ind));
+ else
+ return -EINVAL;
+
+ if (TLVP_PRES_LEN(tp, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver)))
+ cont->prot_ver = TLVP_VAL(tp, BSSGP_IE_RIM_PROTOCOL_VERSION)[0];
+ else
+ cont->prot_ver = 1;
+
+ return 0;
+}
+
+static uint8_t *enc_rim_cont_common(uint8_t *buf, size_t len, const struct bssgp_ran_inf_x_cont *cont)
+{
+
+ uint32_t seq_num = osmo_htonl(cont->seq_num);
+ uint8_t app_id_temp;
+ uint8_t *buf_ptr = buf;
+
+ if (len <
+ TVLV_HDR_MAXLEN * 4 + sizeof(app_id_temp) + sizeof(seq_num) + sizeof(cont->pdu_ind) +
+ sizeof(cont->prot_ver))
+ return NULL;
+
+ app_id_temp = cont->app_id;
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_APP_IDENTITY, sizeof(app_id_temp), &app_id_temp);
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_SEQ_NR, sizeof(seq_num), (uint8_t *) & seq_num);
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_PDU_INDICATIONS, sizeof(cont->pdu_ind), (uint8_t *) & cont->pdu_ind);
+ if (cont->prot_ver > 0)
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver), &cont->prot_ver);
+
+ return buf_ptr;
+}
+
+/* 3GPP TS 48.018, table 11.3.61.b: RIM Application Identity coding */
+const struct value_string bssgp_ran_inf_app_id_strs[] = {
+ { BSSGP_RAN_INF_APP_ID_NACC, "Network Assisted Cell Change (NACC)" },
+ { BSSGP_RAN_INF_APP_ID_SI3, "System Information 3 (SI3)" },
+ { BSSGP_RAN_INF_APP_ID_MBMS, "MBMS data channel" },
+ { BSSGP_RAN_INF_APP_ID_SON, "SON Transfer" },
+ { BSSGP_RAN_INF_APP_ID_UTRA_SI, "UTRA System Information (UTRA SI)" },
+ { 0, NULL }
+};
+
+/*! Decode a RAN Information Request RIM Container (3GPP TS 48.018, table 11.3.62a.1.b).
+ * \param[out] user provided memory for decoded data struct.
+ * \param[in] buf user provided memory with the encoded value data of the IE.
+ * \returns 0 on success, -EINVAL on error. */
+int bssgp_dec_ran_inf_req_rim_cont(struct bssgp_ran_inf_req_rim_cont *cont, const uint8_t *buf, size_t len)
+{
+ int rc;
+ struct tlv_parsed tp;
+
+ memset(cont, 0, sizeof(*cont));
+
+ rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
+ if (rc < 0)
+ return -EINVAL;
+
+ rc = dec_rim_cont_common((struct bssgp_ran_inf_x_cont *)cont, &tp);
+ if (rc < 0)
+ return -EINVAL;
+
+ if (TLVP_PRESENT(&tp, BSSGP_IE_RIM_REQ_APP_CONTAINER)) {
+ switch (cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ rc = bssgp_dec_ran_inf_req_app_cont_nacc(&cont->u.app_cont_nacc,
+ TLVP_VAL(&tp, BSSGP_IE_RIM_REQ_APP_CONTAINER),
+ TLVP_LEN(&tp, BSSGP_IE_RIM_REQ_APP_CONTAINER));
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ /* TODO: add parsers for Si3, MBMS, SON, UTRA-SI app containers */
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+
+ if (rc < 0)
+ return rc;
+ }
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID, 1)) {
+ cont->son_trans_app_id = TLVP_VAL(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
+ cont->son_trans_app_id_len = TLVP_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
+ }
+
+ return 0;
+}
+
+/* Dub a TLVP header into a given buffer. The value part of the IE must start
+ * at the 2nd octet. Should the length field make a 3 octet TLVP header
+ * necessary (unlikely, but possible) the value part is moved ahead by one
+ * octet. The function returns a pointer to the end of value part. */
+static uint8_t *dub_tlvp_header(uint8_t *buf, uint8_t iei, uint16_t len)
+{
+ uint8_t *buf_ptr = buf;
+
+ buf_ptr[0] = iei;
+ if (len <= TVLV_MAX_ONEBYTE) {
+ buf_ptr[1] = (uint8_t) len;
+ buf_ptr[1] |= 0x80;
+ buf_ptr += TVLV_HDR_LEN;
+ } else {
+ memmove(buf_ptr + 1, buf_ptr, len);
+ buf_ptr[1] = len >> 8;
+ buf_ptr[2] = len & 0xff;
+ buf_ptr += TVLV_HDR_MAXLEN;
+ }
+ buf_ptr += len;
+
+ return buf_ptr;
+}
+
+/*! Encode a RAN Information Request RIM Container (3GPP TS 48.018, table 11.3.62a.1.b).
+ * \param[out] buf user provided memory for the generated value part of the IE.
+ * \param[in] cont user provided input data struct.
+ * \returns length of encoded octets, -EINVAL on error. */
+int bssgp_enc_ran_inf_req_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_req_rim_cont *cont)
+{
+ uint8_t *buf_ptr = buf;
+ int app_cont_len = 0;
+ int remaining_buf_len;
+
+ buf_ptr = enc_rim_cont_common(buf_ptr, len, (struct bssgp_ran_inf_x_cont *)cont);
+ if (!buf_ptr)
+ return -EINVAL;
+
+ remaining_buf_len = len - (int)(buf_ptr - buf);
+ if (remaining_buf_len <= 0)
+ return -EINVAL;
+
+ switch (cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ app_cont_len =
+ bssgp_enc_ran_inf_req_app_cont_nacc(buf_ptr + TVLV_HDR_LEN, remaining_buf_len - TVLV_HDR_MAXLEN,
+ &cont->u.app_cont_nacc);
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ /* TODO: add encoders for Si3, MBMS, SON, UTRA-SI app containers */
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+
+ if (app_cont_len < 0)
+ return -EINVAL;
+ buf_ptr = dub_tlvp_header(buf_ptr, BSSGP_IE_RIM_REQ_APP_CONTAINER, app_cont_len);
+
+ remaining_buf_len = len - (int)(buf_ptr - buf);
+ if (remaining_buf_len < 0)
+ return -EINVAL;
+
+ if (cont->son_trans_app_id && cont->son_trans_app_id_len > 0) {
+ if (remaining_buf_len < cont->son_trans_app_id_len + TVLV_HDR_MAXLEN)
+ return -EINVAL;
+ buf_ptr =
+ tvlv_put(buf_ptr, BSSGP_IE_SON_TRANSFER_APP_ID, cont->son_trans_app_id_len, cont->son_trans_app_id);
+ }
+ return (int)(buf_ptr - buf);
+}
+
+/*! Decode a RAN Information RIM Container (3GPP TS 48.018, table 11.3.62a.2.b).
+ * \param[out] user provided memory for decoded data struct.
+ * \param[in] buf user provided memory with the encoded value data of the IE.
+ * \returns 0 on success, -EINVAL on error. */
+int bssgp_dec_ran_inf_rim_cont(struct bssgp_ran_inf_rim_cont *cont, const uint8_t *buf, size_t len)
+{
+ int rc;
+ struct tlv_parsed tp;
+
+ memset(cont, 0, sizeof(*cont));
+
+ rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
+ if (rc < 0)
+ return -EINVAL;
+
+ rc = dec_rim_cont_common((struct bssgp_ran_inf_x_cont *)cont, &tp);
+ if (rc < 0)
+ return -EINVAL;
+
+ if (TLVP_PRESENT(&tp, BSSGP_IE_RAN_INFO_APP_CONTAINER)) {
+ switch (cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ rc = bssgp_dec_ran_inf_app_cont_nacc(&cont->u.app_cont_nacc,
+ TLVP_VAL(&tp, BSSGP_IE_RAN_INFO_APP_CONTAINER),
+ TLVP_LEN(&tp, BSSGP_IE_RAN_INFO_APP_CONTAINER));
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ /* TODO: add parsers for Si3, MBMS, SON, UTRA-SI app containers */
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+
+ if (rc < 0)
+ return rc;
+ } else if (TLVP_PRESENT(&tp, BSSGP_IE_APP_ERROR_CONTAINER)) {
+ switch (cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ rc = bssgp_dec_app_err_cont_nacc(&cont->u.app_err_cont_nacc,
+ TLVP_VAL(&tp, BSSGP_IE_APP_ERROR_CONTAINER), TLVP_LEN(&tp,
+ BSSGP_IE_APP_ERROR_CONTAINER));
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ /* TODO: add parsers for Si3, MBMS, SON, UTRA-SI app containers */
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+ if (rc < 0)
+ return rc;
+ cont->app_err = true;
+ }
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID, 1)) {
+ cont->son_trans_app_id = TLVP_VAL(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
+ cont->son_trans_app_id_len = TLVP_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
+ }
+
+ return 0;
+}
+
+/*! Encode a RAN Information RIM Container (3GPP TS 48.018, table 11.3.62a.2.b).
+ * \param[out] buf user provided memory for the generated value part of the IE.
+ * \param[in] cont user provided input data struct.
+ * \returns length of encoded octets, -EINVAL on error. */
+int bssgp_enc_ran_inf_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_rim_cont *cont)
+{
+ uint8_t *buf_ptr = buf;
+ int app_cont_len = 0;
+ int remaining_buf_len;
+
+ buf_ptr = enc_rim_cont_common(buf_ptr, len, (struct bssgp_ran_inf_x_cont *)cont);
+ if (!buf_ptr)
+ return -EINVAL;
+
+ remaining_buf_len = len - (int)(buf_ptr - buf);
+ if (remaining_buf_len <= 0)
+ return -EINVAL;
+
+ if (cont->app_err) {
+ switch (cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ app_cont_len =
+ bssgp_enc_app_err_cont_nacc(buf_ptr + TVLV_HDR_LEN, remaining_buf_len - TVLV_HDR_MAXLEN,
+ &cont->u.app_err_cont_nacc);
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ /* TODO: add encoders for Si3, MBMS, SON, UTRA-SI app containers */
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+ if (app_cont_len < 0)
+ return -EINVAL;
+ buf_ptr = dub_tlvp_header(buf_ptr, BSSGP_IE_APP_ERROR_CONTAINER, app_cont_len);
+ } else {
+ switch (cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ app_cont_len =
+ bssgp_enc_ran_inf_app_cont_nacc(buf_ptr + TVLV_HDR_LEN, remaining_buf_len - TVLV_HDR_MAXLEN,
+ &cont->u.app_cont_nacc);
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ /* TODO: add encoders for Si3, MBMS, SON, UTRA-SI app containers */
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+ if (app_cont_len < 0)
+ return -EINVAL;
+ buf_ptr = dub_tlvp_header(buf_ptr, BSSGP_IE_RAN_INFO_APP_CONTAINER, app_cont_len);
+ }
+
+ remaining_buf_len = len - (int)(buf_ptr - buf);
+ if (remaining_buf_len < 0)
+ return -EINVAL;
+
+ if (cont->son_trans_app_id && cont->son_trans_app_id_len > 0) {
+ if (remaining_buf_len < cont->son_trans_app_id_len + TVLV_HDR_MAXLEN)
+ return -EINVAL;
+ buf_ptr =
+ tvlv_put(buf_ptr, BSSGP_IE_SON_TRANSFER_APP_ID, cont->son_trans_app_id_len, cont->son_trans_app_id);
+ }
+ return (int)(buf_ptr - buf);
+}
+
+/*! Decode a RAN Information ACK RIM Container (3GPP TS 48.018, table 11.3.62a.3.b).
+ * \param[out] user provided memory for decoded data struct.
+ * \param[in] buf user provided memory with the encoded value data of the IE.
+ * \returns 0 on success, -EINVAL on error. */
+int bssgp_dec_ran_inf_ack_rim_cont(struct bssgp_ran_inf_ack_rim_cont *cont, const uint8_t *buf, size_t len)
+{
+ int rc;
+ struct tlv_parsed tp;
+
+ memset(cont, 0, sizeof(*cont));
+
+ rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
+ if (rc < 0)
+ return -EINVAL;
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_APP_IDENTITY, sizeof(uint8_t)))
+ cont->app_id = TLVP_VAL(&tp, BSSGP_IE_RIM_APP_IDENTITY)[0];
+ else
+ return -EINVAL;
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_SEQ_NR, sizeof(cont->seq_num)))
+ cont->seq_num = tlvp_val32be(&tp, BSSGP_IE_RIM_SEQ_NR);
+ else
+ return -EINVAL;
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver)))
+ cont->prot_ver = TLVP_VAL(&tp, BSSGP_IE_RIM_PROTOCOL_VERSION)[0];
+ else
+ cont->prot_ver = 1;
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID, 1)) {
+ cont->son_trans_app_id = TLVP_VAL(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
+ cont->son_trans_app_id_len = TLVP_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
+ }
+
+ return 0;
+}
+
+/*! Encode a RAN Information ACK RIM Container (3GPP TS 48.018, table 11.3.62a.3.b).
+ * \param[out] buf user provided memory for the generated value part of the IE.
+ * \param[in] cont user provided input data struct.
+ * \returns length of encoded octets, -EINVAL on error. */
+int bssgp_enc_ran_inf_ack_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_ack_rim_cont *cont)
+{
+ uint8_t *buf_ptr = buf;
+ uint32_t seq_num = osmo_htonl(cont->seq_num);
+ uint8_t app_id_temp;
+
+ if (len <
+ 4 * TVLV_HDR_MAXLEN + sizeof(app_id_temp) + sizeof(seq_num) + sizeof(cont->prot_ver) +
+ cont->son_trans_app_id_len)
+ return -EINVAL;
+
+ app_id_temp = cont->app_id;
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_APP_IDENTITY, sizeof(app_id_temp), &app_id_temp);
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_SEQ_NR, sizeof(seq_num), (uint8_t *) & seq_num);
+
+ if (cont->prot_ver > 0)
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver), &cont->prot_ver);
+
+ if (cont->son_trans_app_id && cont->son_trans_app_id_len > 0)
+ buf_ptr =
+ tvlv_put(buf_ptr, BSSGP_IE_SON_TRANSFER_APP_ID, cont->son_trans_app_id_len, cont->son_trans_app_id);
+
+ return (int)(buf_ptr - buf);
+}
+
+/*! Decode a RAN Information Error RIM Container (3GPP TS 48.018, table 11.3.62a.4.b).
+ * \param[out] user provided memory for decoded data struct.
+ * \param[in] buf user provided memory with the encoded value data of the IE.
+ * \returns 0 on success, -EINVAL on error. */
+int bssgp_dec_ran_inf_err_rim_cont(struct bssgp_ran_inf_err_rim_cont *cont, const uint8_t *buf, size_t len)
+{
+ int rc;
+ struct tlv_parsed tp;
+
+ memset(cont, 0, sizeof(*cont));
+
+ rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
+ if (rc < 0)
+ return -EINVAL;
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_APP_IDENTITY, sizeof(uint8_t)))
+ cont->app_id = TLVP_VAL(&tp, BSSGP_IE_RIM_APP_IDENTITY)[0];
+ else
+ return -EINVAL;
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_CAUSE, sizeof(cont->cause)))
+ cont->cause = TLVP_VAL(&tp, BSSGP_IE_CAUSE)[0];
+ else
+ return -EINVAL;
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver)))
+ cont->prot_ver = TLVP_VAL(&tp, BSSGP_IE_RIM_PROTOCOL_VERSION)[0];
+ else
+ cont->prot_ver = 1;
+
+ if (TLVP_PRESENT(&tp, BSSGP_IE_PDU_IN_ERROR)) {
+ cont->err_pdu = TLVP_VAL(&tp, BSSGP_IE_PDU_IN_ERROR);
+ cont->err_pdu_len = TLVP_LEN(&tp, BSSGP_IE_PDU_IN_ERROR);
+ } else {
+ return -EINVAL;
+ }
+
+ if (TLVP_PRES_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID, 1)) {
+ cont->son_trans_app_id = TLVP_VAL(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
+ cont->son_trans_app_id_len = TLVP_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
+ }
+
+ return 0;
+}
+
+/*! Encode a RAN Information Error RIM Container (3GPP TS 48.018, table 11.3.62a.4.b).
+ * \param[out] buf user provided memory for the generated value part of the IE.
+ * \param[in] cont user provided input data struct.
+ * \returns length of encoded octets, -EINVAL on error. */
+int bssgp_enc_ran_inf_err_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_err_rim_cont *cont)
+{
+ uint8_t *buf_ptr = buf;
+ uint8_t app_id_temp;
+
+ if (len <
+ TVLV_HDR_MAXLEN * 5 + sizeof(app_id_temp) + sizeof(cont->cause) + sizeof(cont->prot_ver) +
+ cont->err_pdu_len + cont->son_trans_app_id_len)
+ return -EINVAL;
+
+ app_id_temp = cont->app_id;
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_APP_IDENTITY, sizeof(app_id_temp), &app_id_temp);
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_CAUSE, sizeof(cont->cause), &cont->cause);
+
+ if (cont->prot_ver > 0)
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver), &cont->prot_ver);
+
+ if (cont->err_pdu && cont->err_pdu_len > 0)
+ buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_PDU_IN_ERROR, cont->err_pdu_len, cont->err_pdu);
+ else
+ return -EINVAL;
+
+ if (cont->son_trans_app_id && cont->son_trans_app_id_len > 0)
+ buf_ptr =
+ tvlv_put(buf_ptr, BSSGP_IE_SON_TRANSFER_APP_ID, cont->son_trans_app_id_len, cont->son_trans_app_id);
+
+ return (int)(buf_ptr - buf);
+}
+
+/*! Decode a RAN Information Application Error RIM Container (3GPP TS 48.018, table 11.3.62a.5.b).
+ * \param[out] user provided memory for decoded data struct.
+ * \param[in] buf user provided memory with the encoded value data of the IE.
+ * \returns 0 on success, -EINVAL on error. */
+int bssgp_dec_ran_inf_app_err_rim_cont(struct bssgp_ran_inf_app_err_rim_cont *cont, const uint8_t *buf, size_t len)
+{
+ int rc;
+ struct tlv_parsed tp;
+
+ memset(cont, 0, sizeof(*cont));
+
+ rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
+ if (rc < 0)
+ return -EINVAL;
+
+ rc = dec_rim_cont_common((struct bssgp_ran_inf_x_cont *)cont, &tp);
+ if (rc < 0)
+ return -EINVAL;
+
+ switch (cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ rc = bssgp_dec_app_err_cont_nacc(&cont->u.app_err_cont_nacc,
+ TLVP_VAL(&tp, BSSGP_IE_APP_ERROR_CONTAINER), TLVP_LEN(&tp,
+ BSSGP_IE_APP_ERROR_CONTAINER));
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ /* TODO: add parsers for Si3, MBMS, SON, UTRA-SI app containers */
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+/*! Encode a RAN Information Application Error RIM Container (3GPP TS 48.018, table 11.3.62a.5.b).
+ * \param[out] buf user provided memory for the generated value part of the IE.
+ * \param[in] cont user provided input data struct.
+ * \returns length of encoded octets, -EINVAL on error. */
+int bssgp_enc_ran_inf_app_err_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_app_err_rim_cont *cont)
+{
+ uint8_t *buf_ptr = buf;
+ int app_cont_len = 0;
+ int remaining_buf_len;
+
+ buf_ptr = enc_rim_cont_common(buf_ptr, len, (struct bssgp_ran_inf_x_cont *)cont);
+ if (!buf_ptr)
+ return -EINVAL;
+
+ remaining_buf_len = len - (int)(buf_ptr - buf);
+ if (remaining_buf_len <= 0)
+ return -EINVAL;
+
+ switch (cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ app_cont_len =
+ bssgp_enc_app_err_cont_nacc(buf_ptr + TVLV_HDR_LEN, remaining_buf_len - TVLV_HDR_MAXLEN,
+ &cont->u.app_err_cont_nacc);
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ /* TODO: add encoders for Si3, MBMS, SON, UTRA-SI app containers */
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+ if (app_cont_len < 0)
+ return -EINVAL;
+ buf_ptr = dub_tlvp_header(buf_ptr, BSSGP_IE_APP_ERROR_CONTAINER, app_cont_len);
+
+ return (int)(buf_ptr - buf);
+}
+
+/*! Parse a given message buffer into a rim-pdu struct.
+ * \param[out] pdu user provided memory for the resulting RAN INFORMATION PDU.
+ * \param[in] msg BSSGP message buffer that contains the encoded RAN INFORMATION PDU.
+ * \returns 0 on sccess, -EINVAL on error. */
+int bssgp_parse_rim_pdu(struct bssgp_ran_information_pdu *pdu, const struct msgb *msg)
+{
+ struct tlv_parsed tp[2];
+ struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *)msgb_bssgph(msg);
+ int data_len;
+ int rc;
+ uint16_t nsei = msgb_nsei(msg);
+
+ memset(pdu, 0, sizeof(*pdu));
+
+ data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
+ if (data_len < 0)
+ return -EINVAL;
+
+ rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, tp, ARRAY_SIZE(tp), bgph->pdu_type, bgph->data, data_len, 0, 0,
+ DLBSSGP, __func__);
+ if (rc < 0)
+ return -EINVAL;
+
+ if (TLVP_PRESENT(&tp[0], BSSGP_IE_RIM_ROUTING_INFO)) {
+ rc = bssgp_parse_rim_ri(&pdu->routing_info_dest, TLVP_VAL(&tp[0], BSSGP_IE_RIM_ROUTING_INFO),
+ TLVP_LEN(&tp[0], BSSGP_IE_RIM_ROUTING_INFO));
+ if (rc < 0) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) invalid Destination Cell Identifier IE\n", nsei);
+ return -EINVAL;
+ }
+ } else {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) missing Destination Cell Identifier IE\n", nsei);
+ return -EINVAL;
+ }
+
+ if (TLVP_PRESENT(&tp[1], BSSGP_IE_RIM_ROUTING_INFO)) {
+ rc = bssgp_parse_rim_ri(&pdu->routing_info_src, TLVP_VAL(&tp[1], BSSGP_IE_RIM_ROUTING_INFO),
+ TLVP_LEN(&tp[1], BSSGP_IE_RIM_ROUTING_INFO));
+ if (rc < 0) {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) invalid Destination Cell Identifier IE\n", nsei);
+ return -EINVAL;
+ }
+ } else {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) missing Source Cell Identifier IE\n", nsei);
+ return -EINVAL;
+ }
+
+ if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_REQ_RIM_CONTAINER))
+ pdu->rim_cont_iei = BSSGP_IE_RI_REQ_RIM_CONTAINER;
+ else if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_RIM_CONTAINER))
+ pdu->rim_cont_iei = BSSGP_IE_RI_RIM_CONTAINER;
+ else if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_APP_ERROR_RIM_CONT))
+ pdu->rim_cont_iei = BSSGP_IE_RI_APP_ERROR_RIM_CONT;
+ else if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_ACK_RIM_CONTAINER))
+ pdu->rim_cont_iei = BSSGP_IE_RI_ACK_RIM_CONTAINER;
+ else if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_ERROR_RIM_COINTAINER))
+ pdu->rim_cont_iei = BSSGP_IE_RI_ERROR_RIM_COINTAINER;
+ else {
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) missing or wrong RIM Container IE\n", nsei);
+ return -EINVAL;
+ }
+
+ pdu->rim_cont = TLVP_VAL(&tp[0], pdu->rim_cont_iei);
+ pdu->rim_cont_len = TLVP_LEN(&tp[0], pdu->rim_cont_iei);
+
+ /* Make sure the rim container field is not empty */
+ if (pdu->rim_cont_len < 1)
+ return -EINVAL;
+ if (!pdu->rim_cont)
+ return -EINVAL;
+
+ /* Note: It is not an error if we fail to parse the RIM container,
+ * since there are applications where parsing the RIM container
+ * is not necessary (routing). It is up to the API user to check
+ * the results. */
+ switch (pdu->rim_cont_iei) {
+ case BSSGP_IE_RI_REQ_RIM_CONTAINER:
+ rc = bssgp_dec_ran_inf_req_rim_cont(&pdu->decoded.req_rim_cont, pdu->rim_cont, pdu->rim_cont_len);
+ break;
+ case BSSGP_IE_RI_RIM_CONTAINER:
+ rc = bssgp_dec_ran_inf_rim_cont(&pdu->decoded.rim_cont, pdu->rim_cont, pdu->rim_cont_len);
+ break;
+ case BSSGP_IE_RI_APP_ERROR_RIM_CONT:
+ rc = bssgp_dec_ran_inf_app_err_rim_cont(&pdu->decoded.app_err_rim_cont, pdu->rim_cont,
+ pdu->rim_cont_len);
+ break;
+ case BSSGP_IE_RI_ACK_RIM_CONTAINER:
+ rc = bssgp_dec_ran_inf_ack_rim_cont(&pdu->decoded.ack_rim_cont, pdu->rim_cont, pdu->rim_cont_len);
+ break;
+ case BSSGP_IE_RI_ERROR_RIM_COINTAINER:
+ rc = bssgp_dec_ran_inf_err_rim_cont(&pdu->decoded.err_rim_cont, pdu->rim_cont, pdu->rim_cont_len);
+ break;
+ default:
+ LOGP(DLBSSGP, LOGL_DEBUG, "BSSGP RIM (NSEI=%u) cannot parse unknown RIM container.\n", nsei);
+ return 0;
+ }
+ if (rc < 0) {
+ LOGP(DLBSSGP, LOGL_DEBUG, "BSSGP RIM (NSEI=%u) unable to parse RIM container.\n", nsei);
+ return 0;
+ }
+ pdu->decoded_present = true;
+
+ return 0;
+}
+
+/*! Encode a given rim-pdu struct into a message buffer.
+ * \param[out] pdu user provided memory that contains the RAN INFORMATION PDU to encode.
+ * \returns BSSGP message buffer on sccess, NULL on error. */
+struct msgb *bssgp_encode_rim_pdu(const struct bssgp_ran_information_pdu *pdu)
+{
+ struct msgb *msg = bssgp_msgb_alloc();
+ struct bssgp_normal_hdr *bgph;
+ uint8_t rim_ri_buf[BSSGP_RIM_ROUTING_INFO_MAXLEN];
+ int rc;
+
+ if (!msg)
+ return NULL;
+ bgph = (struct bssgp_normal_hdr *)msgb_put(msg, sizeof(*bgph));
+
+ /* Set PDU type based on RIM container type */
+ switch (pdu->rim_cont_iei) {
+ case BSSGP_IE_RI_REQ_RIM_CONTAINER:
+ bgph->pdu_type = BSSGP_PDUT_RAN_INFO_REQ;
+ break;
+ case BSSGP_IE_RI_RIM_CONTAINER:
+ bgph->pdu_type = BSSGP_PDUT_RAN_INFO;
+ break;
+ case BSSGP_IE_RI_APP_ERROR_RIM_CONT:
+ bgph->pdu_type = BSSGP_PDUT_RAN_INFO_APP_ERROR;
+ break;
+ case BSSGP_IE_RI_ACK_RIM_CONTAINER:
+ bgph->pdu_type = BSSGP_PDUT_RAN_INFO_ACK;
+ break;
+ case BSSGP_IE_RI_ERROR_RIM_COINTAINER:
+ bgph->pdu_type = BSSGP_PDUT_RAN_INFO_ERROR;
+ break;
+ default:
+ /* The caller must correctly specify the container type! */
+ OSMO_ASSERT(false);
+ }
+
+ /* Put RIM routing information */
+ rc = bssgp_create_rim_ri(rim_ri_buf, &pdu->routing_info_dest);
+ if (rc < 0 || rc > BSSGP_RIM_ROUTING_INFO_MAXLEN)
+ goto error;
+ msgb_tvlv_put(msg, BSSGP_IE_RIM_ROUTING_INFO, rc, rim_ri_buf);
+ rc = bssgp_create_rim_ri(rim_ri_buf, &pdu->routing_info_src);
+ if (rc < 0 || rc > BSSGP_RIM_ROUTING_INFO_MAXLEN)
+ goto error;
+ msgb_tvlv_put(msg, BSSGP_IE_RIM_ROUTING_INFO, rc, rim_ri_buf);
+
+ /* Put RIM container */
+ if (pdu->decoded_present) {
+ uint8_t *rim_cont_buf = talloc_zero_size(msg, msg->data_len);
+ if (!rim_cont_buf)
+ goto error;
+
+ switch (pdu->rim_cont_iei) {
+ case BSSGP_IE_RI_REQ_RIM_CONTAINER:
+ rc = bssgp_enc_ran_inf_req_rim_cont(rim_cont_buf, msg->data_len, &pdu->decoded.req_rim_cont);
+ break;
+ case BSSGP_IE_RI_RIM_CONTAINER:
+ rc = bssgp_enc_ran_inf_rim_cont(rim_cont_buf, msg->data_len, &pdu->decoded.rim_cont);
+ break;
+ case BSSGP_IE_RI_APP_ERROR_RIM_CONT:
+ rc = bssgp_enc_ran_inf_app_err_rim_cont(rim_cont_buf, msg->data_len,
+ &pdu->decoded.app_err_rim_cont);
+ break;
+ case BSSGP_IE_RI_ACK_RIM_CONTAINER:
+ rc = bssgp_enc_ran_inf_ack_rim_cont(rim_cont_buf, msg->data_len, &pdu->decoded.ack_rim_cont);
+ break;
+ case BSSGP_IE_RI_ERROR_RIM_COINTAINER:
+ rc = bssgp_enc_ran_inf_err_rim_cont(rim_cont_buf, msg->data_len, &pdu->decoded.err_rim_cont);
+ break;
+ default:
+ /* The API user must set the iei properly! */
+ OSMO_ASSERT(false);
+ }
+ if (rc < 0) {
+ talloc_free(rim_cont_buf);
+ goto error;
+ }
+
+ msgb_tvlv_put(msg, pdu->rim_cont_iei, rc, rim_cont_buf);
+ talloc_free(rim_cont_buf);
+ } else {
+ /* Make sure the RIM container is actually present. */
+ OSMO_ASSERT(pdu->rim_cont_iei != 0 && pdu->rim_cont_len > 0 && pdu->rim_cont);
+ msgb_tvlv_put(msg, pdu->rim_cont_iei, pdu->rim_cont_len, pdu->rim_cont);
+ }
+
+ return msg;
+error:
+ msgb_free(msg);
+ return 0;
+}
+
+/*! Send RIM RAN INFORMATION REQUEST via BSSGP (3GPP TS 48.018, section 10.6.1).
+ * \param[in] pdu user provided memory for the RAN INFORMATION PDU to be sent.
+ * \param[in] nsei BSSGP network service entity identifier (NSEI).
+ * \returns 0 on sccess, -EINVAL on error. */
+int bssgp_tx_rim(const struct bssgp_ran_information_pdu *pdu, uint16_t nsei)
+{
+ struct msgb *msg;
+ struct bssgp_normal_hdr *bgph;
+ char ri_src_str[64];
+ char ri_dest_str[64];
+
+ /* Encode RIM PDU into mesage buffer */
+ msg = bssgp_encode_rim_pdu(pdu);
+ if (!msg) {
+ LOGP(DLBSSGP, LOGL_ERROR,
+ "BSSGP RIM (NSEI=%u) unable to encode BSSGP RIM PDU\n", nsei);
+ return -EINVAL;
+ }
+
+ msgb_nsei(msg) = nsei;
+ msgb_bvci(msg) = 0; /* Signalling */
+
+ bgph = (struct bssgp_normal_hdr *)msgb_bssgph(msg);
+ DEBUGP(DLBSSGP, "BSSGP BVCI=0 NSEI=%u Tx RIM-PDU:%s, src=%s, dest=%s\n",
+ nsei, bssgp_pdu_str(bgph->pdu_type),
+ bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &pdu->routing_info_src),
+ bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &pdu->routing_info_dest));
+
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
+}
+
+/*! Send encoded RAN TRANSPARENT CONTAINER via BSSGP (3GPP TS 29.060, section 7.7.43).
+ * \param[in] msg user provided memory for the encoded RAN TRANSPARENT CONTAINER to be sent.
+ * (this function will take ownership of msg).
+ * \param[in] nsei BSSGP network service entity identifier (NSEI).
+ * \returns 0 on sccess, -EINVAL on error. */
+int bssgp_tx_rim_encoded(struct msgb *msg, uint16_t nsei)
+{
+ struct bssgp_normal_hdr *bgph;
+
+ msgb_nsei(msg) = nsei;
+ msgb_bvci(msg) = 0; /* Signalling */
+
+ bgph = (struct bssgp_normal_hdr *)msgb_bssgph(msg);
+ DEBUGP(DLBSSGP, "BSSGP BVCI=0 NSEI=%u Tx RIM-PDU:%s\n",
+ nsei, bssgp_pdu_str(bgph->pdu_type));
+
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
+}
+
+/* For internal use only (called from gprs_bssgp.c) */
+int bssgp_rx_rim(struct msgb *msg, struct tlv_parsed *tp, uint16_t bvci)
+{
+ struct osmo_bssgp_prim nmp;
+ uint16_t nsei = msgb_nsei(msg);
+ struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *)msgb_bssgph(msg);
+ enum bssgp_prim prim;
+ char ri_src_str[64];
+ char ri_dest_str[64];
+
+ /* Specify PRIM type based on the RIM PDU */
+ switch (bgph->pdu_type) {
+ case BSSGP_PDUT_RAN_INFO:
+ case BSSGP_PDUT_RAN_INFO_REQ:
+ case BSSGP_PDUT_RAN_INFO_ACK:
+ case BSSGP_PDUT_RAN_INFO_ERROR:
+ case BSSGP_PDUT_RAN_INFO_APP_ERROR:
+ prim = PRIM_BSSGP_RIM_PDU_TRANSFER;
+ break;
+ default:
+ /* Caller already makes sure that this can't happen. */
+ OSMO_ASSERT(false);
+ }
+
+ /* Send BSSGP RIM indication to NM */
+ memset(&nmp, 0, sizeof(nmp));
+ nmp.nsei = nsei;
+ nmp.bvci = bvci;
+ nmp.tp = tp;
+ if (bssgp_parse_rim_pdu(&nmp.u.rim_pdu, msg) < 0)
+ return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
+ DEBUGP(DLBSSGP, "BSSGP BVCI=%u Rx RIM-PDU:%s, src=%s, dest=%s\n",
+ bvci, bssgp_pdu_str(bgph->pdu_type),
+ bssgp_rim_ri_name_buf(ri_src_str, sizeof(ri_src_str), &nmp.u.rim_pdu.routing_info_src),
+ bssgp_rim_ri_name_buf(ri_dest_str, sizeof(ri_dest_str), &nmp.u.rim_pdu.routing_info_dest));
+ osmo_prim_init(&nmp.oph, SAP_BSSGP_RIM, prim, PRIM_OP_INDICATION, msg);
+ bssgp_prim_cb(&nmp.oph, NULL);
+
+ return 0;
+}
diff --git a/src/gb/gprs_bssgp_util.c b/src/gb/gprs_bssgp_util.c
index 669dfb86..92896c1f 100644
--- a/src/gb/gprs_bssgp_util.c
+++ b/src/gb/gprs_bssgp_util.c
@@ -32,7 +32,7 @@
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns.h>
-#include "common_vty.h"
+#include "gprs_bssgp_internal.h"
struct gprs_ns_inst *bssgp_nsi;
@@ -43,7 +43,7 @@ struct gprs_ns_inst *bssgp_nsi;
static const struct value_string bssgp_cause_strings[] = {
{ BSSGP_CAUSE_PROC_OVERLOAD, "Processor overload" },
{ BSSGP_CAUSE_EQUIP_FAIL, "Equipment Failure" },
- { BSSGP_CAUSE_TRASIT_NET_FAIL, "Transit netowkr service failure" },
+ { BSSGP_CAUSE_TRASIT_NET_FAIL, "Transit network service failure" },
{ BSSGP_CAUSE_CAPA_GREATER_0KPBS, "Transmission capacity modified" },
{ BSSGP_CAUSE_UNKNOWN_MS, "Unknown MS" },
{ BSSGP_CAUSE_UNKNOWN_BVCI, "Unknown BVCI" },
@@ -106,6 +106,8 @@ static const struct value_string bssgp_pdu_strings[] = {
{ BSSGP_PDUT_UL_UNITDATA, "UL-UNITDATA" },
{ BSSGP_PDUT_RA_CAPABILITY, "RA-CAPABILITY" },
{ BSSGP_PDUT_PTM_UNITDATA, "PTM-UNITDATA" },
+ { BSSGP_PDUT_DL_MMBS_UNITDATA, "DL-MBMS-UNITDATA" },
+ { BSSGP_PDUT_UL_MMBS_UNITDATA, "UL-MBMS-UNITDATA" },
{ BSSGP_PDUT_PAGING_PS, "PAGING-PS" },
{ BSSGP_PDUT_PAGING_CS, "PAGING-CS" },
{ BSSGP_PDUT_RA_CAPA_UDPATE, "RA-CAPABILITY-UPDATE" },
@@ -117,6 +119,10 @@ static const struct value_string bssgp_pdu_strings[] = {
{ BSSGP_PDUT_RESUME, "RESUME" },
{ BSSGP_PDUT_RESUME_ACK, "RESUME-ACK" },
{ BSSGP_PDUT_RESUME_NACK, "RESUME-NACK" },
+ { BSSGP_PDUT_DUMMY_PAGING_PS, "DUMMY-PAGING-PS" },
+ { BSSGP_PDUT_DUMMY_PAGING_PS_RESP, "DUMMY-PAGING-PS-RESP" },
+ { BSSGP_PDUT_MS_REGISTR_ENQ, "MS-REGISTRATION-ENQ" },
+ { BSSGP_PDUT_MS_REGISTR_ENQ_RESP, "MS-REGISTRATION-ENQ-RESP" },
{ BSSGP_PDUT_BVC_BLOCK, "BVC-BLOCK" },
{ BSSGP_PDUT_BVC_BLOCK_ACK, "BVC-BLOCK-ACK" },
{ BSSGP_PDUT_BVC_RESET, "BVC-RESET" },
@@ -130,8 +136,11 @@ static const struct value_string bssgp_pdu_strings[] = {
{ BSSGP_PDUT_FLUSH_LL, "FLUSH-LL" },
{ BSSGP_PDUT_FLUSH_LL_ACK, "FLUSH-LL-ACK" },
{ BSSGP_PDUT_LLC_DISCARD, "LLC DISCARDED" },
+ { BSSGP_PDUT_FLOW_CONTROL_PFC, "FLOW-CONTROL-PFC" },
+ { BSSGP_PDUT_FLOW_CONTROL_PFC_ACK, "FLOW-CONTROL-PFC-ACK" },
{ BSSGP_PDUT_SGSN_INVOKE_TRACE, "SGSN-INVOKE-TRACE" },
{ BSSGP_PDUT_STATUS, "STATUS" },
+ { BSSGP_PDUT_OVERLOAD, "OVERLOAD" },
{ BSSGP_PDUT_DOWNLOAD_BSS_PFC, "DOWNLOAD-BSS-PFC" },
{ BSSGP_PDUT_CREATE_BSS_PFC, "CREATE-BSS-PFC" },
{ BSSGP_PDUT_CREATE_BSS_PFC_ACK, "CREATE-BSS-PFC-ACK" },
@@ -140,9 +149,338 @@ static const struct value_string bssgp_pdu_strings[] = {
{ BSSGP_PDUT_MODIFY_BSS_PFC_ACK, "MODIFY-BSS-PFC-ACK" },
{ BSSGP_PDUT_DELETE_BSS_PFC, "DELETE-BSS-PFC" },
{ BSSGP_PDUT_DELETE_BSS_PFC_ACK, "DELETE-BSS-PFC-ACK" },
+ { BSSGP_PDUT_DELETE_BSS_PFC_REQ, "DELETE-BSS-PFC-REQ" },
+ { BSSGP_PDUT_PS_HO_REQUIRED, "PS-HO-REQUIRED" },
+ { BSSGP_PDUT_PS_HO_REQUIRED_ACK, "PS-HO-REQUIRED-ACK" },
+ { BSSGP_PDUT_PS_HO_REQUIRED_NACK, "PS-HO-REQUIRED-NACK" },
+ { BSSGP_PDUT_PS_HO_REQUEST, "PS-HO-REQUEST" },
+ { BSSGP_PDUT_PS_HO_REQUEST_ACK, "PS-HO-REQUEST-ACK" },
+ { BSSGP_PDUT_PS_HO_REQUEST_NACK, "PS-HO-REQUEST-NACK" },
+ { BSSGP_PDUT_PS_HO_COMPLETE, "PS-HO-COMPLETE" },
+ { BSSGP_PDUT_PS_HO_CANCEL, "PS-HO-CANCEL" },
+ { BSSGP_PDUT_PS_HO_COMPLETE_ACK, "PS-HO-COMPLETE-ACK" },
+ { BSSGP_PDUT_PERFORM_LOC_REQ, "PERFORM-LOC-REQ" },
+ { BSSGP_PDUT_PERFORM_LOC_RESP, "PERFORM-LOC-RESP" },
+ { BSSGP_PDUT_PERFORM_LOC_ABORT, "PERFORM-LOC-ABORT" },
+ { BSSGP_PDUT_POSITION_COMMAND, "POSITION-COMMAND" },
+ { BSSGP_PDUT_POSITION_RESPONSE, "POSITION-RESPONSE" },
+ { BSSGP_PDUT_RAN_INFO, "RAN-INFO" },
+ { BSSGP_PDUT_RAN_INFO_REQ, "RAN-INFO-REQ" },
+ { BSSGP_PDUT_RAN_INFO_ACK, "RAN-INFO-ACK" },
+ { BSSGP_PDUT_RAN_INFO_ERROR, "RAN-INFO-ERROR" },
+ { BSSGP_PDUT_RAN_INFO_APP_ERROR, "RAN-INFO-APP-ERROR" },
+ { BSSGP_PDUT_MBMS_START_REQ, "MBMS-START-REQ" },
+ { BSSGP_PDUT_MBMS_START_RESP, "MBMS-START-RESP" },
+ { BSSGP_PDUT_MBMS_STOP_REQ, "MBMS-STOP-REQ" },
+ { BSSGP_PDUT_MBMS_STOP_RESP, "MBMS-STOP-RESP" },
+ { BSSGP_PDUT_MBMS_UPDATE_REQ, "MBMS-UPDATE-REQ" },
+ { BSSGP_PDUT_MBMS_UPDATE_RESP, "MBMS-UPDATE-RESP" },
{ 0, NULL },
};
+static const uint8_t dl_ud_ies[] = { BSSGP_IE_PDU_LIFETIME };
+static const uint8_t ul_ud_ies[] = { BSSGP_IE_CELL_ID };
+static const uint8_t ra_cap_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_MS_RADIO_ACCESS_CAP };
+static const uint8_t dl_mb_ud_ies[] = { BSSGP_IE_PDU_LIFETIME, BSSGP_IE_TMGI, BSSGP_IE_LLC_PDU };
+static const uint8_t ul_mb_ud_ies[] = { BSSGP_IE_PDU_LIFETIME, BSSGP_IE_TMGI, BSSGP_IE_LLC_PDU };
+static const uint8_t pag_ps_ies[] = { BSSGP_IE_IMSI, BSSGP_IE_QOS_PROFILE };
+static const uint8_t pag_cs_ies[] = { BSSGP_IE_IMSI, BSSGP_IE_DRX_PARAMS };
+static const uint8_t ra_cap_upd_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_TAG };
+static const uint8_t ra_cap_upd_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_TAG, BSSGP_IE_RA_CAP_UPD_CAUSE };
+static const uint8_t rad_sts_ies[] = { BSSGP_IE_RADIO_CAUSE };
+static const uint8_t suspend_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_ROUTEING_AREA };
+static const uint8_t suspend_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_ROUTEING_AREA, BSSGP_IE_SUSPEND_REF_NR };
+static const uint8_t suspend_nack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_ROUTEING_AREA };
+static const uint8_t resume_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_ROUTEING_AREA, BSSGP_IE_SUSPEND_REF_NR };
+static const uint8_t resume_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_ROUTEING_AREA };
+static const uint8_t resume_nack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_ROUTEING_AREA };
+static const uint8_t d_pag_ps_ies[] = { BSSGP_IE_IMSI };
+static const uint8_t d_pag_ps_resp_ies[] = { BSSGP_IE_IMSI, BSSGP_IE_T_UNTIL_NEXT_PAGING };
+static const uint8_t d_pag_ps_rej_ies[] = { BSSGP_IE_IMSI, BSSGP_IE_T_UNTIL_NEXT_PAGING };
+static const uint8_t ms_reg_enq_ies[] = { BSSGP_IE_IMSI };
+static const uint8_t ms_reg_enq_res_ies[] = { BSSGP_IE_IMSI };
+static const uint8_t flush_ll_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_BVCI };
+static const uint8_t flush_ll_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_FLUSH_ACTION };
+static const uint8_t llc_disc_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_LLC_FRAMES_DISCARDED, BSSGP_IE_BVCI,
+ BSSGP_IE_NUM_OCT_AFF };
+static const uint8_t fc_bvc_ies[] = { BSSGP_IE_TAG, BSSGP_IE_BVC_BUCKET_SIZE, BSSGP_IE_BUCKET_LEAK_RATE,
+ BSSGP_IE_BMAX_DEFAULT_MS, BSSGP_IE_R_DEFAULT_MS };
+static const uint8_t fc_bvc_ack_ies[] = { BSSGP_IE_TAG };
+static const uint8_t fc_ms_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_TAG, BSSGP_IE_MS_BUCKET_SIZE,
+ BSSGP_IE_BUCKET_LEAK_RATE };
+static const uint8_t fc_ms_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_TAG };
+static const uint8_t block_ies[] = { BSSGP_IE_BVCI, BSSGP_IE_CAUSE };
+static const uint8_t block_ack_ies[] = { BSSGP_IE_BVCI };
+static const uint8_t unblock_ies[] = { BSSGP_IE_BVCI };
+static const uint8_t unblock_ack_ies[] = { BSSGP_IE_BVCI };
+static const uint8_t reset_ies[] = { BSSGP_IE_BVCI, BSSGP_IE_CAUSE };
+static const uint8_t reset_ack_ies[] = { BSSGP_IE_BVCI };
+static const uint8_t status_ies[] = { BSSGP_IE_CAUSE };
+static const uint8_t inv_trc_ies[] = { BSSGP_IE_TRACE_TYPE, BSSGP_IE_TRACE_REFERENC };
+static const uint8_t dl_bss_pfc_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_PACKET_FLOW_ID };
+static const uint8_t crt_bss_pfc_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_PACKET_FLOW_ID,
+ BSSGP_IE_PACKET_FLOW_TIMER, BSSGP_IE_AGG_BSS_QOS_PROFILE };
+static const uint8_t crt_bss_pfc_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_PACKET_FLOW_ID,
+ BSSGP_IE_AGG_BSS_QOS_PROFILE };
+static const uint8_t crt_bss_pfc_nack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_PACKET_FLOW_ID, BSSGP_IE_CAUSE };
+static const uint8_t mod_bss_pfc_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_PACKET_FLOW_ID,
+ BSSGP_IE_AGG_BSS_QOS_PROFILE };
+static const uint8_t mod_bss_pfc_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_PACKET_FLOW_ID,
+ BSSGP_IE_PACKET_FLOW_TIMER, BSSGP_IE_AGG_BSS_QOS_PROFILE };
+static const uint8_t del_bss_pfc_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_PACKET_FLOW_ID };
+static const uint8_t del_bss_pfc_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_PACKET_FLOW_ID };
+static const uint8_t fc_pfc_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_TAG, BSSGP_IE_PFC_FLOW_CTRL_PARAMS };
+static const uint8_t fc_pfc_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_TAG };
+static const uint8_t del_bss_pfc_req_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_PACKET_FLOW_ID, BSSGP_IE_CAUSE };
+static const uint8_t ps_ho_required_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_CAUSE, BSSGP_IE_CELL_ID,
+ BSSGP_IE_ACTIVE_PFC_LIST };
+static const uint8_t ps_ho_required_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_LIST_OF_SETUP_PFC };
+static const uint8_t ps_ho_required_nack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_CAUSE };
+static const uint8_t ps_ho_request_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_IMSI, BSSGP_IE_CAUSE,
+ BSSGP_IE_CELL_ID, BSSGP_IE_SBSS_TO_TBSS_TR_CONT,
+ BSSGP_IE_PFC_TO_BE_SETUP_LIST };
+static const uint8_t ps_ho_request_ack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_LIST_OF_SETUP_PFC,
+ BSSGP_IE_TBSS_TO_SBSS_TR_CONT };
+static const uint8_t ps_ho_request_nack_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_CAUSE };
+static const uint8_t ps_ho_compl_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_IMSI };
+static const uint8_t ps_ho_cancel_ies[] = { BSSGP_IE_TLLI, BSSGP_IE_CAUSE, BSSGP_IE_CELL_ID };
+static const uint8_t ps_ho_compl_ack_ies[] = { BSSGP_IE_TLLI };
+static const uint8_t overload_ies[] = { BSSGP_IE_PRIO_CLASS_IND };
+static const uint8_t rinfo_ies[] = { BSSGP_IE_RIM_ROUTING_INFO, BSSGP_IE_RI_RIM_CONTAINER };
+static const uint8_t rinfo_req_ies[] = { BSSGP_IE_RIM_ROUTING_INFO, BSSGP_IE_RI_REQ_RIM_CONTAINER };
+static const uint8_t rinfo_ack_ies[] = { BSSGP_IE_RIM_ROUTING_INFO, BSSGP_IE_RI_ACK_RIM_CONTAINER };
+static const uint8_t rinfo_err_ies[] = { BSSGP_IE_RIM_ROUTING_INFO, BSSGP_IE_RI_ERROR_RIM_COINTAINER };
+static const uint8_t rinfo_aerr_ies[] = { BSSGP_IE_RIM_ROUTING_INFO, BSSGP_IE_RI_APP_ERROR_RIM_CONT };
+
+#define DL BSSGP_PDUF_DL
+#define UL BSSGP_PDUF_UL
+#define SIG BSSGP_PDUF_SIG
+#define PTP BSSGP_PDUF_PTP
+#define PTM BSSGP_PDUF_PTM
+
+const struct osmo_tlv_prot_def osmo_pdef_bssgp = {
+ .name = "BSSGP",
+ .tlv_def = &tvlv_att_def,
+ .msg_def = {
+ [BSSGP_PDUT_DL_UNITDATA] = MSG_DEF("DL-UNITDATA", dl_ud_ies, DL|PTP),
+ [BSSGP_PDUT_UL_UNITDATA] = MSG_DEF("UL-UNITDATA", ul_ud_ies, UL|PTP),
+ [BSSGP_PDUT_RA_CAPABILITY] = MSG_DEF("RA-CAPABILITY", ra_cap_ies, DL|PTP),
+ [BSSGP_PDUT_DL_MMBS_UNITDATA] = MSG_DEF("DL-MBMS-UNITDATA", dl_mb_ud_ies, DL|PTM),
+ [BSSGP_PDUT_UL_MMBS_UNITDATA] = MSG_DEF("UL-MBMS-UNITDATA", ul_mb_ud_ies, UL|PTM),
+ [BSSGP_PDUT_PAGING_PS] = MSG_DEF("PAGING-PS", pag_ps_ies, DL|PTP|SIG),
+ [BSSGP_PDUT_PAGING_CS] = MSG_DEF("PAGING-CS", pag_cs_ies, DL|PTP|SIG),
+ [BSSGP_PDUT_RA_CAPA_UDPATE] = MSG_DEF("RA-CAPABILITY-UPDATE", ra_cap_upd_ies, UL|PTP),
+ [BSSGP_PDUT_RA_CAPA_UPDATE_ACK] = MSG_DEF("RA-CAPABILITY-UPDATE-ACK", ra_cap_upd_ack_ies, DL|PTP),
+ [BSSGP_PDUT_RADIO_STATUS] = MSG_DEF("RADIO-STATUS", rad_sts_ies, UL|PTP),
+ [BSSGP_PDUT_SUSPEND] = MSG_DEF("SUSPEND", suspend_ies, UL|SIG),
+ [BSSGP_PDUT_SUSPEND_ACK] = MSG_DEF("SUSPEND-ACK", suspend_ack_ies, DL|SIG),
+ [BSSGP_PDUT_SUSPEND_NACK] = MSG_DEF("SUSPEND-NACK", suspend_nack_ies, DL|SIG),
+ [BSSGP_PDUT_RESUME] = MSG_DEF("RESUME", resume_ies, UL|SIG),
+ [BSSGP_PDUT_RESUME_ACK] = MSG_DEF("RESUME-ACK", resume_ack_ies, DL|SIG),
+ [BSSGP_PDUT_RESUME_NACK] = MSG_DEF("RESUME-NACK", resume_nack_ies, DL|SIG),
+ [BSSGP_PDUT_DUMMY_PAGING_PS] = MSG_DEF("DUMMY-PAGING-PS", d_pag_ps_ies, DL|SIG|PTP),
+ [BSSGP_PDUT_DUMMY_PAGING_PS_RESP] = MSG_DEF("DUMMY-PAGING-PS-RESP", d_pag_ps_resp_ies, UL|SIG|PTP),
+ [BSSGP_PDUT_PAGING_PS_REJECT] = MSG_DEF("PAGING-PS-REJ", d_pag_ps_rej_ies, UL|SIG|PTP),
+ [BSSGP_PDUT_MS_REGISTR_ENQ] = MSG_DEF("MS-REGISRATION-ENQ", ms_reg_enq_ies, UL|SIG),
+ [BSSGP_PDUT_MS_REGISTR_ENQ_RESP] = MSG_DEF("MS-REGISRATION-ENQ-RESP", ms_reg_enq_res_ies, DL|SIG),
+ [BSSGP_PDUT_FLUSH_LL] = MSG_DEF("FLUSH-LL", flush_ll_ies, DL|SIG),
+ [BSSGP_PDUT_FLUSH_LL_ACK] = MSG_DEF("FLUSH-LL-ACK", flush_ll_ack_ies, UL|SIG),
+ [BSSGP_PDUT_LLC_DISCARD] = MSG_DEF("LLC-DISCARDED", llc_disc_ies, UL|SIG),
+ [BSSGP_PDUT_FLOW_CONTROL_BVC] = MSG_DEF("FC-BVC", fc_bvc_ies, UL|PTP),
+ [BSSGP_PDUT_FLOW_CONTROL_BVC_ACK] = MSG_DEF("FC-BVC-ACK", fc_bvc_ack_ies, DL|PTP),
+ [BSSGP_PDUT_FLOW_CONTROL_MS] = MSG_DEF("FC-MS", fc_ms_ies, UL|PTP),
+ [BSSGP_PDUT_FLOW_CONTROL_MS_ACK] = MSG_DEF("FC-MS-ACK", fc_ms_ack_ies, DL|PTP),
+ [BSSGP_PDUT_BVC_BLOCK] = MSG_DEF("BVC-BLOCK", block_ies, UL|SIG),
+ [BSSGP_PDUT_BVC_BLOCK_ACK] = MSG_DEF("BVC-BLOCK-ACK", block_ack_ies, DL|SIG),
+ [BSSGP_PDUT_BVC_UNBLOCK] = MSG_DEF("BVC-UNBLOCK", unblock_ies, UL|SIG),
+ [BSSGP_PDUT_BVC_UNBLOCK_ACK] = MSG_DEF("BVC-UNBLOCK-ACK", unblock_ack_ies, DL|SIG),
+ [BSSGP_PDUT_BVC_RESET] = MSG_DEF("BVC-RESET", reset_ies, UL|DL|SIG|PTP),
+ [BSSGP_PDUT_BVC_RESET_ACK] = MSG_DEF("BVC-RESET-ACK", reset_ack_ies, UL|DL|SIG|PTP),
+ [BSSGP_PDUT_STATUS] = MSG_DEF("STATUS", status_ies, UL|DL|PTP|SIG|PTM),
+ [BSSGP_PDUT_SGSN_INVOKE_TRACE] = MSG_DEF("SGSN-INVOKE-TRACE", inv_trc_ies, DL|SIG),
+ [BSSGP_PDUT_DOWNLOAD_BSS_PFC] = MSG_DEF("DOWNLOAD-BSS-PFC", dl_bss_pfc_ies, UL|PTP),
+ [BSSGP_PDUT_CREATE_BSS_PFC] = MSG_DEF("CREATE-BSS-PFC", crt_bss_pfc_ies, DL|PTP),
+ [BSSGP_PDUT_CREATE_BSS_PFC_ACK] = MSG_DEF("CREATE-BSS-PFC-ACK", crt_bss_pfc_ack_ies, UL|PTP),
+ [BSSGP_PDUT_CREATE_BSS_PFC_NACK] = MSG_DEF("CREATE-BSS-PFC-NACK", crt_bss_pfc_nack_ies, UL|PTP),
+ [BSSGP_PDUT_MODIFY_BSS_PFC] = MSG_DEF("MODIFY-BSS-PFC", mod_bss_pfc_ies, DL|PTP),
+ [BSSGP_PDUT_MODIFY_BSS_PFC_ACK] = MSG_DEF("MODIFY-BSS-PFC-ACK", mod_bss_pfc_ack_ies, UL|PTP),
+ [BSSGP_PDUT_DELETE_BSS_PFC] = MSG_DEF("DELETE-BSS-PFC", del_bss_pfc_ies, DL|PTP),
+ [BSSGP_PDUT_DELETE_BSS_PFC_ACK] = MSG_DEF("DELETE-BSS-PFC-ACK", del_bss_pfc_ack_ies, UL|PTP),
+ [BSSGP_PDUT_FLOW_CONTROL_PFC] = MSG_DEF("FC-PFC", fc_pfc_ies, UL|PTP),
+ [BSSGP_PDUT_FLOW_CONTROL_PFC_ACK] = MSG_DEF("FC-PFC-ACK", fc_pfc_ack_ies, DL|PTP),
+ [BSSGP_PDUT_DELETE_BSS_PFC_REQ] = MSG_DEF("DELETE-BSS-PFC-REQ", del_bss_pfc_req_ies, UL|PTP),
+ [BSSGP_PDUT_PS_HO_REQUIRED] = MSG_DEF("PS-HO-REQUIRED", ps_ho_required_ies, UL|PTP),
+ [BSSGP_PDUT_PS_HO_REQUIRED_ACK] = MSG_DEF("PS-HO-REQUIRED-ACK", ps_ho_required_ack_ies, DL|PTP),
+ [BSSGP_PDUT_PS_HO_REQUIRED_NACK] = MSG_DEF("PS-HO-REQUIRED-NACK", ps_ho_required_nack_ies, DL|PTP),
+ [BSSGP_PDUT_PS_HO_REQUEST] = MSG_DEF("PS-HO-REQUEST", ps_ho_request_ies, DL|PTP),
+ [BSSGP_PDUT_PS_HO_REQUEST_ACK] = MSG_DEF("PS-HO-REQUEST-ACK", ps_ho_request_ack_ies, UL|PTP),
+ [BSSGP_PDUT_PS_HO_REQUEST_NACK] = MSG_DEF("PS-HO-REQUEST-NACK", ps_ho_request_nack_ies, UL|PTP),
+ [BSSGP_PDUT_PS_HO_COMPLETE] = MSG_DEF("PS-HO-COMPLETE", ps_ho_compl_ies, UL|PTP),
+ [BSSGP_PDUT_PS_HO_CANCEL] = MSG_DEF("PS-HO-CANCEL", ps_ho_cancel_ies, UL|PTP),
+ [BSSGP_PDUT_PS_HO_COMPLETE_ACK] = MSG_DEF("PS-HO-COMPLETE-ACK", ps_ho_compl_ack_ies, DL|PTP),
+ [BSSGP_PDUT_OVERLOAD] = MSG_DEF("OVERLOAD", overload_ies, DL|SIG),
+ /* TODO: Messages on LCS SAP */
+ /* Messages on RIM SAP */
+ [BSSGP_PDUT_RAN_INFO] = MSG_DEF("RAN-INFORMATION", rinfo_ies, DL|UL|SIG),
+ [BSSGP_PDUT_RAN_INFO_REQ] = MSG_DEF("RAN-INFORMATION-REQUEST", rinfo_req_ies, DL|UL|SIG),
+ [BSSGP_PDUT_RAN_INFO_ACK] = MSG_DEF("RAN-INFORMATION-ACK", rinfo_ack_ies, DL|UL|SIG),
+ [BSSGP_PDUT_RAN_INFO_ERROR] = MSG_DEF("RAN-INFORMATION-ERROR", rinfo_err_ies, DL|UL|SIG),
+ [BSSGP_PDUT_RAN_INFO_APP_ERROR] = MSG_DEF("RAN-INFORMATION-APP-ERROR", rinfo_aerr_ies, DL|UL|SIG),
+ /* TODO: Messages on MBMS SAP */
+ },
+ .ie_def = {
+ [BSSGP_IE_ALIGNMENT] = { 0, "Alignment Octets" },
+ [BSSGP_IE_BMAX_DEFAULT_MS] = { 2, "Bmax default MS" },
+ [BSSGP_IE_BSS_AREA_ID] = { 1, "BSS Area Indication" },
+ [BSSGP_IE_BUCKET_LEAK_RATE] = { 2, "Bucket Leak Rate (R)" },
+ [BSSGP_IE_BVC_BUCKET_SIZE] = { 2, "BVC Bucket Size" },
+ [BSSGP_IE_BVCI] = { 2, "BVCI" },
+ [BSSGP_IE_BVC_MEASUREMENT] = {2, "BVC Measurement" },
+ [BSSGP_IE_CAUSE] = { 1, "Cause" },
+ [BSSGP_IE_CELL_ID] = { 8, "Cell Identifier" },
+ [BSSGP_IE_CHAN_NEEDED] = { 1, "Channel Needed" },
+ [BSSGP_IE_DRX_PARAMS] = { 2, "DRX Parameters" },
+ [BSSGP_IE_EMLPP_PRIO] = { 3, "eMLPP Priority" },
+ [BSSGP_IE_FLUSH_ACTION] = { 1, "Flush Action" },
+ [BSSGP_IE_IMSI] = { 1, "Mobile Identity" },
+ [BSSGP_IE_LLC_PDU] = { 0, "LLC-PDU" },
+ [BSSGP_IE_LLC_FRAMES_DISCARDED] = { 1, "LLC Frames Discarded" },
+ [BSSGP_IE_LOCATION_AREA] = { 5, "Location Area" },
+ [BSSGP_IE_LSA_ID_LIST] = { 3, "LSA Identifier List" },
+ [BSSGP_IE_LSA_INFORMATION] = { 5, "LSA Information" },
+ [BSSGP_IE_MOBILE_ID] = { 1, "Mobile Identity" },
+ [BSSGP_IE_MS_BUCKET_SIZE] = { 2, "MS Bucket Size" },
+ [BSSGP_IE_MS_RADIO_ACCESS_CAP] = { 1, "MS Radio Access Capability" },
+ [BSSGP_IE_OMC_ID] = { 1, "OMC Id" },
+ [BSSGP_IE_PDU_IN_ERROR] = { 0, "PDU In Error" },
+ [BSSGP_IE_PDU_LIFETIME] = { 2, "PDU Lifetime" },
+ [BSSGP_IE_PRIORITY] = { 1, "Priority" },
+ [BSSGP_IE_QOS_PROFILE] = { 3, "QoS Profile" },
+ [BSSGP_IE_RADIO_CAUSE] = { 1, "Radio Cause" },
+ [BSSGP_IE_RA_CAP_UPD_CAUSE] = { 1, "RA-Cap-UPD-Cause" },
+ [BSSGP_IE_ROUTEING_AREA] = { 6, "Routeing Area" },
+ [BSSGP_IE_R_DEFAULT_MS] = { 2, "R_default_MS" },
+ [BSSGP_IE_SUSPEND_REF_NR] = { 1, "Suspend Reference Number" },
+ [BSSGP_IE_TAG] = { 1, "Tag" },
+ [BSSGP_IE_TLLI] = { 4, "TLLI" },
+ [BSSGP_IE_TMSI] = { 4, "TMSI" },
+ [BSSGP_IE_TRACE_REFERENC] = { 2, "Trace Reference" },
+ [BSSGP_IE_TRACE_TYPE] = { 1, "Trace Type" },
+ [BSSGP_IE_TRANSACTION_ID] = { 2, "Transaction Id" },
+ [BSSGP_IE_TRIGGER_ID] = { 1, "Trigger Id" },
+ [BSSGP_IE_NUM_OCT_AFF] = { 3, "Number of octets affected" },
+ [BSSGP_IE_PACKET_FLOW_ID] = { 1, "Packet Flow Identifier (PFI)" },
+ [BSSGP_IE_AGG_BSS_QOS_PROFILE] = { 14, "Aggregate BSS QoS Profile" },
+ [BSSGP_IE_PACKET_FLOW_TIMER] = { 1, "GPRS Timer" },
+ [BSSGP_IE_FEATURE_BITMAP] = { 1, "Feature Bitmap" },
+ [BSSGP_IE_BUCKET_FULL_RATIO] = { 1, "Bucket Full Ratio" },
+ [BSSGP_IE_SERVICE_UTRAN_CCO] = { 1, "Service UTRAN COO" },
+ [BSSGP_IE_NSEI] = { 2, "NSEI" },
+ [BSSGP_IE_RRLP_APDU] = { 1, "RLLP APDU" },
+ [BSSGP_IE_LCS_QOS] = { 4, "LCS QoS" },
+ [BSSGP_IE_LCS_CLIENT_TYPE] = { 1, "LCS Client Type" },
+ [BSSGP_IE_REQUESTED_GPS_AST_DATA] = { 4, "Requested GPS Assistance Data" },
+ [BSSGP_IE_LOCATION_TYPE] = { 2, "Location Type" },
+ [BSSGP_IE_LOCATION_ESTIMATE] = { 1, "Location Estimate" },
+ [BSSGP_IE_POSITIONING_DATA] = { 1, "Positioning Data" },
+ [BSSGP_IE_DECIPHERING_KEYS] = { 15, "Deciphering Keys" },
+ [BSSGP_IE_LCS_PRIORITY] = { 1, "LCS Priority" },
+ [BSSGP_IE_LCS_CAUSE] = { 1, "LCS Cause" },
+ [BSSGP_IE_LCS_CAPABILITY] = { 1, "LCS Capability" },
+ [BSSGP_IE_RRLP_FLAGS] = { 1, "RRLP Flags" },
+ [BSSGP_IE_RIM_APP_IDENTITY] = { 1, "RIM Application Identity" },
+ [BSSGP_IE_RIM_SEQ_NR] = { 4, "RIM Sequence Number" },
+ [BSSGP_IE_RIM_REQ_APP_CONTAINER] = { 12, "RIM-REQUEST RIM Container" },
+ [BSSGP_IE_RAN_INFO_APP_CONTAINER] = { 12, "RAN-INFORMATION RIM Container" },
+ [BSSGP_IE_RI_ACK_RIM_CONTAINER] = { 9, "RAN-INFORMATION-ACK RIM Container" },
+ [BSSGP_IE_RI_ERROR_RIM_COINTAINER] = { 9, "RAN-INFOIRMATION-ERROR RIM Container" },
+ [BSSGP_IE_RI_APP_ERROR_RIM_CONT] = { 14, "RAN-INFORMATION-APP-ERROR RIM Container" },
+ [BSSGP_IE_RIM_PDU_INDICATIONS] = { 1, "RIM PDU Indications" },
+ [BSSGP_IE_RIM_PROTOCOL_VERSION] = { 1, "RIM Protocol Version Number" },
+ [BSSGP_IE_PFC_FLOW_CTRL_PARAMS] = { 7, "PFC FLow Control Parameters" },
+ [BSSGP_IE_GLOBAL_CN_ID] = { 5, "Global CN-Id" },
+ [BSSGP_IE_RIM_ROUTING_INFO] = { 1, "RIM Routing Information" },
+ [BSSGP_IE_MBMS_SESSION_ID] = { 0, "MBMS Session Identity" },
+ [BSSGP_IE_MBMS_SESSION_DURATION] = { 0, "MBMS Session Duration" },
+ [BSSGP_IE_MBMS_SA_ID_LIST] = { 3, "MBMS Service Area Identity List" },
+ [BSSGP_IE_MBMS_RESPONSE] = { 1, "MBMS Response" },
+ [BSSGP_IE_MBMS_RA_LIST] = { 9, "MBMS Routing Area List" },
+ [BSSGP_IE_MBMS_SESSION_INFO] = { 1, "MBMS Session Information" },
+ [BSSGP_IE_TMGI] = { 6, "TMGI" },
+ [BSSGP_IE_MBMS_STOP_CAUSE] = { 1, "MBM Stop Cause" },
+ [BSSGP_IE_SBSS_TO_TBSS_TR_CONT] = { 7, "Source BSS to Target BSS Transparent Container" },
+ [BSSGP_IE_TBSS_TO_SBSS_TR_CONT] = { 0, "Target BSS to Source BSS Transparent Container" },
+ [BSSGP_IE_NAS_CONT_FOR_PS_HO] = { 0, "NAS container for PS Handover" },
+ [BSSGP_IE_PFC_TO_BE_SETUP_LIST] = { 9, "PFCs to be set-up list" },
+ [BSSGP_IE_LIST_OF_SETUP_PFC] = { 1, "List of set-up PFCs" },
+ [BSSGP_IE_EXT_FEATURE_BITMAP] = { 1, "Extended Feature Bitmap" },
+ [BSSGP_IE_SRC_TO_TGT_TR_CONT] = { 0, "Source to Target Transparent Container" },
+ [BSSGP_IE_TGT_TO_SRC_TR_CONT] = { 0, "Target to Source Transparent Container" },
+ [BSSGP_IE_NC_ID] = { 8, "RNC Identifier" },
+ [BSSGP_IE_PAGE_MODE] = { 1, "Page Mode" },
+ [BSSGP_IE_CONTAINER_ID] = { 1, "Container ID" },
+ [BSSGP_IE_GLOBAL_TFI] = { 1, "Global TFI" },
+ [BSSGP_IE_IMEI] = { 1, "IMEI" },
+ [BSSGP_IE_TIME_TO_MBMS_DATA_XFR] = { 1, "Time to MBMS Data Transfer" },
+ [BSSGP_IE_MBMS_SESSION_REP_NR] = { 1, "MBMS Session Repetition Number" },
+ [BSSGP_IE_INTER_RAT_HO_INFO] = { 0, "Inter RAT Handover Info" },
+ [BSSGP_IE_PS_HO_COMMAND] = { 0, "PS Handover Command" },
+ [BSSGP_IE_PS_HO_INDICATIONS] = { 1, "PS Handover Indications" },
+ [BSSGP_IE_SI_PSI_CONTAINER] = { 1, "SI/PSI Container" },
+ [BSSGP_IE_ACTIVE_PFC_LIST] = { 2, "Active PFCs List" },
+ [BSSGP_IE_VELOCITY_DATA] = { 0, "Velocity Data" },
+ [BSSGP_IE_DTM_HO_COMMAND] = { 0, "DTM Handover Command" },
+ [BSSGP_IE_CS_INDICATION] = { 1, "CS Indication" },
+ [BSSGP_IE_RQD_GANNS_AST_DATA] = { 0, "Requested GANSS Assistance Data" },
+ [BSSGP_IE_GANSS_LOCATION_TYPE] = { 1, "GANSS Location Type" },
+ [BSSGP_IE_GANSS_POSITIONING_DATA] = { 0, "GANSS Positioning Data" },
+ [BSSGP_IE_FLOW_CTRL_GRANULARITY] = { 1, "Flow Control Granularity" },
+ [BSSGP_IE_ENB_ID] = { 6, "eNB Identifier" },
+ [BSSGP_IE_EUTRAN_IRAT_HO_INFO] = { 0, "E-UTRAN Inter RAT Handover Info" },
+ [BSSGP_IE_SUB_PID4RAT_FREQ_PRIO] = { 1, "Subscriber Profile ID for RAT/Frequency priority" },
+ [BSSGP_IE_REQ4IRAT_HO_INFO] = { 1, "Request for Inter-RAT Handover Info" },
+ [BSSGP_IE_RELIABLE_IRAT_HO_INFO] = { 1, "Reliable Inter-RAT Handover Info" },
+ [BSSGP_IE_SON_TRANSFER_APP_ID] = { 0, "SON Transfer Application Identity" },
+ [BSSGP_IE_CSG_ID] = { 5, "CSG Identifier" },
+ [BSSGP_IE_TAC] = { 3, "Tracking Area Code" },
+ [BSSGP_IE_REDIRECT_ATTEMPT_FLAG] = { 1, "Redirect Attempt Flag" },
+ [BSSGP_IE_REDIRECTION_INDICATION] = { 1, "Redirection Indication" },
+ [BSSGP_IE_REDIRECTION_COMPLETED] = { 1, "Redirection Completed" },
+ [BSSGP_IE_UNCONF_SEND_STATE_VAR] = { 2, "Unconfirmed send state variable" },
+ [BSSGP_IE_IRAT_MEASUREMENT_CONF] = { 10, "IRAT Measurement Configuration" },
+ [BSSGP_IE_SCI] = { 1, "SCI" },
+ [BSSGP_IE_GGSN_PGW_LOCATION] = { 1, "GGSN/P-GW Location" },
+ [BSSGP_IE_SELECTED_PLMN_ID] = { 3, "Selected PLMN ID" },
+ [BSSGP_IE_PRIO_CLASS_IND] = { 1, "Priority Class Indication" },
+ [BSSGP_IE_SOURCE_CELL_ID] = { 6, "Source Cell ID" },
+ [BSSGP_IE_IRAT_MEAS_CFG_E_EARFCN] = { 10, "IRAT Measurement Configuration (extended E-ARFCNs)" },
+ [BSSGP_IE_EDRX_PARAMETERS] = { 1, "eDRX Parameters" },
+ [BSSGP_IE_T_UNTIL_NEXT_PAGING] = { 2, "Time Until Next Paging Occasion" },
+ [BSSGP_IE_COVERAGE_CLASS] = { 1, "Coverage Class" },
+ [BSSGP_IE_PAGING_ATTEMPT_INFO] = { 1, "Paging Attempt Information" },
+ [BSSGP_IE_EXCEPTION_REPORT_FLAG] = { 1, "Exception Report Flag" },
+ [BSSGP_IE_OLD_RA_ID] = { 6, "Old Routing Area Identification" },
+ [BSSGP_IE_ATTACH_IND] = { 1, "Attach Indicator" },
+ [BSSGP_IE_PLMN_ID] = { 3, "PLMN Identity" },
+ [BSSGP_IE_MME_QUERY] = { 1, "MME Query" },
+ [BSSGP_IE_SGSN_GROUP_ID] = { 3, "SGSN Group Identity" },
+ [BSSGP_IE_ADDITIONAL_PTMSI] = { 4, "Additional P-TMSI" },
+ [BSSGP_IE_UE_USAGE_TYPE] = { 1, "UE Usage Type" },
+ [BSSGP_IE_MLAT_TIMER] = { 1, "Multilateration Timer" },
+ [BSSGP_IE_MLAT_TA] = { 2, "Multilateration Timing Advance" },
+ [BSSGP_IE_MS_SYNC_ACCURACY] = { 1, "MS Sync Accuracy" },
+ [BSSGP_IE_BTS_RX_ACCURACY_LVL] = { 1, "BTS Reception Accuracy Level" },
+ [BSSGP_IE_TA_REQ] = { 1, "Timing Advance Request (TAR)" },
+ },
+};
+
+#undef DL
+#undef UL
+#undef SIG
+#undef PTP
+#undef PTM
+
+
const char *bssgp_cause_str(enum gprs_bssgp_cause cause)
{
return get_value_string(bssgp_cause_strings, cause);
@@ -210,7 +548,7 @@ int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei,
_bvci = osmo_htons(bvci);
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* Chapter 10.4.14: Status */
@@ -224,17 +562,17 @@ int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg)
cause is either "BVCI blocked" or "BVCI unknown" */
if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED) {
if (bvci == NULL)
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP Tx STATUS, cause=%s: "
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP Tx STATUS, cause=%s: "
"missing conditional BVCI\n",
bssgp_cause_str(cause));
} else {
if (bvci != NULL)
- LOGP(DBSSGP, LOGL_ERROR, "BSSGP Tx STATUS, cause=%s: "
+ LOGP(DLBSSGP, LOGL_ERROR, "BSSGP Tx STATUS, cause=%s: "
"unexpected conditional BVCI\n",
bssgp_cause_str(cause));
}
- LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Tx STATUS, cause=%s\n",
+ LOGP(DLBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Tx STATUS, cause=%s\n",
bvci ? *bvci : 0, bssgp_cause_str(cause));
msgb_nsei(msg) = msgb_nsei(orig_msg);
msgb_bvci(msg) = 0;
@@ -248,5 +586,5 @@ int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg)
msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR,
msgb_bssgp_len(orig_msg), msgb_bssgph(orig_msg));
- return gprs_ns_sendmsg(bssgp_nsi, msg);
+ return bssgp_ns_send(bssgp_ns_send_data, msg);
}
diff --git a/src/gb/gprs_bssgp_vty.c b/src/gb/gprs_bssgp_vty.c
index 5dab94e7..d04641d9 100644
--- a/src/gb/gprs_bssgp_vty.c
+++ b/src/gb/gprs_bssgp_vty.c
@@ -44,8 +44,6 @@
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/misc.h>
-#include "common_vty.h"
-
static void log_set_bvc_filter(struct log_target *target,
struct bssgp_bvc_ctx *bctx)
{
@@ -207,15 +205,15 @@ DEFUN(logging_fltr_bvc,
int bssgp_vty_init(void)
{
- install_element_ve(&show_bssgp_cmd);
- install_element_ve(&show_bssgp_stats_cmd);
- install_element_ve(&show_bvc_cmd);
- install_element_ve(&logging_fltr_bvc_cmd);
- install_element_ve(&bvc_reset_cmd);
+ install_lib_element_ve(&show_bssgp_cmd);
+ install_lib_element_ve(&show_bssgp_stats_cmd);
+ install_lib_element_ve(&show_bvc_cmd);
+ install_lib_element_ve(&logging_fltr_bvc_cmd);
+ install_lib_element_ve(&bvc_reset_cmd);
- install_element(CFG_LOG_NODE, &logging_fltr_bvc_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_fltr_bvc_cmd);
- install_element(CONFIG_NODE, &cfg_bssgp_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_bssgp_cmd);
install_node(&bssgp_node, config_write_bssgp);
return 0;
diff --git a/src/gb/gprs_ns.c b/src/gb/gprs_ns.c
index c77ebb37..304fe7c1 100644
--- a/src/gb/gprs_ns.c
+++ b/src/gb/gprs_ns.c
@@ -29,7 +29,7 @@
* @{
*
* GPRS Networks Service (NS) messages on the Gb interface
- * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
*
* Some introduction into NS: NS is used typically on top of frame relay,
* but in the ip.access world it is encapsulated in UDP packets. It serves
@@ -51,7 +51,7 @@
*
* There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will
* therefore identify the BSSGP virtual connection by a BVCI passed down to NS.
- * NS then has to firgure out which NSVC's are responsible for this BVCI.
+ * NS then has to figure out which NSVC's are responsible for this BVCI.
* Those mappings are administratively configured.
*
* This implementation has the following limitations:
@@ -318,7 +318,8 @@ struct gprs_nsvc *gprs_nsvc_create2(struct gprs_ns_inst *nsi, uint16_t nsvci,
return NULL;
}
- LOGP(DNS, LOGL_INFO, "NSVCI=%u Creating NS-VC\n", nsvci);
+ LOGP(DNS, LOGL_INFO, "NSVCI=%u Creating NS-VC with Signal weight %u, Data weight %u\n",
+ nsvci, sig_weight, data_weight);
nsvc = talloc_zero(nsi, struct gprs_nsvc);
if (!nsvc)
@@ -326,7 +327,7 @@ struct gprs_nsvc *gprs_nsvc_create2(struct gprs_ns_inst *nsi, uint16_t nsvci,
nsvc->nsvci = nsvci;
nsvc->nsvci_is_valid = 1;
/* before RESET procedure: BLOCKED and DEAD */
- if (nsi->bss_sns_fi)
+ if (nsi->bss_sns_fi || !nsi->nsip.use_reset_block_unblock)
ns_set_state(nsvc, 0);
else
ns_set_state(nsvc, NSE_S_BLOCKED);
@@ -346,19 +347,12 @@ struct gprs_nsvc *gprs_nsvc_create2(struct gprs_ns_inst *nsi, uint16_t nsvci,
return nsvc;
}
-/*! Old API for creating a NS-VC. Uses gprs_nsvc_create2 with fixed weights. */
-struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci)
-{
- return gprs_nsvc_create2(nsi, nsvci, 1, 1);
-}
-
/*! Delete given NS-VC
* \param[in] nsvc gprs_nsvc to be deleted
*/
void gprs_nsvc_delete(struct gprs_nsvc *nsvc)
{
- if (osmo_timer_pending(&nsvc->timer))
- osmo_timer_del(&nsvc->timer);
+ osmo_timer_del(&nsvc->timer);
llist_del(&nsvc->list);
rate_ctr_group_free(nsvc->ctrg);
osmo_stat_item_group_free(nsvc->statg);
@@ -491,8 +485,8 @@ static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg)
}
/* Increment number of Uplink bytes */
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_OUT]);
- rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_OUT], msgb_l2len(msg));
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_PKTS_OUT));
+ rate_ctr_add(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BYTES_OUT), msgb_l2len(msg));
switch (nsvc->ll) {
case GPRS_NS_LL_UDP:
@@ -538,7 +532,7 @@ static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type)
/*! Transmit a NS-RESET on a given NSVC
* \param[in] nsvc NS-VC used for transmission
- * \paam[in] cause Numeric NS cause value
+ * \param[in] cause Numeric NS cause value
*/
int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause)
{
@@ -624,7 +618,7 @@ int gprs_ns_tx_status(struct gprs_nsvc *nsvc, uint8_t cause,
return gprs_ns_tx(nsvc, msg);
}
-/*! Transmit a NS-BLOCK on a tiven NS-VC
+/*! Transmit a NS-BLOCK on a given NS-VC
* \param[in] nsvc NS-VC on which the NS-BLOCK is to be transmitted
* \param[in] cause Numeric NS Cause value
* \returns 0 in case of success
@@ -648,7 +642,7 @@ int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause)
/* be conservative and mark it as blocked even now! */
ns_mark_blocked(nsvc);
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BLOCKED));
msg->l2h = msgb_put(msg, sizeof(*nsh));
nsh = (struct gprs_ns_hdr *) msg->l2h;
@@ -755,8 +749,7 @@ static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode)
nsvc->nsei, get_value_string(timer_mode_strs, mode),
seconds);
- if (osmo_timer_pending(&nsvc->timer))
- osmo_timer_del(&nsvc->timer);
+ osmo_timer_del(&nsvc->timer);
osmo_gettimeofday(&nsvc->timer_started, NULL);
nsvc->timer_mode = mode;
@@ -786,15 +779,15 @@ static void gprs_ns_timer_cb(void *data)
switch (nsvc->timer_mode) {
case NSVC_TIMER_TNS_ALIVE:
/* Tns-alive case: we expired without response ! */
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_LOST_ALIVE]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_LOST_ALIVE));
nsvc->alive_retries++;
if (nsvc->alive_retries >
nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
/* mark as dead (and blocked unless IP-SNS) */
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]);
- if (!nsvc->nsi->bss_sns_fi) {
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_DEAD));
+ if (!nsvc->nsi->bss_sns_fi && nsvc->nsi->nsip.use_reset_block_unblock) {
ns_set_state(nsvc, NSE_S_BLOCKED);
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BLOCKED));
} else
ns_set_state(nsvc, 0);
LOGP(DNS, LOGL_NOTICE,
@@ -803,7 +796,7 @@ static void gprs_ns_timer_cb(void *data)
nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]);
ns_osmo_signal_dispatch(nsvc, S_NS_ALIVE_EXP, 0);
/* FIXME: should we send this signal in case of SNS? */
- if (!nsvc->nsi->bss_sns_fi)
+ if (!nsvc->nsi->bss_sns_fi && nsvc->nsi->nsip.use_reset_block_unblock)
ns_osmo_signal_dispatch(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED);
return;
}
@@ -821,7 +814,7 @@ static void gprs_ns_timer_cb(void *data)
nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE);
break;
case NSVC_TIMER_TNS_RESET:
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_LOST_RESET]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_LOST_RESET));
if (!(nsvc->state & NSE_S_RESET))
LOGP(DNS, LOGL_NOTICE,
"NSEI=%u Reset timed out but RESET flag is not set\n",
@@ -1078,10 +1071,10 @@ int gprs_ns_tx_sns_size_ack(struct gprs_nsvc *nsvc, uint8_t *cause)
* \param[in] msg struct msgb to be trasnmitted
*
* This function obtains the NS-VC by the msgb_nsei(msg) and then checks
- * if the NS-VC is ALIVEV and not BLOCKED. After that, it adds a NS
+ * if the NS-VC is ALIVE and not BLOCKED. After that, it adds a NS
* header for the NS-UNITDATA message type and sends it off.
*
- * Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive
+ * Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive
*/
int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg)
{
@@ -1256,7 +1249,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
NS_PDUT_RESET,
NS_IE_VCI);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_VCI]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_INV_VCI));
gprs_ns_tx_reset_ack(*nsvc);
return 0;
}
@@ -1268,7 +1261,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc **nsvc, struct msgb *msg)
nsvci, (*nsvc)->nsvci,
gprs_ns_ll_str(*nsvc));
orig_nsvc = *nsvc;
- *nsvc = gprs_nsvc_create((*nsvc)->nsi, nsvci);
+ *nsvc = gprs_nsvc_create2((*nsvc)->nsi, nsvci, 1, 1);
(*nsvc)->nsei = nsei;
}
}
@@ -1282,14 +1275,14 @@ static int gprs_ns_rx_reset(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
NS_PDUT_RESET,
NS_IE_NSEI);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_NSEI]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_INV_NSEI));
rc = gprs_ns_tx_reset_ack(*nsvc);
CHECK_TX_RC(rc, *nsvc);
return 0;
}
/* NSEI has changed */
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_NSEI_CHG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_NSEI_CHG));
(*nsvc)->nsei = nsei;
}
@@ -1297,7 +1290,7 @@ static int gprs_ns_rx_reset(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_set_state(*nsvc, NSE_S_BLOCKED | NSE_S_ALIVE);
if (orig_nsvc) {
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_REPLACED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_REPLACED));
ns_osmo_signal_dispatch_replaced(*nsvc, orig_nsvc);
/* Update the ll info fields */
@@ -1395,7 +1388,7 @@ static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
NS_PDUT_RESET_ACK,
NS_IE_VCI);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_VCI]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_INV_VCI));
LOGP(DNS, LOGL_ERROR,
"NS RESET ACK Unknown NS-VCI %d (%s NSEI=%d) "
"from %s\n",
@@ -1406,7 +1399,7 @@ static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg)
}
/* Notify others */
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_REPLACED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_REPLACED));
ns_osmo_signal_dispatch_replaced(*nsvc, orig_nsvc);
/* Update the ll info fields */
@@ -1420,7 +1413,7 @@ static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg)
ns_osmo_signal_dispatch_mismatch(*nsvc, msg,
NS_PDUT_RESET_ACK,
NS_IE_NSEI);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_NSEI]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_INV_NSEI));
LOGP(DNS, LOGL_ERROR,
"NS RESET ACK Unknown NSEI %d (NS-VCI=%u) from %s\n",
nsei, nsvci, gprs_ns_ll_str(*nsvc));
@@ -1428,14 +1421,14 @@ static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg)
}
/* NSEI has changed */
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_NSEI_CHG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_NSEI_CHG));
(*nsvc)->nsei = nsei;
}
/* Mark NS-VC as blocked and alive */
ns_set_state(*nsvc, NSE_S_BLOCKED | NSE_S_ALIVE);
ns_set_remote_state(*nsvc, NSE_S_BLOCKED | NSE_S_ALIVE);
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_BLOCKED));
if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) {
/* stop RESET timer */
osmo_timer_del(&(*nsvc)->timer);
@@ -1476,7 +1469,7 @@ static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg)
//nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI);
ns_osmo_signal_dispatch(nsvc, S_NS_BLOCK, *cause);
- rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BLOCKED));
return gprs_ns_tx_block_ack(nsvc);
}
@@ -1690,7 +1683,7 @@ int gprs_ns_vc_create(struct gprs_ns_inst *nsi, struct msgb *msg,
* simply have changed addresses, or it is a SGSN */
existing_nsvc = gprs_nsvc_by_nsvci(nsi, nsvci);
if (!existing_nsvc) {
- *new_nsvc = gprs_nsvc_create(nsi, 0xffff);
+ *new_nsvc = gprs_nsvc_create2(nsi, 0xffff, 1, 1);
(*new_nsvc)->nsvci_is_valid = 0;
log_set_context(LOG_CTX_GB_NSVC, *new_nsvc);
gprs_ns_ll_copy(*new_nsvc, fallback_nsvc);
@@ -1710,7 +1703,7 @@ int gprs_ns_vc_create(struct gprs_ns_inst *nsi, struct msgb *msg,
existing_nsvc->nsei = nsei;
/* Do statistics */
- rate_ctr_inc(&existing_nsvc->ctrg->ctr[NS_CTR_NSEI_CHG]);
+ rate_ctr_inc(rate_ctr_group_get_ctr(existing_nsvc->ctrg, NS_CTR_NSEI_CHG));
}
*new_nsvc = existing_nsvc;
@@ -1739,8 +1732,8 @@ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
log_set_context(LOG_CTX_GB_NSVC, *nsvc);
/* Increment number of Incoming bytes */
- rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_PKTS_IN]);
- rate_ctr_add(&(*nsvc)->ctrg->ctr[NS_CTR_BYTES_IN], msgb_l2len(msg));
+ rate_ctr_inc(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_PKTS_IN));
+ rate_ctr_add(rate_ctr_group_get_ctr((*nsvc)->ctrg, NS_CTR_BYTES_IN), msgb_l2len(msg));
if (nsvc_is_not_used(*nsvc) && !ns_is_sns(nsh->pdu_type) && nsh->pdu_type != NS_PDUT_STATUS) {
LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx %s on unused/pre-configured endpoint, discarding\n",
@@ -1757,13 +1750,17 @@ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg,
* fine. */
if ((*nsvc)->state == NSE_S_BLOCKED)
rc = gprs_nsvc_reset((*nsvc), NS_CAUSE_PDU_INCOMP_PSTATE);
- else if (!((*nsvc)->state & NSE_S_RESET))
+ else if (!((*nsvc)->state & NSE_S_RESET)) {
+ /* if we're not alive, we cannot transmit the ACK; set ALIVE */
+ if (!((*nsvc)->state & NSE_S_ALIVE))
+ ns_mark_alive(*nsvc);
rc = gprs_ns_tx_alive_ack(*nsvc);
+ }
break;
case NS_PDUT_ALIVE_ACK:
ns_mark_alive(*nsvc);
if ((*nsvc)->timer_mode == NSVC_TIMER_TNS_ALIVE)
- osmo_stat_item_set((*nsvc)->statg->items[NS_STAT_ALIVE_DELAY],
+ osmo_stat_item_set(osmo_stat_item_group_get_item((*nsvc)->statg, NS_STAT_ALIVE_DELAY),
nsvc_timer_elapsed_ms(*nsvc));
/* stop Tns-alive and start Tns-test */
nsvc_start_timer(*nsvc, NSVC_TIMER_TNS_TEST);
@@ -1884,13 +1881,18 @@ static bool gprs_sns_fsm_registered = false;
*/
struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb, void *ctx)
{
- struct gprs_ns_inst *nsi = talloc_zero(ctx, struct gprs_ns_inst);
+ struct gprs_ns_inst *nsi;
if (!gprs_sns_fsm_registered) {
- gprs_sns_init();
+ int rc = gprs_sns_init();
+ if (rc < 0)
+ return NULL;
gprs_sns_fsm_registered = true;
}
+ nsi = talloc_zero(ctx, struct gprs_ns_inst);
+ if (!nsi)
+ return NULL;
nsi->cb = cb;
INIT_LLIST_HEAD(&nsi->gprs_nsvcs);
nsi->timeout[NS_TOUT_TNS_BLOCK] = 3;
@@ -1904,11 +1906,16 @@ struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb, void *ctx)
/* Create the dummy NSVC that we use for sending
* messages to non-existant/unknown NS-VC's */
- nsi->unknown_nsvc = gprs_nsvc_create(nsi, 0xfffe);
+ nsi->unknown_nsvc = gprs_nsvc_create2(nsi, 0xfffe, 1, 1);
nsi->unknown_nsvc->nsvci_is_valid = 0;
llist_del(&nsi->unknown_nsvc->list);
INIT_LLIST_HEAD(&nsi->unknown_nsvc->list);
+ /* By default we are in IPA compatible mode, that is we use NS-RESET, NS-BLOCK
+ * and NS-UNBLOCK procedures even for an IP/UDP based Gb interface, in violation
+ * of 3GPP TS 48.016. */
+ nsi->nsip.use_reset_block_unblock = true;
+
return nsi;
}
@@ -2059,7 +2066,8 @@ int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi)
osmo_sock_init2_ofd(&nsi->nsip.fd, AF_INET, SOCK_DGRAM,
IPPROTO_UDP, inet_ntoa(in),
nsi->nsip.local_port, remote_str,
- nsi->nsip.remote_port, OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
+ nsi->nsip.remote_port, OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT |
+ OSMO_SOCK_F_BIND | OSMO_SOCK_F_DSCP(nsi->nsip.dscp));
LOGP(DNS, LOGL_NOTICE,
"Listening for nsip packets from %s:%u on %s:%u\n",
@@ -2067,7 +2075,8 @@ int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi)
} else {
/* Accept UDP packets from any source IP/Port */
ret = osmo_sock_init_ofd(&nsi->nsip.fd, AF_INET, SOCK_DGRAM,
- IPPROTO_UDP, inet_ntoa(in), nsi->nsip.local_port, OSMO_SOCK_F_BIND);
+ IPPROTO_UDP, inet_ntoa(in), nsi->nsip.local_port,
+ OSMO_SOCK_F_BIND | OSMO_SOCK_F_DSCP(nsi->nsip.dscp));
LOGP(DNS, LOGL_NOTICE, "Listening for nsip packets on %s:%u\n", inet_ntoa(in), nsi->nsip.local_port);
}
@@ -2078,13 +2087,6 @@ int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi)
return ret;
}
- ret = setsockopt(nsi->nsip.fd.fd, IPPROTO_IP, IP_TOS,
- &nsi->nsip.dscp, sizeof(nsi->nsip.dscp));
- if (ret < 0)
- LOGP(DNS, LOGL_ERROR,
- "Failed to set the DSCP to %d with ret(%d) errno(%d)\n",
- nsi->nsip.dscp, ret, errno);
-
LOGP(DNS, LOGL_NOTICE, "NS UDP socket at %s:%d\n", inet_ntoa(in), nsi->nsip.local_port);
return ret;
@@ -2140,7 +2142,7 @@ struct gprs_nsvc *gprs_ns_nsip_connect(struct gprs_ns_inst *nsi,
nsvc = gprs_nsvc_by_rem_addr(nsi, dest);
if (!nsvc)
- nsvc = gprs_nsvc_create(nsi, nsvci);
+ nsvc = gprs_nsvc_create2(nsi, nsvci, 1, 1);
nsvc->ip.bts_addr = *dest;
nsvc->nsei = nsei;
nsvc->remote_end_is_sgsn = 1;
diff --git a/src/gb/gprs_ns2.c b/src/gb/gprs_ns2.c
new file mode 100644
index 00000000..4e496c1f
--- /dev/null
+++ b/src/gb/gprs_ns2.c
@@ -0,0 +1,1695 @@
+/*! \file gprs_ns2.c
+ * GPRS Networks Service (NS) messages on the Gb interface.
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * as well as its successor 3GPP TS 48.016 */
+
+/* (C) 2009-2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2016-2017,2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*! \addtogroup libgb
+ * @{
+ *
+ * GPRS Networks Service (NS) messages on the Gb interface
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ *
+ * Some introduction into NS: NS is used typically on top of frame relay,
+ * but in the ip.access world it is encapsulated in UDP packets. It serves
+ * as an intermediate shim betwen BSSGP and the underlying medium. It doesn't
+ * do much, apart from providing congestion notification and status indication.
+ *
+ * Terms:
+ *
+ * NS Network Service
+ * NSVC NS Virtual Connection
+ * NSEI NS Entity Identifier
+ * NSVL NS Virtual Link
+ * NSVLI NS Virtual Link Identifier
+ * BVC BSSGP Virtual Connection
+ * BVCI BSSGP Virtual Connection Identifier
+ * NSVCG NS Virtual Connection Goup
+ * Blocked NS-VC cannot be used for user traffic
+ * Alive Ability of a NS-VC to provide communication
+ *
+ * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will
+ * therefore identify the BSSGP virtual connection by a BVCI passed down to NS.
+ * NS then has to figure out which NSVC's are responsible for this BVCI.
+ * Those mappings are administratively configured.
+ *
+ * This implementation has the following limitations:
+ * - NSVCI 65535 and 65534 are reserved for internal use
+ * - There are no BLOCK and UNBLOCK timers (yet?)
+ *
+ * \file gprs_ns2.c */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gprs/gprs_msgb.h>
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gsm/tlv.h>
+
+#include "gprs_ns2_internal.h"
+
+#define ns_set_state(ns_, st_) ns_set_state_with_log(ns_, st_, false, __FILE__, __LINE__)
+#define ns_set_remote_state(ns_, st_) ns_set_state_with_log(ns_, st_, true, __FILE__, __LINE__)
+#define ns_mark_blocked(ns_) ns_set_state(ns_, (ns_)->state | NSE_S_BLOCKED)
+#define ns_mark_unblocked(ns_) ns_set_state(ns_, (ns_)->state & (~NSE_S_BLOCKED));
+#define ns_mark_alive(ns_) ns_set_state(ns_, (ns_)->state | NSE_S_ALIVE)
+#define ns_mark_dead(ns_) ns_set_state(ns_, (ns_)->state & (~NSE_S_ALIVE));
+
+/* HACK: The NS_IE_IP_ADDR does not follow any known TLV rules.
+ * Since it's a hard ABI break to implement 16 bit tag with fixed length entries to workaround it,
+ * the parser will be called with ns_att_tlvdef1 and if it's failed with ns_att_tlvdef2.
+ * The TLV parser depends on 8bit tag in many places.
+ * The NS_IE_IP_ADDR is only valid for SNS_ACK SNS_ADD and SNS_DELETE.
+ */
+static const struct tlv_definition ns_att_tlvdef1 = {
+ .def = {
+ [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_IPv4_LIST] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_IPv6_LIST] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_MAX_NR_NSVC] = { TLV_TYPE_FIXED, 2 },
+ [NS_IE_IPv4_EP_NR] = { TLV_TYPE_FIXED, 2 },
+ [NS_IE_IPv6_EP_NR] = { TLV_TYPE_FIXED, 2 },
+ [NS_IE_RESET_FLAG] = { TLV_TYPE_TV, 0 },
+ /* NS_IE_IP_ADDR in the IPv4 version */
+ [NS_IE_IP_ADDR] = { TLV_TYPE_FIXED, 5 },
+ },
+};
+
+static const struct tlv_definition ns_att_tlvdef2 = {
+ .def = {
+ [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_IPv4_LIST] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_IPv6_LIST] = { TLV_TYPE_TvLV, 0 },
+ [NS_IE_MAX_NR_NSVC] = { TLV_TYPE_FIXED, 2 },
+ [NS_IE_IPv4_EP_NR] = { TLV_TYPE_FIXED, 2 },
+ [NS_IE_IPv6_EP_NR] = { TLV_TYPE_FIXED, 2 },
+ [NS_IE_RESET_FLAG] = { TLV_TYPE_TV, 0 },
+ /* NS_IE_IP_ADDR in the IPv6 version */
+ [NS_IE_IP_ADDR] = { TLV_TYPE_FIXED, 17 },
+ },
+};
+
+
+/* Section 10.3.2, Table 13 */
+const struct value_string gprs_ns2_cause_strs[] = {
+ { NS_CAUSE_TRANSIT_FAIL, "Transit network failure" },
+ { NS_CAUSE_OM_INTERVENTION, "O&M intervention" },
+ { NS_CAUSE_EQUIP_FAIL, "Equipment failure" },
+ { NS_CAUSE_NSVC_BLOCKED, "NS-VC blocked" },
+ { NS_CAUSE_NSVC_UNKNOWN, "NS-VC unknown" },
+ { NS_CAUSE_BVCI_UNKNOWN, "BVCI unknown" },
+ { NS_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" },
+ { NS_CAUSE_PDU_INCOMP_PSTATE, "PDU not compatible with protocol state" },
+ { NS_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" },
+ { NS_CAUSE_INVAL_ESSENT_IE, "Invalid essential IE" },
+ { NS_CAUSE_MISSING_ESSENT_IE, "Missing essential IE" },
+ { NS_CAUSE_INVAL_NR_IPv4_EP, "Invalid Number of IPv4 Endpoints" },
+ { NS_CAUSE_INVAL_NR_IPv6_EP, "Invalid Number of IPv6 Endpoints" },
+ { NS_CAUSE_INVAL_NR_NS_VC, "Invalid Number of NS-VCs" },
+ { NS_CAUSE_INVAL_WEIGH, "Invalid Weights" },
+ { NS_CAUSE_UNKN_IP_EP, "Unknown IP Endpoint" },
+ { NS_CAUSE_UNKN_IP_ADDR, "Unknown IP Address" },
+ { NS_CAUSE_UNKN_IP_TEST_FAILED, "IP Test Failed" },
+ { 0, NULL }
+};
+
+static const struct rate_ctr_desc ns_ctr_description[] = {
+ [NS_CTR_PKTS_IN] = { "packets:in", "Packets at NS Level ( In)" },
+ [NS_CTR_PKTS_OUT] = { "packets:out", "Packets at NS Level (Out)" },
+ [NS_CTR_PKTS_OUT_DROP] = { "packets:out:drop", "Dropped Packets (Out)" },
+ [NS_CTR_BYTES_IN] = { "bytes:in", "Bytes at NS Level ( In)" },
+ [NS_CTR_BYTES_OUT] = { "bytes:out", "Bytes at NS Level (Out)" },
+ [NS_CTR_BYTES_OUT_DROP] = { "bytes:out:drop", "Dropped Bytes (Out)" },
+ [NS_CTR_BLOCKED] = { "blocked", "NS-VC Block count " },
+ [NS_CTR_UNBLOCKED] = { "unblocked", "NS-VC Unblock count " },
+ [NS_CTR_DEAD] = { "dead", "NS-VC gone dead count " },
+ [NS_CTR_REPLACED] = { "replaced", "NS-VC replaced other count" },
+ [NS_CTR_NSEI_CHG] = { "nsei-chg", "NS-VC changed NSEI count " },
+ [NS_CTR_INV_VCI] = { "inv-nsvci", "NS-VCI was invalid count " },
+ [NS_CTR_INV_NSEI] = { "inv-nsei", "NSEI was invalid count " },
+ [NS_CTR_LOST_ALIVE] = { "lost:alive", "ALIVE ACK missing count " },
+ [NS_CTR_LOST_RESET] = { "lost:reset", "RESET ACK missing count " },
+};
+
+static const struct rate_ctr_group_desc nse_ctrg_desc = {
+ .group_name_prefix = "ns:nse",
+ .group_description = "NSE Peer Statistics",
+ .num_ctr = ARRAY_SIZE(ns_ctr_description),
+ .ctr_desc = ns_ctr_description,
+ .class_id = OSMO_STATS_CLASS_PEER,
+};
+
+static const struct rate_ctr_group_desc nsvc_ctrg_desc = {
+ .group_name_prefix = "ns:nsvc",
+ .group_description = "NSVC Peer Statistics",
+ .num_ctr = ARRAY_SIZE(ns_ctr_description),
+ .ctr_desc = ns_ctr_description,
+ .class_id = OSMO_STATS_CLASS_PEER,
+};
+
+
+static const struct osmo_stat_item_desc nsvc_stat_description[] = {
+ [NS_STAT_ALIVE_DELAY] = { "alive.delay", "ALIVE response time ", "ms", 16, 0 },
+};
+
+static const struct osmo_stat_item_group_desc nsvc_statg_desc = {
+ .group_name_prefix = "ns.nsvc",
+ .group_description = "NSVC Peer Statistics",
+ .num_items = ARRAY_SIZE(nsvc_stat_description),
+ .item_desc = nsvc_stat_description,
+ .class_id = OSMO_STATS_CLASS_PEER,
+};
+
+const struct osmo_stat_item_desc nsbind_stat_description[] = {
+ [NS2_BIND_STAT_BACKLOG_LEN] = { "tx_backlog_length", "Transmit backlog length", "packets", 16, 0 },
+};
+
+static const struct osmo_stat_item_group_desc nsbind_statg_desc = {
+ .group_name_prefix = "ns.bind",
+ .group_description = "NS Bind Statistics",
+ .num_items = ARRAY_SIZE(nsbind_stat_description),
+ .item_desc = nsbind_stat_description,
+ .class_id = OSMO_STATS_CLASS_PEER,
+};
+
+const struct value_string gprs_ns2_aff_cause_prim_strs[] = {
+ { GPRS_NS2_AFF_CAUSE_VC_FAILURE, "NSVC failure" },
+ { GPRS_NS2_AFF_CAUSE_VC_RECOVERY, "NSVC recovery" },
+ { GPRS_NS2_AFF_CAUSE_FAILURE, "NSE failure" },
+ { GPRS_NS2_AFF_CAUSE_RECOVERY, "NSE recovery" },
+ { GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED, "NSE SNS configured" },
+ { GPRS_NS2_AFF_CAUSE_SNS_FAILURE, "NSE SNS failure" },
+ { GPRS_NS2_AFF_CAUSE_SNS_NO_ENDPOINTS, "NSE SNS no endpoints"},
+ { GPRS_NS2_AFF_CAUSE_MTU_CHANGE, "NSE MTU changed" },
+ { 0, NULL }
+};
+
+const struct value_string gprs_ns2_prim_strs[] = {
+ { GPRS_NS2_PRIM_UNIT_DATA, "UNIT DATA" },
+ { GPRS_NS2_PRIM_CONGESTION, "CONGESTION" },
+ { GPRS_NS2_PRIM_STATUS, "STATUS" },
+ { 0, NULL }
+};
+
+const struct value_string gprs_ns2_lltype_strs[] = {
+ { GPRS_NS2_LL_UDP, "UDP" },
+ { GPRS_NS2_LL_FR_GRE, "FR_GRE" },
+ { GPRS_NS2_LL_FR, "FR" },
+ { 0, NULL }
+};
+
+/*! string-format a given NS-VC into a user-supplied buffer.
+ * \param[in] buf user-allocated output buffer
+ * \param[in] buf_len size of user-allocated output buffer in bytes
+ * \param[in] nsvc NS-VC to be string-formatted
+ * \return pointer to buf on success; NULL on error */
+char *gprs_ns2_ll_str_buf(char *buf, size_t buf_len, struct gprs_ns2_vc *nsvc)
+{
+ const struct osmo_sockaddr *local;
+ const struct osmo_sockaddr *remote;
+ struct osmo_sockaddr_str local_str;
+ struct osmo_sockaddr_str remote_str;
+
+ if (!buf_len)
+ return NULL;
+
+ switch (nsvc->nse->ll) {
+ case GPRS_NS2_LL_UDP:
+ if (!gprs_ns2_is_ip_bind(nsvc->bind)) {
+ buf[0] = '\0';
+ return buf;
+ }
+
+ local = gprs_ns2_ip_bind_sockaddr(nsvc->bind);
+ remote = gprs_ns2_ip_vc_remote(nsvc);
+ if (osmo_sockaddr_str_from_sockaddr(&local_str, &local->u.sas))
+ strcpy(local_str.ip, "invalid");
+ if (osmo_sockaddr_str_from_sockaddr(&remote_str, &remote->u.sas))
+ strcpy(remote_str.ip, "invalid");
+
+ if (nsvc->nsvci_is_valid)
+ snprintf(buf, buf_len, "udp)[%s]:%u<%u>[%s]:%u",
+ local_str.ip, local_str.port,
+ nsvc->nsvci,
+ remote_str.ip, remote_str.port);
+ else
+ snprintf(buf, buf_len, "udp)[%s]:%u<>[%s]:%u",
+ local_str.ip, local_str.port,
+ remote_str.ip, remote_str.port);
+ break;
+ case GPRS_NS2_LL_FR_GRE:
+ snprintf(buf, buf_len, "frgre)");
+ break;
+ case GPRS_NS2_LL_FR:
+ snprintf(buf, buf_len, "fr)netif: %s dlci: %u", gprs_ns2_fr_bind_netif(nsvc->bind),
+ gprs_ns2_fr_nsvc_dlci(nsvc));
+ break;
+ default:
+ snprintf(buf, buf_len, "unknown)");
+ break;
+ }
+
+ buf[buf_len - 1] = '\0';
+
+ return buf;
+}
+
+/* udp is the longest: udp)[IP6]:65536<65536>[IP6]:65536 */
+#define NS2_LL_MAX_STR 4+2*(INET6_ADDRSTRLEN+9)+8
+
+/*! string-format a given NS-VC to a thread-local static buffer.
+ * \param[in] nsvc NS-VC to be string-formatted
+ * \return pointer to the string on success; NULL on error */
+const char *gprs_ns2_ll_str(struct gprs_ns2_vc *nsvc)
+{
+ static __thread char buf[NS2_LL_MAX_STR];
+ return gprs_ns2_ll_str_buf(buf, sizeof(buf), nsvc);
+}
+
+/*! string-format a given NS-VC to a dynamically allocated string.
+ * \param[in] ctx talloc context from which to allocate
+ * \param[in] nsvc NS-VC to be string-formatted
+ * \return pointer to the string on success; NULL on error */
+char *gprs_ns2_ll_str_c(const void *ctx, struct gprs_ns2_vc *nsvc)
+{
+ char *buf = talloc_size(ctx, NS2_LL_MAX_STR);
+ if (!buf)
+ return buf;
+ return gprs_ns2_ll_str_buf(buf, NS2_LL_MAX_STR, nsvc);
+}
+
+/*! Return the current state name of a given NS-VC to a thread-local static buffer.
+ * \param[in] nsvc NS-VC to return the state of
+ * \return pointer to the string on success; NULL on error */
+const char *gprs_ns2_nsvc_state_name(struct gprs_ns2_vc *nsvc)
+{
+ return osmo_fsm_inst_state_name(nsvc->fi);
+}
+
+/* select a signalling NSVC and respect sig_counter
+ * param[out] reset_counter - all counter has to be resetted to their signal weight
+ * return the chosen nsvc or NULL
+ */
+static struct gprs_ns2_vc *ns2_load_sharing_signal(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc = NULL, *last = NULL, *tmp;
+
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (tmp->sig_weight == 0)
+ continue;
+ if (!ns2_vc_is_unblocked(tmp))
+ continue;
+ if (tmp->sig_counter == 0) {
+ last = tmp;
+ continue;
+ }
+
+ tmp->sig_counter--;
+ nsvc = tmp;
+ break;
+ }
+
+ /* all counter were zero, but there are valid nsvc */
+ if (!nsvc && last) {
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ tmp->sig_counter = tmp->sig_weight;
+ }
+
+ last->sig_counter--;
+ return last;
+ } else {
+ return nsvc;
+ }
+}
+
+/* 4.4.1 Load Sharing function for the Frame Relay Sub-Network */
+static struct gprs_ns2_vc *ns2_load_sharing_modulo(
+ struct gprs_ns2_nse *nse,
+ uint16_t bvci,
+ uint32_t load_selector)
+{
+ struct gprs_ns2_vc *tmp;
+ uint32_t mod;
+ uint32_t i = 0;
+
+ if (nse->nsvc_count == 0)
+ return NULL;
+
+ mod = (bvci + load_selector) % nse->nsvc_count;
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (!ns2_vc_is_unblocked(tmp))
+ continue;
+ if (i == mod)
+ return tmp;
+ i++;
+ }
+
+ return NULL;
+}
+
+/* 4.4.2 Load Sharing function for the IP Sub-Network
+ *
+ * Implement a simple approach for UDP load sharing of data weight based on the modulo of the lsp.
+ *
+ * E.g. 3 NSVC: 1st weight 5, 2nd weight 3, 3rd weight 1, lsp = 3.
+ * sum all weights = 9
+ * target_weight = lsp % sum = 3
+ *
+ * 1st NSVC will be the target for 0-4
+ * 2nd NSVC will be the target for 5-7
+ * 3rd NSVC will be the target for 8
+ *
+ * The 1st NSVC will be used.
+ * E.g. lsp = 7. The 2nd NSVC will used.
+ */
+static struct gprs_ns2_vc *ns2_load_sharing_weight_modulo(
+ struct gprs_ns2_nse *nse,
+ uint16_t bvci,
+ uint32_t load_selector)
+{
+ struct gprs_ns2_vc *tmp;
+ uint32_t mod;
+ uint32_t i = 0;
+
+ if (nse->nsvc_count == 0)
+ return NULL;
+
+ mod = (bvci + load_selector) % nse->sum_data_weight;
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (tmp->data_weight == 0)
+ continue;
+ if (!ns2_vc_is_unblocked(tmp))
+ continue;
+ if (i == mod || mod < i + tmp->data_weight)
+ return tmp;
+ i += tmp->data_weight;
+ }
+
+ return NULL;
+}
+
+/* pick the first available data NSVC - no load sharing */
+struct gprs_ns2_vc *ns2_load_sharing_first(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc = NULL, *tmp;
+
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (!ns2_vc_is_unblocked(tmp))
+ continue;
+ if (tmp->data_weight == 0)
+ continue;
+
+ nsvc = tmp;
+ break;
+ }
+
+ return nsvc;
+}
+
+
+static struct gprs_ns2_vc *ns2_load_sharing(
+ struct gprs_ns2_nse *nse,
+ uint16_t bvci,
+ uint32_t link_selector)
+{
+ struct gprs_ns2_vc *nsvc = NULL;
+
+ switch (nse->ll) {
+ case GPRS_NS2_LL_FR:
+ nsvc = ns2_load_sharing_modulo(nse, bvci, link_selector);
+ break;
+ case GPRS_NS2_LL_UDP:
+ default:
+ if (bvci == 0) {
+ /* signalling */
+ nsvc = ns2_load_sharing_signal(nse);
+ } else {
+ /* data with load sharing parameter */
+ nsvc = ns2_load_sharing_weight_modulo(nse, bvci, link_selector);
+ }
+ break;
+ }
+
+ return nsvc;
+}
+
+/*! Receive a primitive from the NS User (Gb).
+ * \param[in] nsi NS instance to which the primitive is issued
+ * \param[in] oph The primitive
+ * \return 0 on success; negative on error */
+int gprs_ns2_recv_prim(struct gprs_ns2_inst *nsi, struct osmo_prim_hdr *oph)
+{
+ /* TODO: implement resource distribution */
+ /* TODO: check for empty PDUs which can be sent to Request/Confirm
+ * the IP endpoint */
+ struct osmo_gprs_ns2_prim *nsp;
+ struct gprs_ns2_nse *nse = NULL;
+ struct gprs_ns2_vc *nsvc = NULL;
+ uint16_t bvci, nsei;
+ uint8_t sducontrol = 0;
+ int rc = 0;
+
+ if (oph->sap != SAP_NS) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
+
+ if (oph->operation != PRIM_OP_REQUEST || oph->primitive != GPRS_NS2_PRIM_UNIT_DATA) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (!oph->msg) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ bvci = nsp->bvci;
+ nsei = nsp->nsei;
+
+ nse = gprs_ns2_nse_by_nsei(nsi, nsei);
+ if (!nse) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (!nse->alive) {
+ goto out;
+ }
+
+ nsvc = ns2_load_sharing(nse, bvci, nsp->u.unitdata.link_selector);
+
+ /* TODO: send a status primitive back */
+ if (!nsvc)
+ goto out;
+
+ if (nsp->u.unitdata.change == GPRS_NS2_ENDPOINT_REQUEST_CHANGE)
+ sducontrol = 1;
+ else if (nsp->u.unitdata.change == GPRS_NS2_ENDPOINT_CONFIRM_CHANGE)
+ sducontrol = 2;
+
+ return ns2_tx_unit_data(nsvc, bvci, sducontrol, oph->msg);
+
+out:
+ msgb_free(oph->msg);
+ return rc;
+}
+
+/*! Send a STATUS.ind primitive to the specified NS instance user.
+ * \param[in] nsi NS instance on which we operate
+ * \param[in] nsei NSEI to which the statue relates
+ * \param[in] bvci BVCI to which the status relates
+ * \param[in] cause The cause of the status */
+void ns2_prim_status_ind(struct gprs_ns2_nse *nse,
+ struct gprs_ns2_vc *nsvc,
+ uint16_t bvci,
+ enum gprs_ns2_affecting_cause cause)
+{
+ char nsvc_str[NS2_LL_MAX_STR];
+ struct osmo_gprs_ns2_prim nsp = {};
+ nsp.nsei = nse->nsei;
+ nsp.bvci = bvci;
+ nsp.u.status.cause = cause;
+ nsp.u.status.transfer = ns2_count_transfer_cap(nse, bvci);
+ nsp.u.status.first = nse->first;
+ nsp.u.status.persistent = nse->persistent;
+ if (nse->mtu < 4)
+ nsp.u.status.mtu = 0;
+ else
+ nsp.u.status.mtu = nse->mtu - 4; /* 1 Byte NS PDU type, 1 Byte NS SDU control, 2 Byte BVCI */
+
+ if (nsvc) {
+ nsp.u.status.nsvc = gprs_ns2_ll_str_buf(nsvc_str, sizeof(nsvc_str), nsvc);
+ LOGNSVC(nsvc, LOGL_NOTICE, "NS-STATUS.ind(bvci=%05u): cause=%s, transfer=%d, first=%d, mtu=%d\n",
+ nsp.bvci, gprs_ns2_aff_cause_prim_str(nsp.u.status.cause),
+ nsp.u.status.transfer, nsp.u.status.first, nsp.u.status.mtu);
+ } else {
+ LOGNSE(nse, LOGL_NOTICE, "NS-STATUS.ind(bvci=%05u): cause=%s, transfer=%d, first=%d, mtu=%d\n",
+ nsp.bvci, gprs_ns2_aff_cause_prim_str(nsp.u.status.cause),
+ nsp.u.status.transfer, nsp.u.status.first, nsp.u.status.mtu);
+ }
+
+ osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_STATUS, PRIM_OP_INDICATION, NULL);
+ nse->nsi->cb(&nsp.oph, nse->nsi->cb_data);
+}
+
+/*! Allocate a NS-VC within the given bind + NSE.
+ * \param[in] bind The 'bind' on which we operate
+ * \param[in] nse The NS Entity on which we operate
+ * \param[in] initiater - if this is an incoming remote (!initiater) or a local outgoing connection (initater)
+ * \param[in] id - human-readable identifier
+ * \return newly allocated NS-VC on success; NULL on error */
+struct gprs_ns2_vc *ns2_vc_alloc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_nse *nse, bool initiater,
+ enum gprs_ns2_vc_mode vc_mode, const char *id)
+{
+ /* Sanity check */
+ OSMO_ASSERT(bind->ll == nse->ll);
+
+ struct gprs_ns2_vc *nsvc = talloc_zero(bind, struct gprs_ns2_vc);
+
+ if (!nsvc)
+ return NULL;
+
+ nsvc->bind = bind;
+ nsvc->nse = nse;
+ nsvc->mode = vc_mode;
+ nsvc->sig_weight = 1;
+ nsvc->data_weight = 1;
+
+ nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, bind->nsi->nsvc_rate_ctr_idx);
+ if (!nsvc->ctrg) {
+ goto err;
+ }
+ nsvc->statg = osmo_stat_item_group_alloc(nsvc, &nsvc_statg_desc, bind->nsi->nsvc_rate_ctr_idx);
+ if (!nsvc->statg)
+ goto err_group;
+ if (!ns2_vc_fsm_alloc(nsvc, id, initiater))
+ goto err_statg;
+
+ bind->nsi->nsvc_rate_ctr_idx++;
+
+ rate_ctr_group_set_name(nsvc->ctrg, id);
+ osmo_stat_item_group_set_name(nsvc->statg, id);
+
+ llist_add_tail(&nsvc->list, &nse->nsvc);
+ llist_add_tail(&nsvc->blist, &bind->nsvc);
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nsvc->ts_alive_change);
+ ns2_nse_update_mtu(nse);
+
+ return nsvc;
+
+err_statg:
+ osmo_stat_item_group_free(nsvc->statg);
+err_group:
+ rate_ctr_group_free(nsvc->ctrg);
+err:
+ talloc_free(nsvc);
+
+ return NULL;
+}
+
+/*! Destroy/release given NS-VC.
+ * \param[in] nsvc NS-VC to destroy */
+void gprs_ns2_free_nsvc(struct gprs_ns2_vc *nsvc)
+{
+ if (!nsvc || nsvc->freed)
+ return;
+ nsvc->freed = true;
+ ns2_prim_status_ind(nsvc->nse, nsvc, 0, GPRS_NS2_AFF_CAUSE_VC_FAILURE);
+
+ llist_del(&nsvc->list);
+ llist_del(&nsvc->blist);
+
+ /* notify nse this nsvc is unavailable */
+ ns2_nse_notify_unblocked(nsvc, false);
+
+ /* check if sns is using this VC */
+ ns2_sns_replace_nsvc(nsvc);
+ osmo_fsm_inst_term(nsvc->fi, OSMO_FSM_TERM_REQUEST, NULL);
+
+ /* let the driver/bind clean up it's internal state */
+ if (nsvc->priv && nsvc->bind->free_vc)
+ nsvc->bind->free_vc(nsvc);
+
+ osmo_stat_item_group_free(nsvc->statg);
+ rate_ctr_group_free(nsvc->ctrg);
+
+ talloc_free(nsvc);
+}
+
+void ns2_free_nsvcs(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc;
+
+ /* prevent recursive free() when the user reacts on a down event and free() a second time */
+ while (!llist_empty(&nse->nsvc)) {
+ nsvc = llist_first_entry(&nse->nsvc, struct gprs_ns2_vc, list);
+ gprs_ns2_free_nsvc(nsvc);
+ }
+}
+
+/*! Destroy/release all NS-VC of given NSE
+ * \param[in] nse NSE
+ */
+void gprs_ns2_free_nsvcs(struct gprs_ns2_nse *nse)
+{
+ if (!nse || nse->freed)
+ return;
+
+ if (nse->bss_sns_fi) {
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_FREE_NSVCS, NULL);
+ } else {
+ ns2_free_nsvcs(nse);
+ }
+}
+
+/*! Allocate a message buffer for use with the NS2 stack. */
+struct msgb *ns2_msgb_alloc(void)
+{
+ struct msgb *msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM,
+ "GPRS/NS");
+ if (!msg) {
+ LOGP(DLNS, LOGL_ERROR, "Failed to allocate NS message of size %d\n",
+ NS_ALLOC_SIZE);
+ }
+ return msg;
+}
+
+/*! Create a status message to be sent over a new connection.
+ * \param[in] orig_msg the original message
+ * \param[in] tp TLVP parsed of the original message
+ * \param[out] reject callee-allocated message buffer of the generated NS-STATUS
+ * \param[in] cause Cause for the rejection
+ * \return 0 on success */
+static int reject_status_msg(struct msgb *orig_msg, struct tlv_parsed *tp, struct msgb **reject, enum ns_cause cause)
+{
+ struct msgb *msg = ns2_msgb_alloc();
+ struct gprs_ns_hdr *nsh;
+ bool have_vci = false;
+ uint8_t _cause = cause;
+ uint16_t nsei = 0;
+
+ if (!msg)
+ return -ENOMEM;
+
+ if (TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
+ nsei = tlvp_val16be(tp, NS_IE_NSEI);
+
+ LOGP(DLNS, LOGL_NOTICE, "NSEI=%u Rejecting message without NSVCI. Tx NS STATUS (cause=%s)\n",
+ nsei, gprs_ns2_cause_str(cause));
+ }
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_STATUS;
+
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &_cause);
+ have_vci = TLVP_PRES_LEN(tp, NS_IE_VCI, 2);
+
+ /* Section 9.2.7.1: Static conditions for NS-VCI */
+ if (cause == NS_CAUSE_NSVC_BLOCKED ||
+ cause == NS_CAUSE_NSVC_UNKNOWN) {
+ if (!have_vci) {
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, TLVP_VAL(tp, NS_IE_VCI));
+ }
+
+ /* Section 9.2.7.2: Static conditions for NS PDU */
+ switch (cause) {
+ case NS_CAUSE_SEM_INCORR_PDU:
+ case NS_CAUSE_PDU_INCOMP_PSTATE:
+ case NS_CAUSE_PROTO_ERR_UNSPEC:
+ case NS_CAUSE_INVAL_ESSENT_IE:
+ case NS_CAUSE_MISSING_ESSENT_IE:
+ msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg),
+ orig_msg->l2h);
+ break;
+ default:
+ break;
+ }
+
+ *reject = msg;
+ return 0;
+}
+
+/*! Resolve a NS Entity based on its NSEI.
+ * \param[in] nsi NS Instance in which we do the look-up
+ * \param[in] nsei NSEI to look up
+ * \return NS Entity in successful case; NULL if none found */
+struct gprs_ns2_nse *gprs_ns2_nse_by_nsei(struct gprs_ns2_inst *nsi, uint16_t nsei)
+{
+ struct gprs_ns2_nse *nse;
+
+ llist_for_each_entry(nse, &nsi->nse, list) {
+ if (nse->nsei == nsei)
+ return nse;
+ }
+
+ return NULL;
+}
+
+/*! Resolve a NS-VC Entity based on its NS-VCI.
+ * \param[in] nsi NS Instance in which we do the look-up
+ * \param[in] nsvci NS-VCI to look up
+ * \return NS-VC Entity in successful case; NULL if none found */
+struct gprs_ns2_vc *gprs_ns2_nsvc_by_nsvci(struct gprs_ns2_inst *nsi, uint16_t nsvci)
+{
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc;
+
+ llist_for_each_entry(nse, &nsi->nse, list) {
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ if (nsvc->nsvci_is_valid && nsvc->nsvci == nsvci)
+ return nsvc;
+ }
+ }
+
+ return NULL;
+}
+
+/*! Create a NS Entity within given NS instance.
+ * \param[in] nsi NS instance in which to create NS Entity
+ * \param[in] nsei NS Entity Identifier of to-be-created NSE
+ * \param[in] ip_sns_role_sgsn Does local side implement SGSN role?
+ * \returns newly-allocated NS-E in successful case; NULL on error */
+struct gprs_ns2_nse *gprs_ns2_create_nse2(struct gprs_ns2_inst *nsi, uint16_t nsei,
+ enum gprs_ns2_ll linklayer, enum gprs_ns2_dialect dialect,
+ bool ip_sns_role_sgsn)
+{
+ struct gprs_ns2_nse *nse;
+
+ nse = gprs_ns2_nse_by_nsei(nsi, nsei);
+ if (nse) {
+ LOGNSE(nse, LOGL_ERROR, "Can not create a NSE with already taken NSEI\n");
+ return nse;
+ }
+
+ nse = talloc_zero(nsi, struct gprs_ns2_nse);
+ if (!nse)
+ return NULL;
+ nse->dialect = GPRS_NS2_DIALECT_UNDEF;
+ nse->ip_sns_role_sgsn = ip_sns_role_sgsn;
+
+ if (ns2_nse_set_dialect(nse, dialect) < 0) {
+ talloc_free(nse);
+ return NULL;
+ }
+
+ nse->ctrg = rate_ctr_group_alloc(nse, &nse_ctrg_desc, nsei);
+ if (!nse->ctrg) {
+ talloc_free(nse);
+ return NULL;
+ }
+
+ nse->ll = linklayer;
+ nse->nsei = nsei;
+ nse->nsi = nsi;
+ nse->first = true;
+ nse->mtu = 0;
+ llist_add_tail(&nse->list, &nsi->nse);
+ INIT_LLIST_HEAD(&nse->nsvc);
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nse->ts_alive_change);
+
+ return nse;
+}
+
+int ns2_nse_set_dialect(struct gprs_ns2_nse *nse, enum gprs_ns2_dialect dialect)
+{
+ char sns[16];
+
+ if (nse->dialect == dialect)
+ return 0;
+
+ switch (nse->dialect) {
+ case GPRS_NS2_DIALECT_UNDEF:
+ if (dialect == GPRS_NS2_DIALECT_SNS) {
+ snprintf(sns, sizeof(sns), "NSE%05u-SNS", nse->nsei);
+ if (nse->ip_sns_role_sgsn)
+ nse->bss_sns_fi = ns2_sns_sgsn_fsm_alloc(nse, sns);
+ else
+ nse->bss_sns_fi = ns2_sns_bss_fsm_alloc(nse, sns);
+ if (!nse->bss_sns_fi)
+ return -1;
+ }
+ nse->dialect = dialect;
+ break;
+ default:
+ if (dialect == GPRS_NS2_DIALECT_UNDEF) {
+ if (nse->bss_sns_fi)
+ osmo_fsm_inst_term(nse->bss_sns_fi, OSMO_FSM_TERM_REQUEST, NULL);
+ nse->bss_sns_fi = NULL;
+ nse->dialect = GPRS_NS2_DIALECT_UNDEF;
+ } else {
+ /* we don't support arbitrary changes without going through UNDEF first */
+ return -EPERM;
+ }
+ }
+
+ return 0;
+}
+
+/*! Create a NS Entity within given NS instance.
+ * \param[in] nsi NS instance in which to create NS Entity
+ * \param[in] nsei NS Entity Identifier of to-be-created NSE
+ * \returns newly-allocated NS-E in successful case; NULL on error */
+struct gprs_ns2_nse *gprs_ns2_create_nse(struct gprs_ns2_inst *nsi, uint16_t nsei,
+ enum gprs_ns2_ll linklayer, enum gprs_ns2_dialect dialect)
+{
+ return gprs_ns2_create_nse2(nsi, nsei, linklayer, dialect, false);
+}
+
+/*! Return the NSEI
+ * \param[in] nse NS Entity
+ * \return the nsei.
+ */
+uint16_t gprs_ns2_nse_nsei(struct gprs_ns2_nse *nse)
+{
+ return nse->nsei;
+}
+
+/*! Destroy given NS Entity.
+ * \param[in] nse NS Entity to destroy */
+void gprs_ns2_free_nse(struct gprs_ns2_nse *nse)
+{
+ if (!nse || nse->freed)
+ return;
+
+ nse->freed = true;
+ nse->alive = false;
+ if (nse->bss_sns_fi) {
+ osmo_fsm_inst_term(nse->bss_sns_fi, OSMO_FSM_TERM_REQUEST, NULL);
+ nse->bss_sns_fi = NULL;
+ }
+
+ gprs_ns2_free_nsvcs(nse);
+ ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_FAILURE);
+ rate_ctr_group_free(nse->ctrg);
+ ns2_free_nsvcs(nse);
+
+ llist_del(&nse->list);
+ talloc_free(nse);
+}
+
+void gprs_ns2_free_nses(struct gprs_ns2_inst *nsi)
+{
+ struct gprs_ns2_nse *nse;
+
+ /* prevent recursive free() when the user reacts on a down event and free() a second time */
+ while (!llist_empty(&nsi->nse)) {
+ nse = llist_first_entry(&nsi->nse, struct gprs_ns2_nse, list);
+ gprs_ns2_free_nse(nse);
+ }
+}
+
+static inline int ns2_tlv_parse(struct tlv_parsed *dec,
+ const uint8_t *buf, int buf_len, uint8_t lv_tag,
+ uint8_t lv_tag2)
+{
+ /* workaround for NS_IE_IP_ADDR not following any known TLV rules.
+ * See comment of ns_att_tlvdef1. */
+ int rc = tlv_parse(dec, &ns_att_tlvdef1, buf, buf_len, lv_tag, lv_tag2);
+ if (rc < 0)
+ return tlv_parse(dec, &ns_att_tlvdef2, buf, buf_len, lv_tag, lv_tag2);
+ return rc;
+}
+
+static enum ns2_cs ns2_create_vc_sns(struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *remote,
+ struct gprs_ns2_vc **success, uint16_t nsei)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse;
+
+ nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, remote);
+ /* ns2_create_vc() is only called if no NS-VC could be found */
+ OSMO_ASSERT(!nsvc);
+
+ nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
+ if (!nse) {
+ if (!bind->accept_sns) {
+ struct osmo_sockaddr_str remote_str;
+ osmo_sockaddr_str_from_sockaddr(&remote_str, &remote->u.sas);
+ /* no dynamic creation of IP-SNS NSE permitted */
+ LOGP(DLNS, LOGL_ERROR, "[%s]:%u: Dynamic creation of NSE(%05u) via IP-SNS not "
+ "permitted. Check your config.\n", remote_str.ip, remote_str.port, nsei);
+ return NS2_CS_ERROR;
+ }
+ nse = gprs_ns2_create_nse2(bind->nsi, nsei, bind->ll, GPRS_NS2_DIALECT_SNS, true);
+ if (!nse) {
+ LOGP(DLNS, LOGL_ERROR, "Failed to create NSE(%05u)\n", nsei);
+ return NS2_CS_ERROR;
+ }
+ /* add configured list of default binds; if that fails, use only current bind */
+ if (!ns2_sns_add_sns_default_binds(nse))
+ gprs_ns2_sns_add_bind(nse, bind);
+ } else {
+ /* nsei already known */
+ if (nse->ll != bind->ll) {
+ LOGNSE(nse, LOGL_ERROR, "Received NS-RESET with wrong linklayer(%s)"
+ " for already known NSE(%s)\n", gprs_ns2_lltype_str(bind->ll),
+ gprs_ns2_lltype_str(nse->ll));
+ return NS2_CS_SKIPPED;
+ }
+ }
+
+ nsvc = ns2_ip_bind_connect(bind, nse, remote);
+ if (!nsvc)
+ return NS2_CS_SKIPPED;
+
+ nsvc->nsvci_is_valid = false;
+
+ *success = nsvc;
+
+ return NS2_CS_CREATED;
+}
+
+/*! Create a new NS-VC based on a [received] message. Depending on the bind it might create a NSE.
+ * \param[in] bind the bind through which msg was received
+ * \param[in] msg the actual received message
+ * \param[in] remote address of remote peer sending message
+ * \param[in] logname A name to describe the VC. E.g. ip address pair
+ * \param[out] reject A message filled to be sent back. Only used in failure cases.
+ * \param[out] success A pointer which will be set to the new VC on success
+ * \return enum value indicating the status, e.g. GPRS_NS2_CS_CREATED */
+enum ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
+ struct msgb *msg,
+ const struct osmo_sockaddr *remote,
+ const char *logname,
+ struct msgb **reject,
+ struct gprs_ns2_vc **success)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h;
+ struct tlv_parsed tp;
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse;
+ enum gprs_ns2_dialect dialect;
+ enum gprs_ns2_vc_mode vc_mode;
+ uint16_t nsvci;
+ uint16_t nsei;
+ const struct osmo_sockaddr *local;
+ char idbuf[256], tmp[INET6_ADDRSTRLEN + 8];
+
+ int rc, tlv;
+
+ if (msg->len < sizeof(struct gprs_ns_hdr))
+ return NS2_CS_ERROR;
+
+ /* parse the tlv early to allow reject status msg to
+ * work with valid tp.
+ * Ignore the return code until the pdu type is parsed because
+ * an unknown pdu type should be ignored */
+ tlv = ns2_tlv_parse(&tp, nsh->data,
+ msgb_l2len(msg) - sizeof(*nsh), 0, 0);
+
+ if (bind->ll == GPRS_NS2_LL_UDP && nsh->pdu_type == SNS_PDUT_SIZE && tlv >= 0) {
+ uint16_t nsei;
+
+ if (!TLVP_PRES_LEN(&tp, NS_IE_NSEI, 2)) {
+ rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_MISSING_ESSENT_IE);
+ if (rc < 0)
+ LOGP(DLNS, LOGL_ERROR, "Failed to generate reject message (%d)\n", rc);
+ return NS2_CS_REJECTED;
+ }
+ nsei = tlvp_val16be(&tp, NS_IE_NSEI);
+ /* Create NS-VC, and if required, even NSE dynamically */
+ return ns2_create_vc_sns(bind, remote, success, nsei);
+ }
+
+ switch (nsh->pdu_type) {
+ case NS_PDUT_STATUS:
+ /* Do not respond, see 3GPP TS 08.16, 7.5.1 */
+ LOGP(DLNS, LOGL_INFO, "Ignoring NS STATUS from %s "
+ "for non-existing NS-VC\n",
+ logname);
+ return NS2_CS_SKIPPED;
+ case NS_PDUT_ALIVE_ACK:
+ /* Ignore this, see 3GPP TS 08.16, 7.4.1 */
+ LOGP(DLNS, LOGL_INFO, "Ignoring NS ALIVE ACK from %s "
+ "for non-existing NS-VC\n",
+ logname);
+ return NS2_CS_SKIPPED;
+ case NS_PDUT_RESET_ACK:
+ /* Ignore this, see 3GPP TS 08.16, 7.3.1 */
+ LOGP(DLNS, LOGL_INFO, "Ignoring NS RESET ACK from %s "
+ "for non-existing NS-VC\n",
+ logname);
+ return NS2_CS_SKIPPED;
+ case NS_PDUT_RESET:
+ /* accept PDU RESET when vc_mode matches */
+ if (bind->accept_ipaccess) {
+ dialect = GPRS_NS2_DIALECT_IPACCESS;
+ break;
+ }
+
+ rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_PDU_INCOMP_PSTATE);
+ if (rc < 0)
+ LOGP(DLNS, LOGL_ERROR, "Failed to generate reject message (%d)\n", rc);
+ return NS2_CS_REJECTED;
+ default:
+ rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_PDU_INCOMP_PSTATE);
+ if (rc < 0)
+ LOGP(DLNS, LOGL_ERROR, "Failed to generate reject message (%d)\n", rc);
+ return NS2_CS_REJECTED;
+ }
+
+ if (tlv < 0) {
+ /* TODO: correct behaviour would checking what's wrong.
+ * If it's an essential TLV for the PDU return NS_CAUSE_INVAL_ESSENT_IE.
+ * Otherwise ignore the non-essential TLV. */
+ LOGP(DLNS, LOGL_ERROR, "Rx NS RESET Error %d during "
+ "TLV Parse\n", tlv);
+ rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_PROTO_ERR_UNSPEC);
+ if (rc < 0)
+ LOGP(DLNS, LOGL_ERROR, "Failed to generate reject message (%d)\n", rc);
+ return NS2_CS_REJECTED;
+ }
+
+ if (!TLVP_PRES_LEN(&tp, NS_IE_CAUSE, 1) ||
+ !TLVP_PRES_LEN(&tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(&tp, NS_IE_NSEI, 2)) {
+ LOGP(DLNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n");
+ rc = reject_status_msg(msg, &tp, reject, NS_CAUSE_MISSING_ESSENT_IE);
+ if (rc < 0)
+ LOGP(DLNS, LOGL_ERROR, "Failed to generate reject message (%d)\n", rc);
+ return NS2_CS_REJECTED;
+ }
+
+ nsei = tlvp_val16be(&tp, NS_IE_NSEI);
+ nsvci = tlvp_val16be(&tp, NS_IE_VCI);
+
+ /* find or create NSE */
+ nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
+ if (!nse) {
+ /* only create nse for udp & ipaccess */
+ if (bind->ll != GPRS_NS2_LL_UDP || dialect != GPRS_NS2_DIALECT_IPACCESS)
+ return NS2_CS_SKIPPED;
+
+ if (!bind->accept_ipaccess)
+ return NS2_CS_SKIPPED;
+
+ nse = gprs_ns2_create_nse(bind->nsi, nsei, bind->ll, dialect);
+ if (!nse) {
+ LOGP(DLNS, LOGL_ERROR, "Failed to create NSE(%05u)\n", nsei);
+ return NS2_CS_ERROR;
+ }
+ } else {
+ /* nsei already known */
+ if (nse->ll != bind->ll) {
+ LOGNSE(nse, LOGL_ERROR, "Received NS-RESET NS-VCI(%05u) with wrong linklayer(%s)"
+ " for already known NSE(%s)\n", nsvci, gprs_ns2_lltype_str(bind->ll),
+ gprs_ns2_lltype_str(nse->ll));
+ return NS2_CS_SKIPPED;
+ }
+ }
+
+ nsvc = gprs_ns2_nsvc_by_nsvci(bind->nsi, nsvci);
+ if (nsvc) {
+ if (nsvc->persistent) {
+ LOGNSVC(nsvc, LOGL_ERROR, "Received NS-RESET for a persistent NSE over wrong connection.\n");
+ return NS2_CS_SKIPPED;
+ }
+ /* destroy old dynamic nsvc */
+ gprs_ns2_free_nsvc(nsvc);
+ }
+
+ /* do nse persistent check late to be more precise on the error message */
+ if (nse->persistent) {
+ LOGNSE(nse, LOGL_ERROR, "Received NS-RESET for a persistent NSE but the unknown "
+ "NS-VCI(%05u)\n", nsvci);
+ return NS2_CS_SKIPPED;
+ }
+
+ nsvci = tlvp_val16be(&tp, NS_IE_VCI);
+ vc_mode = ns2_dialect_to_vc_mode(dialect);
+
+ local = gprs_ns2_ip_bind_sockaddr(bind);
+ osmo_sockaddr_to_str_buf(tmp, sizeof(tmp), local);
+ snprintf(idbuf, sizeof(idbuf), "%s-NSE%05u-NSVC%05u-%s-%s", gprs_ns2_lltype_str(nse->ll),
+ nse->nsei, nsvci, tmp, osmo_sockaddr_to_str(remote));
+ osmo_identifier_sanitize_buf(idbuf, NULL, '_');
+ nsvc = ns2_vc_alloc(bind, nse, false, vc_mode, idbuf);
+ if (!nsvc)
+ return NS2_CS_SKIPPED;
+
+ nsvc->nsvci = nsvci;
+ nsvc->nsvci_is_valid = true;
+
+ *success = nsvc;
+
+ return NS2_CS_CREATED;
+}
+
+/*! Create, and connect an inactive, new IP-based NS-VC
+ * \param[in] bind bind in which the new NS-VC is to be created
+ * \param[in] remote remote address to which to connect
+ * \param[in] nse NS Entity in which the NS-VC is to be created
+ * \param[in] nsvci is only required when bind->vc_mode == NS2_VC_MODE_BLOCKRESET
+ * \return pointer to newly-allocated, connected and inactive NS-VC; NULL on error */
+struct gprs_ns2_vc *gprs_ns2_ip_connect_inactive(struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *remote,
+ struct gprs_ns2_nse *nse,
+ uint16_t nsvci)
+{
+ struct gprs_ns2_vc *nsvc;
+
+ nsvc = ns2_ip_bind_connect(bind, nse, remote);
+ if (!nsvc)
+ return NULL;
+
+ if (nsvc->mode == GPRS_NS2_VC_MODE_BLOCKRESET) {
+ nsvc->nsvci = nsvci;
+ nsvc->nsvci_is_valid = true;
+ }
+
+ return nsvc;
+}
+
+/*! Create, connect and activate a new IP-based NS-VC
+ * \param[in] bind bind in which the new NS-VC is to be created
+ * \param[in] remote remote address to which to connect
+ * \param[in] nse NS Entity in which the NS-VC is to be created
+ * \param[in] nsvci is only required when bind->vc_mode == NS2_VC_MODE_BLOCKRESET
+ * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
+struct gprs_ns2_vc *gprs_ns2_ip_connect(struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *remote,
+ struct gprs_ns2_nse *nse,
+ uint16_t nsvci)
+{
+ struct gprs_ns2_vc *nsvc;
+ nsvc = gprs_ns2_ip_connect_inactive(bind, remote, nse, nsvci);
+ if (!nsvc)
+ return NULL;
+
+ ns2_vc_fsm_start(nsvc);
+
+ return nsvc;
+}
+
+/*! Create, connect and activate a new IP-based NS-VC
+ * \param[in] bind bind in which the new NS-VC is to be created
+ * \param[in] remote remote address to which to connect
+ * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
+ * \param[in] nsvci is only required when bind->vc_mode == NS2_VC_MODE_BLOCKRESET
+ * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
+struct gprs_ns2_vc *gprs_ns2_ip_connect2(struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *remote,
+ uint16_t nsei,
+ uint16_t nsvci,
+ enum gprs_ns2_dialect dialect)
+{
+ struct gprs_ns2_nse *nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
+
+ if (!nse) {
+ nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_UDP, dialect);
+ if (!nse)
+ return NULL;
+ }
+
+ return gprs_ns2_ip_connect(bind, remote, nse, nsvci);
+}
+
+/*! Find NS-VC for given socket address.
+ * \param[in] nse NS Entity in which to search
+ * \param[in] sockaddr socket address to search for
+ * \return NS-VC matching sockaddr; NULL if none found */
+struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_nse(struct gprs_ns2_nse *nse,
+ const struct osmo_sockaddr *sockaddr)
+{
+ struct gprs_ns2_vc *nsvc;
+ const struct osmo_sockaddr *remote;
+
+ OSMO_ASSERT(nse);
+ OSMO_ASSERT(sockaddr);
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ remote = gprs_ns2_ip_vc_remote(nsvc);
+ if (!osmo_sockaddr_cmp(sockaddr, remote))
+ return nsvc;
+ }
+
+ return NULL;
+}
+
+/*!
+ * Iterate over all nsvc of a NS Entity and call the callback.
+ * If the callback returns < 0 it aborts the loop and returns the callback return code.
+ * \param[in] nse NS Entity to iterate over all nsvcs
+ * \param[in] cb the callback to call
+ * \param[inout] cb_data the private data of the callback
+ * \return 0 if the loop completes. If a callback returns < 0 it will returns this value.
+ */
+int gprs_ns2_nse_foreach_nsvc(struct gprs_ns2_nse *nse, gprs_ns2_foreach_nsvc_cb cb, void *cb_data)
+{
+ struct gprs_ns2_vc *nsvc, *tmp;
+ int rc = 0;
+ llist_for_each_entry_safe(nsvc, tmp, &nse->nsvc, list) {
+ rc = cb(nsvc, cb_data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+
+
+/*! Bottom-side entry-point for received NS PDU from the driver/bind
+ * \param[in] nsvc NS-VC for which the message was received
+ * \param msg the received message. Ownership is transferred, caller must not free it!
+ * \return 0 on success; negative on error */
+int ns2_recv_vc(struct gprs_ns2_vc *nsvc,
+ struct msgb *msg)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct tlv_parsed tp = { };
+ int rc = 0;
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_IN);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_IN, msg->len);
+
+ if (msg->len < sizeof(struct gprs_ns_hdr)) {
+ rc = -EINVAL;
+ goto freemsg;
+ }
+
+ if (nsh->pdu_type != NS_PDUT_UNITDATA)
+ LOG_NS_RX_SIGNAL(nsvc, nsh->pdu_type);
+ else
+ LOG_NS_DATA(nsvc, "Rx", nsh->pdu_type, LOGL_INFO, "\n");
+
+ switch (nsh->pdu_type) {
+ case SNS_PDUT_CONFIG:
+ /* one additional byte ('end flag') before the TLV part starts */
+ rc = ns2_tlv_parse(&tp, nsh->data+1,
+ msgb_l2len(msg) - sizeof(*nsh)-1, 0, 0);
+ if (rc < 0) {
+ LOGP(DLNS, LOGL_NOTICE, "Error during TLV Parse in %s\n", msgb_hexdump(msg));
+ goto freemsg;
+ }
+ /* All sub-network service related message types */
+ return ns2_sns_rx(nsvc, msg, &tp);
+ case SNS_PDUT_ACK:
+ case SNS_PDUT_ADD:
+ case SNS_PDUT_CHANGE_WEIGHT:
+ case SNS_PDUT_DELETE:
+ /* weird layout: NSEI TLV, then value-only transaction IE, then TLV again */
+ rc = ns2_tlv_parse(&tp, nsh->data+5,
+ msgb_l2len(msg) - sizeof(*nsh)-5, 0, 0);
+ if (rc < 0) {
+ LOGP(DLNS, LOGL_NOTICE, "Error during TLV Parse in %s\n", msgb_hexdump(msg));
+ goto freemsg;
+ }
+ tp.lv[NS_IE_NSEI].val = nsh->data+2;
+ tp.lv[NS_IE_NSEI].len = 2;
+ tp.lv[NS_IE_TRANS_ID].val = nsh->data+4;
+ tp.lv[NS_IE_TRANS_ID].len = 1;
+ return ns2_sns_rx(nsvc, msg, &tp);
+ case SNS_PDUT_CONFIG_ACK:
+ case SNS_PDUT_SIZE:
+ case SNS_PDUT_SIZE_ACK:
+ rc = ns2_tlv_parse(&tp, nsh->data,
+ msgb_l2len(msg) - sizeof(*nsh), 0, 0);
+ if (rc < 0) {
+ LOGP(DLNS, LOGL_NOTICE, "Error during TLV Parse in %s\n", msgb_hexdump(msg));
+ goto freemsg;
+ }
+ /* All sub-network service related message types */
+ return ns2_sns_rx(nsvc, msg, &tp);
+ case NS_PDUT_UNITDATA:
+ return ns2_vc_rx(nsvc, msg, &tp);
+ default:
+ rc = ns2_tlv_parse(&tp, nsh->data,
+ msgb_l2len(msg) - sizeof(*nsh), 0, 0);
+ if (rc < 0) {
+ LOGP(DLNS, LOGL_NOTICE, "Error during TLV Parse\n");
+ if (nsh->pdu_type != NS_PDUT_STATUS)
+ ns2_tx_status(nsvc, NS_CAUSE_PROTO_ERR_UNSPEC, 0, msg, NULL);
+ return rc;
+ }
+ return ns2_vc_rx(nsvc, msg, &tp);
+ }
+freemsg:
+ msgb_free(msg);
+
+ return rc;
+}
+
+/* summarize all active data nsvcs */
+void ns2_nse_data_sum(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc;
+
+ nse->nsvc_count = 0;
+ nse->sum_data_weight = 0;
+ nse->sum_sig_weight = 0;
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ if (!ns2_vc_is_unblocked(nsvc))
+ continue;
+
+ nse->nsvc_count++;
+ nse->sum_data_weight += nsvc->data_weight;
+ nse->sum_sig_weight += nsvc->sig_weight;
+ }
+}
+
+/*! Notify a nse about the change of a NS-VC.
+ * \param[in] nsvc NS-VC which has detected the change (and shall not be notified).
+ * \param[in] unblocked whether the NSE should be marked as unblocked (true) or blocked (false) */
+void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked)
+{
+ struct gprs_ns2_nse *nse = nsvc->nse;
+ struct gprs_ns2_inst *nsi = nse->nsi;
+ uint16_t nsei = nse->nsei;
+
+ ns2_nse_data_sum(nse);
+ ns2_sns_notify_alive(nse, nsvc, unblocked);
+
+ /* NSE could have been freed, try to get it again */
+ nse = gprs_ns2_nse_by_nsei(nsi, nsei);
+
+ if (!nse || unblocked == nse->alive)
+ return;
+
+ /* wait until both data_weight and sig_weight are != 0 before declaring NSE as alive */
+ if (unblocked && nse->sum_data_weight && nse->sum_sig_weight) {
+ nse->alive = true;
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nse->ts_alive_change);
+ ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_RECOVERY);
+ nse->first = false;
+ return;
+ }
+
+ if (nse->alive && (nse->sum_data_weight == 0 || nse->sum_sig_weight == 0)) {
+ /* nse became unavailable */
+ nse->alive = false;
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nse->ts_alive_change);
+ ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_FAILURE);
+ }
+}
+
+/*! Create a new GPRS NS instance
+ * \param[in] ctx a talloc context to allocate NS instance from
+ * \param[in] cb Call-back function for dispatching primitives to the user. The Call-back must free all msgb* given in the primitive.
+ * \param[in] cb_data transparent user data passed to Call-back
+ * \returns dynamically allocated gprs_ns_inst; NULL on error */
+struct gprs_ns2_inst *gprs_ns2_instantiate(void *ctx, osmo_prim_cb cb, void *cb_data)
+{
+ struct gprs_ns2_inst *nsi;
+
+ nsi = talloc_zero(ctx, struct gprs_ns2_inst);
+ if (!nsi)
+ return NULL;
+
+ nsi->cb = cb;
+ nsi->cb_data = cb_data;
+ INIT_LLIST_HEAD(&nsi->binding);
+ INIT_LLIST_HEAD(&nsi->nse);
+
+ nsi->timeout[NS_TOUT_TNS_BLOCK] = 3;
+ nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES] = 3;
+ nsi->timeout[NS_TOUT_TNS_RESET] = 3;
+ nsi->timeout[NS_TOUT_TNS_RESET_RETRIES] = 3;
+ nsi->timeout[NS_TOUT_TNS_TEST] = 30;
+ nsi->timeout[NS_TOUT_TNS_ALIVE] = 3;
+ nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES] = 10;
+ nsi->timeout[NS_TOUT_TSNS_PROV] = 3; /* 1..10 */
+ nsi->timeout[NS_TOUT_TSNS_SIZE_RETRIES] = 3;
+ nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES] = 3;
+ nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES] = 3;
+
+ nsi->txqueue_max_length = NS_DEFAULT_TXQUEUE_MAX_LENGTH;
+
+ return nsi;
+}
+
+/*! Destroy a NS Instance (including all its NSEs, binds, ...).
+ * \param[in] nsi NS instance to destroy */
+void gprs_ns2_free(struct gprs_ns2_inst *nsi)
+{
+ if (!nsi)
+ return;
+
+ gprs_ns2_free_nses(nsi);
+ gprs_ns2_free_binds(nsi);
+
+ talloc_free(nsi);
+}
+
+/*! Start the NS-ALIVE FSM in all NS-VCs of given NSE.
+ * \param[in] nse NS Entity in whihc to start NS-ALIVE FSMs */
+void gprs_ns2_start_alive_all_nsvcs(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc;
+ OSMO_ASSERT(nse);
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ /* A pre-configured endpoint shall not be used for NSE data or signalling traffic
+ * (with the exception of Size and Configuration procedures) unless it is
+ * configured by the SGSN using the auto-configuration procedures */
+ if (nsvc->sns_only)
+ continue;
+
+ ns2_vc_fsm_start(nsvc);
+ }
+}
+
+/*! Destroy a given bind.
+ * \param[in] bind the bind we want to destroy */
+void gprs_ns2_free_bind(struct gprs_ns2_vc_bind *bind)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse;
+ if (!bind || bind->freed)
+ return;
+ bind->freed = true;
+
+ if (gprs_ns2_is_ip_bind(bind)) {
+ llist_for_each_entry(nse, &bind->nsi->nse, list) {
+ gprs_ns2_sns_del_bind(nse, bind);
+ }
+ }
+
+ /* prevent recursive free() when the user reacts on a down event and free() a second time */
+ while (!llist_empty(&bind->nsvc)) {
+ nsvc = llist_first_entry(&bind->nsvc, struct gprs_ns2_vc, blist);
+ gprs_ns2_free_nsvc(nsvc);
+ }
+
+ if (bind->driver->free_bind)
+ bind->driver->free_bind(bind);
+
+ llist_del(&bind->list);
+ osmo_stat_item_group_free(bind->statg);
+ talloc_free((char *)bind->name);
+ talloc_free(bind);
+}
+
+void gprs_ns2_free_binds(struct gprs_ns2_inst *nsi)
+{
+ struct gprs_ns2_vc_bind *bind;
+
+ /* prevent recursive free() when the user reacts on a down event and free() a second time */
+ while (!llist_empty(&nsi->binding)) {
+ bind = llist_first_entry(&nsi->binding, struct gprs_ns2_vc_bind, list);
+ gprs_ns2_free_bind(bind);
+ }
+}
+
+/*! Search for a bind with a unique name
+ * \param[in] nsi NS instance on which we operate
+ * \param[in] name The unique bind name to search for
+ * \return the bind or NULL if not found
+ */
+struct gprs_ns2_vc_bind *gprs_ns2_bind_by_name(struct gprs_ns2_inst *nsi, const char *name)
+{
+ struct gprs_ns2_vc_bind *bind;
+
+ llist_for_each_entry(bind, &nsi->binding, list) {
+ if (!strcmp(bind->name, name))
+ return bind;
+ }
+
+ return NULL;
+}
+
+enum gprs_ns2_vc_mode ns2_dialect_to_vc_mode(enum gprs_ns2_dialect dialect)
+{
+ switch (dialect) {
+ case GPRS_NS2_DIALECT_SNS:
+ case GPRS_NS2_DIALECT_STATIC_ALIVE:
+ return GPRS_NS2_VC_MODE_ALIVE;
+ case GPRS_NS2_DIALECT_STATIC_RESETBLOCK:
+ case GPRS_NS2_DIALECT_IPACCESS:
+ return GPRS_NS2_VC_MODE_BLOCKRESET;
+ default:
+ return -1;
+ }
+}
+
+static void add_bind_array(struct gprs_ns2_vc_bind **array,
+ struct gprs_ns2_vc_bind *bind, int size)
+{
+ int i;
+ for (i=0; i < size; i++) {
+ if (array[i] == bind)
+ return;
+ if (!array[i])
+ break;
+ }
+
+ if (i == size)
+ return;
+
+ array[i] = bind;
+}
+
+void ns2_nse_update_mtu(struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc;
+ int mtu = 0;
+
+ if (llist_empty(&nse->nsvc)) {
+ nse->mtu = 0;
+ return;
+ }
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ if (mtu == 0)
+ mtu = nsvc->bind->mtu;
+ else if (mtu > nsvc->bind->mtu)
+ mtu = nsvc->bind->mtu;
+ }
+
+ if (nse->mtu == mtu)
+ return;
+
+ nse->mtu = mtu;
+ if (nse->alive)
+ ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_MTU_CHANGE);
+}
+
+/*! calculate the transfer capabilities for a nse
+ * \param nse the nse to count the transfer capability
+ * \param bvci a bvci - unused
+ * \return the transfer capability in mbit. On error < 0.
+ */
+int ns2_count_transfer_cap(struct gprs_ns2_nse *nse,
+ uint16_t bvci)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_vc_bind **active_binds;
+ int i, active_nsvcs = 0, transfer_cap = 0;
+
+ /* calculate the transfer capabilities based on the binds.
+ * A bind has a transfer capability which is shared across all NSVCs.
+ * Take care the bind cap is not counted twice within a NSE.
+ * This should be accurate for FR and UDP but not for FR/GRE. */
+
+ if (!nse->alive)
+ return 0;
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ if (ns2_vc_is_unblocked(nsvc))
+ active_nsvcs++;
+ }
+
+ if (!active_nsvcs)
+ return 0;
+
+ active_binds = talloc_zero_array(nse, struct gprs_ns2_vc_bind*, active_nsvcs);
+ if (!active_binds)
+ return -ENOMEM;
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ if (!ns2_vc_is_unblocked(nsvc))
+ continue;
+ add_bind_array(active_binds, nsvc->bind, active_nsvcs);
+ }
+
+ /* TODO: change calcuation for FR/GRE */
+ for (i = 0; i < active_nsvcs; i++) {
+ if (active_binds[i])
+ transfer_cap += active_binds[i]->transfer_capability;
+ }
+
+ talloc_free(active_binds);
+ return transfer_cap;
+}
+
+/*! common allocation + low-level initialization of a bind. Called by vc-drivers */
+int ns2_bind_alloc(struct gprs_ns2_inst *nsi, const char *name,
+ struct gprs_ns2_vc_bind **result)
+{
+ struct gprs_ns2_vc_bind *bind;
+
+ if (!name)
+ return -EINVAL;
+
+ if (gprs_ns2_bind_by_name(nsi, name))
+ return -EALREADY;
+
+ bind = talloc_zero(nsi, struct gprs_ns2_vc_bind);
+ if (!bind)
+ return -ENOMEM;
+
+ bind->name = talloc_strdup(bind, name);
+ if (!bind->name) {
+ talloc_free(bind);
+ return -ENOMEM;
+ }
+
+ bind->statg = osmo_stat_item_group_alloc(bind, &nsbind_statg_desc, nsi->bind_rate_ctr_idx);
+ if (!bind->statg) {
+ talloc_free(bind);
+ return -ENOMEM;
+ }
+
+ bind->sns_sig_weight = 1;
+ bind->sns_data_weight = 1;
+ bind->nsi = nsi;
+ INIT_LLIST_HEAD(&bind->nsvc);
+ llist_add_tail(&bind->list, &nsi->binding);
+
+ nsi->bind_rate_ctr_idx++;
+
+ if (result)
+ *result = bind;
+
+ return 0;
+}
+
+/*! @} */
diff --git a/src/gb/gprs_ns2_fr.c b/src/gb/gprs_ns2_fr.c
new file mode 100644
index 00000000..f6bee39c
--- /dev/null
+++ b/src/gb/gprs_ns2_fr.c
@@ -0,0 +1,988 @@
+/*! \file gprs_ns2_fr.c
+ * NS-over-FR-over-GRE implementation.
+ * GPRS Networks Service (NS) messages on the Gb interface.
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * as well as its successor 3GPP TS 48.016 */
+
+/* (C) 2009-2021 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <arpa/inet.h>
+#include <linux/if.h>
+
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <linux/if_ether.h>
+#include <linux/hdlc.h>
+#include <linux/hdlc/ioctl.h>
+#include <linux/sockios.h>
+
+#include <osmocom/gprs/frame_relay.h>
+#include <osmocom/core/byteswap.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gprs/gprs_ns2.h>
+#include <osmocom/core/netdev.h>
+#include <osmocom/gprs/protocol/gsm_08_16.h>
+#include <osmocom/gprs/protocol/gsm_08_18.h>
+
+#include "config.h"
+#include "common_vty.h"
+#include "gprs_ns2_internal.h"
+
+#define GRE_PTYPE_FR 0x6559
+#define GRE_PTYPE_IPv4 0x0800
+#define GRE_PTYPE_IPv6 0x86dd
+#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
+
+#ifndef IPPROTO_GRE
+# define IPPROTO_GRE 47
+#endif
+
+#define E1_LINERATE 2048000
+#define E1_SLOTS_TOTAL 32
+#define E1_SLOTS_USED 31
+/* usable bitrate of the E1 superchannel with 31 of 32 timeslots */
+#define SUPERCHANNEL_LINERATE (E1_LINERATE*E1_SLOTS_USED)/E1_SLOTS_TOTAL
+/* nanoseconds per bit (504) */
+#define BIT_DURATION_NS (1000000000 / SUPERCHANNEL_LINERATE)
+
+static void free_bind(struct gprs_ns2_vc_bind *bind);
+static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg);
+
+struct gprs_ns2_vc_driver vc_driver_fr = {
+ .name = "GB frame relay",
+ .free_bind = free_bind,
+};
+
+struct priv_bind {
+ struct osmo_netdev *netdev;
+ char netif[IFNAMSIZ];
+ struct osmo_fr_link *link;
+ int ifindex;
+ bool if_running;
+ /* backlog queue for AF_PACKET / ENOBUFS handling (see OS#4995) */
+ struct {
+ /* file-descriptor for AF_PACKET socket */
+ struct osmo_fd ofd;
+ /* LMI bucket (we only store the last LMI message, no need to queue */
+ struct msgb *lmi_msg;
+ /* list of NS msgb (backlog) */
+ struct llist_head list;
+ /* timer to trigger next attempt of AF_PACKET write */
+ struct osmo_timer_list timer;
+ /* re-try after that many micro-seconds */
+ uint32_t retry_us;
+ } backlog;
+};
+
+struct priv_vc {
+ struct osmo_sockaddr remote;
+ uint16_t dlci;
+ struct osmo_fr_dlc *dlc;
+};
+
+static void free_vc(struct gprs_ns2_vc *nsvc)
+{
+ if (!nsvc)
+ return;
+
+ if (!nsvc->priv)
+ return;
+
+ OSMO_ASSERT(gprs_ns2_is_fr_bind(nsvc->bind));
+ talloc_free(nsvc->priv);
+ nsvc->priv = NULL;
+}
+
+static void dump_vty(const struct gprs_ns2_vc_bind *bind, struct vty *vty, bool stats)
+{
+ struct priv_bind *priv;
+ struct gprs_ns2_vc *nsvc;
+ struct osmo_fr_link *fr_link;
+
+ if (!bind)
+ return;
+
+ priv = bind->priv;
+ fr_link = priv->link;
+
+ vty_out(vty, "FR bind: %s, role: %s, link: %s%s", priv->netif,
+ osmo_fr_role_str(fr_link->role), priv->if_running ? "UP" : "DOWN", VTY_NEWLINE);
+
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ ns2_vty_dump_nsvc(vty, nsvc, stats);
+ }
+
+ priv = bind->priv;
+}
+
+/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
+static void free_bind(struct gprs_ns2_vc_bind *bind)
+{
+ struct priv_bind *priv;
+ struct msgb *msg, *msg2;
+
+ if (!bind)
+ return;
+
+ OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
+ priv = bind->priv;
+
+ OSMO_ASSERT(llist_empty(&bind->nsvc));
+
+ osmo_timer_del(&priv->backlog.timer);
+ llist_for_each_entry_safe(msg, msg2, &priv->backlog.list, list) {
+ msgb_free(msg);
+ }
+ msgb_free(priv->backlog.lmi_msg);
+
+ osmo_netdev_free(priv->netdev);
+ osmo_fr_link_free(priv->link);
+ osmo_fd_close(&priv->backlog.ofd);
+ talloc_free(priv);
+}
+
+static void fr_dlci_status_cb(struct osmo_fr_dlc *dlc, void *cb_data, bool active)
+{
+ struct gprs_ns2_vc *nsvc = cb_data;
+
+ if (active) {
+ ns2_vc_fsm_start(nsvc);
+ } else {
+ ns2_vc_force_unconfigured(nsvc);
+ }
+}
+
+static struct priv_vc *fr_alloc_vc(struct gprs_ns2_vc_bind *bind,
+ struct gprs_ns2_vc *nsvc,
+ uint16_t dlci)
+{
+ struct priv_bind *privb = bind->priv;
+ struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
+ if (!priv)
+ return NULL;
+
+ OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
+ nsvc->priv = priv;
+ priv->dlci = dlci;
+ priv->dlc = osmo_fr_dlc_alloc(privb->link, dlci);
+ if (!priv->dlc) {
+ nsvc->priv = NULL;
+ talloc_free(priv);
+ return NULL;
+ }
+
+ priv->dlc->cb_data = nsvc;
+ priv->dlc->rx_cb = fr_dlci_rx_cb;
+ priv->dlc->status_cb = fr_dlci_status_cb;
+
+ return priv;
+}
+
+int gprs_ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,
+ uint16_t dlci,
+ struct gprs_ns2_vc **result)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct priv_vc *vcpriv;
+
+ OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
+ if (!result)
+ return -EINVAL;
+
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ vcpriv = nsvc->priv;
+ if (vcpriv->dlci != dlci) {
+ *result = nsvc;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* PDU from the network interface towards the fr layer (upwards) */
+static int fr_netif_ofd_cb(struct osmo_fd *bfd, uint32_t what)
+{
+ struct gprs_ns2_vc_bind *bind = bfd->data;
+ struct priv_bind *priv = bind->priv;
+ struct msgb *msg;
+ struct sockaddr_ll sll;
+ socklen_t sll_len = sizeof(sll);
+ int rc = 0;
+
+ /* we only handle read here. write to AF_PACKET sockets cannot be triggered
+ * by select or poll, see OS#4995 */
+ if (!(what & OSMO_FD_READ))
+ return 0;
+
+ msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR Rx");
+ if (!msg)
+ return -ENOMEM;
+
+ rc = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, (struct sockaddr *)&sll, &sll_len);
+ if (rc < 0) {
+ LOGBIND(bind, LOGL_ERROR, "recv error %s during NS-FR recv\n", strerror(errno));
+ goto out_err;
+ } else if (rc == 0) {
+ goto out_err;
+ }
+
+ /* ignore any packets that we might have received for a different interface, between
+ * the socket() and the bind() call */
+ if (sll.sll_ifindex != priv->ifindex)
+ goto out_err;
+
+ msgb_put(msg, rc);
+ msg->dst = priv->link;
+ return osmo_fr_rx(msg);
+
+out_err:
+ msgb_free(msg);
+ return rc;
+}
+
+/* PDU from the frame relay towards the NS-VC (upwards) */
+static int fr_dlci_rx_cb(void *cb_data, struct msgb *msg)
+{
+ int rc;
+ struct gprs_ns2_vc *nsvc = cb_data;
+
+ rc = ns2_recv_vc(nsvc, msg);
+
+ return rc;
+}
+
+static int fr_netif_write_one(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
+{
+ struct priv_bind *priv = bind->priv;
+ unsigned int len = msgb_length(msg);
+ int rc;
+
+ /* estimate the retry time based on the data rate it takes to transmit */
+ priv->backlog.retry_us = (BIT_DURATION_NS * 8 * len) / 1000;
+
+ rc = write(priv->backlog.ofd.fd, msgb_data(msg), len);
+ if (rc == len) {
+ msgb_free(msg);
+ return 0;
+ } else if (rc < 0) {
+ /* don't free, the caller might want to re-transmit */
+ switch (errno) {
+ case EAGAIN:
+ case ENOBUFS:
+ /* not a real error, but more a normal event on AF_PACKET */
+ /* don't free the message and let the caller re-enqueue */
+ return -errno;
+ default:
+ /* an actual error, like -ENETDOWN, -EMSGSIZE */
+ LOGBIND(bind, LOGL_ERROR, "error during write to AF_PACKET: %s\n", strerror(errno));
+ msgb_free(msg);
+ return 0;
+ }
+ } else {
+ /* short write */
+ LOGBIND(bind, LOGL_ERROR, "short write on AF_PACKET: %d < %d\n", rc, len);
+ msgb_free(msg);
+ return 0;
+ }
+}
+
+/*! determine if given bind is for FR-GRE encapsulation. */
+int gprs_ns2_is_fr_bind(struct gprs_ns2_vc_bind *bind)
+{
+ return (bind->driver == &vc_driver_fr);
+}
+
+/* PDU from the NS-VC towards the frame relay layer (downwards) */
+static int fr_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
+{
+ struct priv_vc *vcpriv = nsvc->priv;
+ unsigned int vc_len = msgb_length(msg);
+ int rc;
+
+ msg->dst = vcpriv->dlc;
+ rc = osmo_fr_tx_dlc(msg);
+ if (OSMO_LIKELY(rc >= 0)) {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT, vc_len);
+ } else {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT_DROP);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT_DROP, vc_len);
+ }
+ return rc;
+}
+
+static void enqueue_at_head(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
+{
+ struct priv_bind *priv = bind->priv;
+ llist_add(&msg->list, &priv->backlog.list);
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(bind->statg, NS2_BIND_STAT_BACKLOG_LEN), 1);
+ osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
+}
+
+static void enqueue_at_tail(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
+{
+ struct priv_bind *priv = bind->priv;
+ llist_add_tail(&msg->list, &priv->backlog.list);
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(bind->statg, NS2_BIND_STAT_BACKLOG_LEN), 1);
+ osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
+}
+
+#define LMI_Q933A_DLCI 0
+
+/* enqueue to backlog (LMI, signaling) or drop (userdata msg) */
+static int backlog_enqueue_or_free(struct gprs_ns2_vc_bind *bind, struct msgb *msg)
+{
+ struct priv_bind *priv = bind->priv;
+ uint8_t dlci = msg->data[0];
+ uint8_t ns_pdu_type;
+ uint16_t bvci;
+
+ if (msgb_length(msg) < 1)
+ goto out_free;
+
+ /* we want to enqueue only Q.933 LMI traffic or NS signaling; NOT user traffic */
+ switch (dlci) {
+ case LMI_Q933A_DLCI:
+ /* always store only the last LMI message in the lmi_msg bucket */
+ msgb_free(priv->backlog.lmi_msg);
+ priv->backlog.lmi_msg = msg;
+ return 0;
+ default:
+ /* there's no point in trying to enqueue messages if the interface is down */
+ if (!priv->if_running)
+ break;
+
+ if (msgb_length(msg) < 3)
+ break;
+ ns_pdu_type = msg->data[2];
+ switch (ns_pdu_type) {
+ case NS_PDUT_UNITDATA:
+ if (msgb_length(msg) < 6)
+ break;
+ bvci = osmo_load16be(msg->data + 4);
+ /* enqueue BVCI=0 traffic at tail of queue */
+ if (bvci == BVCI_SIGNALLING) {
+ enqueue_at_tail(bind, msg);
+ return 0;
+ }
+ break;
+ default:
+ /* enqueue NS signaling traffic at head of queue */
+ enqueue_at_head(bind, msg);
+ return 0;
+ }
+ break;
+ }
+
+out_free:
+ /* drop everything that is not LMI, NS-signaling or BVCI-0 */
+ msgb_free(msg);
+ return -1;
+}
+
+static void fr_backlog_timer_cb(void *data)
+{
+ struct gprs_ns2_vc_bind *bind = data;
+ struct priv_bind *priv = bind->priv;
+ int i, rc;
+
+ /* first try to get rid of the LMI message, if any */
+ if (priv->backlog.lmi_msg) {
+ rc = fr_netif_write_one(bind, priv->backlog.lmi_msg);
+ if (rc < 0)
+ goto restart_timer;
+ /* fr_netif_write_one() has just free'd it */
+ priv->backlog.lmi_msg = NULL;
+ }
+
+ /* attempt to send up to 10 messages in every timer */
+ for (i = 0; i < 10; i++) {
+ struct msgb *msg = msgb_dequeue(&priv->backlog.list);
+ if (!msg)
+ break;
+
+ rc = fr_netif_write_one(bind, msg);
+ if (rc < 0) {
+ /* re-add at head of list */
+ llist_add(&msg->list, &priv->backlog.list);
+ break;
+ }
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(bind->statg, NS2_BIND_STAT_BACKLOG_LEN), 1);
+ }
+
+restart_timer:
+ /* re-start timer if we still have data in the queue */
+ if (!llist_empty(&priv->backlog.list))
+ osmo_timer_schedule(&priv->backlog.timer, 0, priv->backlog.retry_us);
+}
+
+/* PDU from the frame relay layer towards the network interface (downwards) */
+int fr_tx_cb(void *data, struct msgb *msg)
+{
+ struct gprs_ns2_vc_bind *bind = data;
+ struct priv_bind *priv = bind->priv;
+ int rc;
+
+ if (llist_empty(&priv->backlog.list)) {
+ /* attempt to transmit right now */
+ rc = fr_netif_write_one(bind, msg);
+ if (rc < 0) {
+ /* enqueue to backlog in case it fails */
+ return backlog_enqueue_or_free(bind, msg);
+ }
+ } else {
+ /* enqueue to backlog */
+ return backlog_enqueue_or_free(bind, msg);
+ }
+
+ return 0;
+}
+
+static int devname2ifindex(const char *ifname)
+{
+ struct ifreq ifr;
+ int sk, rc;
+
+ sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sk < 0)
+ return sk;
+
+
+ memset(&ifr, 0, sizeof(ifr));
+ OSMO_STRLCPY_ARRAY(ifr.ifr_name, ifname);
+
+ rc = ioctl(sk, SIOCGIFINDEX, &ifr);
+ close(sk);
+ if (rc < 0)
+ return rc;
+
+ return ifr.ifr_ifindex;
+}
+
+static int open_socket(int ifindex, const struct gprs_ns2_vc_bind *nsbind)
+{
+ struct sockaddr_ll addr;
+ int fd, rc;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sll_family = AF_PACKET;
+ addr.sll_protocol = htons(ETH_P_ALL);
+ addr.sll_ifindex = ifindex;
+
+ fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_HDLC));
+ if (fd < 0) {
+ LOGBIND(nsbind, LOGL_ERROR, "Can not create AF_PACKET socket. Are you root or have CAP_NET_RAW?\n");
+ return fd;
+ }
+
+ /* there's a race condition between the above syscall and the bind() call below,
+ * causing other packets to be received in between */
+
+ rc = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (rc < 0) {
+ LOGBIND(nsbind, LOGL_ERROR, "Can not bind AF_PACKET socket to ifindex %d\n", ifindex);
+ close(fd);
+ return rc;
+ }
+
+ return fd;
+}
+
+static int gprs_n2_fr_ifupdown_ind_cb(struct osmo_netdev *netdev, bool if_running)
+{
+ struct gprs_ns2_vc_bind *bind = osmo_netdev_get_priv_data(netdev);
+ struct priv_bind *bpriv = bind->priv;
+ struct msgb *msg, *msg2;
+
+ if (bpriv->if_running == if_running)
+ return 0;
+
+ LOGBIND(bind, LOGL_NOTICE, "FR net-device '%s': Physical link state changed: %s\n",
+ bpriv->netif, if_running ? "UP" : "DOWN");
+
+ /* free any backlog, both on IFUP and IFDOWN. Keep the LMI, as it makes
+ * sense to get one out of the door ASAP. */
+ llist_for_each_entry_safe(msg, msg2, &bpriv->backlog.list, list) {
+ msgb_free(msg);
+ }
+
+ if (if_running) {
+ /* interface just came up */
+ if (bpriv->backlog.lmi_msg)
+ osmo_timer_schedule(&bpriv->backlog.timer, 0, bpriv->backlog.retry_us);
+ } else {
+ /* interface just went down; no need to retransmit */
+ osmo_timer_del(&bpriv->backlog.timer);
+ }
+
+ bpriv->if_running = if_running;
+ return 0;
+}
+
+static int gprs_n2_fr_mtu_chg_cb(struct osmo_netdev *netdev, uint32_t new_mtu)
+{
+ struct gprs_ns2_vc_bind *bind = osmo_netdev_get_priv_data(netdev);
+ struct priv_bind *bpriv = bind->priv;
+ struct gprs_ns2_nse *nse;
+
+ /* 2 byte DLCI header */
+ if (new_mtu <= 2)
+ return 0;
+ new_mtu -= 2;
+
+ if (new_mtu == bind->mtu)
+ return 0;
+
+ LOGBIND(bind, LOGL_INFO, "MTU changed from %d to %d.\n",
+ bind->mtu + 2, new_mtu + 2);
+
+ bind->mtu = new_mtu;
+ if (!bpriv->if_running)
+ return 0;
+
+ llist_for_each_entry(nse, &bind->nsi->nse, list) {
+ ns2_nse_update_mtu(nse);
+ }
+ return 0;
+}
+
+static int set_ifupdown(const char *netif, bool up)
+{
+ int sock, rc;
+ struct ifreq req;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ return sock;
+
+ memset(&req, 0, sizeof req);
+ OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
+
+ rc = ioctl(sock, SIOCGIFFLAGS, &req);
+ if (rc < 0) {
+ close(sock);
+ return rc;
+ }
+
+ if ((req.ifr_flags & IFF_UP) == up) {
+ close(sock);
+ return 0;
+ }
+
+ if (up)
+ req.ifr_flags |= IFF_UP;
+
+ rc = ioctl(sock, SIOCSIFFLAGS, &req);
+ close(sock);
+ return rc;
+}
+
+static int setup_device(const char *netif, const struct gprs_ns2_vc_bind *bind)
+{
+ int sock, rc;
+ char buffer[128];
+ fr_proto *fr = (void*)buffer;
+ struct ifreq req;
+
+ sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sock < 0) {
+ LOGBIND(bind, LOGL_ERROR, "%s: Unable to create socket: %s\n",
+ netif, strerror(errno));
+ return sock;
+ }
+
+ memset(&req, 0, sizeof(struct ifreq));
+ memset(&buffer, 0, sizeof(buffer));
+ OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
+ req.ifr_settings.ifs_ifsu.sync = (void*)buffer;
+ req.ifr_settings.size = sizeof(buffer);
+ req.ifr_settings.type = IF_GET_PROTO;
+
+ /* EINVAL is returned when no protocol has been set */
+ rc = ioctl(sock, SIOCWANDEV, &req);
+ if (rc < 0 && errno != EINVAL) {
+ LOGBIND(bind, LOGL_ERROR, "%s: Unable to get FR protocol information: %s\n",
+ netif, strerror(errno));
+ goto err;
+ }
+
+ /* check if the device is good */
+ if (rc == 0 && req.ifr_settings.type == IF_PROTO_FR && fr->lmi == LMI_NONE) {
+ LOGBIND(bind, LOGL_NOTICE, "%s: has correct frame relay mode and lmi\n", netif);
+ goto ifup;
+ }
+
+ /* modify the device to match */
+ rc = set_ifupdown(netif, false);
+ if (rc) {
+ LOGBIND(bind, LOGL_ERROR, "Unable to bring down the device %s: %s\n",
+ netif, strerror(errno));
+ goto err;
+ }
+
+ memset(&req, 0, sizeof(struct ifreq));
+ memset(fr, 0, sizeof(fr_proto));
+ OSMO_STRLCPY_ARRAY(req.ifr_name, netif);
+ req.ifr_settings.type = IF_PROTO_FR;
+ req.ifr_settings.size = sizeof(fr_proto);
+ req.ifr_settings.ifs_ifsu.fr = fr;
+ fr->lmi = LMI_NONE;
+ /* even those settings aren't used, they must be in the range */
+ /* polling verification timer*/
+ fr->t391 = 10;
+ /* link integrity verification polling timer */
+ fr->t392 = 15;
+ /* full status polling counter*/
+ fr->n391 = 6;
+ /* error threshold */
+ fr->n392 = 3;
+ /* monitored events count */
+ fr->n393 = 4;
+
+ LOGBIND(bind, LOGL_INFO, "%s: Setting frame relay related parameters\n", netif);
+ rc = ioctl(sock, SIOCWANDEV, &req);
+ if (rc) {
+ LOGBIND(bind, LOGL_ERROR, "%s: Unable to set FR protocol on information: %s\n",
+ netif, strerror(errno));
+ goto err;
+ }
+
+ifup:
+ rc = set_ifupdown(netif, true);
+ if (rc)
+ LOGBIND(bind, LOGL_ERROR, "Unable to bring up the device %s: %s\n",
+ netif, strerror(errno));
+err:
+ close(sock);
+ return rc;
+}
+
+/*! Create a new bind for NS over FR.
+ * \param[in] nsi NS instance in which to create the bind
+ * \param[in] netif Network interface to bind to
+ * \param[in] fr_network
+ * \param[in] fr_role
+ * \param[out] result pointer to the created bind or if a bind with the name exists return the bind.
+ * \return 0 on success; negative on error. -EALREADY returned in case a bind with the name exists */
+int gprs_ns2_fr_bind(struct gprs_ns2_inst *nsi,
+ const char *name,
+ const char *netif,
+ struct osmo_fr_network *fr_network,
+ enum osmo_fr_role fr_role,
+ struct gprs_ns2_vc_bind **result)
+{
+ struct gprs_ns2_vc_bind *bind;
+ struct priv_bind *priv;
+ struct osmo_fr_link *fr_link;
+ int rc = 0;
+
+ if (strlen(netif) > IFNAMSIZ)
+ return -EINVAL;
+
+ bind = gprs_ns2_bind_by_name(nsi, name);
+ if (bind) {
+ if (result)
+ *result = bind;
+ return -EALREADY;
+ }
+
+ rc = ns2_bind_alloc(nsi, name, &bind);
+ if (rc < 0)
+ return rc;
+
+ bind->driver = &vc_driver_fr;
+ bind->ll = GPRS_NS2_LL_FR;
+ /* 2 mbit */
+ bind->transfer_capability = 2;
+ bind->send_vc = fr_vc_sendmsg;
+ bind->free_vc = free_vc;
+ bind->dump_vty = dump_vty;
+ bind->mtu = FRAME_RELAY_SDU;
+ priv = bind->priv = talloc_zero(bind, struct priv_bind);
+ if (!priv) {
+ rc = -ENOMEM;
+ goto err_bind;
+ }
+
+ INIT_LLIST_HEAD(&priv->backlog.list);
+ OSMO_STRLCPY_ARRAY(priv->netif, netif);
+
+ /* FIXME: move fd handling into socket.c */
+ fr_link = osmo_fr_link_alloc(fr_network, fr_role, netif);
+ if (!fr_link) {
+ rc = -EINVAL;
+ goto err_bind;
+ }
+
+ fr_link->tx_cb = fr_tx_cb;
+ fr_link->cb_data = bind;
+ priv->link = fr_link;
+
+ priv->ifindex = rc = devname2ifindex(netif);
+ if (rc < 0) {
+ LOGBIND(bind, LOGL_ERROR, "Can not get interface index for interface %s\n", netif);
+ goto err_fr;
+ }
+
+ priv->netdev = osmo_netdev_alloc(bind, name);
+ if (!priv->netdev) {
+ rc = -ENOENT;
+ goto err_fr;
+ }
+ osmo_netdev_set_priv_data(priv->netdev, bind);
+ osmo_netdev_set_ifupdown_ind_cb(priv->netdev, gprs_n2_fr_ifupdown_ind_cb);
+ osmo_netdev_set_mtu_chg_cb(priv->netdev, gprs_n2_fr_mtu_chg_cb);
+ rc = osmo_netdev_set_ifindex(priv->netdev, priv->ifindex);
+ if (rc < 0)
+ goto err_free_netdev;
+ rc = osmo_netdev_register(priv->netdev);
+ if (rc < 0)
+ goto err_free_netdev;
+
+ /* set protocol frame relay and lmi */
+ rc = setup_device(priv->netif, bind);
+ if(rc < 0) {
+ LOGBIND(bind, LOGL_ERROR, "Failed to setup the interface %s for frame relay and lmi\n", netif);
+ goto err_free_netdev;
+ }
+
+ rc = open_socket(priv->ifindex, bind);
+ if (rc < 0)
+ goto err_free_netdev;
+ priv->backlog.retry_us = 2500; /* start with some non-zero value; this corrsponds to 496 bytes */
+ osmo_timer_setup(&priv->backlog.timer, fr_backlog_timer_cb, bind);
+ osmo_fd_setup(&priv->backlog.ofd, rc, OSMO_FD_READ, fr_netif_ofd_cb, bind, 0);
+ rc = osmo_fd_register(&priv->backlog.ofd);
+ if (rc < 0)
+ goto err_fd;
+
+ if (result)
+ *result = bind;
+
+ return rc;
+
+err_fd:
+ close(priv->backlog.ofd.fd);
+err_free_netdev:
+ osmo_netdev_free(priv->netdev);
+ priv->netdev = NULL;
+err_fr:
+ osmo_fr_link_free(fr_link);
+ priv->link = NULL;
+err_bind:
+ gprs_ns2_free_bind(bind);
+
+ return rc;
+}
+
+/*! Return the frame relay role of a bind
+ * \param[in] bind The bind
+ * \return the frame relay role or -EINVAL if bind is not frame relay
+ */
+enum osmo_fr_role gprs_ns2_fr_bind_role(struct gprs_ns2_vc_bind *bind)
+{
+ struct priv_bind *priv;
+
+ if (bind->driver != &vc_driver_fr)
+ return -EINVAL;
+
+ priv = bind->priv;
+ return priv->link->role;
+}
+
+/*! Return the network interface of the bind
+ * \param[in] bind The bind
+ * \return the network interface
+ */
+const char *gprs_ns2_fr_bind_netif(struct gprs_ns2_vc_bind *bind)
+{
+ struct priv_bind *priv;
+
+ if (bind->driver != &vc_driver_fr)
+ return NULL;
+
+ priv = bind->priv;
+ return priv->netif;
+}
+
+/*! Find NS bind for a given network interface
+ * \param[in] nsi NS instance
+ * \param[in] netif the network interface to search for
+ * \return the bind or NULL if not found
+ */
+struct gprs_ns2_vc_bind *gprs_ns2_fr_bind_by_netif(
+ struct gprs_ns2_inst *nsi,
+ const char *netif)
+{
+ struct gprs_ns2_vc_bind *bind;
+ const char *_netif;
+
+ OSMO_ASSERT(nsi);
+ OSMO_ASSERT(netif);
+
+ llist_for_each_entry(bind, &nsi->binding, list) {
+ if (!gprs_ns2_is_fr_bind(bind))
+ continue;
+
+ _netif = gprs_ns2_fr_bind_netif(bind);
+ if (!strncmp(_netif, netif, IFNAMSIZ))
+ return bind;
+ }
+
+ return NULL;
+}
+
+/*! Create, connect and activate a new FR-based NS-VC
+ * \param[in] bind bind in which the new NS-VC is to be created
+ * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
+ * \param[in] dlci Data Link connection identifier
+ * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
+struct gprs_ns2_vc *gprs_ns2_fr_connect(struct gprs_ns2_vc_bind *bind,
+ struct gprs_ns2_nse *nse,
+ uint16_t nsvci,
+ uint16_t dlci)
+{
+ struct gprs_ns2_vc *nsvc = NULL;
+ struct priv_vc *priv = NULL;
+ struct priv_bind *bpriv = bind->priv;
+ char idbuf[64];
+
+ OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
+ nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
+ if (nsvc) {
+ goto err;
+ }
+
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-NSVC%05u-%s-%s-DLCI%u", nse->nsei, nsvci,
+ gprs_ns2_lltype_str(nse->ll), bpriv->netif, dlci);
+ osmo_identifier_sanitize_buf(idbuf, NULL, '_');
+ nsvc = ns2_vc_alloc(bind, nse, true, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
+ if (!nsvc)
+ goto err;
+
+ nsvc->priv = priv = fr_alloc_vc(bind, nsvc, dlci);
+ if (!priv)
+ goto err;
+
+ nsvc->nsvci = nsvci;
+ nsvc->nsvci_is_valid = true;
+
+ return nsvc;
+
+err:
+ gprs_ns2_free_nsvc(nsvc);
+ return NULL;
+}
+
+
+/*! Create, connect and activate a new FR-based NS-VC
+ * \param[in] bind bind in which the new NS-VC is to be created
+ * \param[in] nsei NSEI of the NS Entity in which the NS-VC is to be created
+ * \param[in] dlci Data Link connection identifier
+ * \return pointer to newly-allocated, connected and activated NS-VC; NULL on error */
+struct gprs_ns2_vc *gprs_ns2_fr_connect2(struct gprs_ns2_vc_bind *bind,
+ uint16_t nsei,
+ uint16_t nsvci,
+ uint16_t dlci)
+{
+ bool created_nse = false;
+ struct gprs_ns2_vc *nsvc = NULL;
+ struct gprs_ns2_nse *nse;
+
+ OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
+ nse = gprs_ns2_nse_by_nsei(bind->nsi, nsei);
+ if (!nse) {
+ nse = gprs_ns2_create_nse(bind->nsi, nsei, GPRS_NS2_LL_FR, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ if (!nse)
+ return NULL;
+ created_nse = true;
+ }
+
+ nsvc = gprs_ns2_fr_connect(bind, nse, nsvci, dlci);
+ if (!nsvc)
+ goto err_nse;
+
+ return nsvc;
+
+err_nse:
+ if (created_nse)
+ gprs_ns2_free_nse(nse);
+
+ return NULL;
+}
+
+/*! Return the nsvc by dlci.
+ * \param[in] bind
+ * \param[in] dlci Data Link connection identifier
+ * \return the nsvc or NULL if not found
+ */
+struct gprs_ns2_vc *gprs_ns2_fr_nsvc_by_dlci(struct gprs_ns2_vc_bind *bind,
+ uint16_t dlci)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct priv_vc *vcpriv;
+
+ OSMO_ASSERT(gprs_ns2_is_fr_bind(bind));
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ vcpriv = nsvc->priv;
+
+ if (dlci == vcpriv->dlci)
+ return nsvc;
+ }
+
+ return NULL;
+}
+
+/*! Return the dlci of the nsvc
+ * \param[in] nsvc
+ * \return the dlci or 0 on error. 0 is not a valid dlci.
+ */
+uint16_t gprs_ns2_fr_nsvc_dlci(const struct gprs_ns2_vc *nsvc)
+{
+ struct priv_vc *vcpriv;
+
+ if (!nsvc->bind)
+ return 0;
+
+ if (nsvc->bind->driver != &vc_driver_fr)
+ return 0;
+
+ vcpriv = nsvc->priv;
+ return vcpriv->dlci;
+}
diff --git a/src/gb/gprs_ns2_frgre.c b/src/gb/gprs_ns2_frgre.c
new file mode 100644
index 00000000..b99761eb
--- /dev/null
+++ b/src/gb/gprs_ns2_frgre.c
@@ -0,0 +1,616 @@
+/*! \file gprs_ns2_frgre.c
+ * NS-over-FR-over-GRE implementation.
+ * GPRS Networks Service (NS) messages on the Gb interface.
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * as well as its successor 3GPP TS 48.016 */
+
+/* (C) 2009-2010,2014,2017 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/byteswap.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gprs/gprs_ns2.h>
+
+#include "gprs_ns2_internal.h"
+
+#define GRE_PTYPE_FR 0x6559
+#define GRE_PTYPE_IPv4 0x0800
+#define GRE_PTYPE_IPv6 0x86dd
+#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
+
+#ifndef IPPROTO_GRE
+# define IPPROTO_GRE 47
+#endif
+
+struct gre_hdr {
+ uint16_t flags;
+ uint16_t ptype;
+} __attribute__ ((packed));
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__CYGWIN__)
+/**
+ * On BSD the IPv4 struct is called struct ip and instead of iXX
+ * the members are called ip_XX. One could change this code to use
+ * struct ip but that would require to define _BSD_SOURCE and that
+ * might have other complications. Instead make sure struct iphdr
+ * is present on FreeBSD. The below is taken from GLIBC.
+ *
+ * The GNU C 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 2.1 of the License, or (at your option) any later version.
+ */
+struct iphdr
+ {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned int ihl:4;
+ unsigned int version:4;
+#elif BYTE_ORDER == BIG_ENDIAN
+ unsigned int version:4;
+ unsigned int ihl:4;
+#endif
+ u_int8_t tos;
+ u_int16_t tot_len;
+ u_int16_t id;
+ u_int16_t frag_off;
+ u_int8_t ttl;
+ u_int8_t protocol;
+ u_int16_t check;
+ u_int32_t saddr;
+ u_int32_t daddr;
+ /*The options start here. */
+ };
+#endif
+
+
+static void free_bind(struct gprs_ns2_vc_bind *bind);
+static inline int frgre_sendmsg(struct gprs_ns2_vc_bind *bind,
+ struct msgb *msg,
+ struct osmo_sockaddr *dest);
+
+struct gprs_ns2_vc_driver vc_driver_frgre = {
+ .name = "GB frame relay over GRE",
+ .free_bind = free_bind,
+};
+
+struct priv_bind {
+ struct osmo_fd fd;
+ struct osmo_sockaddr addr;
+ uint16_t dlci;
+ int dscp;
+};
+
+struct priv_vc {
+ struct osmo_sockaddr remote;
+ uint16_t dlci;
+};
+
+static void free_vc(struct gprs_ns2_vc *nsvc)
+{
+ OSMO_ASSERT(nsvc);
+
+ if (!nsvc->priv)
+ return;
+
+ talloc_free(nsvc->priv);
+ nsvc->priv = NULL;
+}
+
+
+/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
+static void free_bind(struct gprs_ns2_vc_bind *bind)
+{
+ struct priv_bind *priv;
+
+ if (!bind)
+ return;
+
+ priv = bind->priv;
+
+ OSMO_ASSERT(llist_empty(&bind->nsvc));
+
+ osmo_fd_close(&priv->fd);
+ talloc_free(priv);
+}
+
+static struct priv_vc *frgre_alloc_vc(struct gprs_ns2_vc_bind *bind,
+ struct gprs_ns2_vc *nsvc,
+ struct osmo_sockaddr *remote,
+ uint16_t dlci)
+{
+ struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
+ if (!priv)
+ return NULL;
+
+ nsvc->priv = priv;
+ priv->remote = *remote;
+ priv->dlci = dlci;
+
+ return priv;
+}
+
+static int handle_rx_gre_ipv6(struct osmo_fd *bfd, struct msgb *msg,
+ struct ip6_hdr *ip6hdr, struct gre_hdr *greh)
+{
+ /* RFC 7676 IPv6 Support for Generic Routing Encapsulation (GRE) */
+ struct gprs_ns2_vc_bind *bind = bfd->data;
+ struct priv_bind *priv = bind->priv;
+ int gre_payload_len;
+ struct ip6_hdr *inner_ip6h;
+ struct gre_hdr *inner_greh;
+ struct sockaddr_in6 daddr;
+ struct in6_addr ia6;
+
+ gre_payload_len = msg->len - (sizeof(*ip6hdr) + sizeof(*greh));
+
+ inner_ip6h = (struct ip6_hdr *) ((uint8_t *)greh + sizeof(*greh));
+
+ if (gre_payload_len < sizeof(*ip6hdr) + sizeof(*inner_greh)) {
+ LOGBIND(bind, LOGL_ERROR, "GRE keepalive too short\n");
+ return -EIO;
+ }
+
+ if (!memcmp(&inner_ip6h->ip6_src, &ip6hdr->ip6_src, sizeof(struct in6_addr)) ||
+ !memcmp(&inner_ip6h->ip6_dst, &ip6hdr->ip6_dst, sizeof(struct in6_addr))) {
+ LOGBIND(bind, LOGL_ERROR, "GRE keepalive with wrong tunnel addresses\n");
+ return -EIO;
+ }
+
+ /* Are IPv6 extensions header are allowed in the *inner*? In the outer they are */
+ if (inner_ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_GRE) {
+ LOGBIND(bind, LOGL_ERROR, "GRE keepalive with wrong protocol\n");
+ return -EIO;
+ }
+
+ inner_greh = (struct gre_hdr *) ((uint8_t *)inner_ip6h + sizeof(struct ip6_hdr));
+ if (inner_greh->ptype != osmo_htons(GRE_PTYPE_KAR)) {
+ LOGBIND(bind, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n");
+ return -EIO;
+ }
+
+ /* Actually send the response back */
+
+ daddr.sin6_family = AF_INET6;
+ daddr.sin6_addr = inner_ip6h->ip6_dst;
+ daddr.sin6_port = IPPROTO_GRE;
+
+ ia6 = ip6hdr->ip6_src;
+ char ip6str[INET6_ADDRSTRLEN] = {};
+ inet_ntop(AF_INET6, &ia6, ip6str, INET6_ADDRSTRLEN);
+ LOGBIND(bind, LOGL_DEBUG, "GRE keepalive from %s, responding\n", ip6str);
+
+ /* why does it reduce the gre_payload_len by the ipv6 header?
+ * make it similiar to ipv4 even this seems to be wrong */
+ return sendto(priv->fd.fd, inner_greh,
+ gre_payload_len - sizeof(*inner_ip6h), 0,
+ (struct sockaddr *)&daddr, sizeof(daddr));
+}
+
+/* IPv4 messages inside the GRE tunnel might be GRE keepalives */
+static int handle_rx_gre_ipv4(struct osmo_fd *bfd, struct msgb *msg,
+ struct iphdr *iph, struct gre_hdr *greh)
+{
+ struct gprs_ns2_vc_bind *bind = bfd->data;
+ struct priv_bind *priv = bind->priv;
+ int gre_payload_len;
+ struct iphdr *inner_iph;
+ struct gre_hdr *inner_greh;
+ struct sockaddr_in daddr;
+ struct in_addr ia;
+
+ gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh));
+
+ inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh));
+
+ if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) {
+ LOGBIND(bind, LOGL_ERROR, "GRE keepalive too short\n");
+ return -EIO;
+ }
+
+ if (inner_iph->saddr != iph->daddr ||
+ inner_iph->daddr != iph->saddr) {
+ LOGBIND(bind, LOGL_ERROR, "GRE keepalive with wrong tunnel addresses\n");
+ return -EIO;
+ }
+
+ if (inner_iph->protocol != IPPROTO_GRE) {
+ LOGBIND(bind, LOGL_ERROR, "GRE keepalive with wrong protocol\n");
+ return -EIO;
+ }
+
+ inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4);
+ if (inner_greh->ptype != osmo_htons(GRE_PTYPE_KAR)) {
+ LOGBIND(bind, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n");
+ return -EIO;
+ }
+
+ /* Actually send the response back */
+
+ daddr.sin_family = AF_INET;
+ daddr.sin_addr.s_addr = inner_iph->daddr;
+ daddr.sin_port = IPPROTO_GRE;
+
+ ia.s_addr = iph->saddr;
+ LOGBIND(bind, LOGL_DEBUG, "GRE keepalive from %s, responding\n", inet_ntoa(ia));
+
+ /* why does it reduce the gre_payload_len by the ipv4 header? */
+ return sendto(priv->fd.fd, inner_greh,
+ gre_payload_len - inner_iph->ihl*4, 0,
+ (struct sockaddr *)&daddr, sizeof(daddr));
+}
+
+static struct msgb *read_nsfrgre_msg(struct osmo_fd *bfd, int *error,
+ struct osmo_sockaddr *saddr, uint16_t *dlci,
+ const struct gprs_ns2_vc_bind *bind)
+{
+ struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
+ int ret = 0;
+ socklen_t saddr_len = sizeof(*saddr);
+ struct iphdr *iph = NULL;
+ struct ip6_hdr *ip6h = NULL;
+ size_t ip46hdr;
+ struct gre_hdr *greh;
+ uint8_t *frh;
+
+ if (!msg) {
+ *error = -ENOMEM;
+ return NULL;
+ }
+
+ ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
+ &saddr->u.sa, &saddr_len);
+ if (ret < 0) {
+ LOGBIND(bind, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n", strerror(errno));
+ *error = ret;
+ goto out_err;
+ } else if (ret == 0) {
+ *error = ret;
+ goto out_err;
+ }
+
+ msgb_put(msg, ret);
+
+ /* we've received a raw packet including the IPv4 or IPv6 header */
+ switch (saddr->u.sa.sa_family) {
+ case AF_INET:
+ ip46hdr = sizeof(struct iphdr);
+ break;
+ case AF_INET6:
+ ip46hdr = sizeof(struct ip6_hdr);
+ break;
+ default:
+ *error = -EIO;
+ goto out_err;
+ break;
+ }
+
+ /* TODO: add support for the extension headers */
+ if (msg->len < ip46hdr + sizeof(*greh) + 2) {
+ LOGBIND(bind, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
+ *error = -EIO;
+ goto out_err;
+ }
+
+ switch (saddr->u.sa.sa_family) {
+ case AF_INET:
+ iph = (struct iphdr *) msg->data;
+ if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) {
+ LOGBIND(bind, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
+ *error = -EIO;
+ goto out_err;
+ }
+ break;
+ case AF_INET6:
+ ip6h = (struct ip6_hdr *) msg->data;
+ break;
+ }
+
+ if (iph)
+ greh = (struct gre_hdr *) (msg->data + iph->ihl*4);
+ else
+ greh = (struct gre_hdr *) (msg->data + sizeof(struct ip6_hdr));
+
+ if (greh->flags) {
+ LOGBIND(bind, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n", osmo_ntohs(greh->flags));
+ }
+
+ switch (osmo_ntohs(greh->ptype)) {
+ case GRE_PTYPE_IPv4:
+ /* IPv4 messages might be GRE keepalives */
+ if (iph)
+ *error = handle_rx_gre_ipv4(bfd, msg, iph, greh);
+ else
+ *error = -EIO;
+ goto out_err;
+ break;
+ case GRE_PTYPE_IPv6:
+ if (ip6h)
+ *error = handle_rx_gre_ipv6(bfd, msg, ip6h, greh);
+ else
+ *error = -EIO;
+ goto out_err;
+ break;
+ case GRE_PTYPE_FR:
+ /* continue as usual */
+ break;
+ default:
+ LOGBIND(bind, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n", osmo_ntohs(greh->ptype));
+ *error = -EIO;
+ goto out_err;
+ break;
+ }
+
+ if (msg->len < sizeof(*greh) + 2) {
+ LOGBIND(bind, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
+ *error = -EIO;
+ goto out_err;
+ }
+
+ frh = (uint8_t *)greh + sizeof(*greh);
+ if (frh[0] & 0x01) {
+ LOGBIND(bind, LOGL_NOTICE, "Unsupported single-byte FR address\n");
+ *error = -EIO;
+ goto out_err;
+ }
+ *dlci = ((frh[0] & 0xfc) << 2);
+ if ((frh[1] & 0x0f) != 0x01) {
+ LOGBIND(bind, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n", frh[1]);
+ *error = -EIO;
+ goto out_err;
+ }
+ *dlci |= (frh[1] >> 4);
+
+ msg->l2h = frh+2;
+
+ return msg;
+
+out_err:
+ msgb_free(msg);
+ return NULL;
+}
+
+static int ns2_find_vc_by_dlci(struct gprs_ns2_vc_bind *bind,
+ uint16_t dlci,
+ struct gprs_ns2_vc **result)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct priv_vc *vcpriv;
+
+ if (!result)
+ return -EINVAL;
+
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ vcpriv = nsvc->priv;
+ if (vcpriv->dlci != dlci) {
+ *result = nsvc;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int handle_nsfrgre_read(struct osmo_fd *bfd)
+{
+ int rc;
+ struct osmo_sockaddr saddr;
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_vc_bind *bind = bfd->data;
+ struct msgb *msg;
+ struct msgb *reject;
+ uint16_t dlci;
+
+ msg = read_nsfrgre_msg(bfd, &rc, &saddr, &dlci, bind);
+ if (!msg)
+ return rc;
+
+ if (dlci == 0 || dlci == 1023) {
+ LOGBIND(bind, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n", dlci);
+ rc = 0;
+ goto out;
+ }
+
+ rc = ns2_find_vc_by_dlci(bind, dlci, &nsvc);
+ if (rc) {
+ /* VC not found */
+ rc = ns2_create_vc(bind, msg, &saddr, "newconnection", &reject, &nsvc);
+ switch (rc) {
+ case NS2_CS_FOUND:
+ break;
+ case NS2_CS_ERROR:
+ case NS2_CS_SKIPPED:
+ rc = 0;
+ goto out;
+ case NS2_CS_REJECTED:
+ /* nsip_sendmsg will free reject */
+ rc = frgre_sendmsg(bind, reject, &saddr);
+ goto out;
+ case NS2_CS_CREATED:
+ frgre_alloc_vc(bind, nsvc, &saddr, dlci);
+ ns2_vc_fsm_start(nsvc);
+ break;
+ }
+ }
+
+ rc = ns2_recv_vc(nsvc, msg);
+out:
+ msgb_free(msg);
+
+ return rc;
+}
+
+static int handle_nsfrgre_write(struct osmo_fd *bfd)
+{
+ /* FIXME: actually send the data here instead of nsip_sendmsg() */
+ return -EIO;
+}
+
+static inline int frgre_sendmsg(struct gprs_ns2_vc_bind *bind,
+ struct msgb *msg,
+ struct osmo_sockaddr *dest)
+{
+ int rc;
+ struct priv_bind *priv = bind->priv;
+
+ rc = sendto(priv->fd.fd, msg->data, msg->len, 0,
+ &dest->u.sa, sizeof(*dest));
+
+ msgb_free(msg);
+
+ return rc;
+}
+
+static int frgre_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
+{
+ struct gprs_ns2_vc_bind *bind = nsvc->bind;
+ struct priv_vc *vcpriv = nsvc->priv;
+ struct priv_bind *bindpriv = bind->priv;
+
+ uint16_t dlci = osmo_htons(bindpriv->dlci);
+ uint8_t *frh;
+ struct gre_hdr *greh;
+ unsigned int vc_len = msgb_length(msg);
+ int rc;
+
+ /* Prepend the FR header */
+ frh = msgb_push(msg, 2);
+ frh[0] = (dlci >> 2) & 0xfc;
+ frh[1] = ((dlci & 0xf)<<4) | 0x01;
+
+ /* Prepend the GRE header */
+ greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
+ greh->flags = 0;
+ greh->ptype = osmo_htons(GRE_PTYPE_FR);
+
+ rc = frgre_sendmsg(bind, msg, &vcpriv->remote);
+ if (OSMO_LIKELY(rc >= 0)) {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT, rc);
+ } else {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT_DROP);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT_DROP, vc_len);
+ }
+ return rc;
+}
+
+static int frgre_fd_cb(struct osmo_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & OSMO_FD_READ)
+ rc = handle_nsfrgre_read(bfd);
+ if (what & OSMO_FD_WRITE)
+ rc = handle_nsfrgre_write(bfd);
+
+ return rc;
+}
+
+/*! determine if given bind is for FR-GRE encapsulation. */
+int gprs_ns2_is_frgre_bind(struct gprs_ns2_vc_bind *bind)
+{
+ return (bind->driver == &vc_driver_frgre);
+}
+
+/*! Create a new bind for NS over FR-GRE.
+ * \param[in] nsi NS instance in which to create the bind
+ * \param[in] local local address on which to bind
+ * \param[in] dscp DSCP/TOS bits to use for transmitted data on this bind
+ * \param[out] result pointer to the created bind or if a bind with the name exists return the bind.
+ * \return 0 on success; negative on error. -EALREADY returned in case a bind with the name exists */
+int gprs_ns2_frgre_bind(struct gprs_ns2_inst *nsi,
+ const char *name,
+ const struct osmo_sockaddr *local,
+ int dscp,
+ struct gprs_ns2_vc_bind **result)
+{
+ struct gprs_ns2_vc_bind *bind;
+ struct priv_bind *priv;
+ int rc;
+
+ if (local->u.sa.sa_family != AF_INET && local->u.sa.sa_family != AF_INET6)
+ return -EINVAL;
+
+ if (dscp < 0 || dscp > 63)
+ return -EINVAL;
+
+ bind = gprs_ns2_bind_by_name(nsi, name);
+ if (bind) {
+ if (result)
+ *result = bind;
+ return -EALREADY;
+ }
+
+ rc = ns2_bind_alloc(nsi, name, &bind);
+ if (rc < 0)
+ return rc;
+
+ bind->driver = &vc_driver_frgre;
+ bind->ll = GPRS_NS2_LL_FR_GRE;
+ /* 2 mbit transfer capability. Counting should be done different for this. */
+ bind->transfer_capability = 2;
+ bind->send_vc = frgre_vc_sendmsg;
+ bind->free_vc = free_vc;
+ bind->nsi = nsi;
+ /* TODO: allow to set the MTU via vty. It can not be automatic detected because it goes over an
+ * ethernet device and the MTU here must match the FR side of the FR-to-GRE gateway.
+ */
+ bind->mtu = FRAME_RELAY_SDU;
+
+ priv = bind->priv = talloc_zero(bind, struct priv_bind);
+ if (!priv) {
+ gprs_ns2_free_bind(bind);
+ return -ENOMEM;
+ }
+ priv->fd.cb = frgre_fd_cb;
+ priv->fd.data = bind;
+ priv->addr = *local;
+ INIT_LLIST_HEAD(&bind->nsvc);
+ priv->dscp = dscp;
+
+ rc = osmo_sock_init_osa_ofd(&priv->fd, SOCK_RAW, IPPROTO_GRE,
+ local, NULL,
+ OSMO_SOCK_F_BIND | OSMO_SOCK_F_DSCP(priv->dscp));
+ if (rc < 0) {
+ gprs_ns2_free_bind(bind);
+ return rc;
+ }
+
+ if (result)
+ *result = bind;
+
+ return rc;
+}
diff --git a/src/gb/gprs_ns2_internal.h b/src/gb/gprs_ns2_internal.h
new file mode 100644
index 00000000..2e7dac3f
--- /dev/null
+++ b/src/gb/gprs_ns2_internal.h
@@ -0,0 +1,503 @@
+/*! \file gprs_ns2_internal.h */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/gprs/protocol/gsm_08_16.h>
+#include <osmocom/gprs/gprs_ns2.h>
+
+#define LOGNSE(nse, lvl, fmt, args ...) \
+ LOGP(DLNS, lvl, "NSE(%05u) " fmt, (nse)->nsei, ## args)
+
+#define LOGBIND(bind, lvl, fmt, args ...) \
+ LOGP(DLNS, lvl, "BIND(%s) " fmt, (bind)->name, ## args)
+
+#define LOGNSVC_SS(ss, nsvc, lvl, fmt, args ...) \
+ do { \
+ if ((nsvc)->nsvci_is_valid) { \
+ LOGP(ss, lvl, "NSE(%05u)-NSVC(%05u) " fmt, \
+ (nsvc)->nse->nsei, (nsvc)->nsvci, ## args); \
+ } else { \
+ LOGP(ss, lvl, "NSE(%05u)-NSVC(none) " fmt, \
+ (nsvc)->nse->nsei, ## args); \
+ } \
+ } while (0)
+
+#define LOGNSVC(nsvc, lvl, fmt, args ...) \
+ LOGNSVC_SS(DLNS, nsvc, lvl, fmt, ## args)
+
+#define LOG_NS_SIGNAL(nsvc, direction, pdu_type, lvl, fmt, args ...) \
+ LOGNSVC_SS(DLNSSIGNAL, nsvc, lvl, "%s %s" fmt, direction, get_value_string(gprs_ns_pdu_strings, pdu_type), ## args)
+
+#define LOG_NS_DATA(nsvc, direction, pdu_type, lvl, fmt, args ...) \
+ LOGNSVC_SS(DLNSDATA, nsvc, lvl, "%s %s" fmt, direction, get_value_string(gprs_ns_pdu_strings, pdu_type), ## args)
+
+#define LOG_NS_RX_SIGNAL(nsvc, pdu_type) LOG_NS_SIGNAL(nsvc, "Rx", pdu_type, LOGL_INFO, "\n")
+#define LOG_NS_TX_SIGNAL(nsvc, pdu_type) LOG_NS_SIGNAL(nsvc, "Tx", pdu_type, LOGL_INFO, "\n")
+
+#define RATE_CTR_INC_NS(nsvc, ctr) \
+ do { \
+ struct gprs_ns2_vc *_nsvc = (nsvc); \
+ rate_ctr_inc(rate_ctr_group_get_ctr(_nsvc->ctrg, ctr)); \
+ rate_ctr_inc(rate_ctr_group_get_ctr(_nsvc->nse->ctrg, ctr)); \
+ } while (0)
+
+#define RATE_CTR_ADD_NS(nsvc, ctr, val) \
+ do { \
+ struct gprs_ns2_vc *_nsvc = (nsvc); \
+ rate_ctr_add(rate_ctr_group_get_ctr(_nsvc->ctrg, ctr), val); \
+ rate_ctr_add(rate_ctr_group_get_ctr(_nsvc->nse->ctrg, ctr), val); \
+ } while (0)
+
+
+struct osmo_fsm_inst;
+struct tlv_parsed;
+struct vty;
+
+struct gprs_ns2_vc_driver;
+struct gprs_ns2_vc_bind;
+
+#define NS_TIMERS_COUNT 11
+
+#define TNS_BLOCK_STR "tns-block"
+#define TNS_BLOCK_RETRIES_STR "tns-block-retries"
+#define TNS_RESET_STR "tns-reset"
+#define TNS_RESET_RETRIES_STR "tns-reset-retries"
+#define TNS_TEST_STR "tns-test"
+#define TNS_ALIVE_STR "tns-alive"
+#define TNS_ALIVE_RETRIES_STR "tns-alive-retries"
+#define TSNS_PROV_STR "tsns-prov"
+#define TSNS_SIZE_RETRIES_STR "tsns-size-retries"
+#define TSNS_CONFIG_RETRIES_STR "tsns-config-retries"
+#define TSNS_PROCEDURES_RETRIES_STR "tsns-procedures-retries"
+#define NS_TIMERS "(" TNS_BLOCK_STR "|" TNS_BLOCK_RETRIES_STR "|" TNS_RESET_STR "|" TNS_RESET_RETRIES_STR "|" TNS_TEST_STR "|"\
+ TNS_ALIVE_STR "|" TNS_ALIVE_RETRIES_STR "|" TSNS_PROV_STR "|" TSNS_SIZE_RETRIES_STR "|" TSNS_CONFIG_RETRIES_STR "|"\
+ TSNS_PROCEDURES_RETRIES_STR ")"
+
+#define NS_TIMERS_HELP \
+ "(un)blocking Timer (Tns-block) timeout\n" \
+ "(un)blocking Timer (Tns-block) number of retries\n" \
+ "Reset Timer (Tns-reset) timeout\n" \
+ "Reset Timer (Tns-reset) number of retries\n" \
+ "Test Timer (Tns-test) timeout\n" \
+ "Alive Timer (Tns-alive) timeout\n" \
+ "Alive Timer (Tns-alive) number of retries\n" \
+ "SNS Provision Timer (Tsns-prov) timeout\n" \
+ "SNS Size number of retries\n" \
+ "SNS Config number of retries\n" \
+ "SNS Procedures number of retries\n" \
+
+/* Educated guess - LLC user payload is 1500 bytes plus possible headers */
+#define NS_ALLOC_SIZE 3072
+#define NS_ALLOC_HEADROOM 20
+
+#define NS_DEFAULT_TXQUEUE_MAX_LENGTH 128
+
+enum ns2_timeout {
+ NS_TOUT_TNS_BLOCK,
+ NS_TOUT_TNS_BLOCK_RETRIES,
+ NS_TOUT_TNS_RESET,
+ NS_TOUT_TNS_RESET_RETRIES,
+ NS_TOUT_TNS_TEST,
+ NS_TOUT_TNS_ALIVE,
+ NS_TOUT_TNS_ALIVE_RETRIES,
+ NS_TOUT_TSNS_PROV,
+ NS_TOUT_TSNS_SIZE_RETRIES,
+ NS_TOUT_TSNS_CONFIG_RETRIES,
+ NS_TOUT_TSNS_PROCEDURES_RETRIES,
+};
+
+enum nsvc_timer_mode {
+ /* standard timers */
+ NSVC_TIMER_TNS_TEST,
+ NSVC_TIMER_TNS_ALIVE,
+ NSVC_TIMER_TNS_RESET,
+ _NSVC_TIMER_NR,
+};
+
+enum ns2_vc_stat {
+ NS_STAT_ALIVE_DELAY,
+};
+
+enum ns2_bind_stat {
+ NS2_BIND_STAT_BACKLOG_LEN,
+};
+
+/*! Osmocom NS2 VC create status */
+enum ns2_cs {
+ NS2_CS_CREATED, /*!< A NSVC object has been created */
+ NS2_CS_FOUND, /*!< A NSVC object has been found */
+ NS2_CS_REJECTED, /*!< Rejected and answered message */
+ NS2_CS_SKIPPED, /*!< Skipped message */
+ NS2_CS_ERROR, /*!< Failed to process message */
+};
+
+enum ns_ctr {
+ NS_CTR_PKTS_IN,
+ NS_CTR_PKTS_OUT,
+ NS_CTR_PKTS_OUT_DROP,
+ NS_CTR_BYTES_IN,
+ NS_CTR_BYTES_OUT,
+ NS_CTR_BYTES_OUT_DROP,
+ NS_CTR_BLOCKED,
+ NS_CTR_UNBLOCKED,
+ NS_CTR_DEAD,
+ NS_CTR_REPLACED,
+ NS_CTR_NSEI_CHG,
+ NS_CTR_INV_VCI,
+ NS_CTR_INV_NSEI,
+ NS_CTR_LOST_ALIVE,
+ NS_CTR_LOST_RESET,
+};
+
+#define NSE_S_BLOCKED 0x0001
+#define NSE_S_ALIVE 0x0002
+#define NSE_S_RESET 0x0004
+
+#define NS_DESC_B(st) ((st) & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED")
+#define NS_DESC_A(st) ((st) & NSE_S_ALIVE ? "ALIVE" : "DEAD")
+#define NS_DESC_R(st) ((st) & NSE_S_RESET ? "RESET" : "UNRESET")
+
+/*! An instance of the NS protocol stack */
+struct gprs_ns2_inst {
+ /*! callback to the user for incoming UNIT DATA IND */
+ osmo_prim_cb cb;
+
+ /*! callback data */
+ void *cb_data;
+
+ /*! linked lists of all NSVC binds (e.g. IPv4 bind, but could be also E1 */
+ struct llist_head binding;
+
+ /*! linked lists of all NSVC in this instance */
+ struct llist_head nse;
+
+ uint16_t timeout[NS_TIMERS_COUNT];
+
+ /*! workaround for rate counter until rate counter accepts char str as index */
+ uint32_t nsvc_rate_ctr_idx;
+ uint32_t bind_rate_ctr_idx;
+
+ uint32_t txqueue_max_length;
+};
+
+
+/*! Structure repesenting a NSE. The BSS/PCU will only have a single NSE, while SGSN has one for each BSS/PCU */
+struct gprs_ns2_nse {
+ uint16_t nsei;
+
+ /*! entry back to ns2_inst */
+ struct gprs_ns2_inst *nsi;
+
+ /*! llist entry for gprs_ns2_inst */
+ struct llist_head list;
+
+ /*! llist head to hold all nsvc */
+ struct llist_head nsvc;
+
+ /*! count all active NSVCs */
+ int nsvc_count;
+
+ /*! true if this NSE was created by VTY or pcu socket) */
+ bool persistent;
+
+ /*! true if this NSE wasn't yet alive at all.
+ * Will be true after the first status ind with NS_AFF_CAUSE_RECOVERY */
+ bool first;
+
+ /*! true if this NSE has at least one alive VC */
+ bool alive;
+
+ /*! which link-layer are we based on? */
+ enum gprs_ns2_ll ll;
+
+ /*! which dialect does this NSE speaks? */
+ enum gprs_ns2_dialect dialect;
+
+ struct osmo_fsm_inst *bss_sns_fi;
+
+ /*! sum of all the data weight of _alive_ NS-VCs */
+ uint32_t sum_data_weight;
+
+ /*! sum of all the signalling weight of _alive_ NS-VCs */
+ uint32_t sum_sig_weight;
+
+ /*! MTU of a NS PDU. This is the lowest MTU of all NSVCs */
+ uint16_t mtu;
+
+ /*! are we implementing the SGSN role? */
+ bool ip_sns_role_sgsn;
+
+ /*! NSE-wide statistics */
+ struct rate_ctr_group *ctrg;
+
+ /*! recursive anchor */
+ bool freed;
+
+ /*! when the NSE became alive or dead */
+ struct timespec ts_alive_change;
+};
+
+/*! Structure representing a single NS-VC */
+struct gprs_ns2_vc {
+ /*! list of NS-VCs within NSE */
+ struct llist_head list;
+
+ /*! list of NS-VCs within bind, bind is the owner! */
+ struct llist_head blist;
+
+ /*! pointer to NS Instance */
+ struct gprs_ns2_nse *nse;
+
+ /*! pointer to NS VL bind. bind own the memory of this instance */
+ struct gprs_ns2_vc_bind *bind;
+
+ /*! true if this NS was created by VTY or pcu socket) */
+ bool persistent;
+
+ /*! uniquely identifies NS-VC if VC contains nsvci */
+ uint16_t nsvci;
+
+ /*! signalling weight. 0 = don't use for signalling (BVCI == 0)*/
+ uint8_t sig_weight;
+
+ /*! signalling packet counter for the load sharing function */
+ uint8_t sig_counter;
+
+ /*! data weight. 0 = don't use for user data (BVCI != 0) */
+ uint8_t data_weight;
+
+ /*! can be used by the bind/driver of the virtual circuit. e.g. ipv4/ipv6/frgre/e1 */
+ void *priv;
+
+ bool nsvci_is_valid;
+ /*! should this NS-VC only be used for SNS-SIZE and SNS-CONFIG? */
+ bool sns_only;
+
+ struct rate_ctr_group *ctrg;
+ struct osmo_stat_item_group *statg;
+
+ enum gprs_ns2_vc_mode mode;
+
+ struct osmo_fsm_inst *fi;
+
+ /*! recursive anchor */
+ bool freed;
+
+ /*! if blocked by O&M/vty */
+ bool om_blocked;
+
+ /*! when the NSVC became alive or dead */
+ struct timespec ts_alive_change;
+};
+
+/*! Structure repesenting a bind instance. E.g. IPv4 listen port. */
+struct gprs_ns2_vc_bind {
+ /*! unique name */
+ const char *name;
+ /*! list entry in nsi */
+ struct llist_head list;
+ /*! list of all VC */
+ struct llist_head nsvc;
+ /*! driver private structure */
+ void *priv;
+ /*! a pointer back to the nsi */
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_driver *driver;
+
+ bool accept_ipaccess;
+ bool accept_sns;
+
+ /*! transfer capability in mbit */
+ int transfer_capability;
+
+ /*! MTU of a NS PDU on this bind. */
+ uint16_t mtu;
+
+ /*! which link-layer are we based on? */
+ enum gprs_ns2_ll ll;
+
+ /*! send a msg over a VC */
+ int (*send_vc)(struct gprs_ns2_vc *nsvc, struct msgb *msg);
+
+ /*! free the vc priv data */
+ void (*free_vc)(struct gprs_ns2_vc *nsvc);
+
+ /*! allow to show information for the vty */
+ void (*dump_vty)(const struct gprs_ns2_vc_bind *bind,
+ struct vty *vty, bool stats);
+
+ /*! the IP-SNS signalling weight when doing dynamic configuration */
+ uint8_t sns_sig_weight;
+ /*! the IP-SNS data weight when doing dynamic configuration */
+ uint8_t sns_data_weight;
+
+ struct osmo_stat_item_group *statg;
+
+ /*! recursive anchor */
+ bool freed;
+};
+
+struct gprs_ns2_vc_driver {
+ const char *name;
+ void *priv;
+ void (*free_bind)(struct gprs_ns2_vc_bind *driver);
+};
+
+enum ns2_sns_event {
+ NS2_SNS_EV_REQ_SELECT_ENDPOINT, /*!< Select a SNS endpoint from the list */
+ NS2_SNS_EV_RX_SIZE,
+ NS2_SNS_EV_RX_SIZE_ACK,
+ NS2_SNS_EV_RX_CONFIG,
+ NS2_SNS_EV_RX_CONFIG_END, /*!< SNS-CONFIG with end flag received */
+ NS2_SNS_EV_RX_CONFIG_ACK,
+ NS2_SNS_EV_RX_ADD,
+ NS2_SNS_EV_RX_DELETE,
+ NS2_SNS_EV_RX_CHANGE_WEIGHT,
+ NS2_SNS_EV_RX_ACK, /*!< Rx of SNS-ACK (response to ADD/DELETE/CHG_WEIGHT */
+ NS2_SNS_EV_REQ_NO_NSVC, /*!< no more NS-VC remaining (all dead) */
+ NS2_SNS_EV_REQ_FREE_NSVCS, /*!< free all NS-VCs */
+ NS2_SNS_EV_REQ_NSVC_ALIVE, /*!< a NS-VC became alive */
+ NS2_SNS_EV_REQ_ADD_BIND, /*!< add a new local bind to this NSE */
+ NS2_SNS_EV_REQ_DELETE_BIND, /*!< remove a local bind from this NSE */
+ NS2_SNS_EV_REQ_CHANGE_WEIGHT, /*!< a bind changed its weight */
+};
+
+enum ns2_cs ns2_create_vc(struct gprs_ns2_vc_bind *bind,
+ struct msgb *msg,
+ const struct osmo_sockaddr *remote,
+ const char *logname,
+ struct msgb **reject,
+ struct gprs_ns2_vc **success);
+
+int ns2_recv_vc(struct gprs_ns2_vc *nsvc,
+ struct msgb *msg);
+
+struct gprs_ns2_vc *ns2_vc_alloc(struct gprs_ns2_vc_bind *bind,
+ struct gprs_ns2_nse *nse,
+ bool initiater,
+ enum gprs_ns2_vc_mode vc_mode,
+ const char *id);
+
+void ns2_free_nsvcs(struct gprs_ns2_nse *nse);
+int ns2_bind_alloc(struct gprs_ns2_inst *nsi, const char *name,
+ struct gprs_ns2_vc_bind **result);
+
+struct msgb *ns2_msgb_alloc(void);
+
+void ns2_sns_write_vty(struct vty *vty, const struct gprs_ns2_nse *nse);
+void ns2_sns_dump_vty(struct vty *vty, const char *prefix, const struct gprs_ns2_nse *nse, bool stats);
+void ns2_prim_status_ind(struct gprs_ns2_nse *nse,
+ struct gprs_ns2_vc *nsvc,
+ uint16_t bvci,
+ enum gprs_ns2_affecting_cause cause);
+void ns2_nse_notify_alive(struct gprs_ns2_vc *nsvc, bool alive);
+void ns2_nse_update_mtu(struct gprs_ns2_nse *nse);
+int ns2_nse_set_dialect(struct gprs_ns2_nse *nse, enum gprs_ns2_dialect dialect);
+
+/* message */
+int ns2_validate(struct gprs_ns2_vc *nsvc,
+ uint8_t pdu_type,
+ struct msgb *msg,
+ struct tlv_parsed *tp,
+ uint8_t *cause);
+
+/* SNS messages */
+int ns2_tx_sns_ack(struct gprs_ns2_vc *nsvc, uint8_t trans_id, uint8_t *cause,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems);
+int ns2_tx_sns_config(struct gprs_ns2_vc *nsvc, bool end_flag,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems);
+int ns2_tx_sns_config_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause);
+int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_nsvc,
+ int ip4_ep_nr, int ip6_ep_nr);
+int ns2_tx_sns_size_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause);
+
+int ns2_tx_sns_add(struct gprs_ns2_vc *nsvc,
+ uint8_t trans_id,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems);
+int ns2_tx_sns_change_weight(struct gprs_ns2_vc *nsvc,
+ uint8_t trans_id,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems);
+int ns2_tx_sns_del(struct gprs_ns2_vc *nsvc,
+ uint8_t trans_id,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems);
+
+/* transmit message over a VC */
+int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause, uint16_t *nsvci);
+int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc, uint16_t *nsvci);
+
+int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause);
+int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc);
+
+int ns2_tx_unblock(struct gprs_ns2_vc *nsvc);
+int ns2_tx_unblock_ack(struct gprs_ns2_vc *nsvc);
+
+int ns2_tx_alive(struct gprs_ns2_vc *nsvc);
+int ns2_tx_alive_ack(struct gprs_ns2_vc *nsvc);
+
+int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
+ uint16_t bvci, uint8_t sducontrol,
+ struct msgb *msg);
+
+int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
+ uint16_t bvci, struct msgb *orig_msg, uint16_t *nsvci);
+
+/* driver */
+struct gprs_ns2_vc *ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
+ struct gprs_ns2_nse *nse,
+ const struct osmo_sockaddr *remote);
+int ns2_ip_count_bind(struct gprs_ns2_inst *nsi, struct osmo_sockaddr *remote);
+struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_index(struct gprs_ns2_inst *nsi,
+ struct osmo_sockaddr *remote,
+ int index);
+void ns2_ip_set_txqueue_max_length(struct gprs_ns2_vc_bind *bind, unsigned int max_length);
+
+/* sns */
+int ns2_sns_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp);
+struct osmo_fsm_inst *ns2_sns_bss_fsm_alloc(struct gprs_ns2_nse *nse,
+ const char *id);
+struct osmo_fsm_inst *ns2_sns_sgsn_fsm_alloc(struct gprs_ns2_nse *nse, const char *id);
+void ns2_sns_replace_nsvc(struct gprs_ns2_vc *nsvc);
+void ns2_sns_notify_alive(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc, bool alive);
+void ns2_sns_update_weights(struct gprs_ns2_vc_bind *bind);
+
+/* vc */
+struct osmo_fsm_inst *ns2_vc_fsm_alloc(struct gprs_ns2_vc *nsvc,
+ const char *id, bool initiate);
+int ns2_vc_fsm_start(struct gprs_ns2_vc *nsvc);
+int ns2_vc_force_unconfigured(struct gprs_ns2_vc *nsvc);
+int ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp);
+int ns2_vc_is_alive(struct gprs_ns2_vc *nsvc);
+int ns2_vc_is_unblocked(struct gprs_ns2_vc *nsvc);
+int ns2_vc_block(struct gprs_ns2_vc *nsvc);
+int ns2_vc_reset(struct gprs_ns2_vc *nsvc);
+int ns2_vc_unblock(struct gprs_ns2_vc *nsvc);
+void ns2_vty_dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats);
+
+/* nse */
+void ns2_nse_notify_unblocked(struct gprs_ns2_vc *nsvc, bool unblocked);
+enum gprs_ns2_vc_mode ns2_dialect_to_vc_mode(enum gprs_ns2_dialect dialect);
+int ns2_count_transfer_cap(struct gprs_ns2_nse *nse,
+ uint16_t bvci);
+
+/* vty */
+int ns2_sns_add_sns_default_binds(struct gprs_ns2_nse *nse);
diff --git a/src/gb/gprs_ns2_message.c b/src/gb/gprs_ns2_message.c
new file mode 100644
index 00000000..de63b7aa
--- /dev/null
+++ b/src/gb/gprs_ns2_message.c
@@ -0,0 +1,848 @@
+/*! \file gprs_ns2_message.c
+ * NS-over-FR-over-GRE implementation.
+ * GPRS Networks Service (NS) messages on the Gb interface.
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * as well as its successor 3GPP TS 48.016 */
+
+/* (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/byteswap.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gprs/gprs_msgb.h>
+#include <osmocom/gprs/gprs_ns2.h>
+#include <osmocom/gprs/protocol/gsm_08_16.h>
+
+#include "gprs_ns2_internal.h"
+
+#define ERR_IF_NSVC_USES_SNS(nsvc, reason) \
+ do { \
+ if (!nsvc->nse->bss_sns_fi) \
+ break; \
+ LOGNSVC(nsvc, LOGL_DEBUG, "invalid packet %s with SNS\n", reason); \
+ } while (0)
+
+static int ns2_validate_reset(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
+{
+ if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1) ||
+ !TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
+ *cause = NS_CAUSE_MISSING_ESSENT_IE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ns2_validate_reset_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
+{
+ if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
+ *cause = NS_CAUSE_MISSING_ESSENT_IE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ns2_validate_block(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
+{
+ if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
+ *cause = NS_CAUSE_MISSING_ESSENT_IE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ns2_validate_block_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
+{
+ if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2)) {
+ *cause = NS_CAUSE_MISSING_ESSENT_IE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ns2_validate_status(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
+{
+
+ if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
+ *cause = NS_CAUSE_MISSING_ESSENT_IE;
+ return -1;
+ }
+
+ uint8_t _cause = tlvp_val8(tp, NS_IE_CAUSE, 0);
+ switch (_cause) {
+ case NS_CAUSE_NSVC_BLOCKED:
+ case NS_CAUSE_NSVC_UNKNOWN:
+ if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 1)) {
+ *cause = NS_CAUSE_MISSING_ESSENT_IE;
+ return -1;
+ }
+
+ if (nsvc->mode != GPRS_NS2_VC_MODE_BLOCKRESET) {
+ *cause = NS_CAUSE_PDU_INCOMP_PSTATE;
+ return -1;
+ }
+ break;
+ case NS_CAUSE_SEM_INCORR_PDU:
+ case NS_CAUSE_PDU_INCOMP_PSTATE:
+ case NS_CAUSE_PROTO_ERR_UNSPEC:
+ case NS_CAUSE_INVAL_ESSENT_IE:
+ case NS_CAUSE_MISSING_ESSENT_IE:
+ if (!TLVP_PRES_LEN(tp, NS_IE_PDU, 1)) {
+ *cause = NS_CAUSE_MISSING_ESSENT_IE;
+ return -1;
+ }
+ break;
+ case NS_CAUSE_BVCI_UNKNOWN:
+ if (!TLVP_PRES_LEN(tp, NS_IE_BVCI, 2)) {
+ *cause = NS_CAUSE_MISSING_ESSENT_IE;
+ return -1;
+ }
+ break;
+ case NS_CAUSE_UNKN_IP_TEST_FAILED:
+ if (!TLVP_PRESENT(tp, NS_IE_IPv4_LIST) && !TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
+ *cause = NS_CAUSE_MISSING_ESSENT_IE;
+ return -1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int ns2_validate(struct gprs_ns2_vc *nsvc,
+ uint8_t pdu_type,
+ struct msgb *msg,
+ struct tlv_parsed *tp,
+ uint8_t *cause)
+{
+ switch (pdu_type) {
+ case NS_PDUT_RESET:
+ return ns2_validate_reset(nsvc, msg, tp, cause);
+ case NS_PDUT_RESET_ACK:
+ return ns2_validate_reset_ack(nsvc, msg, tp, cause);
+ case NS_PDUT_BLOCK:
+ return ns2_validate_block(nsvc, msg, tp, cause);
+ case NS_PDUT_BLOCK_ACK:
+ return ns2_validate_block_ack(nsvc, msg, tp, cause);
+ case NS_PDUT_STATUS:
+ return ns2_validate_status(nsvc, msg, tp, cause);
+
+ /* following PDUs doesn't have any payloads */
+ case NS_PDUT_ALIVE:
+ case NS_PDUT_ALIVE_ACK:
+ case NS_PDUT_UNBLOCK:
+ case NS_PDUT_UNBLOCK_ACK:
+ if (msgb_l2len(msg) != sizeof(struct gprs_ns_hdr)) {
+ *cause = NS_CAUSE_PROTO_ERR_UNSPEC;
+ return -1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int ns_vc_tx(struct gprs_ns2_vc *nsvc, struct msgb *msg)
+{
+ return nsvc->bind->send_vc(nsvc, msg);
+}
+
+/* transmit functions */
+static int ns2_tx_simple(struct gprs_ns2_vc *nsvc, uint8_t pdu_type)
+{
+ struct msgb *msg = ns2_msgb_alloc();
+ struct gprs_ns_hdr *nsh;
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ if (!msg)
+ return -ENOMEM;
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = pdu_type;
+
+ LOG_NS_TX_SIGNAL(nsvc, nsh->pdu_type);
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Transmit a NS-BLOCK on a given NS-VC.
+ * \param[in] vc NS-VC on which the NS-BLOCK is to be transmitted
+ * \param[in] cause Numeric NS Cause value
+ * \param[in] nsvci if given this NSVCI will be encoded. If NULL the nsvc->nsvci will be used.
+ * \returns 0 in case of success */
+int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause, uint16_t *nsvci)
+{
+ struct msgb *msg;
+ struct gprs_ns_hdr *nsh;
+ uint16_t encoded_nsvci;
+
+ if (nsvci)
+ encoded_nsvci = osmo_htons(*nsvci);
+ else
+ encoded_nsvci = osmo_htons(nsvc->nsvci);
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK");
+
+ msg = ns2_msgb_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BLOCKED));
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_BLOCK;
+
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &encoded_nsvci);
+
+ LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, " cause=%s\n", gprs_ns2_cause_str(cause));
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Transmit a NS-BLOCK-ACK on a given NS-VC.
+ * \param[in] nsvc NS-VC on which the NS-BLOCK is to be transmitted
+ * \param[in] nsvci if given this NSVCI will be encoded. If NULL the nsvc->nsvci will be used.
+ * \returns 0 in case of success */
+int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc, uint16_t *nsvci)
+{
+ struct msgb *msg;
+ struct gprs_ns_hdr *nsh;
+ uint16_t encoded_nsvci;
+
+ if (nsvci)
+ encoded_nsvci = osmo_htons(*nsvci);
+ else
+ encoded_nsvci = osmo_htons(nsvc->nsvci);
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK ACK");
+
+ msg = ns2_msgb_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_BLOCK_ACK;
+
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &encoded_nsvci);
+
+ LOG_NS_TX_SIGNAL(nsvc, nsh->pdu_type);
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Transmit a NS-RESET on a given NS-VC.
+ * \param[in] nsvc NS-VC used for transmission
+ * \param[in] cause Numeric NS cause value
+ * \returns 0 in case of success */
+int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause)
+{
+ struct msgb *msg;
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsvci = osmo_htons(nsvc->nsvci);
+ uint16_t nsei = osmo_htons(nsvc->nse->nsei);
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET");
+
+ msg = ns2_msgb_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_RESET;
+
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei);
+
+ LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, " cause=%s\n", gprs_ns2_cause_str(cause));
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Transmit a NS-RESET-ACK on a given NS-VC.
+ * \param[in] nsvc NS-VC used for transmission
+ * \returns 0 in case of success */
+int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc)
+{
+ struct msgb *msg;
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsvci, nsei;
+
+ /* Section 9.2.6 */
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET ACK");
+
+ msg = ns2_msgb_alloc();
+ if (!msg)
+ return -ENOMEM;
+
+ nsvci = osmo_htons(nsvc->nsvci);
+ nsei = osmo_htons(nsvc->nse->nsei);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+
+ nsh->pdu_type = NS_PDUT_RESET_ACK;
+
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
+
+ LOG_NS_TX_SIGNAL(nsvc, nsh->pdu_type);
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Transmit a NS-UNBLOCK on a given NS-VC.
+ * \param[in] nsvc NS-VC on which the NS-UNBLOCK is to be transmitted
+ * \returns 0 in case of success */
+int ns2_tx_unblock(struct gprs_ns2_vc *nsvc)
+{
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK");
+
+ return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK);
+}
+
+
+/*! Transmit a NS-UNBLOCK-ACK on a given NS-VC.
+ * \param[in] nsvc NS-VC on which the NS-UNBLOCK-ACK is to be transmitted
+ * \returns 0 in case of success */
+int ns2_tx_unblock_ack(struct gprs_ns2_vc *nsvc)
+{
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK ACK");
+
+ return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
+}
+
+/*! Transmit a NS-ALIVE on a given NS-VC.
+ * \param[in] nsvc NS-VC on which the NS-ALIVE is to be transmitted
+ * \returns 0 in case of success */
+int ns2_tx_alive(struct gprs_ns2_vc *nsvc)
+{
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ return ns2_tx_simple(nsvc, NS_PDUT_ALIVE);
+}
+
+/*! Transmit a NS-ALIVE-ACK on a given NS-VC.
+ * \param[in] nsvc NS-VC on which the NS-ALIVE-ACK is to be transmitted
+ * \returns 0 in case of success */
+int ns2_tx_alive_ack(struct gprs_ns2_vc *nsvc)
+{
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ return ns2_tx_simple(nsvc, NS_PDUT_ALIVE_ACK);
+}
+
+/*! Transmit NS-UNITDATA on a given NS-VC.
+ * \param[in] nsvc NS-VC on which the NS-UNITDATA is to be transmitted
+ * \param[in] bvci BVCI to encode in NS-UNITDATA header
+ * \param[in] sducontrol SDU control octet of NS header
+ * \param[in] msg message buffer containing payload
+ * \returns 0 in case of success */
+int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
+ uint16_t bvci, uint8_t sducontrol,
+ struct msgb *msg)
+{
+ struct gprs_ns_hdr *nsh;
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ if (!nsh) {
+ LOGNSVC(nsvc, LOGL_ERROR, "Not enough headroom for NS header\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ nsh->pdu_type = NS_PDUT_UNITDATA;
+ nsh->data[0] = sducontrol;
+ nsh->data[1] = bvci >> 8;
+ nsh->data[2] = bvci & 0xff;
+
+ LOG_NS_DATA(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, "\n");
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Transmit a NS-STATUS on a given NS-VC.
+ * \param[in] nsvc NS-VC to be used for transmission
+ * \param[in] cause Numeric NS cause value
+ * \param[in] bvci BVCI to be reset within NSVC
+ * \param[in] orig_msg message causing the STATUS
+ * \param[in] nsvci if given this NSVCI will be encoded. If NULL the nsvc->nsvci will be used.
+ * \returns 0 in case of success */
+int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
+ uint16_t bvci, struct msgb *orig_msg, uint16_t *nsvci)
+{
+ struct msgb *msg = ns2_msgb_alloc();
+ struct gprs_ns_hdr *nsh;
+ uint16_t encoded_nsvci;
+ unsigned int orig_len, max_orig_len;
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+
+ bvci = osmo_htons(bvci);
+
+ if (!msg)
+ return -ENOMEM;
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_STATUS;
+
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
+
+ switch (cause) {
+ case NS_CAUSE_NSVC_BLOCKED:
+ case NS_CAUSE_NSVC_UNKNOWN:
+ /* Section 9.2.7.1: Static conditions for NS-VCI */
+ if (nsvci)
+ encoded_nsvci = osmo_htons(*nsvci);
+ else
+ encoded_nsvci = osmo_htons(nsvc->nsvci);
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&encoded_nsvci);
+ break;
+ case NS_CAUSE_SEM_INCORR_PDU:
+ case NS_CAUSE_PDU_INCOMP_PSTATE:
+ case NS_CAUSE_PROTO_ERR_UNSPEC:
+ case NS_CAUSE_INVAL_ESSENT_IE:
+ case NS_CAUSE_MISSING_ESSENT_IE:
+ /* Section 9.2.7.2: Static conditions for NS PDU */
+ /* ensure the PDU doesn't exceed the MTU */
+ orig_len = msgb_l2len(orig_msg);
+ max_orig_len = msgb_length(msg) + TVLV_GROSS_LEN(orig_len);
+ if (max_orig_len > nsvc->bind->mtu)
+ orig_len -= max_orig_len - nsvc->bind->mtu;
+ msgb_tvlv_put(msg, NS_IE_PDU, orig_len, orig_msg->l2h);
+ break;
+ case NS_CAUSE_BVCI_UNKNOWN:
+ /* Section 9.2.7.3: Static conditions for BVCI */
+ msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci);
+ break;
+
+ default:
+ break;
+ }
+
+ LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, " cause=%s\n", gprs_ns2_cause_str(cause));
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Encode + Transmit a SNS-ADD/SNS-CHANGE-WEIGHT as per Section 9.3.2/9.3.3.
+ * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
+ * \param[in] pdu The PDU type to send out
+ * \param[in] trans_id The transaction id
+ * \param[in] ip4_elems Array of IPv4 Elements
+ * \param[in] num_ip4_elems number of ip4_elems
+ * \param[in] ip6_elems Array of IPv6 Elements
+ * \param[in] num_ip6_elems number of ip6_elems
+ * \returns 0 on success; negative in case of error */
+static int ns2_tx_sns_procedure(struct gprs_ns2_vc *nsvc,
+ enum ns_pdu_type pdu,
+ uint8_t trans_id,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems)
+{
+ struct msgb *msg;
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsei;
+
+ if (!nsvc)
+ return -EINVAL;
+
+ if (!ip4_elems && !ip6_elems)
+ return -EINVAL;
+
+ msg = ns2_msgb_alloc();
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+ if (!msg)
+ return -ENOMEM;
+
+ if (!nsvc->nse->bss_sns_fi) {
+ LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ nsei = osmo_htons(nsvc->nse->nsei);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = pdu;
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
+ msgb_v_put(msg, trans_id);
+
+ /* List of IP4 Elements 10.3.2c */
+ if (ip4_elems) {
+ msgb_tvlv_put(msg, NS_IE_IPv4_LIST, num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
+ (const uint8_t *)ip4_elems);
+ } else if (ip6_elems) {
+ /* List of IP6 elements 10.3.2d */
+ msgb_tvlv_put(msg, NS_IE_IPv6_LIST, num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
+ (const uint8_t *)ip6_elems);
+ }
+
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Encode + Transmit a SNS-ADD as per Section 9.3.2.
+ * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
+ * \param[in] trans_id The transaction id
+ * \param[in] ip4_elems Array of IPv4 Elements
+ * \param[in] num_ip4_elems number of ip4_elems
+ * \param[in] ip6_elems Array of IPv6 Elements
+ * \param[in] num_ip6_elems number of ip6_elems
+ * \returns 0 on success; negative in case of error */
+int ns2_tx_sns_add(struct gprs_ns2_vc *nsvc,
+ uint8_t trans_id,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems)
+{
+ return ns2_tx_sns_procedure(nsvc, SNS_PDUT_ADD, trans_id, ip4_elems, num_ip4_elems, ip6_elems, num_ip6_elems);
+}
+
+/*! Encode + Transmit a SNS-CHANGE-WEIGHT as per Section 9.3.3.
+ * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
+ * \param[in] trans_id The transaction id
+ * \param[in] ip4_elems Array of IPv4 Elements
+ * \param[in] num_ip4_elems number of ip4_elems
+ * \param[in] ip6_elems Array of IPv6 Elements
+ * \param[in] num_ip6_elems number of ip6_elems
+ * \returns 0 on success; negative in case of error */
+int ns2_tx_sns_change_weight(struct gprs_ns2_vc *nsvc,
+ uint8_t trans_id,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems)
+{
+ return ns2_tx_sns_procedure(nsvc, SNS_PDUT_CHANGE_WEIGHT, trans_id, ip4_elems, num_ip4_elems, ip6_elems, num_ip6_elems);
+}
+
+/*! Encode + Transmit a SNS-DEL as per Section 9.3.6.
+ * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
+ * \param[in] trans_id The transaction id
+ * \param[in] ip4_elems Array of IPv4 Elements
+ * \param[in] num_ip4_elems number of ip4_elems
+ * \param[in] ip6_elems Array of IPv6 Elements
+ * \param[in] num_ip6_elems number of ip6_elems
+ * \returns 0 on success; negative in case of error */
+int ns2_tx_sns_del(struct gprs_ns2_vc *nsvc,
+ uint8_t trans_id,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems)
+{
+ /* TODO: IP Address field */
+ return ns2_tx_sns_procedure(nsvc, SNS_PDUT_DELETE, trans_id, ip4_elems, num_ip4_elems, ip6_elems, num_ip6_elems);
+}
+
+
+/*! Encode + Transmit a SNS-ACK as per Section 9.3.1.
+ * \param[in] nsvc NS-VC through which to transmit the ACK
+ * \param[in] trans_id Transaction ID which to acknowledge
+ * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
+ * \param[in] ip4_elems Array of IPv4 Elements
+ * \param[in] num_ip4_elems number of ip4_elems
+ * \returns 0 on success; negative in case of error */
+int ns2_tx_sns_ack(struct gprs_ns2_vc *nsvc, uint8_t trans_id, uint8_t *cause,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems)
+{
+ struct msgb *msg;
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsei;
+
+ if (!nsvc)
+ return -1;
+
+ msg = ns2_msgb_alloc();
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+ if (!msg)
+ return -ENOMEM;
+
+ if (!nsvc->nse->bss_sns_fi) {
+ LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+
+ nsei = osmo_htons(nsvc->nse->nsei);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+
+ nsh->pdu_type = SNS_PDUT_ACK;
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
+ msgb_v_put(msg, trans_id);
+ if (cause)
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
+ if (ip4_elems) {
+ /* List of IP4 Elements 10.3.2c */
+ msgb_tvlv_put(msg, NS_IE_IPv4_LIST,
+ num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
+ (const uint8_t *)ip4_elems);
+ }
+ if (ip6_elems) {
+ /* List of IP6 elements 10.3.2d */
+ msgb_tvlv_put(msg, NS_IE_IPv6_LIST,
+ num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
+ (const uint8_t *)ip6_elems);
+ }
+
+ LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO,
+ " (trans_id=%u, cause=%s, num_ip4=%u, num_ip6=%u)\n",
+ trans_id, cause ? gprs_ns2_cause_str(*cause) : "NULL", num_ip4_elems, num_ip6_elems);
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Encode + Transmit a SNS-CONFIG as per Section 9.3.4.
+ * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
+ * \param[in] end_flag Whether or not this is the last SNS-CONFIG
+ * \param[in] ip4_elems Array of IPv4 Elements
+ * \param[in] num_ip4_elems number of ip4_elems
+ * \returns 0 on success; negative in case of error */
+int ns2_tx_sns_config(struct gprs_ns2_vc *nsvc, bool end_flag,
+ const struct gprs_ns_ie_ip4_elem *ip4_elems,
+ unsigned int num_ip4_elems,
+ const struct gprs_ns_ie_ip6_elem *ip6_elems,
+ unsigned int num_ip6_elems)
+{
+ struct msgb *msg;
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsei;
+
+ if (!nsvc)
+ return -1;
+
+ msg = ns2_msgb_alloc();
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+ if (!msg)
+ return -ENOMEM;
+
+ if (!nsvc->nse->bss_sns_fi) {
+ LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ nsei = osmo_htons(nsvc->nse->nsei);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+
+ nsh->pdu_type = SNS_PDUT_CONFIG;
+
+ msgb_v_put(msg, end_flag ? 0x01 : 0x00);
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
+
+ /* List of IP4 Elements 10.3.2c */
+ if (ip4_elems) {
+ msgb_tvlv_put(msg, NS_IE_IPv4_LIST, num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
+ (const uint8_t *)ip4_elems);
+ } else if (ip6_elems) {
+ /* List of IP6 elements 10.3.2d */
+ msgb_tvlv_put(msg, NS_IE_IPv6_LIST, num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
+ (const uint8_t *)ip6_elems);
+ }
+
+ LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO,
+ " (end_flag=%u, num_ip4=%u, num_ip6=%u)\n",
+ end_flag, num_ip4_elems, num_ip6_elems);
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Encode + Transmit a SNS-CONFIG-ACK as per Section 9.3.5.
+ * \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG-ACK
+ * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
+ * \returns 0 on success; negative in case of error */
+int ns2_tx_sns_config_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
+{
+ struct msgb *msg;
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsei;
+
+ if (!nsvc)
+ return -1;
+
+ msg = ns2_msgb_alloc();
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+ if (!msg)
+ return -ENOMEM;
+
+ if (!nsvc->nse->bss_sns_fi) {
+ LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ nsei = osmo_htons(nsvc->nse->nsei);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+
+ nsh->pdu_type = SNS_PDUT_CONFIG_ACK;
+
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
+ if (cause)
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
+
+ LOGNSVC(nsvc, LOGL_INFO, "Tx SNS-CONFIG-ACK (cause=%s)\n",
+ cause ? gprs_ns2_cause_str(*cause) : "NULL");
+ LOG_NS_TX_SIGNAL(nsvc, nsh->pdu_type);
+ return ns_vc_tx(nsvc, msg);
+}
+
+
+/*! Encode + transmit a SNS-SIZE as per Section 9.3.7.
+ * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE
+ * \param[in] reset_flag Whether or not to add a RESET flag
+ * \param[in] max_nr_nsvc Maximum number of NS-VCs
+ * \param[in] ip4_ep_nr Number of IPv4 endpoints (< 0 will omit the TLV)
+ * \param[in] ip6_ep_nr Number of IPv6 endpoints (< 0 will omit the TLV)
+ * \returns 0 on success; negative in case of error */
+int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_nsvc,
+ int ip4_ep_nr, int ip6_ep_nr)
+{
+ struct msgb *msg;
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsei;
+
+ if (!nsvc)
+ return -1;
+
+ msg = ns2_msgb_alloc();
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+ if (!msg)
+ return -ENOMEM;
+
+ if (!nsvc->nse->bss_sns_fi) {
+ LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ nsei = osmo_htons(nsvc->nse->nsei);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+
+ nsh->pdu_type = SNS_PDUT_SIZE;
+
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
+ msgb_tv_put(msg, NS_IE_RESET_FLAG, reset_flag ? 0x01 : 0x00);
+ msgb_tv16_put(msg, NS_IE_MAX_NR_NSVC, max_nr_nsvc);
+ if (ip4_ep_nr >= 0)
+ msgb_tv16_put(msg, NS_IE_IPv4_EP_NR, ip4_ep_nr);
+ if (ip6_ep_nr >= 0)
+ msgb_tv16_put(msg, NS_IE_IPv6_EP_NR, ip6_ep_nr);
+
+ LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO,
+ " (reset=%u, max_nr_nsvc=%u, num_ip4=%d, num_ip6=%d)\n",
+ reset_flag, max_nr_nsvc, ip4_ep_nr, ip6_ep_nr);
+ return ns_vc_tx(nsvc, msg);
+}
+
+/*! Encode + Transmit a SNS-SIZE-ACK as per Section 9.3.8.
+ * \param[in] nsvc NS-VC through which to transmit the SNS-SIZE-ACK
+ * \param[in] cause Pointer to cause value (NULL if no cause to be sent)
+ * \returns 0 on success; negative in case of error */
+int ns2_tx_sns_size_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
+{
+ struct msgb *msg = ns2_msgb_alloc();
+ struct gprs_ns_hdr *nsh;
+ uint16_t nsei;
+
+ log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
+ log_set_context(LOG_CTX_GB_NSVC, nsvc);
+ if (!msg)
+ return -ENOMEM;
+
+ if (!nsvc->nse->bss_sns_fi) {
+ LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ nsei = osmo_htons(nsvc->nse->nsei);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+
+ nsh->pdu_type = SNS_PDUT_SIZE_ACK;
+
+ msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
+ if (cause)
+ msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
+
+ LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, " cause=%s\n",
+ cause ? gprs_ns2_cause_str(*cause) : "NULL");
+ return ns_vc_tx(nsvc, msg);
+}
diff --git a/src/gb/gprs_ns2_sns.c b/src/gb/gprs_ns2_sns.c
new file mode 100644
index 00000000..0afc06ed
--- /dev/null
+++ b/src/gb/gprs_ns2_sns.c
@@ -0,0 +1,3106 @@
+/*! \file gprs_ns2_sns.c
+ * NS Sub-Network Service Protocol implementation
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * as well as its successor 3GPP TS 48.016 */
+
+/* (C) 2018-2021 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* The BSS NSE only has one SGSN IP address configured, and it will use the SNS procedures
+ * to communicated its local IPs/ports as well as all the SGSN side IPs/ports and
+ * associated weights. The BSS then uses this to establish a full mesh
+ * of NSVCs between all BSS-side IPs/ports and SGSN-side IPs/ports.
+ *
+ * Known limitation/expectation/bugs:
+ * - No concurrent dual stack. It supports either IPv4 or IPv6, but not both at the same time.
+ * - SNS Add/Change/Delete: Doesn't answer on the same NSVC as received SNS ADD/CHANGE/DELETE PDUs.
+ * - SNS Add/Change/Delete: Doesn't communicated the failed IPv4/IPv6 entries on the SNS_ACK.
+ */
+
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdint.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gprs/gprs_msgb.h>
+#include <osmocom/gprs/gprs_ns2.h>
+#include <osmocom/gprs/protocol/gsm_08_16.h>
+
+#include "gprs_ns2_internal.h"
+
+#define S(x) (1 << (x))
+
+enum ns2_sns_role {
+ GPRS_SNS_ROLE_BSS,
+ GPRS_SNS_ROLE_SGSN,
+};
+
+/* BSS-side-only states _ST_BSS_; SGSN-side only states _ST_SGSN_; others shared */
+enum gprs_sns_bss_state {
+ GPRS_SNS_ST_UNCONFIGURED,
+ GPRS_SNS_ST_BSS_SIZE, /*!< SNS-SIZE procedure ongoing */
+ GPRS_SNS_ST_BSS_CONFIG_BSS, /*!< SNS-CONFIG procedure (BSS->SGSN) ongoing */
+ GPRS_SNS_ST_BSS_CONFIG_SGSN, /*!< SNS-CONFIG procedure (SGSN->BSS) ongoing */
+ GPRS_SNS_ST_CONFIGURED,
+ GPRS_SNS_ST_SGSN_WAIT_CONFIG, /* !< SGSN role: Wait for CONFIG from BSS */
+ GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK, /* !< SGSN role: Wait for CONFIG-ACK from BSS */
+ GPRS_SNS_ST_LOCAL_PROCEDURE, /*!< in process of a ADD/DEL/CHANGE procedure towards SGSN (BSS->SGSN) */
+};
+
+static const struct value_string gprs_sns_event_names[] = {
+ { NS2_SNS_EV_REQ_SELECT_ENDPOINT, "REQ_SELECT_ENDPOINT" },
+ { NS2_SNS_EV_RX_SIZE, "RX_SIZE" },
+ { NS2_SNS_EV_RX_SIZE_ACK, "RX_SIZE_ACK" },
+ { NS2_SNS_EV_RX_CONFIG, "RX_CONFIG" },
+ { NS2_SNS_EV_RX_CONFIG_END, "RX_CONFIG_END" },
+ { NS2_SNS_EV_RX_CONFIG_ACK, "RX_CONFIG_ACK" },
+ { NS2_SNS_EV_RX_ADD, "RX_ADD" },
+ { NS2_SNS_EV_RX_DELETE, "RX_DELETE" },
+ { NS2_SNS_EV_RX_ACK, "RX_ACK" },
+ { NS2_SNS_EV_RX_CHANGE_WEIGHT, "RX_CHANGE_WEIGHT" },
+ { NS2_SNS_EV_REQ_NO_NSVC, "REQ_NO_NSVC" },
+ { NS2_SNS_EV_REQ_FREE_NSVCS, "REQ_FREE_NSVCS" },
+ { NS2_SNS_EV_REQ_NSVC_ALIVE, "REQ_NSVC_ALIVE"},
+ { NS2_SNS_EV_REQ_ADD_BIND, "REQ_ADD_BIND"},
+ { NS2_SNS_EV_REQ_DELETE_BIND, "REQ_DELETE_BIND"},
+ { NS2_SNS_EV_REQ_CHANGE_WEIGHT, "REQ_CHANGE_WEIGHT"},
+ { 0, NULL }
+};
+
+#define GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER (void *) 1
+
+enum sns_procedure {
+ SNS_PROC_NONE, /*!< used as invalid/idle value */
+ SNS_PROC_ADD,
+ SNS_PROC_DEL,
+ SNS_PROC_CHANGE_WEIGHT,
+};
+
+struct sns_endpoint {
+ struct llist_head list;
+ struct osmo_sockaddr saddr;
+};
+
+struct ns2_sns_bind {
+ struct llist_head list;
+ struct gprs_ns2_vc_bind *bind;
+ uint8_t change_weight_state;
+};
+
+struct ns2_sns_procedure {
+ struct llist_head list;
+ struct ns2_sns_bind *sbind;
+ uint16_t sig_weight;
+ uint16_t data_weight;
+ /* copy entry to protect against changes of gss->local */
+ struct gprs_ns_ie_ip4_elem ip4;
+ struct gprs_ns_ie_ip6_elem ip6;
+ enum sns_procedure procedure;
+ uint8_t trans_id;
+ /* is the procedure in process */
+ bool running;
+};
+
+struct ns2_sns_elems {
+ struct gprs_ns_ie_ip4_elem *ip4;
+ unsigned int num_ip4;
+ struct gprs_ns_ie_ip6_elem *ip6;
+ unsigned int num_ip6;
+};
+
+struct ns2_sns_state {
+ struct gprs_ns2_nse *nse;
+
+ /* containing the address family AF_* */
+ int family;
+ enum ns2_sns_role role; /* local role: BSS or SGSN */
+
+ /* holds the list of initial SNS endpoints */
+ struct llist_head sns_endpoints;
+ /* list of used struct ns2_sns_bind */
+ struct llist_head binds;
+ /* pointer to the bind which was used to initiate the SNS connection */
+ struct ns2_sns_bind *initial_bind;
+ /* prevent recursive reselection */
+ bool reselection_running;
+
+ /* protection against recursive free() */
+ bool block_no_nsvc_events;
+
+ /* The current initial SNS endpoints.
+ * The initial connection will be moved into the NSE
+ * if configured via SNS. Otherwise it will be removed
+ * in configured state. */
+ struct sns_endpoint *initial;
+ /* all SNS PDU will be sent over this nsvc */
+ struct gprs_ns2_vc *sns_nsvc;
+ /* timer N */
+ int N;
+ /* true if at least one nsvc is alive */
+ bool alive;
+
+ /* local configuration to send to the remote end */
+ struct ns2_sns_elems local;
+
+ /* local configuration after all local procedures applied */
+ struct ns2_sns_elems local_procedure;
+
+ /* remote configuration as received */
+ struct ns2_sns_elems remote;
+
+ /* local configuration about our capabilities in terms of connections to
+ * remote (SGSN) side */
+ size_t num_max_nsvcs;
+ size_t num_max_ip4_remote;
+ size_t num_max_ip6_remote;
+
+ struct llist_head procedures;
+ struct ns2_sns_procedure *current_procedure;
+ uint8_t trans_id;
+};
+
+static inline struct gprs_ns2_nse *nse_inst_from_fi(struct osmo_fsm_inst *fi)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ return gss->nse;
+}
+
+/* The SNS has failed. Etither restart the SNS (BSS) or remove the SNS (SGSN) */
+#define sns_failed(fi, reason) \
+ _sns_failed(fi, reason, __FILE__, __LINE__)
+static void _sns_failed(struct osmo_fsm_inst *fi, const char *reason, const char *file, int line)
+{
+ struct ns2_sns_state *gss = fi->priv;
+
+ if (reason)
+ LOGPFSMLSRC(fi, LOGL_ERROR, file, line, "NSE %d: SNS failed: %s\n", gss->nse->nsei, reason);
+
+ gss->alive = false;
+ if (gss->role == GPRS_SNS_ROLE_SGSN) {
+ if (!gss->nse->persistent)
+ gprs_ns2_free_nse(gss->nse);
+ else
+ _osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0, file, line);
+ } else {
+ _osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL, file, line);
+ }
+}
+
+/* helper function to compute the sum of all (data or signaling) weights */
+static int ip4_weight_sum(const struct ns2_sns_elems *elems, bool data_weight)
+{
+ unsigned int i;
+ int weight_sum = 0;
+
+ for (i = 0; i < elems->num_ip4; i++) {
+ if (data_weight)
+ weight_sum += elems->ip4[i].data_weight;
+ else
+ weight_sum += elems->ip4[i].sig_weight;
+ }
+ return weight_sum;
+}
+#define ip4_weight_sum_data(elems) ip4_weight_sum(elems, true)
+#define ip4_weight_sum_sig(elems) ip4_weight_sum(elems, false)
+
+/* helper function to compute the sum of all (data or signaling) weights */
+static int ip6_weight_sum(const struct ns2_sns_elems *elems, bool data_weight)
+{
+ unsigned int i;
+ int weight_sum = 0;
+
+ for (i = 0; i < elems->num_ip6; i++) {
+ if (data_weight)
+ weight_sum += elems->ip6[i].data_weight;
+ else
+ weight_sum += elems->ip6[i].sig_weight;
+ }
+ return weight_sum;
+}
+#define ip6_weight_sum_data(elems) ip6_weight_sum(elems, true)
+#define ip6_weight_sum_sig(elems) ip6_weight_sum(elems, false)
+
+static int ip46_weight_sum(const struct ns2_sns_elems *elems, bool data_weight)
+{
+ return ip4_weight_sum(elems, data_weight) +
+ ip6_weight_sum(elems, data_weight);
+}
+#define ip46_weight_sum_data(elems) ip46_weight_sum(elems, true)
+#define ip46_weight_sum_sig(elems) ip46_weight_sum(elems, false)
+
+static struct gprs_ns2_vc *nsvc_by_ip4_elem(struct gprs_ns2_nse *nse,
+ const struct gprs_ns_ie_ip4_elem *ip4)
+{
+ struct osmo_sockaddr sa;
+ /* copy over. Both data structures use network byte order */
+ sa.u.sin.sin_addr.s_addr = ip4->ip_addr;
+ sa.u.sin.sin_port = ip4->udp_port;
+ sa.u.sin.sin_family = AF_INET;
+
+ return gprs_ns2_nsvc_by_sockaddr_nse(nse, &sa);
+}
+
+static struct gprs_ns2_vc *nsvc_by_ip6_elem(struct gprs_ns2_nse *nse,
+ const struct gprs_ns_ie_ip6_elem *ip6)
+{
+ struct osmo_sockaddr sa;
+ /* copy over. Both data structures use network byte order */
+ sa.u.sin6.sin6_addr = ip6->ip_addr;
+ sa.u.sin6.sin6_port = ip6->udp_port;
+ sa.u.sin6.sin6_family = AF_INET;
+
+ return gprs_ns2_nsvc_by_sockaddr_nse(nse, &sa);
+}
+
+/*! Return the initial SNS remote socket address
+ * \param nse NS Entity
+ * \return address of the initial SNS connection; NULL in case of error
+ */
+const struct osmo_sockaddr *gprs_ns2_nse_sns_remote(struct gprs_ns2_nse *nse)
+{
+ struct ns2_sns_state *gss;
+
+ if (!nse->bss_sns_fi)
+ return NULL;
+
+ gss = (struct ns2_sns_state *) nse->bss_sns_fi->priv;
+ return &gss->initial->saddr;
+}
+
+/*! called when a nsvc is beeing freed or the nsvc became dead */
+void ns2_sns_replace_nsvc(struct gprs_ns2_vc *nsvc)
+{
+ struct gprs_ns2_nse *nse = nsvc->nse;
+ struct gprs_ns2_vc *tmp;
+ struct osmo_fsm_inst *fi = nse->bss_sns_fi;
+ struct ns2_sns_state *gss;
+
+ if (!fi)
+ return;
+
+ gss = (struct ns2_sns_state *) fi->priv;
+ if (nsvc != gss->sns_nsvc)
+ return;
+
+ gss->sns_nsvc = NULL;
+ if (gss->alive) {
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (ns2_vc_is_unblocked(tmp)) {
+ gss->sns_nsvc = tmp;
+ return;
+ }
+ }
+ } else {
+ /* the SNS is waiting for its first NS-VC to come up
+ * choose any other nsvc */
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (nsvc != tmp) {
+ gss->sns_nsvc = tmp;
+ return;
+ }
+ }
+ }
+
+ if (gss->block_no_nsvc_events)
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_NO_NSVC, NULL);
+}
+
+static void ns2_clear_elems(struct ns2_sns_elems *elems)
+{
+ TALLOC_FREE(elems->ip4);
+ TALLOC_FREE(elems->ip6);
+
+ elems->num_ip4 = 0;
+ elems->num_ip6 = 0;
+}
+
+static void ns2_clear_procedures(struct ns2_sns_state *gss)
+{
+ struct ns2_sns_procedure *procedure, *tmp;
+ gss->current_procedure = NULL;
+ llist_for_each_entry_safe(procedure, tmp, &gss->procedures, list) {
+ llist_del(&procedure->list);
+ talloc_free(procedure);
+ }
+}
+
+static void ns2_vc_create_ip(struct osmo_fsm_inst *fi, struct gprs_ns2_nse *nse, const struct osmo_sockaddr *remote,
+ uint8_t sig_weight, uint8_t data_weight)
+{
+ struct gprs_ns2_inst *nsi = nse->nsi;
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_vc_bind *bind;
+
+ /* for every bind, create a connection if bind type == IP */
+ llist_for_each_entry(bind, &nsi->binding, list) {
+ if (bind->ll != GPRS_NS2_LL_UDP)
+ continue;
+ /* ignore failed connection */
+ nsvc = gprs_ns2_ip_connect_inactive(bind,
+ remote,
+ nse, 0);
+ if (!nsvc) {
+ LOGPFSML(fi, LOGL_ERROR, "SNS-CONFIG: Failed to create NSVC\n");
+ continue;
+ }
+
+ nsvc->sig_weight = sig_weight;
+ nsvc->data_weight = data_weight;
+ }
+}
+
+static void ns2_nsvc_create_ip4(struct osmo_fsm_inst *fi,
+ struct gprs_ns2_nse *nse,
+ const struct gprs_ns_ie_ip4_elem *ip4)
+{
+ struct osmo_sockaddr remote = { };
+ /* copy over. Both data structures use network byte order */
+ remote.u.sin.sin_family = AF_INET;
+ remote.u.sin.sin_addr.s_addr = ip4->ip_addr;
+ remote.u.sin.sin_port = ip4->udp_port;
+
+ ns2_vc_create_ip(fi, nse, &remote, ip4->sig_weight, ip4->data_weight);
+}
+
+static void ns2_nsvc_create_ip6(struct osmo_fsm_inst *fi,
+ struct gprs_ns2_nse *nse,
+ const struct gprs_ns_ie_ip6_elem *ip6)
+{
+ struct osmo_sockaddr remote = {};
+ /* copy over. Both data structures use network byte order */
+ remote.u.sin6.sin6_family = AF_INET6;
+ remote.u.sin6.sin6_addr = ip6->ip_addr;
+ remote.u.sin6.sin6_port = ip6->udp_port;
+
+ ns2_vc_create_ip(fi, nse, &remote, ip6->sig_weight, ip6->data_weight);
+}
+
+static struct gprs_ns2_vc *nsvc_for_bind_and_remote(struct gprs_ns2_nse *nse,
+ struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *remote)
+{
+ struct gprs_ns2_vc *nsvc;
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ if (nsvc->bind != bind)
+ continue;
+
+ if (!osmo_sockaddr_cmp(remote, gprs_ns2_ip_vc_remote(nsvc)))
+ return nsvc;
+ }
+ return NULL;
+}
+
+static int create_missing_nsvcs(struct osmo_fsm_inst *fi)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns2_vc *nsvc;
+ struct ns2_sns_bind *sbind;
+ struct osmo_sockaddr remote = { };
+ unsigned int i;
+
+ /* iterate over all remote IPv4 endpoints */
+ for (i = 0; i < gss->remote.num_ip4; i++) {
+ const struct gprs_ns_ie_ip4_elem *ip4 = &gss->remote.ip4[i];
+
+ remote.u.sin.sin_family = AF_INET;
+ remote.u.sin.sin_addr.s_addr = ip4->ip_addr;
+ remote.u.sin.sin_port = ip4->udp_port;
+
+ /* iterate over all local binds within this SNS */
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ struct gprs_ns2_vc_bind *bind = sbind->bind;
+
+ /* we only care about UDP binds */
+ if (bind->ll != GPRS_NS2_LL_UDP)
+ continue;
+
+ nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
+ if (!nsvc) {
+ nsvc = gprs_ns2_ip_connect_inactive(bind, &remote, nse, 0);
+ if (!nsvc) {
+ /* TODO: add to a list to send back a NS-STATUS */
+ continue;
+ }
+ }
+
+ /* update data / signalling weight */
+ nsvc->data_weight = ip4->data_weight;
+ nsvc->sig_weight = ip4->sig_weight;
+ nsvc->sns_only = false;
+ }
+ }
+
+ /* iterate over all remote IPv4 endpoints */
+ for (i = 0; i < gss->remote.num_ip6; i++) {
+ const struct gprs_ns_ie_ip6_elem *ip6 = &gss->remote.ip6[i];
+
+ remote.u.sin6.sin6_family = AF_INET6;
+ remote.u.sin6.sin6_addr = ip6->ip_addr;
+ remote.u.sin6.sin6_port = ip6->udp_port;
+
+ /* iterate over all local binds within this SNS */
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ struct gprs_ns2_vc_bind *bind = sbind->bind;
+
+ if (bind->ll != GPRS_NS2_LL_UDP)
+ continue;
+
+ /* we only care about UDP binds */
+ nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
+ if (!nsvc) {
+ nsvc = gprs_ns2_ip_connect_inactive(bind, &remote, nse, 0);
+ if (!nsvc) {
+ /* TODO: add to a list to send back a NS-STATUS */
+ continue;
+ }
+ }
+
+ /* update data / signalling weight */
+ nsvc->data_weight = ip6->data_weight;
+ nsvc->sig_weight = ip6->sig_weight;
+ nsvc->sns_only = false;
+ }
+ }
+
+
+ return 0;
+}
+
+/* Add a given remote IPv4 element to gprs_sns_state */
+static int add_ip4_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip4_elem *ip4)
+{
+ /* check for duplicates */
+ for (unsigned int i = 0; i < elems->num_ip4; i++) {
+ if (memcmp(&elems->ip4[i], ip4, sizeof(*ip4)))
+ continue;
+ return -1;
+ }
+
+ elems->ip4 = talloc_realloc(gss, elems->ip4, struct gprs_ns_ie_ip4_elem,
+ elems->num_ip4+1);
+ elems->ip4[elems->num_ip4] = *ip4;
+ elems->num_ip4 += 1;
+ return 0;
+}
+
+/* Remove a given remote IPv4 element from gprs_sns_state */
+static int remove_ip4_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip4_elem *ip4)
+{
+ unsigned int i;
+
+ for (i = 0; i < elems->num_ip4; i++) {
+ if (memcmp(&elems->ip4[i], ip4, sizeof(*ip4)))
+ continue;
+ /* all array elements < i remain as they are; all > i are shifted left by one */
+ memmove(&elems->ip4[i], &elems->ip4[i+1], elems->num_ip4-i-1);
+ elems->num_ip4 -= 1;
+ return 0;
+ }
+ return -1;
+}
+
+/* update the weights for specified remote IPv4 */
+static int update_ip4_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip4_elem *ip4)
+{
+ unsigned int i;
+
+ for (i = 0; i < elems->num_ip4; i++) {
+ if (elems->ip4[i].ip_addr != ip4->ip_addr ||
+ elems->ip4[i].udp_port != ip4->udp_port)
+ continue;
+
+ elems->ip4[i].sig_weight = ip4->sig_weight;
+ elems->ip4[i].data_weight = ip4->data_weight;
+ return 0;
+ }
+ return -1;
+}
+
+/* Add a given remote IPv6 element to gprs_sns_state */
+static int add_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip6_elem *ip6)
+{
+ /* check for duplicates */
+ for (unsigned int i = 0; i < elems->num_ip6; i++) {
+ if (memcmp(&elems->ip6[i].ip_addr, &ip6->ip_addr, sizeof(ip6->ip_addr)) ||
+ elems->ip6[i].udp_port != ip6->udp_port)
+ continue;
+ return -1;
+ }
+
+ elems->ip6 = talloc_realloc(gss, elems->ip6, struct gprs_ns_ie_ip6_elem,
+ elems->num_ip6+1);
+ elems->ip6[elems->num_ip6] = *ip6;
+ elems->num_ip6 += 1;
+ return 0;
+}
+
+/* Remove a given remote IPv6 element from gprs_sns_state */
+static int remove_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip6_elem *ip6)
+{
+ unsigned int i;
+
+ for (i = 0; i < elems->num_ip6; i++) {
+ if (memcmp(&elems->ip6[i], ip6, sizeof(*ip6)))
+ continue;
+ /* all array elements < i remain as they are; all > i are shifted left by one */
+ memmove(&elems->ip6[i], &elems->ip6[i+1], elems->num_ip6-i-1);
+ elems->num_ip6 -= 1;
+ return 0;
+ }
+ return -1;
+}
+
+/* update the weights for specified remote IPv6 */
+static int update_ip6_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems,
+ const struct gprs_ns_ie_ip6_elem *ip6)
+{
+ unsigned int i;
+
+ for (i = 0; i < elems->num_ip6; i++) {
+ if (memcmp(&elems->ip6[i].ip_addr, &ip6->ip_addr, sizeof(ip6->ip_addr)) ||
+ elems->ip6[i].udp_port != ip6->udp_port)
+ continue;
+ elems->ip6[i].sig_weight = ip6->sig_weight;
+ elems->ip6[i].data_weight = ip6->data_weight;
+ return 0;
+ }
+ return -1;
+}
+
+static int remove_bind_elem(struct ns2_sns_state *gss, struct ns2_sns_elems *elems, struct ns2_sns_bind *sbind)
+{
+ struct gprs_ns_ie_ip4_elem ip4;
+ struct gprs_ns_ie_ip6_elem ip6;
+ const struct osmo_sockaddr *saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+
+ switch (saddr->u.sa.sa_family) {
+ case AF_INET:
+ ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
+ ip4.udp_port = saddr->u.sin.sin_port;
+ ip4.sig_weight = sbind->bind->sns_sig_weight;
+ ip4.data_weight = sbind->bind->sns_data_weight;
+ return remove_ip4_elem(gss, elems, &ip4);
+ case AF_INET6:
+ memcpy(&ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));
+ ip6.udp_port = saddr->u.sin.sin_port;
+ ip6.sig_weight = sbind->bind->sns_sig_weight;
+ ip6.data_weight = sbind->bind->sns_data_weight;
+ return remove_ip6_elem(gss, elems, &ip6);
+ default:
+ return -1;
+ }
+
+ return -1;
+}
+
+static int do_sns_change_weight(struct osmo_fsm_inst *fi, const struct gprs_ns_ie_ip4_elem *ip4, const struct gprs_ns_ie_ip6_elem *ip6)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns2_vc *nsvc;
+ struct osmo_sockaddr sa = {};
+ const struct osmo_sockaddr *remote;
+ uint8_t new_signal;
+ uint8_t new_data;
+
+ /* TODO: Upon receiving an SNS-CHANGEWEIGHT PDU, if the resulting sum of the
+ * signalling weights of all the peer IP endpoints configured for this NSE is
+ * equal to zero or if the resulting sum of the data weights of all the peer IP
+ * endpoints configured for this NSE is equal to zero, the BSS/SGSN shall send an
+ * SNS-ACK PDU with a cause code of "Invalid weights". */
+
+ if (ip4) {
+ if (update_ip4_elem(gss, &gss->remote, ip4))
+ return -NS_CAUSE_UNKN_IP_EP;
+
+ /* copy over. Both data structures use network byte order */
+ sa.u.sin.sin_addr.s_addr = ip4->ip_addr;
+ sa.u.sin.sin_port = ip4->udp_port;
+ sa.u.sin.sin_family = AF_INET;
+ new_signal = ip4->sig_weight;
+ new_data = ip4->data_weight;
+ } else if (ip6) {
+ if (update_ip6_elem(gss, &gss->remote, ip6))
+ return -NS_CAUSE_UNKN_IP_EP;
+
+ /* copy over. Both data structures use network byte order */
+ sa.u.sin6.sin6_addr = ip6->ip_addr;
+ sa.u.sin6.sin6_port = ip6->udp_port;
+ sa.u.sin6.sin6_family = AF_INET6;
+ new_signal = ip6->sig_weight;
+ new_data = ip6->data_weight;
+ } else {
+ OSMO_ASSERT(false);
+ }
+
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ remote = gprs_ns2_ip_vc_remote(nsvc);
+ /* all nsvc in NSE should be IP/UDP nsvc */
+ OSMO_ASSERT(remote);
+
+ if (osmo_sockaddr_cmp(&sa, remote))
+ continue;
+
+ LOGPFSML(fi, LOGL_INFO, "CHANGE-WEIGHT NS-VC %s data_weight %u->%u, sig_weight %u->%u\n",
+ gprs_ns2_ll_str(nsvc), nsvc->data_weight, new_data,
+ nsvc->sig_weight, new_signal);
+
+ nsvc->data_weight = new_data;
+ nsvc->sig_weight = new_signal;
+ }
+
+ return 0;
+}
+
+static int do_sns_delete(struct osmo_fsm_inst *fi,
+ const struct gprs_ns_ie_ip4_elem *ip4,
+ const struct gprs_ns_ie_ip6_elem *ip6)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns2_vc *nsvc, *tmp;
+ const struct osmo_sockaddr *remote;
+ struct osmo_sockaddr sa = {};
+
+ if (ip4) {
+ if (remove_ip4_elem(gss, &gss->remote, ip4) < 0)
+ return -NS_CAUSE_UNKN_IP_EP;
+ /* copy over. Both data structures use network byte order */
+ sa.u.sin.sin_addr.s_addr = ip4->ip_addr;
+ sa.u.sin.sin_port = ip4->udp_port;
+ sa.u.sin.sin_family = AF_INET;
+ } else if (ip6) {
+ if (remove_ip6_elem(gss, &gss->remote, ip6))
+ return -NS_CAUSE_UNKN_IP_EP;
+
+ /* copy over. Both data structures use network byte order */
+ sa.u.sin6.sin6_addr = ip6->ip_addr;
+ sa.u.sin6.sin6_port = ip6->udp_port;
+ sa.u.sin6.sin6_family = AF_INET6;
+ } else {
+ OSMO_ASSERT(false);
+ }
+
+ llist_for_each_entry_safe(nsvc, tmp, &nse->nsvc, list) {
+ remote = gprs_ns2_ip_vc_remote(nsvc);
+ /* all nsvc in NSE should be IP/UDP nsvc */
+ OSMO_ASSERT(remote);
+ if (osmo_sockaddr_cmp(&sa, remote))
+ continue;
+
+ LOGPFSML(fi, LOGL_INFO, "DELETE NS-VC %s\n", gprs_ns2_ll_str(nsvc));
+ gprs_ns2_free_nsvc(nsvc);
+ }
+
+ return 0;
+}
+
+static int do_sns_add(struct osmo_fsm_inst *fi,
+ const struct gprs_ns_ie_ip4_elem *ip4,
+ const struct gprs_ns_ie_ip6_elem *ip6)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns2_vc *nsvc;
+ int rc = 0;
+
+ /* Upon receiving an SNS-ADD PDU, if the consequent number of IPv4 endpoints
+ * exceeds the number of IPv4 endpoints supported by the NSE, the NSE shall send
+ * an SNS-ACK PDU with a cause code set to "Invalid number of IP4 Endpoints". */
+ switch (gss->family) {
+ case AF_INET:
+ if (gss->remote.num_ip4 >= gss->num_max_ip4_remote)
+ return -NS_CAUSE_INVAL_NR_NS_VC;
+ /* TODO: log message duplicate */
+ rc = add_ip4_elem(gss, &gss->remote, ip4);
+ break;
+ case AF_INET6:
+ if (gss->remote.num_ip6 >= gss->num_max_ip6_remote)
+ return -NS_CAUSE_INVAL_NR_NS_VC;
+ /* TODO: log message duplicate */
+ rc = add_ip6_elem(gss, &gss->remote, ip6);
+ break;
+ default:
+ /* the gss->ip is initialized with the bss */
+ OSMO_ASSERT(false);
+ }
+
+ if (rc)
+ return -NS_CAUSE_PROTO_ERR_UNSPEC;
+
+ /* Upon receiving an SNS-ADD PDU containing an already configured IP endpoint the
+ * NSE shall send an SNS-ACK PDU with the cause code "Protocol error -
+ * unspecified" */
+ switch (gss->family) {
+ case AF_INET:
+ nsvc = nsvc_by_ip4_elem(nse, ip4);
+ if (nsvc) {
+ /* the nsvc should be already in sync with the ip4 / ip6 elements */
+ return -NS_CAUSE_PROTO_ERR_UNSPEC;
+ }
+
+ /* TODO: failure case */
+ ns2_nsvc_create_ip4(fi, nse, ip4);
+ break;
+ case AF_INET6:
+ nsvc = nsvc_by_ip6_elem(nse, ip6);
+ if (nsvc) {
+ /* the nsvc should be already in sync with the ip4 / ip6 elements */
+ return -NS_CAUSE_PROTO_ERR_UNSPEC;
+ }
+
+ /* TODO: failure case */
+ ns2_nsvc_create_ip6(fi, nse, ip6);
+ break;
+ }
+
+ gprs_ns2_start_alive_all_nsvcs(nse);
+
+ return 0;
+}
+
+
+static void ns2_sns_st_bss_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
+ /* empty state - SNS Select will start by ns2_sns_st_all_action() */
+}
+
+static void ns2_sns_st_bss_size(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns2_inst *nsi = nse->nsi;
+ struct tlv_parsed *tp = NULL;
+
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
+
+ switch (event) {
+ case NS2_SNS_EV_RX_SIZE_ACK:
+ tp = data;
+ if (TLVP_VAL_MINLEN(tp, NS_IE_CAUSE, 1)) {
+ LOGPFSML(fi, LOGL_ERROR, "SNS-SIZE-ACK with cause %s\n",
+ gprs_ns2_cause_str(*TLVP_VAL(tp, NS_IE_CAUSE)));
+ /* TODO: What to do? */
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_BSS,
+ nsi->timeout[NS_TOUT_TSNS_PROV], 2);
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int ns2_sns_count_num_local_ep(struct osmo_fsm_inst *fi, int ip_proto)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct ns2_sns_bind *sbind;
+ int count = 0;
+
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ const struct osmo_sockaddr *sa = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ if (!sa)
+ continue;
+
+ switch (ip_proto) {
+ case AF_INET:
+ if (sa->u.sas.ss_family == AF_INET)
+ count++;
+ break;
+ case AF_INET6:
+ if (sa->u.sas.ss_family == AF_INET6)
+ count++;
+ break;
+ }
+ }
+ return count;
+}
+
+static int ns2_sns_copy_local_endpoints(struct ns2_sns_state *gss)
+{
+ switch (gss->family) {
+ case AF_INET:
+ gss->local_procedure.ip4 = talloc_realloc(gss, gss->local_procedure.ip4, struct gprs_ns_ie_ip4_elem,
+ gss->local.num_ip4);
+ if (!gss->local_procedure.ip4)
+ return -ENOMEM;
+
+ gss->local_procedure.num_ip4 = gss->local.num_ip4;
+ memcpy(gss->local_procedure.ip4, gss->local.ip4,
+ sizeof(struct gprs_ns_ie_ip4_elem) * gss->local.num_ip4);
+ break;
+ case AF_INET6:
+ gss->local_procedure.ip6 = talloc_realloc(gss, gss->local_procedure.ip6, struct gprs_ns_ie_ip6_elem,
+ gss->local.num_ip6);
+ if (!gss->local_procedure.ip6)
+ return -ENOMEM;
+
+ gss->local_procedure.num_ip6 = gss->local.num_ip6;
+ memcpy(gss->local_procedure.ip6, gss->local.ip6,
+ sizeof(struct gprs_ns_ie_ip6_elem) * gss->local.num_ip6);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ return 0;
+}
+
+static void ns2_sns_compute_local_ep_from_binds(struct osmo_fsm_inst *fi)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns_ie_ip4_elem *ip4_elems;
+ struct gprs_ns_ie_ip6_elem *ip6_elems;
+ struct gprs_ns2_vc_bind *bind;
+ struct ns2_sns_bind *sbind;
+ const struct osmo_sockaddr *remote;
+ const struct osmo_sockaddr *sa;
+ struct osmo_sockaddr local;
+ int count;
+
+ ns2_clear_elems(&gss->local);
+
+ /* no initial available */
+ if (gss->role == GPRS_SNS_ROLE_BSS) {
+ if (!gss->initial)
+ return;
+ remote = &gss->initial->saddr;
+ } else
+ remote = gprs_ns2_ip_vc_remote(gss->sns_nsvc);
+
+ /* count how many bindings are available (only UDP binds) */
+ count = llist_count(&gss->binds);
+ if (count == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "No local binds for this NSE -> cannot determine IP endpoints\n");
+ return;
+ }
+
+ switch (gss->family) {
+ case AF_INET:
+ ip4_elems = talloc_realloc(fi, gss->local.ip4, struct gprs_ns_ie_ip4_elem, count);
+ if (!ip4_elems)
+ return;
+
+ gss->local.ip4 = ip4_elems;
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ bind = sbind->bind;
+ sa = gprs_ns2_ip_bind_sockaddr(bind);
+ if (!sa)
+ continue;
+
+ if (sa->u.sas.ss_family != AF_INET)
+ continue;
+
+ /* check if this is an specific bind */
+ if (sa->u.sin.sin_addr.s_addr == 0) {
+ if (osmo_sockaddr_local_ip(&local, remote))
+ continue;
+
+ ip4_elems->ip_addr = local.u.sin.sin_addr.s_addr;
+ } else {
+ ip4_elems->ip_addr = sa->u.sin.sin_addr.s_addr;
+ }
+
+ ip4_elems->udp_port = sa->u.sin.sin_port;
+ ip4_elems->sig_weight = bind->sns_sig_weight;
+ ip4_elems->data_weight = bind->sns_data_weight;
+ ip4_elems++;
+ }
+
+ gss->local.num_ip4 = count;
+ gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip4_remote * gss->local.num_ip4, 8);
+ break;
+ case AF_INET6:
+ /* IPv6 */
+ ip6_elems = talloc_realloc(fi, gss->local.ip6, struct gprs_ns_ie_ip6_elem, count);
+ if (!ip6_elems)
+ return;
+
+ gss->local.ip6 = ip6_elems;
+
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ bind = sbind->bind;
+ sa = gprs_ns2_ip_bind_sockaddr(bind);
+ if (!sa)
+ continue;
+
+ if (sa->u.sas.ss_family != AF_INET6)
+ continue;
+
+ /* check if this is an specific bind */
+ if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sin6.sin6_addr)) {
+ if (osmo_sockaddr_local_ip(&local, remote))
+ continue;
+
+ ip6_elems->ip_addr = local.u.sin6.sin6_addr;
+ } else {
+ ip6_elems->ip_addr = sa->u.sin6.sin6_addr;
+ }
+
+ ip6_elems->udp_port = sa->u.sin.sin_port;
+ ip6_elems->sig_weight = bind->sns_sig_weight;
+ ip6_elems->data_weight = bind->sns_data_weight;
+
+ ip6_elems++;
+ }
+ gss->local.num_ip6 = count;
+ gss->num_max_nsvcs = OSMO_MAX(gss->num_max_ip6_remote * gss->local.num_ip6, 8);
+ break;
+ }
+
+ ns2_sns_copy_local_endpoints(gss);
+}
+
+static void ns2_sns_choose_next_bind(struct ns2_sns_state *gss)
+{
+ /* take the first bind or take the next bind */
+ if (!gss->initial_bind || gss->initial_bind->list.next == &gss->binds)
+ gss->initial_bind = llist_first_entry_or_null(&gss->binds, struct ns2_sns_bind, list);
+ else
+ gss->initial_bind = llist_entry(gss->initial_bind->list.next, struct ns2_sns_bind, list);
+}
+
+/* setup all dynamic SNS settings, create a new nsvc and send the SIZE */
+static void ns2_sns_st_bss_size_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
+
+ /* on a generic failure, the timer callback will recover */
+ if (old_state != GPRS_SNS_ST_UNCONFIGURED)
+ ns2_prim_status_ind(gss->nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_FAILURE);
+ if (old_state != GPRS_SNS_ST_BSS_SIZE)
+ gss->N = 0;
+
+ ns2_clear_procedures(gss);
+ gss->alive = false;
+
+ ns2_sns_compute_local_ep_from_binds(fi);
+ ns2_sns_choose_next_bind(gss);
+
+ /* setup the NSVC */
+ if (!gss->sns_nsvc) {
+ struct gprs_ns2_vc_bind *bind = gss->initial_bind->bind;
+ struct osmo_sockaddr *remote = &gss->initial->saddr;
+ gss->sns_nsvc = ns2_ip_bind_connect(bind, gss->nse, remote);
+ if (!gss->sns_nsvc)
+ return;
+ /* A pre-configured endpoint shall not be used for NSE data or signalling traffic
+ * (with the exception of Size and Configuration procedures) unless it is configured
+ * by the SGSN using the auto-configuration procedures */
+ gss->sns_nsvc->sns_only = true;
+ }
+
+ if (gss->num_max_ip4_remote > 0)
+ ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, gss->local.num_ip4, -1);
+ else
+ ns2_tx_sns_size(gss->sns_nsvc, true, gss->num_max_nsvcs, -1, gss->local.num_ip6);
+}
+
+static void ns2_sns_st_bss_config_bss(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct tlv_parsed *tp = NULL;
+
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
+
+ switch (event) {
+ case NS2_SNS_EV_RX_CONFIG_ACK:
+ tp = (struct tlv_parsed *) data;
+ if (TLVP_VAL_MINLEN(tp, NS_IE_CAUSE, 1)) {
+ LOGPFSML(fi, LOGL_ERROR, "SNS-CONFIG-ACK with cause %s\n",
+ gprs_ns2_cause_str(*TLVP_VAL(tp, NS_IE_CAUSE)));
+ /* TODO: What to do? */
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_SGSN, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 3);
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void ns2_sns_st_bss_config_bss_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
+
+ if (old_state != GPRS_SNS_ST_BSS_CONFIG_BSS)
+ gss->N = 0;
+
+ /* Transmit SNS-CONFIG */
+ switch (gss->family) {
+ case AF_INET:
+ ns2_tx_sns_config(gss->sns_nsvc, true,
+ gss->local.ip4, gss->local.num_ip4,
+ NULL, 0);
+ break;
+ case AF_INET6:
+ ns2_tx_sns_config(gss->sns_nsvc, true,
+ NULL, 0,
+ gss->local.ip6, gss->local.num_ip6);
+ break;
+ }
+}
+
+/* calculate the timeout of the configured state. the configured
+ * state will fail if not at least one NS-VC is alive within X second.
+ */
+static inline int ns_sns_configured_timeout(struct osmo_fsm_inst *fi)
+{
+ int secs;
+ struct gprs_ns2_inst *nsi = nse_inst_from_fi(fi)->nsi;
+ secs = nsi->timeout[NS_TOUT_TNS_ALIVE] * nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES];
+ secs += nsi->timeout[NS_TOUT_TNS_TEST];
+
+ return secs;
+}
+
+/* append the remote endpoints from the parsed TLV array to the ns2_sns_state */
+static int ns_sns_append_remote_eps(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+
+ if (TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
+ const struct gprs_ns_ie_ip4_elem *v4_list;
+ unsigned int num_v4;
+ v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
+ num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
+
+ if (num_v4 && gss->remote.ip6)
+ return -NS_CAUSE_INVAL_NR_IPv4_EP;
+
+ /* realloc to the new size */
+ gss->remote.ip4 = talloc_realloc(gss, gss->remote.ip4,
+ struct gprs_ns_ie_ip4_elem,
+ gss->remote.num_ip4 + num_v4);
+ /* append the new entries to the end of the list */
+ memcpy(&gss->remote.ip4[gss->remote.num_ip4], v4_list, num_v4*sizeof(*v4_list));
+ gss->remote.num_ip4 += num_v4;
+
+ LOGPFSML(fi, LOGL_INFO, "Rx SNS-CONFIG: Remote IPv4 list now %u entries\n",
+ gss->remote.num_ip4);
+ }
+
+ if (TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
+ const struct gprs_ns_ie_ip6_elem *v6_list;
+ unsigned int num_v6;
+ v6_list = (const struct gprs_ns_ie_ip6_elem *) TLVP_VAL(tp, NS_IE_IPv6_LIST);
+ num_v6 = TLVP_LEN(tp, NS_IE_IPv6_LIST) / sizeof(*v6_list);
+
+ if (num_v6 && gss->remote.ip4)
+ return -NS_CAUSE_INVAL_NR_IPv6_EP;
+
+ /* realloc to the new size */
+ gss->remote.ip6 = talloc_realloc(gss, gss->remote.ip6,
+ struct gprs_ns_ie_ip6_elem,
+ gss->remote.num_ip6 + num_v6);
+ /* append the new entries to the end of the list */
+ memcpy(&gss->remote.ip6[gss->remote.num_ip6], v6_list, num_v6*sizeof(*v6_list));
+ gss->remote.num_ip6 += num_v6;
+
+ LOGPFSML(fi, LOGL_INFO, "Rx SNS-CONFIG: Remote IPv6 list now %d entries\n",
+ gss->remote.num_ip6);
+ }
+
+ return 0;
+}
+
+static void ns2_sns_st_bss_config_sgsn_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
+
+ if (old_state != GPRS_SNS_ST_BSS_CONFIG_SGSN)
+ gss->N = 0;
+}
+
+static void ns2_sns_st_bss_config_sgsn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ uint8_t cause;
+ int rc;
+
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_BSS);
+
+ switch (event) {
+ case NS2_SNS_EV_RX_CONFIG_END:
+ case NS2_SNS_EV_RX_CONFIG:
+ rc = ns_sns_append_remote_eps(fi, data);
+ if (rc < 0) {
+ cause = -rc;
+ ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
+ return;
+ }
+ if (event == NS2_SNS_EV_RX_CONFIG_END) {
+ /* check if sum of data / sig weights == 0 */
+ if (ip46_weight_sum_data(&gss->remote) == 0 || ip46_weight_sum_sig(&gss->remote) == 0) {
+ cause = NS_CAUSE_INVAL_WEIGH;
+ ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
+ return;
+ }
+ create_missing_nsvcs(fi);
+ ns2_tx_sns_config_ack(gss->sns_nsvc, NULL);
+ /* start the test procedure on ALL NSVCs! */
+ gprs_ns2_start_alive_all_nsvcs(nse);
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0);
+ } else {
+ /* just send CONFIG-ACK */
+ ns2_tx_sns_config_ack(gss->sns_nsvc, NULL);
+ osmo_timer_schedule(&fi->timer, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 0);
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* called when receiving NS2_SNS_EV_RX_ADD in state configure */
+static void ns2_sns_st_configured_add(struct osmo_fsm_inst *fi,
+ struct ns2_sns_state *gss,
+ struct tlv_parsed *tp)
+{
+ const struct gprs_ns_ie_ip4_elem *v4_list = NULL;
+ const struct gprs_ns_ie_ip6_elem *v6_list = NULL;
+ int num_v4 = 0, num_v6 = 0;
+ uint8_t trans_id, cause = 0xff;
+ unsigned int i;
+ int rc = 0;
+
+ /* TODO: refactor EV_ADD/CHANGE/REMOVE by
+ * check uniqueness within the lists (no doublicate entries)
+ * check not-known-by-us and sent back a list of unknown/known values
+ * (abnormal behaviour according to 48.016)
+ */
+
+ trans_id = *TLVP_VAL(tp, NS_IE_TRANS_ID);
+ if (gss->family == AF_INET) {
+ if (!TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
+ cause = NS_CAUSE_INVAL_NR_IPv4_EP;
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+
+ v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
+ num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
+ for (i = 0; i < num_v4; i++) {
+ unsigned int j;
+ rc = do_sns_add(fi, &v4_list[i], NULL);
+ if (rc < 0) {
+ /* rollback/undo to restore previous state */
+ for (j = 0; j < i; j++)
+ do_sns_delete(fi, &v4_list[j], NULL);
+ cause = -rc;
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ break;
+ }
+ }
+ } else { /* IPv6 */
+ if (!TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
+ cause = NS_CAUSE_INVAL_NR_IPv6_EP;
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+
+ v6_list = (const struct gprs_ns_ie_ip6_elem *) TLVP_VAL(tp, NS_IE_IPv6_LIST);
+ num_v6 = TLVP_LEN(tp, NS_IE_IPv6_LIST) / sizeof(*v6_list);
+ for (i = 0; i < num_v6; i++) {
+ unsigned int j;
+ rc = do_sns_add(fi, NULL, &v6_list[i]);
+ if (rc < 0) {
+ /* rollback/undo to restore previous state */
+ for (j = 0; j < i; j++)
+ do_sns_delete(fi, NULL, &v6_list[j]);
+ cause = -rc;
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ break;
+ }
+ }
+ }
+
+ /* TODO: correct behaviour is to answer to the *same* NSVC from which the SNS_ADD was received */
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, NULL, v4_list, num_v4, v6_list, num_v6);
+}
+
+static void ns2_sns_st_configured_delete(struct osmo_fsm_inst *fi,
+ struct ns2_sns_state *gss,
+ struct tlv_parsed *tp)
+{
+ const struct gprs_ns_ie_ip4_elem *v4_list = NULL;
+ const struct gprs_ns_ie_ip6_elem *v6_list = NULL;
+ int num_v4 = 0, num_v6 = 0;
+ uint8_t trans_id, cause = 0xff;
+ unsigned int i;
+ int rc = 0;
+
+ /* TODO: split up delete into v4 + v6
+ * TODO: check if IPv4_LIST or IP_ADDR(v4) is present on IPv6 and vice versa
+ * TODO: check if IPv4_LIST/IPv6_LIST and IP_ADDR is present at the same time
+ */
+ trans_id = *TLVP_VAL(tp, NS_IE_TRANS_ID);
+ if (gss->family == AF_INET) {
+ if (TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
+ v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
+ num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
+ for ( i = 0; i < num_v4; i++) {
+ rc = do_sns_delete(fi, &v4_list[i], NULL);
+ if (rc < 0) {
+ cause = -rc;
+ /* continue to delete others */
+ }
+ }
+ if (cause != 0xff) {
+ /* TODO: create list of not-deleted and return it */
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+
+ } else if (TLVP_PRESENT(tp, NS_IE_IP_ADDR) && TLVP_LEN(tp, NS_IE_IP_ADDR) == 5) {
+ /* delete all NS-VCs for given IPv4 address */
+ const uint8_t *ie = TLVP_VAL(tp, NS_IE_IP_ADDR);
+ struct gprs_ns_ie_ip4_elem *ip4_remote;
+ uint32_t ip_addr = *(uint32_t *)(ie+1);
+ if (ie[0] != 0x01) { /* Address Type != IPv4 */
+ cause = NS_CAUSE_UNKN_IP_ADDR;
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ /* make a copy as do_sns_delete() will change the array underneath us */
+ ip4_remote = talloc_memdup(fi, gss->remote.ip4,
+ gss->remote.num_ip4 * sizeof(*v4_list));
+ for (i = 0; i < gss->remote.num_ip4; i++) {
+ if (ip4_remote[i].ip_addr == ip_addr) {
+ rc = do_sns_delete(fi, &ip4_remote[i], NULL);
+ if (rc < 0) {
+ cause = -rc;
+ /* continue to delete others */
+ }
+ }
+ }
+ talloc_free(ip4_remote);
+ if (cause != 0xff) {
+ /* TODO: create list of not-deleted and return it */
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ } else {
+ cause = NS_CAUSE_INVAL_NR_IPv4_EP;
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ } else { /* IPv6 */
+ if (TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
+ v6_list = (const struct gprs_ns_ie_ip6_elem *) TLVP_VAL(tp, NS_IE_IPv6_LIST);
+ num_v6 = TLVP_LEN(tp, NS_IE_IPv6_LIST) / sizeof(*v6_list);
+ for (i = 0; i < num_v6; i++) {
+ rc = do_sns_delete(fi, NULL, &v6_list[i]);
+ if (rc < 0) {
+ cause = -rc;
+ /* continue to delete others */
+ }
+ }
+ if (cause != 0xff) {
+ /* TODO: create list of not-deleted and return it */
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ } else if (TLVP_PRES_LEN(tp, NS_IE_IP_ADDR, 17)) {
+ /* delete all NS-VCs for given IPv4 address */
+ const uint8_t *ie = TLVP_VAL(tp, NS_IE_IP_ADDR);
+ struct gprs_ns_ie_ip6_elem *ip6_remote;
+ struct in6_addr ip6_addr;
+ unsigned int i;
+ if (ie[0] != 0x02) { /* Address Type != IPv6 */
+ cause = NS_CAUSE_UNKN_IP_ADDR;
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ memcpy(&ip6_addr, (ie+1), sizeof(struct in6_addr));
+ /* make a copy as do_sns_delete() will change the array underneath us */
+ ip6_remote = talloc_memdup(fi, gss->remote.ip6,
+ gss->remote.num_ip6 * sizeof(*v4_list));
+ for (i = 0; i < gss->remote.num_ip6; i++) {
+ if (!memcmp(&ip6_remote[i].ip_addr, &ip6_addr, sizeof(struct in6_addr))) {
+ rc = do_sns_delete(fi, NULL, &ip6_remote[i]);
+ if (rc < 0) {
+ cause = -rc;
+ /* continue to delete others */
+ }
+ }
+ }
+
+ talloc_free(ip6_remote);
+ if (cause != 0xff) {
+ /* TODO: create list of not-deleted and return it */
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ } else {
+ cause = NS_CAUSE_INVAL_NR_IPv6_EP;
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ }
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, NULL, v4_list, num_v4, v6_list, num_v6);
+}
+
+static void ns2_sns_st_configured_change(struct osmo_fsm_inst *fi,
+ struct ns2_sns_state *gss,
+ struct tlv_parsed *tp)
+{
+ const struct gprs_ns_ie_ip4_elem *v4_list = NULL;
+ const struct gprs_ns_ie_ip6_elem *v6_list = NULL;
+ int num_v4 = 0, num_v6 = 0;
+ uint8_t trans_id, cause = 0xff;
+ int rc = 0;
+ unsigned int i;
+
+ trans_id = *TLVP_VAL(tp, NS_IE_TRANS_ID);
+ if (TLVP_PRESENT(tp, NS_IE_IPv4_LIST)) {
+ v4_list = (const struct gprs_ns_ie_ip4_elem *) TLVP_VAL(tp, NS_IE_IPv4_LIST);
+ num_v4 = TLVP_LEN(tp, NS_IE_IPv4_LIST) / sizeof(*v4_list);
+ for (i = 0; i < num_v4; i++) {
+ rc = do_sns_change_weight(fi, &v4_list[i], NULL);
+ if (rc < 0) {
+ cause = -rc;
+ /* continue to others */
+ }
+ }
+ if (cause != 0xff) {
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ } else if (TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
+ v6_list = (const struct gprs_ns_ie_ip6_elem *) TLVP_VAL(tp, NS_IE_IPv6_LIST);
+ num_v6 = TLVP_LEN(tp, NS_IE_IPv6_LIST) / sizeof(*v6_list);
+ for (i = 0; i < num_v6; i++) {
+ rc = do_sns_change_weight(fi, NULL, &v6_list[i]);
+ if (rc < 0) {
+ cause = -rc;
+ /* continue to others */
+ }
+ }
+ if (cause != 0xff) {
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ } else {
+ cause = NS_CAUSE_INVAL_NR_IPv4_EP;
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, &cause, NULL, 0, NULL, 0);
+ return;
+ }
+ ns2_tx_sns_ack(gss->sns_nsvc, trans_id, NULL, v4_list, num_v4, v6_list, num_v6);
+}
+
+static void ns2_sns_st_configured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct tlv_parsed *tp = data;
+
+ switch (event) {
+ case NS2_SNS_EV_RX_ADD:
+ ns2_sns_st_configured_add(fi, gss, tp);
+ break;
+ case NS2_SNS_EV_RX_DELETE:
+ ns2_sns_st_configured_delete(fi, gss, tp);
+ break;
+ case NS2_SNS_EV_RX_CHANGE_WEIGHT:
+ ns2_sns_st_configured_change(fi, gss, tp);
+ break;
+ case NS2_SNS_EV_REQ_NSVC_ALIVE:
+ osmo_timer_del(&fi->timer);
+ break;
+ }
+}
+
+static void ns2_sns_st_configured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ /* NS-VC status updates are only parsed in ST_CONFIGURED.
+ * Do an initial check if there are any nsvc alive atm */
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ if (ns2_vc_is_unblocked(nsvc)) {
+ gss->alive = true;
+ osmo_timer_del(&fi->timer);
+ break;
+ }
+ }
+
+ /* remove the initial NSVC if the NSVC isn't part of the configuration */
+ if (gss->sns_nsvc->sns_only)
+ gprs_ns2_free_nsvc(gss->sns_nsvc);
+
+ if (old_state != GPRS_SNS_ST_LOCAL_PROCEDURE)
+ ns2_prim_status_ind(nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_CONFIGURED);
+
+ if (!llist_empty(&gss->procedures)) {
+ osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
+ gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
+ }
+}
+
+static void ns2_sns_st_local_procedure_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+
+ /* check if resend or not */
+ if (!gss->current_procedure) {
+ /* take next procedure */
+ gss->current_procedure = llist_first_entry_or_null(&gss->procedures,
+ struct ns2_sns_procedure, list);
+ if (!gss->current_procedure) {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, 0, 0);
+ return;
+ }
+ gss->N = 0;
+ gss->current_procedure->running = true;
+ gss->current_procedure->trans_id = ++gss->trans_id;
+ if (gss->trans_id == 0)
+ gss->trans_id = gss->current_procedure->trans_id = 1;
+
+ }
+
+ /* also takes care of retransmitting */
+ switch (gss->current_procedure->procedure) {
+ case SNS_PROC_ADD:
+ if (gss->family == AF_INET)
+ ns2_tx_sns_add(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
+ else
+ ns2_tx_sns_add(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
+ break;
+ case SNS_PROC_CHANGE_WEIGHT:
+ if (gss->family == AF_INET)
+ ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
+ else
+ ns2_tx_sns_change_weight(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
+ break;
+ case SNS_PROC_DEL:
+ if (gss->family == AF_INET)
+ ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure->trans_id, &gss->current_procedure->ip4, 1, NULL, 0);
+ else
+ ns2_tx_sns_del(gss->sns_nsvc, gss->current_procedure->trans_id, NULL, 0, &gss->current_procedure->ip6, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void create_nsvc_for_new_sbind(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind)
+{
+ struct gprs_ns2_nse *nse = gss->nse;
+ struct gprs_ns2_vc_bind *bind = sbind->bind;
+ struct gprs_ns2_vc *nsvc;
+ struct osmo_sockaddr remote = { };
+ unsigned int i;
+
+ /* iterate over all remote IPv4 endpoints */
+ for (i = 0; i < gss->remote.num_ip4; i++) {
+ const struct gprs_ns_ie_ip4_elem *ip4 = &gss->remote.ip4[i];
+
+ remote.u.sin.sin_family = AF_INET;
+ remote.u.sin.sin_addr.s_addr = ip4->ip_addr;
+ remote.u.sin.sin_port = ip4->udp_port;
+ /* we only care about UDP binds */
+ if (bind->ll != GPRS_NS2_LL_UDP)
+ continue;
+
+ nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
+ if (!nsvc) {
+ nsvc = gprs_ns2_ip_connect_inactive(bind, &remote, nse, 0);
+ if (!nsvc) {
+ /* TODO: add to a list to send back a NS-STATUS */
+ continue;
+ }
+ }
+
+ /* update data / signalling weight */
+ nsvc->data_weight = ip4->data_weight;
+ nsvc->sig_weight = ip4->sig_weight;
+ nsvc->sns_only = false;
+ }
+
+ /* iterate over all remote IPv4 endpoints */
+ for (i = 0; i < gss->remote.num_ip6; i++) {
+ const struct gprs_ns_ie_ip6_elem *ip6 = &gss->remote.ip6[i];
+
+ remote.u.sin6.sin6_family = AF_INET6;
+ remote.u.sin6.sin6_addr = ip6->ip_addr;
+ remote.u.sin6.sin6_port = ip6->udp_port;
+
+ /* we only care about UDP binds */
+ nsvc = nsvc_for_bind_and_remote(nse, bind, &remote);
+ if (!nsvc) {
+ nsvc = gprs_ns2_ip_connect_inactive(bind, &remote, nse, 0);
+ if (!nsvc) {
+ /* TODO: add to a list to send back a NS-STATUS */
+ continue;
+ }
+ }
+
+ /* update data / signalling weight */
+ nsvc->data_weight = ip6->data_weight;
+ nsvc->sig_weight = ip6->sig_weight;
+ nsvc->sns_only = false;
+ }
+}
+
+static void ns2_sns_st_local_procedure(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns_ie_ip4_elem *ip4, *proc4;
+ struct gprs_ns_ie_ip6_elem *ip6, *proc6;
+ struct tlv_parsed *tp = data;
+ uint8_t trans_id;
+ uint8_t cause;
+
+ switch (event) {
+ case NS2_SNS_EV_RX_ADD:
+ ns2_sns_st_configured_add(fi, gss, tp);
+ break;
+ case NS2_SNS_EV_RX_DELETE:
+ ns2_sns_st_configured_delete(fi, gss, tp);
+ break;
+ case NS2_SNS_EV_RX_CHANGE_WEIGHT:
+ ns2_sns_st_configured_change(fi, gss, tp);
+ break;
+ case NS2_SNS_EV_RX_ACK:
+ /* presence of trans_id is already checked here */
+ trans_id = tlvp_val8(tp, NS_IE_TRANS_ID, 0);
+ if (trans_id != gss->current_procedure->trans_id) {
+ LOGPFSML(fi, LOGL_INFO, "NSEI=%u Rx SNS ACK with invalid transaction id %d. Valid %d\n",
+ nse->nsei, trans_id, gss->current_procedure->trans_id);
+ break;
+ }
+
+ if (TLVP_PRESENT(tp, NS_IE_CAUSE)) {
+ /* what happend on error cause? return to size? */
+ cause = tlvp_val8(tp, NS_IE_CAUSE, 0);
+ LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx SNS ACK trans %d with cause code %d.\n",
+ nse->nsei, trans_id, cause);
+ sns_failed(fi, NULL);
+ break;
+ }
+
+ switch (gss->current_procedure->procedure) {
+ case SNS_PROC_ADD:
+ switch (gss->family) {
+ case AF_INET:
+ add_ip4_elem(gss, &gss->local, &gss->current_procedure->ip4);
+ break;
+ case AF_INET6:
+ add_ip6_elem(gss, &gss->local, &gss->current_procedure->ip6);
+ break;
+ }
+ /* the sbind can be NULL if the bind has been released by del_bind */
+ if (gss->current_procedure->sbind) {
+ create_nsvc_for_new_sbind(gss, gss->current_procedure->sbind);
+ gprs_ns2_start_alive_all_nsvcs(nse);
+ }
+ break;
+ case SNS_PROC_CHANGE_WEIGHT:
+ switch (gss->family) {
+ case AF_INET:
+ proc4 = &gss->current_procedure->ip4;
+ for (unsigned int i=0; i<gss->local.num_ip4; i++) {
+ ip4 = &gss->local.ip4[i];
+ if (ip4->ip_addr != proc4->ip_addr ||
+ ip4->udp_port != proc4->udp_port)
+ continue;
+ ip4->sig_weight = proc4->sig_weight;
+ ip4->data_weight = proc4->data_weight;
+ break;
+ }
+ break;
+ case AF_INET6:
+ proc6 = &gss->current_procedure->ip6;
+ for (unsigned int i=0; i<gss->local.num_ip6; i++) {
+ ip6 = &gss->local.ip6[i];
+ if (memcmp(&ip6->ip_addr, &proc6->ip_addr, sizeof(proc6->ip_addr)) ||
+ ip6->udp_port != proc6->udp_port) {
+ continue;
+ }
+ ip6->sig_weight = proc6->sig_weight;
+ ip6->data_weight = proc6->data_weight;
+ break;
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ break;
+ case SNS_PROC_DEL:
+ switch (gss->family) {
+ case AF_INET:
+ remove_ip4_elem(gss, &gss->local, &gss->current_procedure->ip4);
+ break;
+ case AF_INET6:
+ remove_ip6_elem(gss, &gss->local, &gss->current_procedure->ip6);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ llist_del(&gss->current_procedure->list);
+ talloc_free(gss->current_procedure);
+ gss->current_procedure = NULL;
+
+ if (llist_empty(&gss->procedures))
+ osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_CONFIGURED,
+ 0, 0);
+ else
+ osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
+ gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
+ break;
+ }
+}
+
+static const struct osmo_fsm_state ns2_sns_bss_states[] = {
+ [GPRS_SNS_ST_UNCONFIGURED] = {
+ .in_event_mask = 0, /* handled by all_state_action */
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_BSS_SIZE),
+ .name = "UNCONFIGURED",
+ .action = ns2_sns_st_bss_unconfigured,
+ },
+ [GPRS_SNS_ST_BSS_SIZE] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_SIZE_ACK),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_BSS_SIZE) |
+ S(GPRS_SNS_ST_BSS_CONFIG_BSS),
+ .name = "BSS_SIZE",
+ .action = ns2_sns_st_bss_size,
+ .onenter = ns2_sns_st_bss_size_onenter,
+ },
+ [GPRS_SNS_ST_BSS_CONFIG_BSS] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_CONFIG_ACK),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_BSS_CONFIG_BSS) |
+ S(GPRS_SNS_ST_BSS_CONFIG_SGSN) |
+ S(GPRS_SNS_ST_BSS_SIZE),
+ .name = "BSS_CONFIG_BSS",
+ .action = ns2_sns_st_bss_config_bss,
+ .onenter = ns2_sns_st_bss_config_bss_onenter,
+ },
+ [GPRS_SNS_ST_BSS_CONFIG_SGSN] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_CONFIG) |
+ S(NS2_SNS_EV_RX_CONFIG_END),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_BSS_CONFIG_SGSN) |
+ S(GPRS_SNS_ST_CONFIGURED) |
+ S(GPRS_SNS_ST_BSS_SIZE),
+ .name = "BSS_CONFIG_SGSN",
+ .action = ns2_sns_st_bss_config_sgsn,
+ .onenter = ns2_sns_st_bss_config_sgsn_onenter,
+ },
+ [GPRS_SNS_ST_CONFIGURED] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_ADD) |
+ S(NS2_SNS_EV_RX_DELETE) |
+ S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_NSVC_ALIVE),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_BSS_SIZE) |
+ S(GPRS_SNS_ST_LOCAL_PROCEDURE),
+ .name = "CONFIGURED",
+ .action = ns2_sns_st_configured,
+ .onenter = ns2_sns_st_configured_onenter,
+ },
+ [GPRS_SNS_ST_LOCAL_PROCEDURE] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_ADD) |
+ S(NS2_SNS_EV_RX_DELETE) |
+ S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_RX_ACK) |
+ S(NS2_SNS_EV_REQ_NSVC_ALIVE),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_BSS_SIZE) |
+ S(GPRS_SNS_ST_CONFIGURED) |
+ S(GPRS_SNS_ST_LOCAL_PROCEDURE),
+ .name = "LOCAL_PROCEDURE",
+ .action = ns2_sns_st_local_procedure,
+ .onenter = ns2_sns_st_local_procedure_onenter,
+ },
+
+};
+
+static int ns2_sns_fsm_bss_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns2_inst *nsi = nse->nsi;
+
+ gss->N++;
+ switch (fi->T) {
+ case 1:
+ if (gss->N >= nsi->timeout[NS_TOUT_TSNS_SIZE_RETRIES]) {
+ sns_failed(fi, "Size retries failed. Selecting next IP-SNS endpoint.");
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_SIZE, nsi->timeout[NS_TOUT_TSNS_PROV], 1);
+ }
+ break;
+ case 2:
+ if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
+ sns_failed(fi, "BSS Config retries failed. Selecting next IP-SNS endpoint");
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_BSS, nsi->timeout[NS_TOUT_TSNS_PROV], 2);
+ }
+ break;
+ case 3:
+ if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
+ sns_failed(fi, "SGSN Config retries failed. Selecting next IP-SNS endpoint.");
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_CONFIG_SGSN, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
+ }
+ break;
+ case 4:
+ sns_failed(fi, "Config succeeded but no NS-VC came online. Selecting next IP-SNS endpoint.");
+ break;
+ case 5:
+ if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
+ sns_failed(fi, "SNS Procedure retries failed.");
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV], 5);
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct gprs_ns_ie_ip4_elem *ns2_get_sbind_ip4_entry(struct ns2_sns_state *gss,
+ struct ns2_sns_bind *sbind,
+ struct ns2_sns_elems *endpoints)
+{
+ const struct osmo_sockaddr *addr;
+ struct gprs_ns_ie_ip4_elem *ip4;
+
+ if (gss->family != AF_INET)
+ return NULL;
+
+ addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ if (addr->u.sa.sa_family != AF_INET)
+ return NULL;
+
+ for (unsigned int i=0; i<endpoints->num_ip4; i++) {
+ ip4 = &endpoints->ip4[i];
+ if (ip4->ip_addr == addr->u.sin.sin_addr.s_addr &&
+ ip4->udp_port == addr->u.sin.sin_port)
+ return ip4;
+ }
+
+ return NULL;
+}
+
+static struct gprs_ns_ie_ip6_elem *ns2_get_sbind_ip6_entry(struct ns2_sns_state *gss,
+ struct ns2_sns_bind *sbind,
+ struct ns2_sns_elems *endpoints)
+{
+ const struct osmo_sockaddr *addr;
+ struct gprs_ns_ie_ip6_elem *ip6;
+
+ if (gss->family != AF_INET6)
+ return NULL;
+
+ addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ if (addr->u.sa.sa_family != AF_INET6)
+ return NULL;
+
+ for (unsigned int i=0; i<endpoints->num_ip6; i++) {
+ ip6 = &endpoints->ip6[i];
+ if (memcmp(&ip6->ip_addr, &addr->u.sin6.sin6_addr, sizeof(ip6->ip_addr)) ||
+ ip6->udp_port != addr->u.sin6.sin6_port)
+ return ip6;
+ }
+
+ return NULL;
+}
+
+/* return != 0 if the resulting weight is invalid. return 1 if sbind doesn't have an entry */
+static int ns2_update_weight_entry(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
+ struct ns2_sns_elems *endpoints)
+{
+ struct gprs_ns_ie_ip4_elem *ip4;
+ struct gprs_ns_ie_ip6_elem *ip6;
+
+ switch (gss->family) {
+ case AF_INET:
+ ip4 = ns2_get_sbind_ip4_entry(gss, sbind, endpoints);
+ if (!ip4)
+ return 1;
+ ip4->sig_weight = sbind->bind->sns_sig_weight;
+ ip4->data_weight = sbind->bind->sns_data_weight;
+ return (ip4_weight_sum_sig(endpoints) != 0 && ip4_weight_sum_data(endpoints) != 0);
+ break;
+ case AF_INET6:
+ ip6 = ns2_get_sbind_ip6_entry(gss, sbind, endpoints);
+ if (!ip6)
+ return 1;
+ ip6->sig_weight = sbind->bind->sns_sig_weight;
+ ip6->data_weight = sbind->bind->sns_data_weight;
+ return (ip6_weight_sum_sig(endpoints) != 0 && ip6_weight_sum_data(endpoints) != 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+static void ns2_add_procedure(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
+ enum sns_procedure procedure_type)
+{
+ struct ns2_sns_procedure *procedure = NULL;
+ const struct osmo_sockaddr *saddr;
+ saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+
+ OSMO_ASSERT(saddr->u.sa.sa_family == gss->family);
+
+ switch (procedure_type) {
+ case SNS_PROC_ADD:
+ break;
+ case SNS_PROC_DEL:
+ break;
+ case SNS_PROC_CHANGE_WEIGHT:
+ llist_for_each_entry(procedure, &gss->procedures, list) {
+ if (procedure->sbind == sbind && procedure->procedure == procedure_type &&
+ !procedure->running) {
+ switch(gss->family) {
+ case AF_INET:
+ /* merge it with a previous procedure */
+ procedure->ip4.ip_addr = sbind->bind->sns_sig_weight;
+ procedure->ip4.data_weight = sbind->bind->sns_data_weight;
+ break;
+ case AF_INET6:
+ /* merge it with a previous procedure */
+ procedure->ip6.sig_weight = sbind->bind->sns_sig_weight;
+ procedure->ip6.data_weight = sbind->bind->sns_data_weight;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ return;
+ }
+ }
+ break;
+ default:
+ return;
+ }
+
+ procedure = talloc_zero(gss, struct ns2_sns_procedure);
+ if (!procedure)
+ return;
+
+ switch (procedure_type) {
+ case SNS_PROC_ADD:
+ case SNS_PROC_CHANGE_WEIGHT:
+ procedure->sbind = sbind;
+ break;
+ default:
+ break;
+ }
+
+ llist_add_tail(&procedure->list, &gss->procedures);
+ procedure->procedure = procedure_type;
+ procedure->sig_weight = sbind->bind->sns_sig_weight;
+ procedure->data_weight = sbind->bind->sns_data_weight;
+
+ switch(gss->family) {
+ case AF_INET:
+ procedure->ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
+ procedure->ip4.udp_port = saddr->u.sin.sin_port;
+ procedure->ip4.sig_weight = sbind->bind->sns_sig_weight;
+ procedure->ip4.data_weight = sbind->bind->sns_data_weight;
+ break;
+ case AF_INET6:
+ memcpy(&procedure->ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));
+ procedure->ip6.udp_port = saddr->u.sin.sin_port;
+ procedure->ip6.sig_weight = sbind->bind->sns_sig_weight;
+ procedure->ip6.data_weight = sbind->bind->sns_data_weight;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ if (gss->nse->bss_sns_fi->state == GPRS_SNS_ST_CONFIGURED) {
+ osmo_fsm_inst_state_chg(gss->nse->bss_sns_fi, GPRS_SNS_ST_LOCAL_PROCEDURE,
+ gss->nse->nsi->timeout[NS_TOUT_TSNS_PROV], 5);
+ }
+}
+
+/* add an entrypoint to sns_endpoints */
+static int ns2_sns_add_elements(struct ns2_sns_state *gss, struct ns2_sns_bind *sbind,
+ struct ns2_sns_elems *elems)
+{
+ const struct osmo_sockaddr *saddr;
+ struct gprs_ns_ie_ip4_elem ip4;
+ struct gprs_ns_ie_ip6_elem ip6;
+ int rc = -1;
+
+ saddr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ OSMO_ASSERT(saddr->u.sa.sa_family == gss->family);
+
+ switch (gss->family) {
+ case AF_INET:
+ ip4.ip_addr = saddr->u.sin.sin_addr.s_addr;
+ ip4.udp_port= saddr->u.sin.sin_port;
+ ip4.sig_weight = sbind->bind->sns_sig_weight;
+ ip4.data_weight = sbind->bind->sns_data_weight;
+ rc = add_ip4_elem(gss, elems, &ip4);
+ break;
+ case AF_INET6:
+ memcpy(&ip6.ip_addr, &saddr->u.sin6.sin6_addr, sizeof(struct in6_addr));
+ ip6.udp_port= saddr->u.sin.sin_port;
+ ip6.sig_weight = sbind->bind->sns_sig_weight;
+ ip6.data_weight = sbind->bind->sns_data_weight;
+ rc = add_ip6_elem(gss, elems, &ip6);
+ break;
+ }
+
+ return rc;
+}
+
+/* common allstate-action for both roles */
+static void ns2_sns_st_all_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct ns2_sns_bind *sbind;
+ struct gprs_ns2_vc *nsvc, *nsvc2;
+ struct ns2_sns_procedure *procedure;
+
+ switch (event) {
+ case NS2_SNS_EV_REQ_ADD_BIND:
+ sbind = data;
+ switch (fi->state) {
+ case GPRS_SNS_ST_UNCONFIGURED:
+ if (gss->role == GPRS_SNS_ROLE_BSS)
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ break;
+ case GPRS_SNS_ST_BSS_SIZE:
+ switch (gss->family) {
+ case AF_INET:
+ if (gss->num_max_ip4_remote <= gss->local.num_ip4 ||
+ gss->num_max_ip4_remote * (gss->local.num_ip4 + 1) > gss->num_max_nsvcs) {
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER);
+ return;
+ }
+ break;
+ case AF_INET6:
+ if (gss->num_max_ip6_remote <= gss->local.num_ip6 ||
+ gss->num_max_ip6_remote * (gss->local.num_ip6 + 1) > gss->num_max_nsvcs) {
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER);
+ return;
+ }
+ break;
+ }
+ ns2_sns_add_elements(gss, sbind, &gss->local);
+ break;
+ case GPRS_SNS_ST_BSS_CONFIG_BSS:
+ case GPRS_SNS_ST_BSS_CONFIG_SGSN:
+ case GPRS_SNS_ST_CONFIGURED:
+ switch (gss->family) {
+ case AF_INET:
+ if (gss->num_max_ip4_remote <= gss->local.num_ip4) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
+ nse->nsei, sbind->bind->name);
+ return;
+ }
+ if (gss->remote.num_ip4 * (gss->local.num_ip4 + 1) > gss->num_max_nsvcs) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
+ nse->nsei, sbind->bind->name);
+ return;
+ }
+ break;
+ case AF_INET6:
+ if (gss->num_max_ip6_remote <= gss->local.num_ip6) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
+ nse->nsei, sbind->bind->name);
+ return;
+ }
+ if (gss->remote.num_ip6 * (gss->local.num_ip6 + 1) > gss->num_max_nsvcs) {
+ LOGPFSML(fi, LOGL_ERROR,
+ "NSE %d: ignoring bind %s because there are too many endpoints for the SNS.\n",
+ nse->nsei, sbind->bind->name);
+ return;
+ }
+ break;
+ }
+ ns2_sns_add_elements(gss, sbind, &gss->local_procedure);
+ ns2_add_procedure(gss, sbind, SNS_PROC_ADD);
+ break;
+ }
+ break;
+ case NS2_SNS_EV_REQ_DELETE_BIND:
+ sbind = data;
+ switch (fi->state) {
+ case GPRS_SNS_ST_UNCONFIGURED:
+ break;
+ case GPRS_SNS_ST_BSS_SIZE:
+ llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) {
+ if (nsvc->bind == sbind->bind) {
+ gprs_ns2_free_nsvc(nsvc);
+ }
+ }
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ break;
+ case GPRS_SNS_ST_BSS_CONFIG_BSS:
+ case GPRS_SNS_ST_BSS_CONFIG_SGSN:
+ case GPRS_SNS_ST_CONFIGURED:
+ case GPRS_SNS_ST_LOCAL_PROCEDURE:
+ remove_bind_elem(gss, &gss->local_procedure, sbind);
+ if (ip46_weight_sum(&gss->local_procedure, true) == 0 ||
+ ip46_weight_sum(&gss->local_procedure, false) == 0) {
+ LOGPFSML(fi, LOGL_ERROR, "NSE %d: weight has become invalid because of removing bind %s. Resetting the configuration\n",
+ nse->nsei, sbind->bind->name);
+ sns_failed(fi, NULL);
+ break;
+ }
+ gss->block_no_nsvc_events = true;
+ llist_for_each_entry_safe(nsvc, nsvc2, &nse->nsvc, list) {
+ if (nsvc->bind == sbind->bind) {
+ gprs_ns2_free_nsvc(nsvc);
+ }
+ }
+ gss->block_no_nsvc_events = false;
+ if (nse->sum_sig_weight == 0 || !nse->alive || !gss->alive) {
+ sns_failed(fi, "While deleting a bind the current state became invalid (no signalling weight)");
+ break;
+ }
+
+ /* ensure other procedures doesn't use the sbind */
+ llist_for_each_entry(procedure, &gss->procedures, list) {
+ if (procedure->sbind == sbind)
+ procedure->sbind = NULL;
+ }
+ ns2_add_procedure(gss, sbind, SNS_PROC_DEL);
+ break;
+ }
+
+ /* if this is the last bind, the free_nsvc() will trigger a reselection */
+ talloc_free(sbind);
+ break;
+ case NS2_SNS_EV_REQ_CHANGE_WEIGHT:
+ sbind = data;
+ switch (fi->state) {
+ case GPRS_SNS_ST_UNCONFIGURED:
+ /* select_endpoint will check if this is a valid configuration */
+ if (gss->role == GPRS_SNS_ROLE_BSS)
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+ break;
+ case GPRS_SNS_ST_BSS_SIZE:
+ /* invalid weight? */
+ if (!ns2_update_weight_entry(gss, sbind, &gss->local))
+ sns_failed(fi, "updating weights results in an invalid configuration.");
+ break;
+ default:
+ if (!ns2_update_weight_entry(gss, sbind, &gss->local_procedure)) {
+ sns_failed(fi, "updating weights results in an invalid configuration.");
+ break;
+ }
+ ns2_add_procedure(gss, sbind, SNS_PROC_CHANGE_WEIGHT);
+ break;
+ }
+ }
+}
+
+/* validate the bss configuration (sns endpoint and binds)
+ * - no endpoints -> invalid
+ * - no binds -> invalid
+ * - only v4 sns endpoints, only v6 binds -> invalid
+ * - only v4 sns endpoints, but v4 sig weights == 0 -> invalid ...
+ */
+static int ns2_sns_bss_valid_configuration(struct ns2_sns_state *gss)
+{
+ struct ns2_sns_bind *sbind;
+ struct sns_endpoint *endpoint;
+ const struct osmo_sockaddr *addr;
+ int v4_sig = 0, v4_data = 0, v6_sig = 0, v6_data = 0;
+ bool v4_endpoints = false;
+ bool v6_endpoints = false;
+
+ if (llist_empty(&gss->sns_endpoints) || llist_empty(&gss->binds))
+ return 0;
+
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ addr = gprs_ns2_ip_bind_sockaddr(sbind->bind);
+ if (!addr)
+ continue;
+ switch (addr->u.sa.sa_family) {
+ case AF_INET:
+ v4_sig += sbind->bind->sns_sig_weight;
+ v4_data += sbind->bind->sns_data_weight;
+ break;
+ case AF_INET6:
+ v6_sig += sbind->bind->sns_sig_weight;
+ v6_data += sbind->bind->sns_data_weight;
+ break;
+ }
+ }
+
+ llist_for_each_entry(endpoint, &gss->sns_endpoints, list) {
+ switch (endpoint->saddr.u.sa.sa_family) {
+ case AF_INET:
+ v4_endpoints = true;
+ break;
+ case AF_INET6:
+ v6_endpoints = true;
+ break;
+ }
+ }
+
+ return (v4_endpoints && v4_sig && v4_data) || (v6_endpoints && v6_sig && v6_data);
+}
+
+/* allstate-action for BSS role */
+static void ns2_sns_st_all_action_bss(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+
+ /* reset when receiving NS2_SNS_EV_REQ_NO_NSVC */
+ switch (event) {
+ case NS2_SNS_EV_REQ_NO_NSVC:
+ /* ignore reselection running */
+ if (gss->reselection_running || gss->block_no_nsvc_events)
+ break;
+
+ sns_failed(fi, "no remaining NSVC, resetting SNS FSM");
+ break;
+ case NS2_SNS_EV_REQ_FREE_NSVCS:
+ case NS2_SNS_EV_REQ_SELECT_ENDPOINT:
+ /* TODO: keep the order of binds when data == GPRS_SNS_FLAG_KEEP_SELECT_ENDPOINT_ORDER */
+ /* tear down previous state
+ * gprs_ns2_free_nsvcs() will trigger NO_NSVC, prevent this from triggering a reselection */
+ if (gss->reselection_running || gss->block_no_nsvc_events)
+ break;
+
+ gss->reselection_running = true;
+ ns2_free_nsvcs(nse);
+ ns2_clear_elems(&gss->local);
+ ns2_clear_elems(&gss->remote);
+
+ /* Choose the next sns endpoint. */
+ if (!ns2_sns_bss_valid_configuration(gss)) {
+ gss->initial = NULL;
+ ns2_prim_status_ind(gss->nse, NULL, 0, GPRS_NS2_AFF_CAUSE_SNS_NO_ENDPOINTS);
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 3);
+ gss->reselection_running = false;
+ return;
+ } else if (!gss->initial) {
+ gss->initial = llist_first_entry(&gss->sns_endpoints, struct sns_endpoint, list);
+ } else if (gss->initial->list.next == &gss->sns_endpoints) {
+ /* last entry, continue with first */
+ gss->initial = llist_first_entry(&gss->sns_endpoints, struct sns_endpoint, list);
+ } else {
+ /* next element is an entry */
+ gss->initial = llist_entry(gss->initial->list.next, struct sns_endpoint, list);
+ }
+
+ gss->family = gss->initial->saddr.u.sa.sa_family;
+ gss->reselection_running = false;
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_BSS_SIZE, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 1);
+ break;
+ default:
+ ns2_sns_st_all_action(fi, event, data);
+ break;
+ }
+}
+
+static struct osmo_fsm gprs_ns2_sns_bss_fsm = {
+ .name = "GPRS-NS2-SNS-BSS",
+ .states = ns2_sns_bss_states,
+ .num_states = ARRAY_SIZE(ns2_sns_bss_states),
+ .allstate_event_mask = S(NS2_SNS_EV_REQ_NO_NSVC) |
+ S(NS2_SNS_EV_REQ_FREE_NSVCS) |
+ S(NS2_SNS_EV_REQ_SELECT_ENDPOINT) |
+ S(NS2_SNS_EV_REQ_ADD_BIND) |
+ S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_DELETE_BIND),
+ .allstate_action = ns2_sns_st_all_action_bss,
+ .cleanup = NULL,
+ .timer_cb = ns2_sns_fsm_bss_timer_cb,
+ .event_names = gprs_sns_event_names,
+ .pre_term = NULL,
+ .log_subsys = DLNS,
+};
+
+/*! Allocate an IP-SNS FSM for the BSS side.
+ * \param[in] nse NS Entity in which the FSM runs
+ * \param[in] id string identifier
+ * \returns FSM instance on success; NULL on error */
+struct osmo_fsm_inst *ns2_sns_bss_fsm_alloc(struct gprs_ns2_nse *nse,
+ const char *id)
+{
+ struct osmo_fsm_inst *fi;
+ struct ns2_sns_state *gss;
+
+ fi = osmo_fsm_inst_alloc(&gprs_ns2_sns_bss_fsm, nse, NULL, LOGL_DEBUG, id);
+ if (!fi)
+ return fi;
+
+ gss = talloc_zero(fi, struct ns2_sns_state);
+ if (!gss)
+ goto err;
+
+ fi->priv = gss;
+ gss->nse = nse;
+ gss->role = GPRS_SNS_ROLE_BSS;
+ /* The SGSN doesn't tell the BSS, so we assume there's always sufficient */
+ gss->num_max_ip4_remote = 8192;
+ gss->num_max_ip6_remote = 8192;
+ INIT_LLIST_HEAD(&gss->sns_endpoints);
+ INIT_LLIST_HEAD(&gss->binds);
+ INIT_LLIST_HEAD(&gss->procedures);
+
+ return fi;
+err:
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+ return NULL;
+}
+
+/*! main entry point for receiving SNS messages from the network.
+ * \param[in] nsvc NS-VC on which the message was received
+ * \param[in] msg message buffer of the IP-SNS message
+ * \param[in] tp parsed TLV structure of message
+ * \returns 0 on success; negative on error */
+int ns2_sns_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp)
+{
+ struct gprs_ns2_nse *nse = nsvc->nse;
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ uint16_t nsei = nsvc->nse->nsei;
+ struct ns2_sns_state *gss;
+ struct osmo_fsm_inst *fi;
+ int rc = 0;
+
+ if (!nse->bss_sns_fi) {
+ LOGNSVC(nsvc, LOGL_NOTICE, "Rx %s for NS Instance that has no SNS!\n",
+ get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* FIXME: how to resolve SNS FSM Instance by NSEI (SGSN)? */
+ fi = nse->bss_sns_fi;
+ gss = (struct ns2_sns_state *) fi->priv;
+ gss->sns_nsvc = nsvc;
+
+ LOGPFSML(fi, LOGL_DEBUG, "NSEI=%u Rx SNS PDU type %s\n", nsei,
+ get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
+
+ switch (nsh->pdu_type) {
+ case SNS_PDUT_SIZE:
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_SIZE, tp);
+ break;
+ case SNS_PDUT_SIZE_ACK:
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_SIZE_ACK, tp);
+ break;
+ case SNS_PDUT_CONFIG:
+ if (nsh->data[0] & 0x01)
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CONFIG_END, tp);
+ else
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CONFIG, tp);
+ break;
+ case SNS_PDUT_CONFIG_ACK:
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CONFIG_ACK, tp);
+ break;
+ case SNS_PDUT_ADD:
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_ADD, tp);
+ break;
+ case SNS_PDUT_DELETE:
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_DELETE, tp);
+ break;
+ case SNS_PDUT_CHANGE_WEIGHT:
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_CHANGE_WEIGHT, tp);
+ break;
+ case SNS_PDUT_ACK:
+ osmo_fsm_inst_dispatch(fi, NS2_SNS_EV_RX_ACK, tp);
+ break;
+ default:
+ LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx unknown SNS PDU type %s\n", nsei,
+ get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
+ rc = -EINVAL;
+ }
+
+out:
+ msgb_free(msg);
+
+ return rc;
+}
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/misc.h>
+
+static void vty_dump_sns_ip4(struct vty *vty, const char *prefix, const struct gprs_ns_ie_ip4_elem *ip4)
+{
+ struct in_addr in = { .s_addr = ip4->ip_addr };
+ vty_out(vty, "%s %s:%u, Signalling Weight: %u, Data Weight: %u%s", prefix,
+ inet_ntoa(in), ntohs(ip4->udp_port), ip4->sig_weight, ip4->data_weight, VTY_NEWLINE);
+}
+
+static void vty_dump_sns_ip6(struct vty *vty, const char *prefix, const struct gprs_ns_ie_ip6_elem *ip6)
+{
+ char ip_addr[INET6_ADDRSTRLEN] = {};
+ if (!inet_ntop(AF_INET6, &ip6->ip_addr, ip_addr, (INET6_ADDRSTRLEN)))
+ strcpy(ip_addr, "Invalid IPv6");
+
+ vty_out(vty, "%s %s:%u, Signalling Weight: %u, Data Weight: %u%s", prefix,
+ ip_addr, ntohs(ip6->udp_port), ip6->sig_weight, ip6->data_weight, VTY_NEWLINE);
+}
+
+/*! Dump the IP-SNS state to a vty.
+ * \param[in] vty VTY to which the state shall be printed
+ * \param[in] prefix prefix to print at start of each line (typically indenting)
+ * \param[in] nse NS Entity whose IP-SNS state shall be printed
+ * \param[in] stats Whether or not statistics shall also be printed */
+void ns2_sns_dump_vty(struct vty *vty, const char *prefix, const struct gprs_ns2_nse *nse, bool stats)
+{
+ struct ns2_sns_state *gss;
+ unsigned int i;
+
+ if (!nse->bss_sns_fi)
+ return;
+
+ vty_out_fsm_inst2(vty, prefix, nse->bss_sns_fi);
+ gss = (struct ns2_sns_state *) nse->bss_sns_fi->priv;
+
+ vty_out(vty, "%sMaximum number of remote NS-VCs: %zu, IPv4 Endpoints: %zu, IPv6 Endpoints: %zu%s",
+ prefix, gss->num_max_nsvcs, gss->num_max_ip4_remote, gss->num_max_ip6_remote, VTY_NEWLINE);
+
+ if (gss->local.num_ip4 && gss->remote.num_ip4) {
+ vty_out(vty, "%sLocal IPv4 Endpoints:%s", prefix, VTY_NEWLINE);
+ for (i = 0; i < gss->local.num_ip4; i++)
+ vty_dump_sns_ip4(vty, prefix, &gss->local.ip4[i]);
+
+ vty_out(vty, "%sRemote IPv4 Endpoints:%s", prefix, VTY_NEWLINE);
+ for (i = 0; i < gss->remote.num_ip4; i++)
+ vty_dump_sns_ip4(vty, prefix, &gss->remote.ip4[i]);
+ }
+
+ if (gss->local.num_ip6 && gss->remote.num_ip6) {
+ vty_out(vty, "%sLocal IPv6 Endpoints:%s", prefix, VTY_NEWLINE);
+ for (i = 0; i < gss->local.num_ip6; i++)
+ vty_dump_sns_ip6(vty, prefix, &gss->local.ip6[i]);
+
+ vty_out(vty, "%sRemote IPv6 Endpoints:%s", prefix, VTY_NEWLINE);
+ for (i = 0; i < gss->remote.num_ip6; i++)
+ vty_dump_sns_ip6(vty, prefix, &gss->remote.ip6[i]);
+ }
+}
+
+/*! write IP-SNS to a vty
+ * \param[in] vty VTY to which the state shall be printed
+ * \param[in] nse NS Entity whose IP-SNS state shall be printed */
+void ns2_sns_write_vty(struct vty *vty, const struct gprs_ns2_nse *nse)
+{
+ struct ns2_sns_state *gss;
+ struct osmo_sockaddr_str addr_str;
+ struct sns_endpoint *endpoint;
+
+ if (!nse->bss_sns_fi)
+ return;
+
+ gss = (struct ns2_sns_state *) nse->bss_sns_fi->priv;
+ llist_for_each_entry(endpoint, &gss->sns_endpoints, list) {
+ /* It's unlikely that an error happens, but let's better be safe. */
+ if (osmo_sockaddr_str_from_sockaddr(&addr_str, &endpoint->saddr.u.sas) != 0)
+ addr_str = (struct osmo_sockaddr_str) { .ip = "<INVALID>" };
+ vty_out(vty, " ip-sns-remote %s %u%s", addr_str.ip, addr_str.port, VTY_NEWLINE);
+ }
+}
+
+static struct sns_endpoint *ns2_get_sns_endpoint(struct ns2_sns_state *state,
+ const struct osmo_sockaddr *saddr)
+{
+ struct sns_endpoint *endpoint;
+
+ llist_for_each_entry(endpoint, &state->sns_endpoints, list) {
+ if (!osmo_sockaddr_cmp(saddr, &endpoint->saddr))
+ return endpoint;
+ }
+
+ return NULL;
+}
+
+/*! gprs_ns2_sns_add_endpoint
+ * \param[in] nse
+ * \param[in] sockaddr
+ * \return
+ */
+int gprs_ns2_sns_add_endpoint(struct gprs_ns2_nse *nse,
+ const struct osmo_sockaddr *saddr)
+{
+ struct ns2_sns_state *gss;
+ struct sns_endpoint *endpoint;
+ bool do_selection = false;
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ return -EINVAL;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+ return -EINVAL;
+ }
+
+ gss = nse->bss_sns_fi->priv;
+
+ if (ns2_get_sns_endpoint(gss, saddr))
+ return -EADDRINUSE;
+
+ endpoint = talloc_zero(nse->bss_sns_fi->priv, struct sns_endpoint);
+ if (!endpoint)
+ return -ENOMEM;
+
+ endpoint->saddr = *saddr;
+ if (llist_empty(&gss->sns_endpoints))
+ do_selection = true;
+
+ llist_add_tail(&endpoint->list, &gss->sns_endpoints);
+ if (do_selection)
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_SELECT_ENDPOINT, NULL);
+
+ return 0;
+}
+
+/*! gprs_ns2_sns_del_endpoint
+ * \param[in] nse
+ * \param[in] sockaddr
+ * \return 0 on success, otherwise < 0
+ */
+int gprs_ns2_sns_del_endpoint(struct gprs_ns2_nse *nse,
+ const struct osmo_sockaddr *saddr)
+{
+ struct ns2_sns_state *gss;
+ struct sns_endpoint *endpoint;
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ return -EINVAL;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+ return -EINVAL;
+ }
+
+ gss = nse->bss_sns_fi->priv;
+ endpoint = ns2_get_sns_endpoint(gss, saddr);
+ if (!endpoint)
+ return -ENOENT;
+
+ /* if this is an unused SNS endpoint it's done */
+ if (gss->initial != endpoint) {
+ llist_del(&endpoint->list);
+ talloc_free(endpoint);
+ return 0;
+ }
+
+ /* gprs_ns2_free_nsvcs() will trigger NS2_SNS_EV_REQ_NO_NSVC on the last NS-VC
+ * and restart SNS SIZE procedure which selects a new initial */
+ LOGNSE(nse, LOGL_INFO, "Current in-use SNS endpoint is being removed."
+ "Closing all NS-VC and restart SNS-SIZE procedure"
+ "with a remaining SNS endpoint.\n");
+
+ /* Continue with the next endpoint in the list.
+ * Special case if the endpoint is at the start or end of the list */
+ if (endpoint->list.prev == &gss->sns_endpoints ||
+ endpoint->list.next == &gss->sns_endpoints)
+ gss->initial = NULL;
+ else
+ gss->initial = llist_entry(endpoint->list.next->prev,
+ struct sns_endpoint,
+ list);
+
+ llist_del(&endpoint->list);
+ gprs_ns2_free_nsvcs(nse);
+ talloc_free(endpoint);
+
+ return 0;
+}
+
+/*! gprs_ns2_sns_count
+ * \param[in] nse NS Entity whose IP-SNS endpoints shall be printed
+ * \return the count of endpoints or < 0 if NSE doesn't contain sns.
+ */
+int gprs_ns2_sns_count(struct gprs_ns2_nse *nse)
+{
+ struct ns2_sns_state *gss;
+ struct sns_endpoint *endpoint;
+ int count = 0;
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ return -EINVAL;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+ return -EINVAL;
+ }
+
+ gss = nse->bss_sns_fi->priv;
+ llist_for_each_entry(endpoint, &gss->sns_endpoints, list)
+ count++;
+
+ return count;
+}
+
+void ns2_sns_notify_alive(struct gprs_ns2_nse *nse, struct gprs_ns2_vc *nsvc, bool alive)
+{
+ struct ns2_sns_state *gss;
+ struct gprs_ns2_vc *tmp;
+
+ if (!nse->bss_sns_fi)
+ return;
+
+ gss = nse->bss_sns_fi->priv;
+ if (nse->bss_sns_fi->state != GPRS_SNS_ST_CONFIGURED && nse->bss_sns_fi->state != GPRS_SNS_ST_LOCAL_PROCEDURE)
+ return;
+
+ if (gss->block_no_nsvc_events)
+ return;
+
+ if (gss->alive && nse->sum_sig_weight == 0) {
+ sns_failed(nse->bss_sns_fi, "No signalling NSVC available");
+ return;
+ }
+
+ /* check if this is the current SNS NS-VC */
+ if (nsvc == gss->sns_nsvc && !alive) {
+ /* only replace the SNS NS-VC if there are other alive NS-VC.
+ * There aren't any other alive NS-VC when the SNS fsm just reached CONFIGURED
+ * and couldn't confirm yet if the NS-VC comes up */
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (nsvc == tmp)
+ continue;
+ if (ns2_vc_is_unblocked(nsvc)) {
+ ns2_sns_replace_nsvc(nsvc);
+ break;
+ }
+ }
+ }
+
+ if (alive == gss->alive)
+ return;
+
+ if (alive) {
+ /* we need at least a signalling NSVC before become alive */
+ if (nse->sum_sig_weight == 0)
+ return;
+ gss->alive = true;
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_NSVC_ALIVE, NULL);
+ } else {
+ /* is there at least another alive nsvc? */
+ llist_for_each_entry(tmp, &nse->nsvc, list) {
+ if (ns2_vc_is_unblocked(tmp))
+ return;
+ }
+
+ /* all NS-VC have failed */
+ gss->alive = false;
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_NO_NSVC, NULL);
+ }
+}
+
+int gprs_ns2_sns_add_bind(struct gprs_ns2_nse *nse,
+ struct gprs_ns2_vc_bind *bind)
+{
+ struct ns2_sns_state *gss;
+ struct ns2_sns_bind *tmp;
+
+ OSMO_ASSERT(nse->bss_sns_fi);
+ gss = nse->bss_sns_fi->priv;
+
+ if (!gprs_ns2_is_ip_bind(bind)) {
+ return -EINVAL;
+ }
+
+ if (!llist_empty(&gss->binds)) {
+ llist_for_each_entry(tmp, &gss->binds, list) {
+ if (tmp->bind == bind)
+ return -EALREADY;
+ }
+ }
+
+ tmp = talloc_zero(gss, struct ns2_sns_bind);
+ if (!tmp)
+ return -ENOMEM;
+ tmp->bind = bind;
+ llist_add_tail(&tmp->list, &gss->binds);
+
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_ADD_BIND, tmp);
+ return 0;
+}
+
+/* Remove a bind from the SNS. All assosiated NSVC must be removed. */
+int gprs_ns2_sns_del_bind(struct gprs_ns2_nse *nse,
+ struct gprs_ns2_vc_bind *bind)
+{
+ struct ns2_sns_state *gss;
+ struct ns2_sns_bind *tmp, *tmp2;
+ bool found = false;
+
+ if (!nse->bss_sns_fi)
+ return -EINVAL;
+
+ gss = nse->bss_sns_fi->priv;
+ if (gss->initial_bind && gss->initial_bind->bind == bind) {
+ if (gss->initial_bind->list.prev == &gss->binds)
+ gss->initial_bind = NULL;
+ else
+ gss->initial_bind = llist_entry(gss->initial_bind->list.prev, struct ns2_sns_bind, list);
+ }
+
+ llist_for_each_entry_safe(tmp, tmp2, &gss->binds, list) {
+ if (tmp->bind == bind) {
+ llist_del(&tmp->list);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return -ENOENT;
+
+ osmo_fsm_inst_dispatch(nse->bss_sns_fi, NS2_SNS_EV_REQ_DELETE_BIND, tmp);
+ return 0;
+}
+
+/* Update SNS weights for a bind (local endpoint).
+ * \param[in] bind the bind which has been updated
+ */
+void ns2_sns_update_weights(struct gprs_ns2_vc_bind *bind)
+{
+ struct ns2_sns_bind *sbind;
+ struct gprs_ns2_nse *nse;
+ struct ns2_sns_state *gss;
+ const struct osmo_sockaddr *addr = gprs_ns2_ip_bind_sockaddr(bind);
+
+ llist_for_each_entry(nse, &bind->nsi->nse, list) {
+ if (!nse->bss_sns_fi)
+ continue;
+
+ gss = nse->bss_sns_fi->priv;
+ if (addr->u.sa.sa_family != gss->family)
+ return;
+
+ llist_for_each_entry(sbind, &gss->binds, list) {
+ if (sbind->bind == bind) {
+ osmo_fsm_inst_dispatch(gss->nse->bss_sns_fi, NS2_SNS_EV_REQ_CHANGE_WEIGHT, sbind);
+ break;
+ }
+ }
+ }
+}
+
+
+
+
+/***********************************************************************
+ * SGSN role
+ ***********************************************************************/
+
+/* cleanup all state. If nsvc is given, don't remove this nsvc. (nsvc is given when a SIZE PDU received) */
+static void ns2_clear_sgsn(struct ns2_sns_state *gss, struct gprs_ns2_vc *size_nsvc)
+{
+ struct gprs_ns2_vc *nsvc, *nsvc2;
+
+ ns2_clear_procedures(gss);
+ ns2_clear_elems(&gss->local);
+ ns2_clear_elems(&gss->remote);
+ gss->block_no_nsvc_events = true;
+ llist_for_each_entry_safe(nsvc, nsvc2, &gss->nse->nsvc, list) {
+ /* Ignore the NSVC over which the SIZE PDU got received */
+ if (size_nsvc && size_nsvc == nsvc)
+ continue;
+
+ gprs_ns2_free_nsvc(nsvc);
+ }
+ gss->block_no_nsvc_events = false;
+}
+
+static void ns2_sns_st_sgsn_unconfigured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+
+ ns2_clear_sgsn(gss, NULL);
+}
+
+static void ns2_sns_st_sgsn_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
+ /* do nothing; Rx SNS-SIZE handled in ns2_sns_st_all_action_sgsn() */
+}
+
+/* We're waiting for inbound SNS-CONFIG from the BSS */
+static void ns2_sns_st_sgsn_wait_config(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns2_inst *nsi = nse->nsi;
+ uint8_t cause;
+ int rc;
+
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
+
+ switch (event) {
+ case NS2_SNS_EV_RX_CONFIG:
+ case NS2_SNS_EV_RX_CONFIG_END:
+ rc = ns_sns_append_remote_eps(fi, data);
+ if (rc < 0) {
+ cause = -rc;
+ ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
+ return;
+ }
+ /* only change state if last CONFIG was received */
+ if (event == NS2_SNS_EV_RX_CONFIG_END) {
+ /* ensure sum of data weight / sig weights is > 0 */
+ if (ip46_weight_sum_data(&gss->remote) == 0 || ip46_weight_sum_sig(&gss->remote) == 0) {
+ cause = NS_CAUSE_INVAL_WEIGH;
+ ns2_tx_sns_config_ack(gss->sns_nsvc, &cause);
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
+ break;
+ }
+ ns2_tx_sns_config_ack(gss->sns_nsvc, NULL);
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
+ } else {
+ /* just send CONFIG-ACK */
+ ns2_tx_sns_config_ack(gss->sns_nsvc, NULL);
+ osmo_timer_schedule(&fi->timer, nse->nsi->timeout[NS_TOUT_TSNS_PROV], 0);
+ }
+ break;
+ }
+}
+
+static void ns2_sns_st_sgsn_wait_config_ack_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
+
+ /* transmit SGSN-oriented SNS-CONFIG */
+ ns2_tx_sns_config(gss->sns_nsvc, true, gss->local.ip4, gss->local.num_ip4,
+ gss->local.ip6, gss->local.num_ip6);
+}
+
+/* We're waiting for SNS-CONFIG-ACK from the BSS (in response to our outbound SNS-CONFIG) */
+static void ns2_sns_st_sgsn_wait_config_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct tlv_parsed *tp = NULL;
+
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
+
+ switch (event) {
+ case NS2_SNS_EV_RX_CONFIG_ACK:
+ tp = data;
+ if (TLVP_VAL_MINLEN(tp, NS_IE_CAUSE, 1)) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx SNS-CONFIG-ACK with cause %s\n",
+ gprs_ns2_cause_str(*TLVP_VAL(tp, NS_IE_CAUSE)));
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, 0, 0);
+ break;
+ }
+ /* we currently only send one SNS-CONFIG with END FLAG */
+ if (true) {
+ create_missing_nsvcs(fi);
+ /* start the test procedure on ALL NSVCs! */
+ gprs_ns2_start_alive_all_nsvcs(nse);
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_CONFIGURED, ns_sns_configured_timeout(fi), 4);
+ }
+ break;
+ }
+}
+
+/* SGSN-side SNS state machine */
+static const struct osmo_fsm_state ns2_sns_sgsn_states[] = {
+ [GPRS_SNS_ST_UNCONFIGURED] = {
+ .in_event_mask = 0, /* handled by all_state_action */
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_SGSN_WAIT_CONFIG),
+ .name = "UNCONFIGURED",
+ .action = ns2_sns_st_sgsn_unconfigured,
+ .onenter = ns2_sns_st_sgsn_unconfigured_onenter,
+ },
+ [GPRS_SNS_ST_SGSN_WAIT_CONFIG] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_CONFIG) |
+ S(NS2_SNS_EV_RX_CONFIG_END),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
+ S(GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK),
+ .name = "SGSN_WAIT_CONFIG",
+ .action = ns2_sns_st_sgsn_wait_config,
+ },
+ [GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_CONFIG_ACK),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
+ S(GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK) |
+ S(GPRS_SNS_ST_CONFIGURED),
+ .name = "SGSN_WAIT_CONFIG_ACK",
+ .action = ns2_sns_st_sgsn_wait_config_ack,
+ .onenter = ns2_sns_st_sgsn_wait_config_ack_onenter,
+ },
+ [GPRS_SNS_ST_CONFIGURED] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_ADD) |
+ S(NS2_SNS_EV_RX_DELETE) |
+ S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_NSVC_ALIVE),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_SGSN_WAIT_CONFIG) |
+ S(GPRS_SNS_ST_LOCAL_PROCEDURE),
+ .name = "CONFIGURED",
+ /* shared with BSS side; once configured there's no difference */
+ .action = ns2_sns_st_configured,
+ .onenter = ns2_sns_st_configured_onenter,
+ },
+ [GPRS_SNS_ST_LOCAL_PROCEDURE] = {
+ .in_event_mask = S(NS2_SNS_EV_RX_ADD) |
+ S(NS2_SNS_EV_RX_DELETE) |
+ S(NS2_SNS_EV_RX_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_RX_ACK) |
+ S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_NSVC_ALIVE),
+ .out_state_mask = S(GPRS_SNS_ST_UNCONFIGURED) |
+ S(GPRS_SNS_ST_CONFIGURED) |
+ S(GPRS_SNS_ST_LOCAL_PROCEDURE),
+ .name = "LOCAL_PROCEDURE",
+ /* shared with BSS side; once configured there's no difference */
+ .action = ns2_sns_st_local_procedure,
+ .onenter = ns2_sns_st_local_procedure_onenter,
+ },
+};
+
+static int ns2_sns_fsm_sgsn_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct gprs_ns2_nse *nse = nse_inst_from_fi(fi);
+ struct gprs_ns2_inst *nsi = nse->nsi;
+
+ gss->N++;
+ switch (fi->T) {
+ case 3:
+ if (gss->N >= nsi->timeout[NS_TOUT_TSNS_CONFIG_RETRIES]) {
+ LOGPFSML(fi, LOGL_ERROR, "NSE %d: SGSN Config retries failed. Giving up.\n", nse->nsei);
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_UNCONFIGURED, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SGSN_WAIT_CONFIG_ACK, nsi->timeout[NS_TOUT_TSNS_PROV], 3);
+ }
+ break;
+ case 4:
+ LOGPFSML(fi, LOGL_ERROR, "NSE %d: Config succeeded but no NS-VC came online.\n", nse->nsei);
+ break;
+ case 5:
+ if (gss->N >= nsi->timeout[NS_TOUT_TSNS_PROCEDURES_RETRIES]) {
+ sns_failed(fi, "SNS Procedure retries failed.");
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_LOCAL_PROCEDURE, nsi->timeout[NS_TOUT_TSNS_PROV],
+ fi->T);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/* allstate-action for SGSN role */
+static void ns2_sns_st_all_action_sgsn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct ns2_sns_state *gss = (struct ns2_sns_state *) fi->priv;
+ struct tlv_parsed *tp = NULL;
+ size_t num_local_eps, num_remote_eps;
+ uint8_t flag;
+ uint8_t cause;
+
+ OSMO_ASSERT(gss->role == GPRS_SNS_ROLE_SGSN);
+
+ switch (event) {
+ case NS2_SNS_EV_RX_SIZE:
+ tp = (struct tlv_parsed *) data;
+ /* check for mandatory / conditional IEs */
+ if (!TLVP_PRES_LEN(tp, NS_IE_RESET_FLAG, 1) ||
+ !TLVP_PRES_LEN(tp, NS_IE_MAX_NR_NSVC, 2)) {
+ cause = NS_CAUSE_MISSING_ESSENT_IE;
+ ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
+ sns_failed(fi, "Rx Size: Missing essential IE");
+ break;
+ }
+ if (!TLVP_PRES_LEN(tp, NS_IE_IPv4_EP_NR, 2) &&
+ !TLVP_PRES_LEN(tp, NS_IE_IPv6_EP_NR, 2)) {
+ cause = NS_CAUSE_MISSING_ESSENT_IE;
+ ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
+ sns_failed(fi, "Rx Size: Missing essential IE");
+ break;
+ }
+ if (TLVP_PRES_LEN(tp, NS_IE_IPv4_EP_NR, 2))
+ gss->num_max_ip4_remote = tlvp_val16be(tp, NS_IE_IPv4_EP_NR);
+ if (TLVP_PRES_LEN(tp, NS_IE_IPv6_EP_NR, 2))
+ gss->num_max_ip6_remote = tlvp_val16be(tp, NS_IE_IPv6_EP_NR);
+ /* decide if we go for IPv4 or IPv6 */
+ if (gss->num_max_ip6_remote && ns2_sns_count_num_local_ep(fi, AF_INET6)) {
+ gss->family = AF_INET6;
+ ns2_sns_compute_local_ep_from_binds(fi);
+ num_local_eps = gss->local.num_ip6;
+ num_remote_eps = gss->num_max_ip6_remote;
+ } else if (gss->num_max_ip4_remote && ns2_sns_count_num_local_ep(fi, AF_INET)) {
+ gss->family = AF_INET;
+ ns2_sns_compute_local_ep_from_binds(fi);
+ num_local_eps = gss->local.num_ip4;
+ num_remote_eps = gss->num_max_ip4_remote;
+ } else {
+ if (gss->local.num_ip4 && !gss->num_max_ip4_remote)
+ cause = NS_CAUSE_INVAL_NR_IPv4_EP;
+ else
+ cause = NS_CAUSE_INVAL_NR_IPv6_EP;
+ ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
+ sns_failed(fi, "Rx Size: Invalid Nr of IPv4/IPv6 EPs");
+ break;
+ }
+ /* ensure number of NS-VCs is sufficient for full mesh */
+ gss->num_max_nsvcs = tlvp_val16be(tp, NS_IE_MAX_NR_NSVC);
+ if (gss->num_max_nsvcs < num_remote_eps * num_local_eps) {
+ LOGPFSML(fi, LOGL_ERROR, "%zu local and %zu remote EPs, requires %zu NS-VC, "
+ "but BSS supports only %zu maximum NS-VCs\n", num_local_eps,
+ num_remote_eps, num_local_eps * num_remote_eps, gss->num_max_nsvcs);
+ cause = NS_CAUSE_INVAL_NR_NS_VC;
+ ns2_tx_sns_size_ack(gss->sns_nsvc, &cause);
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED)
+ sns_failed(fi, NULL);
+ break;
+ }
+ /* perform state reset, if requested */
+ flag = *TLVP_VAL(tp, NS_IE_RESET_FLAG);
+ if (flag & 1) {
+ /* clear all state */
+ /* TODO: ensure gss->sns_nsvc is always the NSVC on which we received the SIZE PDU */
+ gss->N = 0;
+ ns2_clear_sgsn(gss, gss->sns_nsvc);
+ /* keep the NSVC we need for SNS, but unconfigure it */
+ gss->sns_nsvc->sig_weight = 0;
+ gss->sns_nsvc->data_weight = 0;
+ gss->block_no_nsvc_events = true;
+ ns2_vc_force_unconfigured(gss->sns_nsvc);
+ gss->block_no_nsvc_events = false;
+ ns2_sns_compute_local_ep_from_binds(fi);
+ }
+
+ if (fi->state == GPRS_SNS_ST_UNCONFIGURED && !(flag & 1)) {
+ sns_failed(fi, "Rx Size without Reset flag, but NSE is unknown");
+ break;
+ }
+
+ /* send SIZE_ACK */
+ ns2_tx_sns_size_ack(gss->sns_nsvc, NULL);
+ /* only wait for SNS-CONFIG in case of Reset flag */
+ if (flag & 1)
+ osmo_fsm_inst_state_chg(fi, GPRS_SNS_ST_SGSN_WAIT_CONFIG, 0, 0);
+ break;
+ case NS2_SNS_EV_REQ_FREE_NSVCS:
+ sns_failed(fi, "On user request to free all NSVCs");
+ break;
+ default:
+ ns2_sns_st_all_action(fi, event, data);
+ break;
+ }
+}
+
+static struct osmo_fsm gprs_ns2_sns_sgsn_fsm = {
+ .name = "GPRS-NS2-SNS-SGSN",
+ .states = ns2_sns_sgsn_states,
+ .num_states = ARRAY_SIZE(ns2_sns_sgsn_states),
+ .allstate_event_mask = S(NS2_SNS_EV_RX_SIZE) |
+ S(NS2_SNS_EV_REQ_NO_NSVC) |
+ S(NS2_SNS_EV_REQ_FREE_NSVCS) |
+ S(NS2_SNS_EV_REQ_ADD_BIND) |
+ S(NS2_SNS_EV_REQ_CHANGE_WEIGHT) |
+ S(NS2_SNS_EV_REQ_DELETE_BIND),
+ .allstate_action = ns2_sns_st_all_action_sgsn,
+ .cleanup = NULL,
+ .timer_cb = ns2_sns_fsm_sgsn_timer_cb,
+ .event_names = gprs_sns_event_names,
+ .pre_term = NULL,
+ .log_subsys = DLNS,
+};
+
+/*! Allocate an IP-SNS FSM for the SGSN side.
+ * \param[in] nse NS Entity in which the FSM runs
+ * \param[in] id string identifier
+ * \returns FSM instance on success; NULL on error */
+struct osmo_fsm_inst *ns2_sns_sgsn_fsm_alloc(struct gprs_ns2_nse *nse, const char *id)
+{
+ struct osmo_fsm_inst *fi;
+ struct ns2_sns_state *gss;
+
+ fi = osmo_fsm_inst_alloc(&gprs_ns2_sns_sgsn_fsm, nse, NULL, LOGL_DEBUG, id);
+ if (!fi)
+ return fi;
+
+ gss = talloc_zero(fi, struct ns2_sns_state);
+ if (!gss)
+ goto err;
+
+ fi->priv = gss;
+ gss->nse = nse;
+ gss->role = GPRS_SNS_ROLE_SGSN;
+ INIT_LLIST_HEAD(&gss->sns_endpoints);
+ INIT_LLIST_HEAD(&gss->binds);
+ INIT_LLIST_HEAD(&gss->procedures);
+
+ return fi;
+err:
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+ return NULL;
+}
+
+
+
+
+/* initialize osmo_ctx on main tread */
+static __attribute__((constructor)) void on_dso_load_ctx(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&gprs_ns2_sns_bss_fsm) == 0);
+ OSMO_ASSERT(osmo_fsm_register(&gprs_ns2_sns_sgsn_fsm) == 0);
+}
diff --git a/src/gb/gprs_ns2_udp.c b/src/gb/gprs_ns2_udp.c
new file mode 100644
index 00000000..1dcc3030
--- /dev/null
+++ b/src/gb/gprs_ns2_udp.c
@@ -0,0 +1,597 @@
+/*! \file gprs_ns2_udp.c
+ * NS-over-UDP implementation.
+ * GPRS Networks Service (NS) messages on the Gb interface.
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * as well as its successor 3GPP TS 48.016 */
+
+/* (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/gprs/gprs_ns2.h>
+
+#include "common_vty.h"
+#include "gprs_ns2_internal.h"
+
+
+static void free_bind(struct gprs_ns2_vc_bind *bind);
+
+
+struct gprs_ns2_vc_driver vc_driver_ip = {
+ .name = "GB UDP IPv4/IPv6",
+ .free_bind = free_bind,
+};
+
+struct priv_bind {
+ struct osmo_io_fd *iofd;
+ struct osmo_sockaddr addr;
+ int dscp;
+ uint8_t priority;
+};
+
+struct priv_vc {
+ struct osmo_sockaddr remote;
+};
+
+/*! clean up all private driver state. Should be only called by gprs_ns2_free_bind() */
+static void free_bind(struct gprs_ns2_vc_bind *bind)
+{
+ struct priv_bind *priv;
+
+ if (!bind)
+ return;
+
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
+
+ priv = bind->priv;
+
+ osmo_iofd_free(priv->iofd);
+ priv->iofd = NULL;
+ talloc_free(priv);
+}
+
+static void free_vc(struct gprs_ns2_vc *nsvc)
+{
+ if (!nsvc)
+ return;
+
+ if (!nsvc->priv)
+ return;
+
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(nsvc->bind));
+ talloc_free(nsvc->priv);
+ nsvc->priv = NULL;
+}
+
+static void dump_vty(const struct gprs_ns2_vc_bind *bind,
+ struct vty *vty, bool stats)
+{
+ struct priv_bind *priv;
+ struct gprs_ns2_vc *nsvc;
+ struct osmo_sockaddr_str sockstr = {};
+ unsigned long nsvcs = 0;
+
+ if (!bind)
+ return;
+
+ priv = bind->priv;
+ if (osmo_sockaddr_str_from_sockaddr(&sockstr, &priv->addr.u.sas))
+ strcpy(sockstr.ip, "invalid");
+
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ nsvcs++;
+ }
+
+ vty_out(vty, "UDP bind: %s:%d DSCP: %d Priority: %u%s", sockstr.ip, sockstr.port,
+ priv->dscp, priv->priority, VTY_NEWLINE);
+ vty_out(vty, " IP-SNS signalling weight: %u data weight: %u%s",
+ bind->sns_sig_weight, bind->sns_data_weight, VTY_NEWLINE);
+ vty_out(vty, " %lu NS-VC:%s", nsvcs, VTY_NEWLINE);
+
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ ns2_vty_dump_nsvc(vty, nsvc, stats);
+ }
+}
+
+
+/*! Find a NS-VC by its remote socket address.
+ * \param[in] bind in which to search
+ * \param[in] rem_addr remote peer socket address to search
+ * \returns NS-VC matching sockaddr; NULL if none found */
+struct gprs_ns2_vc *gprs_ns2_nsvc_by_sockaddr_bind(struct gprs_ns2_vc_bind *bind,
+ const struct osmo_sockaddr *rem_addr)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct priv_vc *vcpriv;
+
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
+
+ llist_for_each_entry(nsvc, &bind->nsvc, blist) {
+ vcpriv = nsvc->priv;
+ if (vcpriv->remote.u.sa.sa_family != rem_addr->u.sa.sa_family)
+ continue;
+ if (osmo_sockaddr_cmp(&vcpriv->remote, rem_addr))
+ continue;
+
+ return nsvc;
+ }
+
+ return NULL;
+}
+
+static inline int nsip_sendmsg(struct gprs_ns2_vc_bind *bind,
+ struct msgb *msg,
+ const struct osmo_sockaddr *dest)
+{
+ struct priv_bind *priv = bind->priv;
+
+ return osmo_iofd_sendto_msgb(priv->iofd, msg, 0, dest);
+}
+
+/*! send the msg and free it afterwards.
+ * \param nsvc NS-VC on which the message shall be sent
+ * \param msg message to be sent
+ * \return number of bytes transmitted; negative on error */
+static int nsip_vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
+{
+ int rc;
+ struct gprs_ns2_vc_bind *bind = nsvc->bind;
+ struct priv_vc *priv = nsvc->priv;
+
+ rc = nsip_sendmsg(bind, msg, &priv->remote);
+
+ return rc;
+}
+
+static struct priv_vc *ns2_driver_alloc_vc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_vc *nsvc, const struct osmo_sockaddr *remote)
+{
+ struct priv_vc *priv = talloc_zero(bind, struct priv_vc);
+ if (!priv)
+ return NULL;
+
+ nsvc->priv = priv;
+ priv->remote = *remote;
+
+ return priv;
+}
+
+static void handle_nsip_recvfrom(struct osmo_io_fd *iofd, int error, struct msgb *msg,
+ const struct osmo_sockaddr *saddr)
+{
+ int rc = 0;
+ struct gprs_ns2_vc_bind *bind = osmo_iofd_get_data(iofd);
+ struct gprs_ns2_vc *nsvc;
+
+ struct msgb *reject;
+
+ msg->l2h = msgb_data(msg);
+
+ /* check if a vc is available */
+ nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, saddr);
+ if (!nsvc) {
+ /* VC not found */
+ rc = ns2_create_vc(bind, msg, saddr, "newconnection", &reject, &nsvc);
+ switch (rc) {
+ case NS2_CS_FOUND:
+ break;
+ case NS2_CS_ERROR:
+ case NS2_CS_SKIPPED:
+ rc = 0;
+ goto out;
+ case NS2_CS_REJECTED:
+ /* nsip_sendmsg will free reject */
+ rc = nsip_sendmsg(bind, reject, saddr);
+ goto out;
+ case NS2_CS_CREATED:
+ ns2_driver_alloc_vc(bind, nsvc, saddr);
+ /* only start the fsm for non SNS. SNS will take care of its own */
+ if (nsvc->nse->dialect != GPRS_NS2_DIALECT_SNS)
+ ns2_vc_fsm_start(nsvc);
+ break;
+ }
+ }
+
+ ns2_recv_vc(nsvc, msg);
+ return;
+
+out:
+ msgb_free(msg);
+}
+
+static void handle_nsip_sendto(struct osmo_io_fd *iofd, int res,
+ struct msgb *msg,
+ const struct osmo_sockaddr *daddr)
+{
+ struct gprs_ns2_vc_bind *bind = osmo_iofd_get_data(iofd);
+ struct gprs_ns2_vc *nsvc;
+
+ nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, daddr);
+ if (!nsvc)
+ return;
+
+ if (OSMO_LIKELY(res >= 0)) {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT, res);
+ } else {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT_DROP);
+ RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT_DROP, msgb_length(msg));
+ }
+}
+
+/*! Find NS bind for a given socket address
+ * \param[in] nsi NS instance
+ * \param[in] sockaddr socket address to search for
+ * \return
+ */
+struct gprs_ns2_vc_bind *gprs_ns2_ip_bind_by_sockaddr(struct gprs_ns2_inst *nsi,
+ const struct osmo_sockaddr *sockaddr)
+{
+ struct gprs_ns2_vc_bind *bind;
+ const struct osmo_sockaddr *local;
+
+ OSMO_ASSERT(nsi);
+ OSMO_ASSERT(sockaddr);
+
+ llist_for_each_entry(bind, &nsi->binding, list) {
+ if (!gprs_ns2_is_ip_bind(bind))
+ continue;
+
+ local = gprs_ns2_ip_bind_sockaddr(bind);
+ if (!osmo_sockaddr_cmp(sockaddr, local))
+ return bind;
+ }
+
+ return NULL;
+}
+
+/*! Bind to an IPv4/IPv6 address
+ * \param[in] nsi NS Instance in which to create the NSVC
+ * \param[in] local the local address to bind to
+ * \param[in] dscp the DSCP/TOS bits used for transmitted data
+ * \param[out] result pointer to the created bind or if a bind with the name exists return the bind.
+ * \return 0 on success; negative on error. -EALREADY returned in case a bind with the name exists */
+int gprs_ns2_ip_bind(struct gprs_ns2_inst *nsi,
+ const char *name,
+ const struct osmo_sockaddr *local,
+ int dscp,
+ struct gprs_ns2_vc_bind **result)
+{
+ struct gprs_ns2_vc_bind *bind;
+ struct priv_bind *priv;
+ int rc;
+
+ struct osmo_io_ops ioops = {
+ .sendto_cb = &handle_nsip_sendto,
+ .recvfrom_cb = &handle_nsip_recvfrom,
+ };
+
+ if (local->u.sa.sa_family != AF_INET && local->u.sa.sa_family != AF_INET6)
+ return -EINVAL;
+
+ if (dscp < 0 || dscp > 63)
+ return -EINVAL;
+
+ bind = gprs_ns2_ip_bind_by_sockaddr(nsi, local);
+ if (bind) {
+ if (result)
+ *result = bind;
+ return -EBUSY;
+ }
+
+ rc = ns2_bind_alloc(nsi, name, &bind);
+ if (rc < 0)
+ return rc;
+
+ bind->driver = &vc_driver_ip;
+ bind->ll = GPRS_NS2_LL_UDP;
+ /* expect 100 mbit at least.
+ * TODO: ask the network layer about the speed. But would require
+ * notification on change. */
+ bind->transfer_capability = 100;
+ bind->send_vc = nsip_vc_sendmsg;
+ bind->free_vc = free_vc;
+ bind->dump_vty = dump_vty;
+
+ priv = bind->priv = talloc_zero(bind, struct priv_bind);
+ if (!priv) {
+ gprs_ns2_free_bind(bind);
+ return -ENOMEM;
+ }
+
+ priv->addr = *local;
+ priv->dscp = dscp;
+
+ rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP,
+ local, NULL,
+ OSMO_SOCK_F_BIND | OSMO_SOCK_F_DSCP(priv->dscp));
+ if (rc < 0) {
+ gprs_ns2_free_bind(bind);
+ return rc;
+ }
+
+ priv->iofd = osmo_iofd_setup(bind, rc, "NS bind", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &ioops, bind);
+ osmo_iofd_register(priv->iofd, rc);
+ osmo_iofd_set_alloc_info(priv->iofd, 4096, 128);
+ osmo_iofd_set_txqueue_max_length(priv->iofd, nsi->txqueue_max_length);
+
+ /* IPv4: max fragmented payload can be (13 bit) * 8 byte => 65535.
+ * IPv6: max payload can be 65535 (RFC 2460).
+ * UDP header = 8 byte */
+ bind->mtu = 65535 - 8;
+ if (result)
+ *result = bind;
+
+ return 0;
+}
+
+/*! Create new NS-VC to a given remote address
+ * \param[in] bind the bind we want to connect
+ * \param[in] nse NS entity to be used for the new NS-VC
+ * \param[in] remote remote address to connect to
+ * \return pointer to newly-allocated and connected NS-VC; NULL on error */
+struct gprs_ns2_vc *ns2_ip_bind_connect(struct gprs_ns2_vc_bind *bind,
+ struct gprs_ns2_nse *nse,
+ const struct osmo_sockaddr *remote)
+{
+ struct gprs_ns2_vc *nsvc;
+ const struct osmo_sockaddr *local;
+ struct priv_vc *priv;
+ enum gprs_ns2_vc_mode vc_mode;
+ char idbuf[256], tmp[INET6_ADDRSTRLEN + 8];
+
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
+
+ vc_mode = ns2_dialect_to_vc_mode(nse->dialect);
+ if ((int) vc_mode == -1) {
+ LOGNSE(nse, LOGL_ERROR, "Can not derive vc mode from dialect %d. Maybe libosmocore is too old.\n",
+ nse->dialect);
+ return NULL;
+ }
+
+ /* duplicate */
+ if (gprs_ns2_nsvc_by_sockaddr_bind(bind, remote))
+ return NULL;
+
+ local = gprs_ns2_ip_bind_sockaddr(bind);
+ osmo_sockaddr_to_str_buf(tmp, sizeof(tmp), local);
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-NSVC-%s-%s-%s", nse->nsei, gprs_ns2_lltype_str(nse->ll),
+ tmp, osmo_sockaddr_to_str(remote));
+ osmo_identifier_sanitize_buf(idbuf, NULL, '_');
+
+ nsvc = ns2_vc_alloc(bind, nse, true, vc_mode, idbuf);
+ if (!nsvc)
+ return NULL;
+
+ nsvc->priv = talloc_zero(bind, struct priv_vc);
+ if (!nsvc->priv) {
+ gprs_ns2_free_nsvc(nsvc);
+ return NULL;
+ }
+
+ priv = nsvc->priv;
+ priv->remote = *remote;
+
+ return nsvc;
+}
+
+/*! Return the socket address of the local peer of a NS-VC.
+ * \param[in] nsvc NS-VC whose local peer we want to know
+ * \return address of the local peer; NULL in case of error */
+const struct osmo_sockaddr *gprs_ns2_ip_vc_local(const struct gprs_ns2_vc *nsvc)
+{
+ struct priv_bind *priv;
+
+ if (nsvc->bind->driver != &vc_driver_ip)
+ return NULL;
+
+ priv = nsvc->bind->priv;
+ return &priv->addr;
+}
+
+/*! Return the socket address of the remote peer of a NS-VC.
+ * \param[in] nsvc NS-VC whose remote peer we want to know
+ * \return address of the remote peer; NULL in case of error */
+const struct osmo_sockaddr *gprs_ns2_ip_vc_remote(const struct gprs_ns2_vc *nsvc)
+{
+ struct priv_vc *priv;
+
+ if (nsvc->bind->driver != &vc_driver_ip)
+ return NULL;
+
+ priv = nsvc->priv;
+ return &priv->remote;
+}
+
+/*! Compare the NS-VC with the given parameter
+ * \param[in] nsvc NS-VC to compare with
+ * \param[in] local The local address
+ * \param[in] remote The remote address
+ * \param[in] nsvci NS-VCI will only be used if the NS-VC in BLOCKRESET mode otherwise NS-VCI isn't applicable.
+ * \return true if the NS-VC has the same properties as given
+ */
+bool gprs_ns2_ip_vc_equal(const struct gprs_ns2_vc *nsvc,
+ const struct osmo_sockaddr *local,
+ const struct osmo_sockaddr *remote,
+ uint16_t nsvci)
+{
+ struct priv_vc *vpriv;
+ struct priv_bind *bpriv;
+
+ if (nsvc->bind->driver != &vc_driver_ip)
+ return false;
+
+ vpriv = nsvc->priv;
+ bpriv = nsvc->bind->priv;
+
+ if (osmo_sockaddr_cmp(local, &bpriv->addr))
+ return false;
+
+ if (osmo_sockaddr_cmp(remote, &vpriv->remote))
+ return false;
+
+ if (nsvc->mode == GPRS_NS2_VC_MODE_BLOCKRESET)
+ if (nsvc->nsvci != nsvci)
+ return false;
+
+ return true;
+}
+
+/*! Return the locally bound socket address of the bind.
+ * \param[in] bind The bind whose local address we want to know
+ * \return address of the local bind */
+const struct osmo_sockaddr *gprs_ns2_ip_bind_sockaddr(struct gprs_ns2_vc_bind *bind)
+{
+ struct priv_bind *priv;
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
+
+ priv = bind->priv;
+ return &priv->addr;
+}
+
+/*! Is the given bind an IP bind? */
+int gprs_ns2_is_ip_bind(struct gprs_ns2_vc_bind *bind)
+{
+ return (bind->driver == &vc_driver_ip);
+}
+
+/*! Set the DSCP (TOS) bit value of the given bind. */
+int gprs_ns2_ip_bind_set_dscp(struct gprs_ns2_vc_bind *bind, int dscp)
+{
+ struct priv_bind *priv;
+ int rc = 0;
+
+ if (dscp < 0 || dscp > 63)
+ return -EINVAL;
+
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
+ priv = bind->priv;
+
+ if (dscp != priv->dscp) {
+ priv->dscp = dscp;
+
+ rc = osmo_sock_set_dscp(osmo_iofd_get_fd(priv->iofd), dscp);
+ if (rc < 0) {
+ LOGBIND(bind, LOGL_ERROR, "Failed to set the DSCP to %u with ret(%d) errno(%d)\n",
+ dscp, rc, errno);
+ }
+ }
+
+ return rc;
+}
+
+/*! Set the socket priority of the given bind. */
+int gprs_ns2_ip_bind_set_priority(struct gprs_ns2_vc_bind *bind, uint8_t priority)
+{
+ struct priv_bind *priv;
+ int rc = 0;
+
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
+ priv = bind->priv;
+
+ if (priority != priv->priority) {
+ priv->priority = priority;
+
+ rc = osmo_sock_set_priority(osmo_iofd_get_fd(priv->iofd), priority);
+ if (rc < 0) {
+ LOGBIND(bind, LOGL_ERROR, "Failed to set the priority to %u with ret(%d) errno(%d)\n",
+ priority, rc, errno);
+ }
+ }
+
+ return rc;
+}
+
+
+/*! Count UDP binds compatible with remote */
+int ns2_ip_count_bind(struct gprs_ns2_inst *nsi, struct osmo_sockaddr *remote)
+{
+ struct gprs_ns2_vc_bind *bind;
+ const struct osmo_sockaddr *sa;
+ int count = 0;
+
+ llist_for_each_entry(bind, &nsi->binding, list) {
+ if (!gprs_ns2_is_ip_bind(bind))
+ continue;
+
+ sa = gprs_ns2_ip_bind_sockaddr(bind);
+ if (!sa)
+ continue;
+
+ if (sa->u.sa.sa_family == remote->u.sa.sa_family)
+ count++;
+ }
+
+ return count;
+}
+
+/* return the matching bind by index */
+struct gprs_ns2_vc_bind *ns2_ip_get_bind_by_index(struct gprs_ns2_inst *nsi,
+ struct osmo_sockaddr *remote,
+ int index)
+{
+ struct gprs_ns2_vc_bind *bind;
+ const struct osmo_sockaddr *sa;
+ int i = 0;
+
+ llist_for_each_entry(bind, &nsi->binding, list) {
+ if (!gprs_ns2_is_ip_bind(bind))
+ continue;
+
+ sa = gprs_ns2_ip_bind_sockaddr(bind);
+ if (!sa)
+ continue;
+
+ if (sa->u.sa.sa_family == remote->u.sa.sa_family) {
+ if (index == i)
+ return bind;
+ i++;
+ }
+ }
+
+ return NULL;
+}
+
+void ns2_ip_set_txqueue_max_length(struct gprs_ns2_vc_bind *bind, unsigned int max_length)
+{
+ struct priv_bind *priv = bind->priv;
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
+
+ osmo_iofd_set_txqueue_max_length(priv->iofd, max_length);
+}
+
+/*! set the signalling and data weight for this bind
+ * \param[in] bind
+ * \param[in] signalling the signalling weight
+ * \param[in] data the data weight
+ */
+void gprs_ns2_ip_bind_set_sns_weight(struct gprs_ns2_vc_bind *bind, uint8_t signalling, uint8_t data)
+{
+ OSMO_ASSERT(gprs_ns2_is_ip_bind(bind));
+ bind->sns_sig_weight = signalling;
+ bind->sns_data_weight = data;
+ ns2_sns_update_weights(bind);
+}
diff --git a/src/gb/gprs_ns2_vc_fsm.c b/src/gb/gprs_ns2_vc_fsm.c
new file mode 100644
index 00000000..9cd83c4f
--- /dev/null
+++ b/src/gb/gprs_ns2_vc_fsm.c
@@ -0,0 +1,992 @@
+/*! \file gprs_ns2_vc_fsm.c
+ * NS virtual circuit FSM implementation
+ * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
+ * as well as its successor 3GPP TS 48.016 */
+
+/* (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* The BSS NSE only has one SGSN IP address configured, and it will use the SNS procedures
+ * to communicated its local IPs/ports as well as all the SGSN side IPs/ports and
+ * associated weights. In theory, the BSS then uses this to establish a full mesh
+ * of NSVCs between all BSS-side IPs/ports and SGSN-side IPs/ports */
+
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/stat_item.h>
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gprs/gprs_msgb.h>
+#include <osmocom/gprs/protocol/gsm_08_16.h>
+
+#include "gprs_ns2_internal.h"
+
+#define S(x) (1 << (x))
+
+struct gprs_ns2_vc_priv {
+ struct gprs_ns2_vc *nsvc;
+ /* how often the timer was triggered */
+ int N;
+ /* The initiator is responsible to UNBLOCK the VC. The BSS is usually the initiator.
+ * It can change during runtime. The side which blocks an unblocked side.*/
+ bool initiator;
+ bool initiate_block;
+ bool initiate_reset;
+ /* if unitdata is forwarded to the user */
+ bool accept_unitdata;
+
+ /* the alive counter is present in all states */
+ struct {
+ struct osmo_timer_list timer;
+ enum ns2_timeout mode;
+ int N;
+ struct timespec timer_started;
+ } alive;
+};
+
+
+/* The FSM covers both the VC with RESET/BLOCK and without RESET/BLOCK procedure..
+ *
+ * With RESET/BLOCK, the state should follow:
+ * - UNCONFIGURED -> RESET -> BLOCK -> UNBLOCKED
+ *
+ * Without RESET/BLOCK, the state should follow:
+ * - UNCONFIGURED -> RECOVERY -> UNBLOCKED
+ *
+ * The UNBLOCKED and TEST states are used to send ALIVE PDU using the timeout Tns-test and Tns-alive.
+ * UNBLOCKED -> TEST: on expire of Tns-Test, send Alive PDU.
+ * TEST -> UNBLOCKED: on receive of Alive_Ack PDU, go into UNBLOCKED.
+ *
+ * The RECOVERY state is used as intermediate, because a VC is only valid if it received an Alive ACK when
+ * not using RESET/BLOCK procedure.
+ */
+
+enum gprs_ns2_vc_state {
+ GPRS_NS2_ST_UNCONFIGURED,
+ GPRS_NS2_ST_RESET,
+ GPRS_NS2_ST_BLOCKED,
+ GPRS_NS2_ST_UNBLOCKED, /* allows sending NS_UNITDATA */
+
+ GPRS_NS2_ST_RECOVERING, /* only used when not using RESET/BLOCK procedure */
+};
+
+enum gprs_ns2_vc_event {
+ GPRS_NS2_EV_REQ_START,
+
+ /* received messages */
+ GPRS_NS2_EV_RX_RESET,
+ GPRS_NS2_EV_RX_RESET_ACK,
+ GPRS_NS2_EV_RX_UNBLOCK,
+ GPRS_NS2_EV_RX_UNBLOCK_ACK,
+ GPRS_NS2_EV_RX_BLOCK,
+ GPRS_NS2_EV_RX_BLOCK_ACK,
+ GPRS_NS2_EV_RX_ALIVE,
+ GPRS_NS2_EV_RX_ALIVE_ACK,
+ GPRS_NS2_EV_RX_STATUS,
+
+ GPRS_NS2_EV_RX_UNITDATA,
+
+ GPRS_NS2_EV_REQ_FORCE_UNCONFIGURED, /* called via vty for tests */
+ GPRS_NS2_EV_REQ_OM_RESET, /* vty cmd: reset */
+ GPRS_NS2_EV_REQ_OM_BLOCK, /* vty cmd: block */
+ GPRS_NS2_EV_REQ_OM_UNBLOCK, /* vty cmd: unblock*/
+ GPRS_NS2_EV_RX_BLOCK_FOREIGN, /* received a BLOCK over another NSVC */
+};
+
+static const struct value_string ns2_vc_event_names[] = {
+ { GPRS_NS2_EV_REQ_START, "REQ-START" },
+ { GPRS_NS2_EV_RX_RESET, "RX-RESET" },
+ { GPRS_NS2_EV_RX_RESET_ACK, "RX-RESET_ACK" },
+ { GPRS_NS2_EV_RX_UNBLOCK, "RX-UNBLOCK" },
+ { GPRS_NS2_EV_RX_UNBLOCK_ACK, "RX-UNBLOCK_ACK" },
+ { GPRS_NS2_EV_RX_BLOCK, "RX-BLOCK" },
+ { GPRS_NS2_EV_RX_BLOCK_FOREIGN, "RX-BLOCK_FOREIGN" },
+ { GPRS_NS2_EV_RX_BLOCK_ACK, "RX-BLOCK_ACK" },
+ { GPRS_NS2_EV_RX_ALIVE, "RX-ALIVE" },
+ { GPRS_NS2_EV_RX_ALIVE_ACK, "RX-ALIVE_ACK" },
+ { GPRS_NS2_EV_RX_STATUS, "RX-STATUS" },
+ { GPRS_NS2_EV_RX_UNITDATA, "RX-UNITDATA" },
+ { GPRS_NS2_EV_REQ_FORCE_UNCONFIGURED, "REQ-FORCE_UNCONFIGURED" },
+ { GPRS_NS2_EV_REQ_OM_RESET, "REQ-O&M-RESET"},
+ { GPRS_NS2_EV_REQ_OM_BLOCK, "REQ-O&M-BLOCK"},
+ { GPRS_NS2_EV_REQ_OM_UNBLOCK, "REQ-O&M-UNBLOCK"},
+ { 0, NULL }
+};
+
+static inline struct gprs_ns2_inst *ns_inst_from_fi(struct osmo_fsm_inst *fi)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+ return priv->nsvc->nse->nsi;
+}
+
+/* Start the NS-TEST procedure, either with transmitting a tx_alive,
+ * (start_tx_alive==true) or with starting tns-test */
+static void start_test_procedure(struct osmo_fsm_inst *fi, bool start_tx_alive)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+ struct gprs_ns2_inst *nsi = priv->nsvc->nse->nsi;
+ unsigned int tout_idx;
+
+ if (osmo_timer_pending(&priv->alive.timer)) {
+ if (start_tx_alive) {
+ if (priv->alive.mode == NS_TOUT_TNS_ALIVE)
+ return;
+ } else {
+ if (priv->alive.mode == NS_TOUT_TNS_TEST)
+ return;
+ }
+ }
+
+ priv->alive.N = 0;
+
+ if (start_tx_alive) {
+ priv->alive.mode = NS_TOUT_TNS_ALIVE;
+ osmo_clock_gettime(CLOCK_MONOTONIC, &priv->alive.timer_started);
+ ns2_tx_alive(priv->nsvc);
+ tout_idx = NS_TOUT_TNS_ALIVE;
+ } else {
+ priv->alive.mode = NS_TOUT_TNS_TEST;
+ tout_idx = NS_TOUT_TNS_TEST;
+ }
+ LOGPFSML(fi, LOGL_DEBUG, "Starting Tns-%s of %u seconds\n",
+ tout_idx == NS_TOUT_TNS_ALIVE ? "alive" : "test", nsi->timeout[tout_idx]);
+ osmo_timer_schedule(&priv->alive.timer, nsi->timeout[tout_idx], 0);
+}
+
+static void stop_test_procedure(struct gprs_ns2_vc_priv *priv)
+{
+ osmo_stat_item_set(osmo_stat_item_group_get_item(priv->nsvc->statg, NS_STAT_ALIVE_DELAY), 0);
+ osmo_timer_del(&priv->alive.timer);
+}
+
+/* how many milliseconds have expired since the last alive timer start? */
+static int alive_timer_elapsed_ms(struct gprs_ns2_vc_priv *priv)
+{
+ struct timespec now, elapsed;
+
+ if (osmo_clock_gettime(CLOCK_MONOTONIC, &now) != 0)
+ return 0;
+
+ timespecsub(&now, &priv->alive.timer_started, &elapsed);
+ return elapsed.tv_sec * 1000 + (elapsed.tv_nsec / 1000000);
+}
+
+/* we just received a NS-ALIVE-ACK; re-schedule after Tns-test */
+static void recv_test_procedure(struct osmo_fsm_inst *fi)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+ struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
+ struct gprs_ns2_vc *nsvc = priv->nsvc;
+
+ /* ignoring ACKs without sending an ALIVE */
+ if (priv->alive.mode != NS_TOUT_TNS_ALIVE)
+ return;
+
+ priv->alive.mode = NS_TOUT_TNS_TEST;
+ osmo_timer_schedule(&priv->alive.timer, nsi->timeout[NS_TOUT_TNS_TEST], 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(nsvc->statg, NS_STAT_ALIVE_DELAY),
+ alive_timer_elapsed_ms(priv));
+}
+
+
+static void alive_timeout_handler(void *data)
+{
+ struct osmo_fsm_inst *fi = data;
+ struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ switch (priv->alive.mode) {
+ case NS_TOUT_TNS_TEST:
+ priv->alive.mode = NS_TOUT_TNS_ALIVE;
+ priv->alive.N = 0;
+ osmo_clock_gettime(CLOCK_MONOTONIC, &priv->alive.timer_started);
+ ns2_tx_alive(priv->nsvc);
+ osmo_timer_schedule(&priv->alive.timer, nsi->timeout[NS_TOUT_TNS_ALIVE], 0);
+ break;
+ case NS_TOUT_TNS_ALIVE:
+ RATE_CTR_INC_NS(priv->nsvc, NS_CTR_LOST_ALIVE);
+ priv->alive.N++;
+
+ if (priv->alive.N <= nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
+ /* retransmission */
+ ns2_tx_alive(priv->nsvc);
+ osmo_timer_schedule(&priv->alive.timer, nsi->timeout[NS_TOUT_TNS_ALIVE], 0);
+ } else {
+ /* lost connection */
+ if (priv->nsvc->mode == GPRS_NS2_VC_MODE_BLOCKRESET) {
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RECOVERING, nsi->timeout[NS_TOUT_TNS_ALIVE], 0);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void ns2_st_unconfigured_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ stop_test_procedure(fi->priv);
+ ns2_nse_notify_unblocked(priv->nsvc, false);
+}
+
+static void ns2_st_unconfigured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+ struct gprs_ns2_inst *nsi = priv->nsvc->nse->nsi;
+
+ priv->initiate_reset = priv->initiate_block = priv->initiator;
+ priv->nsvc->om_blocked = false;
+
+ switch (event) {
+ case GPRS_NS2_EV_REQ_START:
+ switch (priv->nsvc->mode) {
+ case GPRS_NS2_VC_MODE_ALIVE:
+ if (priv->nsvc->nse->dialect == GPRS_NS2_DIALECT_SNS) {
+ /* In IP-SNS, the NS-VC are assumed initially alive, until the alive
+ * procedure should fail at some future point */
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_UNBLOCKED, 0, 0);
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RECOVERING, nsi->timeout[NS_TOUT_TNS_ALIVE], NS_TOUT_TNS_ALIVE);
+ }
+ break;
+ case GPRS_NS2_VC_MODE_BLOCKRESET:
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], NS_TOUT_TNS_RESET);
+ break;
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+
+static void ns2_st_reset_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ if (old_state != GPRS_NS2_ST_RESET)
+ priv->N = 0;
+
+ priv->accept_unitdata = false;
+ if (priv->initiate_reset)
+ ns2_tx_reset(priv->nsvc, NS_CAUSE_OM_INTERVENTION);
+
+ stop_test_procedure(priv);
+ ns2_nse_notify_unblocked(priv->nsvc, false);
+}
+
+static void ns2_st_reset(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ if (priv->initiate_reset) {
+ switch (event) {
+ case GPRS_NS2_EV_RX_RESET:
+ ns2_tx_reset_ack(priv->nsvc);
+ /* fall-through */
+ case GPRS_NS2_EV_RX_RESET_ACK:
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED,
+ nsi->timeout[NS_TOUT_TNS_BLOCK], NS_TOUT_TNS_BLOCK);
+ break;
+ }
+ } else {
+ /* we are on the receiving end */
+ switch (event) {
+ case GPRS_NS2_EV_RX_RESET:
+ ns2_tx_reset_ack(priv->nsvc);
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED,
+ 0, 0);
+ break;
+ }
+ }
+}
+
+static void ns2_st_blocked_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ if (old_state != GPRS_NS2_ST_BLOCKED) {
+ priv->N = 0;
+ RATE_CTR_INC_NS(priv->nsvc, NS_CTR_BLOCKED);
+ }
+
+ ns2_nse_notify_unblocked(priv->nsvc, false);
+ if (priv->nsvc->om_blocked) {
+ /* we are already blocked after a RESET */
+ if (old_state == GPRS_NS2_ST_RESET) {
+ osmo_timer_del(&fi->timer);
+ } else {
+ ns2_tx_block(priv->nsvc, NS_CAUSE_OM_INTERVENTION, NULL);
+ }
+ } else if (priv->initiate_block) {
+ ns2_tx_unblock(priv->nsvc);
+ }
+
+ start_test_procedure(fi, true);
+}
+
+static void ns2_st_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ if (priv->nsvc->om_blocked) {
+ switch (event) {
+ case GPRS_NS2_EV_RX_BLOCK_ACK:
+ priv->accept_unitdata = false;
+ osmo_timer_del(&fi->timer);
+ break;
+ case GPRS_NS2_EV_RX_BLOCK:
+ ns2_tx_block_ack(priv->nsvc, NULL);
+ /* fall through */
+ case GPRS_NS2_EV_RX_BLOCK_FOREIGN:
+ /* the BLOCK ACK for foreign BLOCK PDUs (rx over another nsvc) will be sent
+ * from the receiving nsvc */
+ priv->accept_unitdata = false;
+ osmo_timer_del(&fi->timer);
+ break;
+ case GPRS_NS2_EV_RX_UNBLOCK:
+ priv->accept_unitdata = false;
+ ns2_tx_block(priv->nsvc, NS_CAUSE_OM_INTERVENTION, NULL);
+ osmo_timer_add(&fi->timer);
+ break;
+ }
+ } else if (priv->initiate_block) {
+ switch (event) {
+ case GPRS_NS2_EV_RX_BLOCK_FOREIGN:
+ /* the block ack will be sent by the rx NSVC */
+ break;
+ case GPRS_NS2_EV_RX_BLOCK:
+ /* TODO: BLOCK is a UNBLOCK_NACK */
+ ns2_tx_block_ack(priv->nsvc, NULL);
+ break;
+ case GPRS_NS2_EV_RX_UNBLOCK:
+ ns2_tx_unblock_ack(priv->nsvc);
+ /* fall through */
+ case GPRS_NS2_EV_RX_UNBLOCK_ACK:
+ priv->accept_unitdata = true;
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_UNBLOCKED,
+ 0, NS_TOUT_TNS_TEST);
+ break;
+ }
+ } else {
+ /* we are on the receiving end. The initiator who sent RESET is responsible to UNBLOCK! */
+ switch (event) {
+ case GPRS_NS2_EV_RX_BLOCK_FOREIGN:
+ /* the block ack will be sent by the rx NSVC */
+ break;
+ case GPRS_NS2_EV_RX_BLOCK:
+ ns2_tx_block_ack(priv->nsvc, NULL);
+ break;
+ case GPRS_NS2_EV_RX_UNBLOCK:
+ ns2_tx_unblock_ack(priv->nsvc);
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_UNBLOCKED,
+ 0, 0);
+ break;
+ }
+ }
+}
+
+static void ns2_st_unblocked_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+ struct gprs_ns2_vc *nsvc = priv->nsvc;
+ struct gprs_ns2_nse *nse = nsvc->nse;
+
+ if (old_state != GPRS_NS2_ST_UNBLOCKED) {
+ RATE_CTR_INC_NS(nsvc, NS_CTR_UNBLOCKED);
+ osmo_clock_gettime(CLOCK_MONOTONIC, &nsvc->ts_alive_change);
+ }
+
+ priv->accept_unitdata = true;
+ ns2_nse_notify_unblocked(nsvc, true);
+ ns2_prim_status_ind(nse, nsvc, 0, GPRS_NS2_AFF_CAUSE_VC_RECOVERY);
+
+ /* the closest interpretation of the spec would start Tns-test here first,
+ * and only send a NS-ALIVE after Tns-test has expired (i.e. setting the
+ * second argument to 'false'. However, being quick in detecting unavailability
+ * of a NS-VC seems like a good idea */
+ start_test_procedure(fi, true);
+}
+
+static void ns2_st_unblocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ switch (event) {
+ case GPRS_NS2_EV_RX_UNBLOCK:
+ ns2_tx_unblock_ack(priv->nsvc);
+ break;
+ case GPRS_NS2_EV_RX_BLOCK:
+ ns2_tx_block_ack(priv->nsvc, NULL);
+ /* fall through */
+ case GPRS_NS2_EV_RX_BLOCK_FOREIGN:
+ /* the BLOCK ACK for foreign BLOCK PDUs (rx over another nsvc) will be sent
+ * from the receiving nsvc */
+ priv->initiate_block = false;
+ priv->accept_unitdata = false;
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED,
+ 0, 2);
+ break;
+ }
+}
+
+static void ns2_st_alive(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case GPRS_NS2_EV_RX_ALIVE_ACK:
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_UNBLOCKED, 0, 0);
+ break;
+ }
+}
+
+static void ns2_st_alive_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+ struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
+
+ priv->alive.mode = NS_TOUT_TNS_TEST;
+ osmo_timer_schedule(&priv->alive.timer, nsi->timeout[NS_TOUT_TNS_TEST], 0);
+
+ if (old_state != GPRS_NS2_ST_RECOVERING)
+ priv->N = 0;
+
+ start_test_procedure(fi, true);
+ ns2_nse_notify_unblocked(priv->nsvc, false);
+}
+
+static const struct osmo_fsm_state ns2_vc_states[] = {
+ [GPRS_NS2_ST_UNCONFIGURED] = {
+ .in_event_mask = S(GPRS_NS2_EV_REQ_START),
+ .out_state_mask = S(GPRS_NS2_ST_RESET) |
+ S(GPRS_NS2_ST_RECOVERING) |
+ S(GPRS_NS2_ST_UNBLOCKED),
+ .name = "UNCONFIGURED",
+ .action = ns2_st_unconfigured,
+ .onenter = ns2_st_unconfigured_onenter,
+ },
+ [GPRS_NS2_ST_RESET] = {
+ .in_event_mask = S(GPRS_NS2_EV_RX_RESET_ACK) | S(GPRS_NS2_EV_RX_RESET),
+ .out_state_mask = S(GPRS_NS2_ST_RESET) |
+ S(GPRS_NS2_ST_BLOCKED) |
+ S(GPRS_NS2_ST_UNCONFIGURED),
+ .name = "RESET",
+ .action = ns2_st_reset,
+ .onenter = ns2_st_reset_onenter,
+ },
+ [GPRS_NS2_ST_BLOCKED] = {
+ .in_event_mask = S(GPRS_NS2_EV_RX_BLOCK) | S(GPRS_NS2_EV_RX_BLOCK_ACK) |
+ S(GPRS_NS2_EV_RX_UNBLOCK) | S(GPRS_NS2_EV_RX_UNBLOCK_ACK) |
+ S(GPRS_NS2_EV_RX_BLOCK_FOREIGN),
+ .out_state_mask = S(GPRS_NS2_ST_RESET) |
+ S(GPRS_NS2_ST_UNBLOCKED) |
+ S(GPRS_NS2_ST_BLOCKED) |
+ S(GPRS_NS2_ST_UNCONFIGURED),
+ .name = "BLOCKED",
+ .action = ns2_st_blocked,
+ .onenter = ns2_st_blocked_onenter,
+ },
+ [GPRS_NS2_ST_UNBLOCKED] = {
+ .in_event_mask = S(GPRS_NS2_EV_RX_BLOCK) | S(GPRS_NS2_EV_RX_UNBLOCK_ACK) |
+ S(GPRS_NS2_EV_RX_UNBLOCK) | S(GPRS_NS2_EV_RX_BLOCK_FOREIGN),
+ .out_state_mask = S(GPRS_NS2_ST_RESET) | S(GPRS_NS2_ST_RECOVERING) |
+ S(GPRS_NS2_ST_BLOCKED) |
+ S(GPRS_NS2_ST_UNCONFIGURED),
+ .name = "UNBLOCKED",
+ .action = ns2_st_unblocked,
+ .onenter = ns2_st_unblocked_on_enter,
+ },
+
+ /* ST_RECOVERING is only used on VC without RESET/BLOCK */
+ [GPRS_NS2_ST_RECOVERING] = {
+ .in_event_mask = S(GPRS_NS2_EV_RX_ALIVE_ACK),
+ .out_state_mask = S(GPRS_NS2_ST_RECOVERING) |
+ S(GPRS_NS2_ST_UNBLOCKED) |
+ S(GPRS_NS2_ST_UNCONFIGURED),
+ .name = "RECOVERING",
+ .action = ns2_st_alive,
+ .onenter = ns2_st_alive_onenter,
+ },
+};
+
+static int ns2_vc_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ switch (fi->state) {
+ case GPRS_NS2_ST_RESET:
+ if (priv->initiate_reset) {
+ RATE_CTR_INC_NS(priv->nsvc, NS_CTR_LOST_RESET);
+ priv->N++;
+ if (priv->N <= nsi->timeout[NS_TOUT_TNS_RESET_RETRIES]) {
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
+ } else {
+ priv->N = 0;
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
+ }
+ }
+ break;
+ case GPRS_NS2_ST_BLOCKED:
+ if (priv->initiate_block) {
+ priv->N++;
+ if (priv->nsvc->om_blocked) {
+ if (priv->N <= nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES]) {
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
+ } else {
+ /* 7.2 stop accepting data when BLOCK PDU not responded */
+ priv->accept_unitdata = false;
+ }
+ } else {
+ if (priv->N <= nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES]) {
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
+ } else {
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
+ }
+ }
+ }
+ break;
+ case GPRS_NS2_ST_RECOVERING:
+ if (priv->initiate_reset) {
+ priv->N++;
+ if (priv->N <= nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) {
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RECOVERING, 0, 0);
+ } else {
+ priv->N = 0;
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RECOVERING, 0, 0);
+ }
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static void ns2_recv_unitdata(struct osmo_fsm_inst *fi,
+ struct msgb *msg)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+ struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct osmo_gprs_ns2_prim nsp = {};
+ uint16_t bvci;
+
+ if (msgb_l2len(msg) < sizeof(*nsh) + 3) {
+ msgb_free(msg);
+ return;
+ }
+
+ /* TODO: 7.1: For an IP sub-network, an NS-UNITDATA PDU
+ * for a PTP BVC may indicate a request to change the IP endpoint
+ * and/or a response to a change in the IP endpoint. */
+
+ /* TODO: nsh->data[0] -> C/R only valid in IP SNS */
+ bvci = nsh->data[1] << 8 | nsh->data[2];
+
+ msg->l3h = &nsh->data[3];
+ nsp.bvci = bvci;
+ nsp.nsei = priv->nsvc->nse->nsei;
+
+ /* 10.3.9 NS SDU Control Bits */
+ if (nsh->data[0] & 0x1)
+ nsp.u.unitdata.change = GPRS_NS2_ENDPOINT_REQUEST_CHANGE;
+
+ osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA,
+ PRIM_OP_INDICATION, msg);
+ nsi->cb(&nsp.oph, nsi->cb_data);
+}
+
+static void ns2_vc_fsm_allstate_action(struct osmo_fsm_inst *fi,
+ uint32_t event,
+ void *data)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+ struct gprs_ns2_inst *nsi = ns_inst_from_fi(fi);
+ struct tlv_parsed *tp;
+ struct msgb *msg = data;
+ uint8_t cause;
+
+ switch (event) {
+ case GPRS_NS2_EV_REQ_OM_RESET:
+ if (priv->nsvc->mode != GPRS_NS2_VC_MODE_BLOCKRESET)
+ break;
+ /* move the FSM into reset */
+ if (fi->state != GPRS_NS2_ST_RESET) {
+ priv->initiate_reset = true;
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], NS_TOUT_TNS_RESET);
+ }
+ break;
+ case GPRS_NS2_EV_RX_RESET:
+ if (priv->nsvc->mode != GPRS_NS2_VC_MODE_BLOCKRESET)
+ break;
+
+ /* move the FSM into reset */
+ if (fi->state != GPRS_NS2_ST_RESET) {
+ priv->initiate_reset = false;
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], NS_TOUT_TNS_RESET);
+ }
+ /* pass the event down into FSM action */
+ ns2_st_reset(fi, event, data);
+ break;
+ case GPRS_NS2_EV_RX_ALIVE:
+ switch (fi->state) {
+ case GPRS_NS2_ST_UNCONFIGURED:
+ case GPRS_NS2_ST_RESET:
+ /* ignore ALIVE */
+ break;
+ default:
+ ns2_tx_alive_ack(priv->nsvc);
+ }
+ break;
+ case GPRS_NS2_EV_RX_ALIVE_ACK:
+ /* for VCs without RESET/BLOCK/UNBLOCK, the connections comes after ALIVE_ACK unblocked */
+ if (fi->state == GPRS_NS2_ST_RECOVERING)
+ ns2_st_alive(fi, event, data);
+ else
+ recv_test_procedure(fi);
+ break;
+ case GPRS_NS2_EV_RX_UNITDATA:
+ /* UNITDATA has to handle the release of msg.
+ * If send upwards (gprs_ns2_recv_unitdata) it must NOT free
+ * the msg, the upper layer has to do it.
+ * Otherwise the msg must be freed.
+ */
+
+ LOG_NS_DATA(priv->nsvc, "Rx", NS_PDUT_UNITDATA, LOGL_INFO, "\n");
+ switch (fi->state) {
+ case GPRS_NS2_ST_BLOCKED:
+ /* 7.2.1: the BLOCKED_ACK might be lost */
+ if (priv->accept_unitdata) {
+ ns2_recv_unitdata(fi, msg);
+ return;
+ }
+
+ ns2_tx_status(priv->nsvc,
+ NS_CAUSE_NSVC_BLOCKED,
+ 0, msg, NULL);
+ break;
+ /* ALIVE can receive UNITDATA if the ALIVE_ACK is lost */
+ case GPRS_NS2_ST_RECOVERING:
+ case GPRS_NS2_ST_UNBLOCKED:
+ ns2_recv_unitdata(fi, msg);
+ return;
+ }
+
+ msgb_free(msg);
+ break;
+ case GPRS_NS2_EV_REQ_FORCE_UNCONFIGURED:
+ if (fi->state != GPRS_NS2_ST_UNCONFIGURED) {
+ /* Force the NSVC back to its initial state */
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_UNCONFIGURED, 0, 0);
+ return;
+ }
+ break;
+ case GPRS_NS2_EV_REQ_OM_BLOCK:
+ /* vty cmd: block */
+ priv->initiate_block = true;
+ priv->nsvc->om_blocked = true;
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
+ break;
+ case GPRS_NS2_EV_REQ_OM_UNBLOCK:
+ /* vty cmd: unblock*/
+ if (!priv->nsvc->om_blocked)
+ return;
+ priv->nsvc->om_blocked = false;
+ if (fi->state == GPRS_NS2_ST_BLOCKED)
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
+ break;
+ case GPRS_NS2_EV_RX_STATUS:
+ tp = data;
+ cause = tlvp_val8(tp, NS_IE_CAUSE, 0);
+ switch (cause) {
+ case NS_CAUSE_NSVC_BLOCKED:
+ if (fi->state != GPRS_NS2_ST_BLOCKED) {
+ LOG_NS_SIGNAL(priv->nsvc, "Rx", NS_PDUT_STATUS, LOGL_ERROR, ": remote side reported blocked state.\n");
+ priv->initiate_block = false;
+ priv->accept_unitdata = false;
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_BLOCKED, nsi->timeout[NS_TOUT_TNS_BLOCK], 0);
+ }
+ break;
+ case NS_CAUSE_NSVC_UNKNOWN:
+ if (fi->state != GPRS_NS2_ST_RESET && fi->state != GPRS_NS2_ST_UNCONFIGURED) {
+ LOG_NS_SIGNAL(priv->nsvc, "Rx", NS_PDUT_STATUS, LOGL_ERROR, ": remote side reported unknown nsvc.\n");
+ osmo_fsm_inst_state_chg(fi, GPRS_NS2_ST_RESET, nsi->timeout[NS_TOUT_TNS_RESET], 0);
+ }
+ break;
+ }
+
+ break;
+ }
+}
+
+static void ns2_vc_fsm_clean(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause)
+{
+ struct gprs_ns2_vc_priv *priv = fi->priv;
+
+ osmo_timer_del(&priv->alive.timer);
+}
+
+static struct osmo_fsm ns2_vc_fsm = {
+ .name = "GPRS-NS2-VC",
+ .states = ns2_vc_states,
+ .num_states = ARRAY_SIZE(ns2_vc_states),
+ .allstate_event_mask = S(GPRS_NS2_EV_RX_UNITDATA) |
+ S(GPRS_NS2_EV_RX_RESET) |
+ S(GPRS_NS2_EV_RX_ALIVE) |
+ S(GPRS_NS2_EV_RX_ALIVE_ACK) |
+ S(GPRS_NS2_EV_RX_STATUS) |
+ S(GPRS_NS2_EV_REQ_FORCE_UNCONFIGURED) |
+ S(GPRS_NS2_EV_REQ_OM_RESET) |
+ S(GPRS_NS2_EV_REQ_OM_BLOCK) |
+ S(GPRS_NS2_EV_REQ_OM_UNBLOCK),
+ .allstate_action = ns2_vc_fsm_allstate_action,
+ .cleanup = ns2_vc_fsm_clean,
+ .timer_cb = ns2_vc_fsm_timer_cb,
+ .event_names = ns2_vc_event_names,
+ .pre_term = NULL,
+ .log_subsys = DLNS,
+};
+
+/*!
+ * \brief gprs_ns2_vc_fsm_alloc
+ * \param ctx
+ * \param vc
+ * \param id a char representation of the virtual curcuit
+ * \param initiator initiator is the site which starts the connection. Usually the BSS.
+ * \return NULL on error, otherwise the fsm
+ */
+struct osmo_fsm_inst *ns2_vc_fsm_alloc(struct gprs_ns2_vc *nsvc,
+ const char *id, bool initiator)
+{
+ struct osmo_fsm_inst *fi;
+ struct gprs_ns2_vc_priv *priv;
+
+ fi = osmo_fsm_inst_alloc(&ns2_vc_fsm, nsvc, NULL, LOGL_DEBUG, id);
+ if (!fi)
+ return fi;
+
+ nsvc->fi = fi;
+ priv = fi->priv = talloc_zero(fi, struct gprs_ns2_vc_priv);
+ priv->nsvc = nsvc;
+ priv->initiator = initiator;
+
+ osmo_timer_setup(&priv->alive.timer, alive_timeout_handler, fi);
+
+ return fi;
+}
+
+/*! Start a NS-VC FSM.
+ * \param nsvc the virtual circuit
+ * \return 0 on success; negative on error */
+int ns2_vc_fsm_start(struct gprs_ns2_vc *nsvc)
+{
+ /* allows to call this function even for started nsvc by gprs_ns2_start_alive_all_nsvcs */
+ if (nsvc->fi->state == GPRS_NS2_ST_UNCONFIGURED)
+ return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_START, NULL);
+ return 0;
+}
+
+/*! Reset a NS-VC FSM.
+ * \param nsvc the virtual circuit
+ * \return 0 on success; negative on error */
+int ns2_vc_force_unconfigured(struct gprs_ns2_vc *nsvc)
+{
+ return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_FORCE_UNCONFIGURED, NULL);
+}
+
+/*! Block a NS-VC.
+ * \param nsvc the virtual circuit
+ * \return 0 on success; negative on error */
+int ns2_vc_block(struct gprs_ns2_vc *nsvc)
+{
+ struct gprs_ns2_vc_priv *priv = nsvc->fi->priv;
+ if (priv->nsvc->om_blocked)
+ return -EALREADY;
+
+ return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_OM_BLOCK, NULL);
+}
+
+/*! Unblock a NS-VC.
+ * \param nsvc the virtual circuit
+ * \return 0 on success; negative on error */
+int ns2_vc_unblock(struct gprs_ns2_vc *nsvc)
+{
+ struct gprs_ns2_vc_priv *priv = nsvc->fi->priv;
+ if (!priv->nsvc->om_blocked)
+ return -EALREADY;
+
+ return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_OM_UNBLOCK, NULL);
+}
+
+/*! Reset a NS-VC.
+ * \param nsvc the virtual circuit
+ * \return 0 on success; negative on error */
+int ns2_vc_reset(struct gprs_ns2_vc *nsvc)
+{
+ return osmo_fsm_inst_dispatch(nsvc->fi, GPRS_NS2_EV_REQ_OM_RESET, NULL);
+}
+
+/*! entry point for messages from the driver/VL
+ * \param nsvc virtual circuit on which the message was received
+ * \param msg message that was received
+ * \param tp parsed TLVs of the received message
+ * \return 0 on success; negative on error */
+int ns2_vc_rx(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp)
+{
+ struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h;
+ struct gprs_ns2_vc *target_nsvc = nsvc;
+ struct osmo_fsm_inst *fi = nsvc->fi;
+ int rc = 0;
+ uint8_t cause;
+ uint16_t nsei, nsvci;
+
+ /* TODO: 7.2: on UNBLOCK/BLOCK: check if NS-VCI is correct,
+ * if not answer STATUS with "NS-VC unknown" */
+ /* TODO: handle BLOCK/UNBLOCK/ALIVE with different VCI */
+
+ if (ns2_validate(nsvc, nsh->pdu_type, msg, tp, &cause)) {
+ /* don't answer on a STATUS with a STATUS */
+ if (nsh->pdu_type != NS_PDUT_STATUS) {
+ rc = ns2_tx_status(nsvc, cause, 0, msg, NULL);
+ goto out;
+ }
+ }
+
+ if (TLVP_PRESENT(tp, NS_IE_NSEI)) {
+ nsei = tlvp_val16be(tp, NS_IE_NSEI);
+ if (nsei != nsvc->nse->nsei) {
+ /* 48.016 § 7.3.1 send, RESET_ACK to wrong NSVCI + ignore */
+ if (nsh->pdu_type == NS_PDUT_RESET)
+ ns2_tx_reset_ack(nsvc);
+
+ LOG_NS_SIGNAL(nsvc, "Rx", nsh->pdu_type, LOGL_ERROR, " with wrong NSEI (exp: %05u, got %05u). Ignoring PDU.\n", nsvc->nse->nsei, nsei);
+ goto out;
+ }
+ }
+
+ if (nsvc->nsvci_is_valid && TLVP_PRESENT(tp, NS_IE_VCI)) {
+ nsvci = tlvp_val16be(tp, NS_IE_VCI);
+ if (nsvci != nsvc->nsvci) {
+ /* 48.016 § 7.3.1 send RESET_ACK to wrong NSVCI + ignore */
+ if (nsh->pdu_type == NS_PDUT_RESET) {
+ ns2_tx_reset_ack(nsvc);
+ LOG_NS_SIGNAL(nsvc, "Rx", nsh->pdu_type, LOGL_ERROR, " with wrong NSVCI (exp: %05u, got %05u). Ignoring PDU.\n", nsvc->nsvci, nsvci);
+ goto out;
+ } else if (nsh->pdu_type == NS_PDUT_BLOCK || nsh->pdu_type == NS_PDUT_STATUS) {
+ /* this is a PDU received over a NSVC and reports a status/block for another NSVC */
+ target_nsvc = gprs_ns2_nsvc_by_nsvci(nsvc->nse->nsi, nsvci);
+ if (!target_nsvc) {
+ LOGPFSML(fi, LOGL_ERROR, "Received a %s PDU for unknown NSVC (NSVCI %d)\n",
+ get_value_string(gprs_ns_pdu_strings, nsh->pdu_type), nsvci);
+ if (nsh->pdu_type == NS_PDUT_BLOCK)
+ ns2_tx_status(nsvc, NS_CAUSE_NSVC_UNKNOWN, 0, msg, &nsvci);
+ goto out;
+ }
+
+ if (target_nsvc->nse != nsvc->nse) {
+ LOGPFSML(fi, LOGL_ERROR, "Received a %s PDU for a NSVC (NSVCI %d) but it belongs to a different NSE!\n",
+ get_value_string(gprs_ns_pdu_strings, nsh->pdu_type), nsvci);
+ goto out;
+ }
+
+ /* the status/block will be passed to the nsvc/target nsvc in the switch */
+ } else {
+ LOG_NS_SIGNAL(nsvc, "Rx", nsh->pdu_type, LOGL_ERROR, " with wrong NSVCI=%05u. Ignoring PDU.\n", nsvci);
+ goto out;
+ }
+ }
+ }
+
+ switch (nsh->pdu_type) {
+ case NS_PDUT_RESET:
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_RESET, tp);
+ break;
+ case NS_PDUT_RESET_ACK:
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_RESET_ACK, tp);
+ break;
+ case NS_PDUT_BLOCK:
+ if (target_nsvc != nsvc) {
+ osmo_fsm_inst_dispatch(target_nsvc->fi, GPRS_NS2_EV_RX_BLOCK_FOREIGN, tp);
+ ns2_tx_block_ack(nsvc, &nsvci);
+ } else {
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_BLOCK, tp);
+ }
+ break;
+ case NS_PDUT_BLOCK_ACK:
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_BLOCK_ACK, tp);
+ break;
+ case NS_PDUT_UNBLOCK:
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_UNBLOCK, tp);
+ break;
+ case NS_PDUT_UNBLOCK_ACK:
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_UNBLOCK_ACK, tp);
+ break;
+ case NS_PDUT_ALIVE:
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_ALIVE, tp);
+ break;
+ case NS_PDUT_ALIVE_ACK:
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_ALIVE_ACK, tp);
+ break;
+ case NS_PDUT_UNITDATA:
+ /* UNITDATA have to free msg because it might send the msg layer upwards */
+ osmo_fsm_inst_dispatch(fi, GPRS_NS2_EV_RX_UNITDATA, msg);
+ return 0;
+ case NS_PDUT_STATUS:
+ osmo_fsm_inst_dispatch(target_nsvc->fi, GPRS_NS2_EV_RX_STATUS, tp);
+ break;
+ default:
+ LOGPFSML(fi, LOGL_ERROR, "NSEI=%u Rx unknown NS PDU type %s\n", nsvc->nse->nsei,
+ get_value_string(gprs_ns_pdu_strings, nsh->pdu_type));
+ rc = -EINVAL;
+ break;
+ }
+
+out:
+ msgb_free(msg);
+
+ return rc;
+}
+
+/*! is the given NS-VC unblocked? */
+int ns2_vc_is_unblocked(struct gprs_ns2_vc *nsvc)
+{
+ return (nsvc->fi->state == GPRS_NS2_ST_UNBLOCKED);
+}
+
+/* initialize osmo_ctx on main tread */
+static __attribute__((constructor)) void on_dso_load_ctx(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&ns2_vc_fsm) == 0);
+}
diff --git a/src/gb/gprs_ns2_vty.c b/src/gb/gprs_ns2_vty.c
new file mode 100644
index 00000000..32de49d4
--- /dev/null
+++ b/src/gb/gprs_ns2_vty.c
@@ -0,0 +1,2351 @@
+/*! \file gprs_ns2_vty.c
+ * VTY interface for our GPRS Networks Service (NS) implementation. */
+
+/* (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ * (C) 2021 by Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <osmocom/core/byteswap.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/sockaddr_str.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/gprs/frame_relay.h>
+#include <osmocom/gprs/gprs_ns2.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/misc.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/vty.h>
+
+#include "gprs_ns2_internal.h"
+
+#define SHOW_NS_STR "Display information about the NS protocol\n"
+#define NSVCI_STR "NS Virtual Connection ID (NS-VCI)\n"
+#define DLCI_STR "Data Link connection identifier\n"
+
+static struct gprs_ns2_inst *vty_nsi = NULL;
+static struct osmo_fr_network *vty_fr_network = NULL;
+static struct llist_head binds;
+static struct llist_head nses;
+static struct llist_head ip_sns_default_binds;
+
+struct vty_bind {
+ struct llist_head list;
+ const char *name;
+ enum gprs_ns2_ll ll;
+ int dscp;
+ uint8_t priority;
+ bool accept_ipaccess;
+ bool accept_sns;
+ uint8_t ip_sns_sig_weight;
+ uint8_t ip_sns_data_weight;
+};
+
+struct vty_nse {
+ struct llist_head list;
+ uint16_t nsei;
+ /* list of binds which are valid for this nse. Only IP-SNS uses this
+ * to allow `no listen ..` in the bind context. So "half" created binds are valid for
+ * IP-SNS. This allows changing the bind ip without modifying all NSEs afterwards */
+ struct llist_head binds;
+};
+
+/* used by IP-SNS to connect multiple vty_nse_bind to a vty_nse */
+struct vty_nse_bind {
+ struct llist_head list;
+ struct vty_bind *vbind;
+};
+
+/* TODO: this should into osmo timer */
+const struct value_string gprs_ns_timer_strs[] = {
+ { NS_TOUT_TNS_BLOCK, TNS_BLOCK_STR },
+ { NS_TOUT_TNS_BLOCK_RETRIES, TNS_BLOCK_RETRIES_STR },
+ { NS_TOUT_TNS_RESET, TNS_RESET_STR },
+ { NS_TOUT_TNS_RESET_RETRIES, TNS_RESET_RETRIES_STR },
+ { NS_TOUT_TNS_TEST, TNS_TEST_STR },
+ { NS_TOUT_TNS_ALIVE, TNS_ALIVE_STR },
+ { NS_TOUT_TNS_ALIVE_RETRIES, TNS_ALIVE_RETRIES_STR },
+ { NS_TOUT_TSNS_PROV, TSNS_PROV_STR },
+ { NS_TOUT_TSNS_SIZE_RETRIES, TSNS_SIZE_RETRIES_STR },
+ { NS_TOUT_TSNS_CONFIG_RETRIES, TSNS_CONFIG_RETRIES_STR },
+ { NS_TOUT_TSNS_PROCEDURES_RETRIES, TSNS_PROCEDURES_RETRIES_STR },
+ { 0, NULL }
+};
+
+const struct value_string vty_fr_role_names[] = {
+ { FR_ROLE_USER_EQUIPMENT, "fr" },
+ { FR_ROLE_NETWORK_EQUIPMENT, "frnet" },
+ { 0, NULL }
+};
+
+const struct value_string vty_ll_names[] = {
+ { GPRS_NS2_LL_FR, "fr" },
+ { GPRS_NS2_LL_FR_GRE, "frgre" },
+ { GPRS_NS2_LL_UDP, "udp" },
+ { 0, NULL }
+};
+
+static struct vty_bind *vty_bind_by_name(const char *name)
+{
+ struct vty_bind *vbind;
+ llist_for_each_entry(vbind, &binds, list) {
+ if (!strcmp(vbind->name, name))
+ return vbind;
+ }
+ return NULL;
+}
+
+static struct vty_bind *vty_bind_alloc(const char *name)
+{
+ struct vty_bind *vbind = talloc_zero(vty_nsi, struct vty_bind);
+ if (!vbind)
+ return NULL;
+
+ vbind->name = talloc_strdup(vty_nsi, name);
+ if (!vbind->name) {
+ talloc_free(vbind);
+ return NULL;
+ }
+
+ vbind->ip_sns_sig_weight = 1;
+ vbind->ip_sns_data_weight = 1;
+ llist_add_tail(&vbind->list, &binds);
+ return vbind;
+}
+
+static void vty_bind_free(struct vty_bind *vbind)
+{
+ if (!vbind)
+ return;
+
+ llist_del(&vbind->list);
+ talloc_free(vbind);
+}
+
+static struct vty_nse *vty_nse_by_nsei(uint16_t nsei)
+{
+ struct vty_nse *vnse;
+ llist_for_each_entry(vnse, &nses, list) {
+ if (vnse->nsei == nsei)
+ return vnse;
+ }
+ return NULL;
+}
+
+static struct vty_nse *vty_nse_alloc(uint16_t nsei)
+{
+ struct vty_nse *vnse = talloc_zero(vty_nsi, struct vty_nse);
+ if (!vnse)
+ return NULL;
+
+ vnse->nsei = nsei;
+ INIT_LLIST_HEAD(&vnse->binds);
+ llist_add_tail(&vnse->list, &nses);
+ return vnse;
+}
+
+static void vty_nse_free(struct vty_nse *vnse)
+{
+ if (!vnse)
+ return;
+
+ llist_del(&vnse->list);
+ /* all vbind of the nse will be freed by talloc */
+ talloc_free(vnse);
+}
+
+static int vty_nse_add_vbind(struct vty_nse *vnse, struct vty_bind *vbind)
+{
+ struct vty_nse_bind *vnse_bind;
+
+ if (vbind->ll != GPRS_NS2_LL_UDP)
+ return -EINVAL;
+
+ llist_for_each_entry(vnse_bind, &vnse->binds, list) {
+ if (vnse_bind->vbind == vbind)
+ return -EALREADY;
+ }
+
+ vnse_bind = talloc(vnse, struct vty_nse_bind);
+ if (!vnse_bind)
+ return -ENOMEM;
+ vnse_bind->vbind = vbind;
+
+ llist_add_tail(&vnse_bind->list, &vnse->binds);
+ return 0;
+}
+
+static int vty_nse_remove_vbind(struct vty_nse *vnse, struct vty_bind *vbind)
+{
+ struct vty_nse_bind *vnse_bind, *tmp;
+ if (vbind->ll != GPRS_NS2_LL_UDP)
+ return -EINVAL;
+
+ llist_for_each_entry_safe(vnse_bind, tmp, &vnse->binds, list) {
+ if (vnse_bind->vbind == vbind) {
+ llist_del(&vnse_bind->list);
+ talloc_free(vnse_bind);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+/* check if the NSE still has SNS configuration */
+static bool vty_nse_check_sns(struct gprs_ns2_nse *nse) {
+ struct vty_nse *vnse = vty_nse_by_nsei(nse->nsei);
+
+ int count = gprs_ns2_sns_count(nse);
+ if (count > 0) {
+ /* there are other sns endpoints */
+ return true;
+ }
+
+ if (!vnse)
+ return false;
+
+ if (llist_empty(&vnse->binds))
+ return false;
+
+ return true;
+}
+
+static struct cmd_node ns_node = {
+ L_NS_NODE,
+ "%s(config-ns)# ",
+ 1,
+};
+
+DEFUN(cfg_ns, cfg_ns_cmd,
+ "ns",
+ "Configure the GPRS Network Service")
+{
+ vty->node = L_NS_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_timer, cfg_ns_timer_cmd,
+ "timer " NS_TIMERS " <0-65535>",
+ "Network Service Timer\n"
+ NS_TIMERS_HELP "Timer Value\n")
+{
+ int idx = get_string_value(gprs_ns_timer_strs, argv[0]);
+ int val = atoi(argv[1]);
+
+ if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout))
+ return CMD_WARNING;
+
+ vty_nsi->timeout[idx] = val;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_nsei, cfg_ns_nsei_cmd,
+ "nse <0-65535> [ip-sns-role-sgsn]",
+ "Persistent NS Entity\n"
+ "NS Entity ID (NSEI)\n"
+ "Create NSE in SGSN role (default: BSS)\n"
+ )
+{
+ struct gprs_ns2_nse *nse;
+ struct vty_nse *vnse;
+ uint16_t nsei = atoi(argv[0]);
+ bool sgsn_role = false;
+ bool free_vnse = false;
+ if (argc > 1 && !strcmp(argv[1], "ip-sns-role-sgsn"))
+ sgsn_role = true;
+
+ vnse = vty_nse_by_nsei(nsei);
+ if (!vnse) {
+ vnse = vty_nse_alloc(nsei);
+ if (!vnse) {
+ vty_out(vty, "Failed to create vty NSE!%s", VTY_NEWLINE);
+ return CMD_ERR_INCOMPLETE;
+ }
+ free_vnse = true;
+ }
+
+ nse = gprs_ns2_nse_by_nsei(vty_nsi, nsei);
+ if (!nse) {
+ nse = gprs_ns2_create_nse2(vty_nsi, nsei, GPRS_NS2_LL_UNDEF, GPRS_NS2_DIALECT_UNDEF,
+ sgsn_role);
+ if (!nse) {
+ vty_out(vty, "Failed to create NSE!%s", VTY_NEWLINE);
+ goto err;
+ }
+ nse->persistent = true;
+ }
+
+ if (!nse->persistent) {
+ /* TODO: should the dynamic NSE removed? */
+ vty_out(vty, "A dynamic NSE with the specified NSEI already exists%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ vty->node = L_NS_NSE_NODE;
+ vty->index = nse;
+
+ return CMD_SUCCESS;
+
+err:
+ if (free_vnse)
+ talloc_free(vnse);
+
+ return CMD_ERR_INCOMPLETE;
+}
+
+DEFUN(cfg_no_ns_nsei, cfg_no_ns_nsei_cmd,
+ "no nse <0-65535>",
+ NO_STR
+ "Delete a Persistent NS Entity\n"
+ "NS Entity ID (NSEI)\n"
+ )
+{
+ struct gprs_ns2_nse *nse;
+ struct vty_nse *vnse;
+ uint16_t nsei = atoi(argv[0]);
+
+ nse = gprs_ns2_nse_by_nsei(vty_nsi, nsei);
+ if (!nse) {
+ vty_out(vty, "Can not find NS Entity %s%s", argv[0], VTY_NEWLINE);
+ return CMD_ERR_NOTHING_TODO;
+ }
+
+ if (!nse->persistent) {
+ vty_out(vty, "Ignoring non-persistent NS Entity%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "Deleting NS Entity %u%s", nse->nsei, VTY_NEWLINE);
+ gprs_ns2_free_nse(nse);
+
+ vnse = vty_nse_by_nsei(nsei);
+ vty_nse_free(vnse);
+
+ return CMD_SUCCESS;
+}
+
+/* TODO: add fr/gre */
+DEFUN(cfg_ns_bind, cfg_ns_bind_cmd,
+ "bind (fr|udp) ID",
+ "Configure local Bind\n"
+ "Frame Relay\n" "UDP/IP\n"
+ "Unique identifier for this bind (to reference from NS-VCs, NSEs, ...)\n"
+ )
+{
+ const char *nstype = argv[0];
+ const char *name = argv[1];
+ struct vty_bind *vbind;
+ enum gprs_ns2_ll ll;
+ int rc;
+
+ rc = get_string_value(vty_ll_names, nstype);
+ if (rc < 0)
+ return CMD_WARNING;
+ ll = (enum gprs_ns2_ll) rc;
+
+ if (!osmo_identifier_valid(name)) {
+ vty_out(vty, "Invalid ID. The ID should be only alphanumeric.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind = vty_bind_by_name(name);
+ if (vbind) {
+ if (vbind->ll != ll) {
+ vty_out(vty, "A bind with the specified ID already exists with a different type (fr|frgre|udp)!%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ } else {
+ vbind = vty_bind_alloc(name);
+ if (!vbind) {
+ vty_out(vty, "Can not create bind - out of memory%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ vbind->ll = ll;
+ }
+
+ vty->index = vbind;
+ vty->node = L_NS_BIND_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ns_bind, cfg_no_ns_bind_cmd,
+ "no bind ID",
+ NO_STR
+ "Delete a bind\n"
+ "Unique identifier for this bind\n"
+ )
+{
+ struct vty_bind *vbind;
+ struct gprs_ns2_vc_bind *bind;
+ const char *name = argv[0];
+
+ vbind = vty_bind_by_name(name);
+ if (!vbind) {
+ vty_out(vty, "bind %s does not exist!%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ vty_bind_free(vbind);
+ bind = gprs_ns2_bind_by_name(vty_nsi, name);
+ if (bind)
+ gprs_ns2_free_bind(bind);
+ return CMD_SUCCESS;
+}
+
+
+static void config_write_vbind(struct vty *vty, struct vty_bind *vbind)
+{
+ struct gprs_ns2_vc_bind *bind;
+ const struct osmo_sockaddr *addr;
+ struct osmo_sockaddr_str addr_str;
+ const char *netif, *frrole_str, *llstr;
+ enum osmo_fr_role frrole;
+
+ llstr = get_value_string_or_null(vty_ll_names, vbind->ll);
+ if (!llstr)
+ return;
+ vty_out(vty, " bind %s %s%s", llstr, vbind->name, VTY_NEWLINE);
+
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ switch (vbind->ll) {
+ case GPRS_NS2_LL_FR:
+ if (bind) {
+ netif = gprs_ns2_fr_bind_netif(bind);
+ if (!netif)
+ return;
+ frrole = gprs_ns2_fr_bind_role(bind);
+ if ((int) frrole == -1)
+ return;
+ frrole_str = get_value_string_or_null(vty_fr_role_names, frrole);
+ if (netif && frrole_str)
+ vty_out(vty, " fr %s %s%s", netif, frrole_str, VTY_NEWLINE);
+ }
+ break;
+ case GPRS_NS2_LL_UDP:
+ if (bind) {
+ addr = gprs_ns2_ip_bind_sockaddr(bind);
+ if (!osmo_sockaddr_str_from_sockaddr(&addr_str, &addr->u.sas)) {
+ vty_out(vty, " listen %s %u%s", addr_str.ip, addr_str.port,
+ VTY_NEWLINE);
+ }
+ }
+ if (vbind->accept_ipaccess)
+ vty_out(vty, " accept-ipaccess%s", VTY_NEWLINE);
+ if (vbind->accept_sns)
+ vty_out(vty, " accept-dynamic-ip-sns%s", VTY_NEWLINE);
+ if (vbind->dscp)
+ vty_out(vty, " dscp %u%s", vbind->dscp, VTY_NEWLINE);
+ if (vbind->priority)
+ vty_out(vty, " socket-priority %u%s", vbind->priority, VTY_NEWLINE);
+ vty_out(vty, " ip-sns signalling-weight %u data-weight %u%s",
+ vbind->ip_sns_sig_weight, vbind->ip_sns_data_weight, VTY_NEWLINE);
+ break;
+ default:
+ return;
+ }
+}
+
+static void config_write_nsvc(struct vty *vty, const struct gprs_ns2_vc *nsvc)
+{
+ const char *netif;
+ uint16_t dlci;
+ const struct osmo_sockaddr *addr;
+ struct osmo_sockaddr_str addr_str;
+
+ switch (nsvc->nse->ll) {
+ case GPRS_NS2_LL_UNDEF:
+ break;
+ case GPRS_NS2_LL_UDP:
+ switch (nsvc->nse->dialect) {
+ case GPRS_NS2_DIALECT_IPACCESS:
+ addr = gprs_ns2_ip_vc_remote(nsvc);
+ if (!addr)
+ break;
+ if (osmo_sockaddr_str_from_sockaddr(&addr_str, &addr->u.sas))
+ break;
+ vty_out(vty, " nsvc ipa %s %s %u nsvci %u%s",
+ nsvc->bind->name, addr_str.ip, addr_str.port,
+ nsvc->nsvci, VTY_NEWLINE);
+ break;
+ case GPRS_NS2_DIALECT_STATIC_ALIVE:
+ addr = gprs_ns2_ip_vc_remote(nsvc);
+ if (!addr)
+ break;
+ if (osmo_sockaddr_str_from_sockaddr(&addr_str, &addr->u.sas))
+ break;
+ vty_out(vty, " nsvc udp %s %s %u%s",
+ nsvc->bind->name, addr_str.ip, addr_str.port, VTY_NEWLINE);
+ break;
+ default:
+ break;
+ }
+ break;
+ case GPRS_NS2_LL_FR:
+ netif = gprs_ns2_fr_bind_netif(nsvc->bind);
+ if (!netif)
+ break;
+ dlci = gprs_ns2_fr_nsvc_dlci(nsvc);
+ if (!dlci)
+ break;
+ OSMO_ASSERT(nsvc->nsvci_is_valid);
+ vty_out(vty, " nsvc fr %s dlci %u nsvci %u%s",
+ netif, dlci, nsvc->nsvci, VTY_NEWLINE);
+ break;
+ case GPRS_NS2_LL_FR_GRE:
+ break;
+ }
+}
+
+static void _config_write_ns_nse(struct vty *vty, struct gprs_ns2_nse *nse)
+{
+ struct gprs_ns2_vc *nsvc;
+ struct vty_nse *vnse = vty_nse_by_nsei(nse->nsei);
+ struct vty_nse_bind *vbind;
+
+ OSMO_ASSERT(vnse);
+
+ vty_out(vty, " nse %u%s%s", nse->nsei,
+ nse->ip_sns_role_sgsn ? " ip-sns-role-sgsn" : "", VTY_NEWLINE);
+ switch (nse->dialect) {
+ case GPRS_NS2_DIALECT_SNS:
+ ns2_sns_write_vty(vty, nse);
+ llist_for_each_entry(vbind, &vnse->binds, list) {
+ vty_out(vty, " ip-sns-bind %s%s", vbind->vbind->name, VTY_NEWLINE);
+ }
+ break;
+ default:
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ config_write_nsvc(vty, nsvc);
+ }
+ break;
+ }
+}
+
+static int config_write_ns_nse(struct vty *vty)
+{
+ struct gprs_ns2_nse *nse;
+
+ llist_for_each_entry(nse, &vty_nsi->nse, list) {
+ if (!nse->persistent)
+ continue;
+
+ _config_write_ns_nse(vty, nse);
+ }
+
+ return 0;
+}
+
+static int config_write_ns_bind(struct vty *vty)
+{
+ struct vty_bind *vbind;
+
+ llist_for_each_entry(vbind, &binds, list) {
+ config_write_vbind(vty, vbind);
+ }
+
+ return 0;
+}
+
+static int config_write_ns(struct vty *vty)
+{
+ struct vty_nse_bind *vbind;
+ unsigned int i;
+ int ret;
+
+ vty_out(vty, "ns%s", VTY_NEWLINE);
+
+ for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++)
+ vty_out(vty, " timer %s %u%s",
+ get_value_string(gprs_ns_timer_strs, i),
+ vty_nsi->timeout[i], VTY_NEWLINE);
+
+ if (vty_nsi->txqueue_max_length != NS_DEFAULT_TXQUEUE_MAX_LENGTH)
+ vty_out(vty, " txqueue-max-length %u%s", vty_nsi->txqueue_max_length, VTY_NEWLINE);
+
+ ret = config_write_ns_bind(vty);
+ if (ret)
+ return ret;
+
+ llist_for_each_entry(vbind, &ip_sns_default_binds, list) {
+ vty_out(vty, " ip-sns-default bind %s%s", vbind->vbind->name, VTY_NEWLINE);
+ }
+
+ ret = config_write_ns_nse(vty);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+
+static struct cmd_node ns_bind_node = {
+ L_NS_BIND_NODE,
+ "%s(config-ns-bind)# ",
+ 1,
+};
+
+DEFUN(cfg_ns_bind_listen, cfg_ns_bind_listen_cmd,
+ "listen " VTY_IPV46_CMD " <1-65535>",
+ "Configure local IP + Port of this bind\n"
+ "Local IPv4 Address\n" "Local IPv6 Address\n"
+ "Local UDP Port\n"
+ )
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+ int rc;
+ const char *addr_str = argv[0];
+ unsigned int port = atoi(argv[1]);
+ struct osmo_sockaddr_str sockaddr_str;
+ struct osmo_sockaddr sockaddr;
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "listen can be only used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (osmo_sockaddr_str_from_str(&sockaddr_str, addr_str, port)) {
+ vty_out(vty, "Can not parse the Address %s %s%s", argv[0], argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ osmo_sockaddr_str_to_sockaddr(&sockaddr_str, &sockaddr.u.sas);
+ if (gprs_ns2_ip_bind_by_sockaddr(vty_nsi, &sockaddr)) {
+ vty_out(vty, "A bind with the specified address already exists!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ rc = gprs_ns2_ip_bind(vty_nsi, vbind->name, &sockaddr, vbind->dscp, &bind);
+ if (rc != 0) {
+ vty_out(vty, "Failed to create the bind (rc %d)!%s", rc, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bind->accept_ipaccess = vbind->accept_ipaccess;
+ bind->accept_sns = vbind->accept_sns;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ns_bind_listen, cfg_no_ns_bind_listen_cmd,
+ "no listen",
+ NO_STR
+ "Delete a IP/Port assignment\n"
+ )
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "no listen can be only used with UDP bind%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ if (!bind)
+ return CMD_ERR_NOTHING_TODO;
+
+ OSMO_ASSERT(bind->ll == GPRS_NS2_LL_UDP);
+ gprs_ns2_free_bind(bind);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_bind_dscp, cfg_ns_bind_dscp_cmd,
+ "dscp <0-63>",
+ "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+ uint16_t dscp = atoi(argv[0]);
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "dscp can be only used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind->dscp = dscp;
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ if (bind)
+ gprs_ns2_ip_bind_set_dscp(bind, dscp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ns_bind_dscp, cfg_no_ns_bind_dscp_cmd,
+ "no dscp",
+ "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n")
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+ uint16_t dscp = 0;
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "dscp can be only used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind->dscp = dscp;
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ if (bind)
+ gprs_ns2_ip_bind_set_dscp(bind, dscp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_bind_priority, cfg_ns_bind_priority_cmd,
+ "socket-priority <0-255>",
+ "Set socket priority on the UDP socket\n" "Priority Value (>6 requires CAP_NET_ADMIN)\n")
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+ uint8_t prio = atoi(argv[0]);
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "dscp can be only used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind->priority = prio;
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ if (bind)
+ gprs_ns2_ip_bind_set_priority(bind, prio);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_bind_ipaccess, cfg_ns_bind_ipaccess_cmd,
+ "accept-ipaccess",
+ "Allow to create dynamic NS Entity by NS Reset PDU on UDP (ip.access style)\n"
+ )
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "accept-ipaccess can be only used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind->accept_ipaccess = true;
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ if (bind)
+ bind->accept_ipaccess = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ns_bind_ipaccess, cfg_no_ns_bind_ipaccess_cmd,
+ "no accept-ipaccess",
+ NO_STR
+ "Reject NS Reset PDU on UDP (ip.access style)\n"
+ )
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "no accept-ipaccess can be only used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind->accept_ipaccess = false;
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ if (bind)
+ bind->accept_ipaccess = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_bind_accept_sns, cfg_ns_bind_accept_sns_cmd,
+ "accept-dynamic-ip-sns",
+ "Allow to create dynamic NS Entities by IP-SNS PDUs\n"
+ )
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "accept-dynamic-ip-sns can be only used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind->accept_sns = true;
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ if (bind)
+ bind->accept_sns = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ns_bind_accept_sns, cfg_no_ns_bind_accept_sns_cmd,
+ "no accept-dynamic-ip-sns",
+ NO_STR
+ "Disable dynamic creation of NS Entities by IP-SNS PDUs\n"
+ )
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "no accept-dynamic-ip-sns can be only used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind->accept_sns = false;
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ if (bind)
+ bind->accept_sns = false;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_bind_ip_sns_weight, cfg_ns_bind_ip_sns_weight_cmd,
+ "ip-sns signalling-weight <0-254> data-weight <0-254>",
+ "IP SNS\n"
+ "signalling weight used by IP-SNS dynamic configuration\n"
+ "signalling weight used by IP-SNS dynamic configuration\n"
+ "data weight used by IP-SNS dynamic configuration\n"
+ "data weight used by IP-SNS dynamic configuration\n")
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+
+ int signalling = atoi(argv[0]);
+ int data = atoi(argv[1]);
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "ip-sns signalling-weight <0-254> data-weight <0-254> can be only used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind->ip_sns_data_weight = data;
+ vbind->ip_sns_sig_weight = signalling;
+ bind = gprs_ns2_bind_by_name(vty_nsi, vbind->name);
+ if (bind)
+ gprs_ns2_ip_bind_set_sns_weight(bind, signalling, data);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_bind_fr, cfg_ns_bind_fr_cmd,
+ "fr NETIF (fr|frnet)",
+ "frame relay\n"
+ IFNAME_STR
+ "fr (user) is used by BSS or SGSN attached to UNI of a FR network\n"
+ "frnet (network) is used by SGSN if BSS is directly attached\n"
+ )
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+ const char *netif = argv[0];
+ const char *role = argv[1];
+
+ int rc = 0;
+ enum osmo_fr_role frrole;
+
+ if (vbind->ll != GPRS_NS2_LL_FR) {
+ vty_out(vty, "fr can be only used with frame relay bind%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(role, "fr"))
+ frrole = FR_ROLE_USER_EQUIPMENT;
+ else if (!strcmp(role, "frnet"))
+ frrole = FR_ROLE_NETWORK_EQUIPMENT;
+ else
+ return CMD_WARNING;
+
+ bind = gprs_ns2_fr_bind_by_netif(vty_nsi, netif);
+ if (bind) {
+ vty_out(vty, "Interface %s already used.%s", netif, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ rc = gprs_ns2_fr_bind(vty_nsi, vbind->name, netif, vty_fr_network, frrole, &bind);
+ if (rc < 0) {
+ LOGP(DLNS, LOGL_ERROR, "Failed to bind interface %s on fr. Err: %d\n", netif, rc);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ns_bind_fr, cfg_no_ns_bind_fr_cmd,
+ "no fr NETIF",
+ NO_STR
+ "Delete a frame relay link\n"
+ "Delete a frame relay link\n"
+ IFNAME_STR
+ )
+{
+ struct vty_bind *vbind = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+ const char *netif = argv[0];
+
+ if (vbind->ll != GPRS_NS2_LL_FR) {
+ vty_out(vty, "fr can be only used with frame relay bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bind = gprs_ns2_fr_bind_by_netif(vty_nsi, netif);
+ if (!bind) {
+ vty_out(vty, "Interface not found.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (strcmp(bind->name, vbind->name)) {
+ vty_out(vty, "The specified interface is not bound to this bind.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gprs_ns2_free_bind(bind);
+ return CMD_SUCCESS;
+}
+
+
+static struct cmd_node ns_nse_node = {
+ L_NS_NSE_NODE,
+ "%s(config-ns-nse)# ",
+ 1,
+};
+
+DEFUN(cfg_ns_nse_nsvc_fr, cfg_ns_nse_nsvc_fr_cmd,
+ "nsvc fr NETIF dlci <16-1007> nsvci <0-65535>",
+ "NS Virtual Connection\n"
+ "frame relay\n"
+ "frame relay interface. Must be registered via fr vty\n"
+ NSVCI_STR
+ NSVCI_STR
+ DLCI_STR
+ DLCI_STR
+ )
+{
+ struct gprs_ns2_vc_bind *bind;
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse = vty->index;
+ const char *netif = argv[0];
+ uint16_t dlci = atoi(argv[1]);
+ uint16_t nsvci = atoi(argv[2]);
+ bool dialect_modified = false;
+ bool ll_modified = false;
+
+ if (nse->ll != GPRS_NS2_LL_FR && nse->ll != GPRS_NS2_LL_UNDEF) {
+ vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_STATIC_RESETBLOCK && nse->dialect != GPRS_NS2_DIALECT_UNDEF) {
+ vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (nse->ll == GPRS_NS2_LL_UNDEF) {
+ nse->ll = GPRS_NS2_LL_FR;
+ ll_modified = true;
+ }
+
+ if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ dialect_modified = true;
+ }
+
+
+ bind = gprs_ns2_fr_bind_by_netif(vty_nsi, netif);
+ if (!bind) {
+ vty_out(vty, "Can not find fr interface \"%s\". Please configure it via fr vty.%s",
+ netif, VTY_NEWLINE);
+ goto err;
+ }
+
+ if (gprs_ns2_fr_nsvc_by_dlci(bind, dlci)) {
+ vty_out(vty, "A NS-VC with the specified DLCI already exist!%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (gprs_ns2_nsvc_by_nsvci(vty_nsi, nsvci)) {
+ vty_out(vty, "A NS-VC with the specified NS-VCI already exist!%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ nsvc = gprs_ns2_fr_connect(bind, nse, nsvci, dlci);
+ if (!nsvc) {
+ /* Could not create NS-VC, connect failed */
+ vty_out(vty, "Failed to create the NS-VC%s", VTY_NEWLINE);
+ goto err;
+ }
+ nsvc->persistent = true;
+ return CMD_SUCCESS;
+
+err:
+ if (ll_modified)
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ if (dialect_modified)
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+
+ return CMD_WARNING;
+}
+
+DEFUN(cfg_no_ns_nse_nsvc_fr_dlci, cfg_no_ns_nse_nsvc_fr_dlci_cmd,
+ "no nsvc fr NETIF dlci <16-1007>",
+ NO_STR
+ "Delete frame relay NS-VC\n"
+ "frame relay\n"
+ "frame relay interface. Must be registered via fr vty\n"
+ DLCI_STR
+ DLCI_STR
+ )
+{
+ struct gprs_ns2_vc_bind *bind;
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse = vty->index;
+ const char *netif = argv[0];
+ uint16_t dlci = atoi(argv[1]);
+
+ if (nse->ll != GPRS_NS2_LL_FR) {
+ vty_out(vty, "This NSE doesn't support frame relay.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bind = gprs_ns2_fr_bind_by_netif(vty_nsi, netif);
+ if (!bind) {
+ vty_out(vty, "Can not find fr interface \"%s\"%s",
+ netif, VTY_NEWLINE);
+ return CMD_ERR_NOTHING_TODO;
+ }
+
+ nsvc = gprs_ns2_fr_nsvc_by_dlci(bind, dlci);
+ if (!nsvc) {
+ vty_out(vty, "Can not find a NS-VC on fr interface %s with dlci %u%s",
+ netif, dlci, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nse != nsvc->nse) {
+ vty_out(vty, "The specified NS-VC is not a part of the NSE %u!%s"
+ "To remove this NS-VC go to the vty node 'nse %u'%s",
+ nse->nsei, VTY_NEWLINE,
+ nsvc->nse->nsei, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gprs_ns2_free_nsvc(nsvc);
+ if (llist_empty(&nse->nsvc)) {
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ns_nse_nsvci, cfg_no_ns_nse_nsvci_cmd,
+ "no nsvc nsvci <0-65535>",
+ NO_STR
+ "Delete NSVC\n"
+ NSVCI_STR
+ NSVCI_STR
+ )
+{
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse = vty->index;
+ uint16_t nsvci = atoi(argv[0]);
+
+ switch (nse->dialect) {
+ case GPRS_NS2_DIALECT_SNS:
+ case GPRS_NS2_DIALECT_STATIC_ALIVE:
+ vty_out(vty, "NSE doesn't support NSVCI.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ case GPRS_NS2_DIALECT_UNDEF:
+ vty_out(vty, "No NSVCs configured%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ case GPRS_NS2_DIALECT_IPACCESS:
+ case GPRS_NS2_DIALECT_STATIC_RESETBLOCK:
+ break;
+ }
+
+ nsvc = gprs_ns2_nsvc_by_nsvci(vty_nsi, nsvci);
+ if (!nsvc) {
+ vty_out(vty, "Can not find NS-VC with NS-VCI %u%s", nsvci, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nse != nsvc->nse) {
+ vty_out(vty, "NS-VC with NS-VCI %u is not part of this NSE!%s",
+ nsvci, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gprs_ns2_free_nsvc(nsvc);
+ if (llist_empty(&nse->nsvc)) {
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int ns_nse_nsvc_udp_cmds(struct vty *vty, const char *bind_name, const char *remote_char, uint16_t port,
+ uint16_t sig_weight, uint16_t data_weight)
+{
+ struct gprs_ns2_vc_bind *bind;
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse = vty->index;
+ bool dialect_modified = false;
+ bool ll_modified = false;
+
+ struct osmo_sockaddr_str remote_str;
+ struct osmo_sockaddr remote;
+
+ if (nse->ll == GPRS_NS2_LL_UNDEF) {
+ nse->ll = GPRS_NS2_LL_UDP;
+ ll_modified = true;
+ }
+
+ if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_STATIC_ALIVE);
+ dialect_modified = true;
+ }
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_STATIC_ALIVE) {
+ vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (osmo_sockaddr_str_from_str(&remote_str, remote_char, port)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ bind = gprs_ns2_bind_by_name(vty_nsi, bind_name);
+ if (!bind) {
+ vty_out(vty, "Can not find bind with name %s%s",
+ bind_name, VTY_NEWLINE);
+ goto err;
+ }
+
+ if (bind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "Bind %s is not an UDP bind.%s",
+ bind_name, VTY_NEWLINE);
+ goto err;
+ }
+
+ nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, &remote);
+ if (nsvc) {
+ if (nsvc->nse == nse)
+ vty_out(vty, "Specified NSVC is already present in this NSE.%s", VTY_NEWLINE);
+ else
+ vty_out(vty, "Specified NSVC is already present in another NSE%05u.%s", nsvc->nse->nsei, VTY_NEWLINE);
+ goto err;
+ }
+
+ nsvc = gprs_ns2_ip_connect(bind, &remote, nse, 0);
+ if (!nsvc) {
+ vty_out(vty, "Can not create NS-VC.%s", VTY_NEWLINE);
+ goto err;
+ }
+ nsvc->sig_weight = sig_weight;
+ nsvc->data_weight = data_weight;
+ nsvc->persistent = true;
+
+ return CMD_SUCCESS;
+
+err:
+ if (ll_modified)
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ if (dialect_modified)
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+ return CMD_WARNING;
+}
+
+DEFUN(cfg_ns_nse_nsvc_udp, cfg_ns_nse_nsvc_udp_cmd,
+ "nsvc udp BIND " VTY_IPV46_CMD " <1-65535>",
+ "NS Virtual Connection\n"
+ "NS over UDP\n"
+ "A unique bind identifier created by ns bind\n"
+ "Remote IPv4 Address\n" "Remote IPv6 Address\n"
+ "Remote UDP Port\n")
+{
+ const char *bind_name = argv[0];
+ const char *remote = argv[1];
+ uint16_t port = atoi(argv[2]);
+ uint16_t sig_weight = 1;
+ uint16_t data_weight = 1;
+
+ return ns_nse_nsvc_udp_cmds(vty, bind_name, remote, port, sig_weight, data_weight);
+}
+
+DEFUN(cfg_ns_nse_nsvc_udp_weights, cfg_ns_nse_nsvc_udp_weights_cmd,
+ "nsvc udp BIND " VTY_IPV46_CMD " <1-65535> signalling-weight <0-254> data-weight <0-254>",
+ "NS Virtual Connection\n"
+ "NS over UDP\n"
+ "A unique bind identifier created by ns bind\n"
+ "Remote IPv4 Address\n" "Remote IPv6 Address\n"
+ "Remote UDP Port\n"
+ "Signalling weight of the NSVC (default = 1)\n"
+ "Signalling weight of the NSVC (default = 1)\n"
+ "Data weight of the NSVC (default = 1)\n"
+ "Data weight of the NSVC (default = 1)\n"
+ )
+{
+ const char *bind_name = argv[0];
+ const char *remote = argv[1];
+ uint16_t port = atoi(argv[2]);
+ uint16_t sig_weight = atoi(argv[3]);
+ uint16_t data_weight = atoi(argv[4]);
+
+ return ns_nse_nsvc_udp_cmds(vty, bind_name, remote, port, sig_weight, data_weight);
+}
+
+DEFUN(cfg_no_ns_nse_nsvc_udp, cfg_no_ns_nse_nsvc_udp_cmd,
+ "no nsvc udp BIND " VTY_IPV46_CMD " <1-65535>",
+ NO_STR
+ "Delete a NS Virtual Connection\n"
+ "NS over UDP\n"
+ "A unique bind identifier created by ns bind\n"
+ "Remote IPv4 Address\n" "Remote IPv6 Address\n"
+ "Remote UDP Port\n"
+ )
+{
+ struct gprs_ns2_vc_bind *bind;
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse = vty->index;
+ const char *bind_name = argv[0];
+ struct osmo_sockaddr_str remote_str;
+ struct osmo_sockaddr remote;
+ uint16_t port = atoi(argv[2]);
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_STATIC_ALIVE) {
+ vty_out(vty, "This NSE doesn't support UDP with dialect static alive.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bind = gprs_ns2_bind_by_name(vty_nsi, bind_name);
+ if (!bind) {
+ vty_out(vty, "Can not find bind with name %s%s",
+ bind_name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "Bind %s is not an UDP bind.%s",
+ bind_name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (osmo_sockaddr_str_from_str(&remote_str, argv[1], port)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, &remote);
+ if (!nsvc) {
+ vty_out(vty, "Can not find NS-VC with remote %s:%u%s",
+ remote_str.ip, remote_str.port, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!nsvc->persistent) {
+ vty_out(vty, "NS-VC with remote %s:%u is a dynamic NS-VC. Not configured by vty.%s",
+ remote_str.ip, remote_str.port, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nsvc->nse != nse) {
+ vty_out(vty, "NS-VC is not part of this NSE!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gprs_ns2_free_nsvc(nsvc);
+ if (llist_empty(&nse->nsvc)) {
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_nse_nsvc_ipa, cfg_ns_nse_nsvc_ipa_cmd,
+ "nsvc ipa BIND " VTY_IPV46_CMD " <1-65535> nsvci <0-65535>" ,
+ "NS Virtual Connection\n"
+ "NS over UDP ip.access style (uses RESET/BLOCK)\n"
+ "A unique bind identifier created by ns bind\n"
+ "Remote IPv4 Address\n" "Remote IPv6 Address\n"
+ "Remote UDP Port\n"
+ NSVCI_STR
+ NSVCI_STR
+ )
+{
+ struct gprs_ns2_vc_bind *bind;
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse = vty->index;
+ bool dialect_modified = false;
+ bool ll_modified = false;
+
+ const char *bind_name = argv[0];
+ struct osmo_sockaddr_str remote_str;
+ struct osmo_sockaddr remote;
+ uint16_t port = atoi(argv[2]);
+ uint16_t nsvci = atoi(argv[3]);
+
+ if (nse->ll == GPRS_NS2_LL_UNDEF) {
+ nse->ll = GPRS_NS2_LL_UDP;
+ ll_modified = true;
+ }
+
+ if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_IPACCESS);
+ dialect_modified = true;
+ }
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_IPACCESS) {
+ vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (osmo_sockaddr_str_from_str(&remote_str, argv[1], port)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ bind = gprs_ns2_bind_by_name(vty_nsi, bind_name);
+ if (!bind) {
+ vty_out(vty, "Can not find bind with name %s%s",
+ bind_name, VTY_NEWLINE);
+ goto err;
+ }
+
+ if (bind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "Bind %s is not an UDP bind.%s",
+ bind_name, VTY_NEWLINE);
+ goto err;
+ }
+
+ nsvc = gprs_ns2_ip_connect(bind, &remote, nse, nsvci);
+ if (!nsvc) {
+ vty_out(vty, "Can not create NS-VC.%s", VTY_NEWLINE);
+ goto err;
+ }
+ nsvc->persistent = true;
+
+ return CMD_SUCCESS;
+
+err:
+ if (ll_modified)
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ if (dialect_modified)
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+ return CMD_WARNING;
+}
+
+DEFUN(cfg_no_ns_nse_nsvc_ipa, cfg_no_ns_nse_nsvc_ipa_cmd,
+ "no nsvc ipa BIND " VTY_IPV46_CMD " <1-65535> nsvci <0-65535>",
+ NO_STR
+ "Delete a NS Virtual Connection\n"
+ "NS over UDP\n"
+ "A unique bind identifier created by ns bind\n"
+ "Remote IPv4 Address\n" "Remote IPv6 Address\n"
+ "Remote UDP Port\n"
+ NSVCI_STR
+ NSVCI_STR
+ )
+{
+ struct gprs_ns2_vc_bind *bind;
+ struct gprs_ns2_vc *nsvc;
+ struct gprs_ns2_nse *nse = vty->index;
+ const char *bind_name = argv[0];
+ struct osmo_sockaddr_str remote_str;
+ struct osmo_sockaddr remote;
+ uint16_t port = atoi(argv[2]);
+ uint16_t nsvci = atoi(argv[3]);
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_IPACCESS) {
+ vty_out(vty, "This NSE doesn't support UDP with dialect ipaccess.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bind = gprs_ns2_bind_by_name(vty_nsi, bind_name);
+ if (!bind) {
+ vty_out(vty, "Can not find bind with name %s%s",
+ bind_name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (bind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "Bind %s is not an UDP bind.%s",
+ bind_name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (osmo_sockaddr_str_from_str(&remote_str, argv[1], port)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ nsvc = gprs_ns2_nsvc_by_sockaddr_bind(bind, &remote);
+ if (!nsvc) {
+ vty_out(vty, "Can not find NS-VC with remote %s:%u%s",
+ remote_str.ip, remote_str.port, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!nsvc->persistent) {
+ vty_out(vty, "NS-VC with remote %s:%u is a dynamic NS-VC. Not configured by vty.%s",
+ remote_str.ip, remote_str.port, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nsvc->nse != nse) {
+ vty_out(vty, "NS-VC is not part of this NSE!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!nsvc->nsvci_is_valid) {
+ vty_out(vty, "NS-VC doesn't have a nsvci!%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nsvc->nsvci != nsvci) {
+ vty_out(vty, "NS-VC has a different nsvci (%u)!%s",
+ nsvc->nsvci, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gprs_ns2_free_nsvc(nsvc);
+ if (llist_empty(&nse->nsvc)) {
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_nse_ip_sns_remote, cfg_ns_nse_ip_sns_remote_cmd,
+ "ip-sns-remote " VTY_IPV46_CMD " <1-65535>",
+ "SNS Initial Endpoint\n"
+ "SGSN IPv4 Address\n" "SGSN IPv6 Address\n"
+ "SGSN UDP Port\n"
+ )
+{
+ struct gprs_ns2_nse *nse = vty->index;
+ bool dialect_modified = false;
+ bool ll_modified = false;
+ int rc;
+
+ /* argv[0] */
+ struct osmo_sockaddr_str remote_str;
+ struct osmo_sockaddr remote;
+ uint16_t port = atoi(argv[1]);
+
+ if (nse->ll == GPRS_NS2_LL_UNDEF) {
+ nse->ll = GPRS_NS2_LL_UDP;
+ ll_modified = true;
+ }
+
+ if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
+ if (ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_SNS) < 0)
+ goto err;
+ dialect_modified = true;
+ }
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+ vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (osmo_sockaddr_str_from_str(&remote_str, argv[0], port)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ rc = gprs_ns2_sns_add_endpoint(nse, &remote);
+ switch (rc) {
+ case 0:
+ return CMD_SUCCESS;
+ case -EADDRINUSE:
+ vty_out(vty, "Specified SNS endpoint already part of the NSE.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ default:
+ vty_out(vty, "Can not add specified SNS endpoint.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+err:
+ if (ll_modified)
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ if (dialect_modified)
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+ return CMD_WARNING;
+}
+
+DEFUN(cfg_no_ns_nse_ip_sns_remote, cfg_no_ns_nse_ip_sns_remote_cmd,
+ "no ip-sns-remote " VTY_IPV46_CMD " <1-65535>",
+ NO_STR
+ "Delete a SNS Initial Endpoint\n"
+ "SGSN IPv4 Address\n" "SGSN IPv6 Address\n"
+ "SGSN UDP Port\n"
+ )
+{
+ struct gprs_ns2_nse *nse = vty->index;
+ struct osmo_sockaddr_str remote_str; /* argv[0] */
+ struct osmo_sockaddr remote;
+ uint16_t port = atoi(argv[1]);
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+ vty_out(vty, "This NSE doesn't support UDP with dialect ip-sns.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (osmo_sockaddr_str_from_str(&remote_str, argv[0], port)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (osmo_sockaddr_str_to_sockaddr(&remote_str, &remote.u.sas)) {
+ vty_out(vty, "Can not parse IPv4/IPv6 or port.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (gprs_ns2_sns_del_endpoint(nse, &remote)) {
+ vty_out(vty, "Can not remove specified SNS endpoint.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (vty_nse_check_sns(nse)) {
+ /* there is still sns configuration valid */
+ return CMD_SUCCESS;
+ } else {
+ /* clean up nse to allow other nsvc commands */
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* add all IP-SNS default binds to the given NSE */
+int ns2_sns_add_sns_default_binds(struct gprs_ns2_nse *nse)
+{
+ struct vty_nse_bind *vnse_bind;
+ int count = 0;
+
+ OSMO_ASSERT(nse->ll == GPRS_NS2_LL_UDP);
+ OSMO_ASSERT(nse->dialect == GPRS_NS2_DIALECT_SNS);
+
+ llist_for_each_entry(vnse_bind, &ip_sns_default_binds, list) {
+ struct gprs_ns2_vc_bind *bind = gprs_ns2_bind_by_name(vty_nsi, vnse_bind->vbind->name);
+ /* the bind might not yet created because "listen" is missing. */
+ if (!bind)
+ continue;
+ gprs_ns2_sns_add_bind(nse, bind);
+ count++;
+ }
+ return count;
+}
+
+DEFUN(cfg_ns_ip_sns_default_bind, cfg_ns_ip_sns_default_bind_cmd,
+ "ip-sns-default bind ID",
+ "Defaults for dynamically created NSEs created by IP-SNS in SGSN role\n"
+ "IP SNS binds\n"
+ "Name of NS udp bind whose IP endpoint will be used as IP-SNS local endpoint. Can be given multiple times.\n")
+{
+ struct vty_bind *vbind;
+ struct vty_nse_bind *vnse_bind;
+ const char *name = argv[0];
+
+ vbind = vty_bind_by_name(name);
+ if (!vbind) {
+ vty_out(vty, "Can not find the given bind '%s'%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "ip-sns-default bind can only be used with UDP bind%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_for_each_entry(vnse_bind, &ip_sns_default_binds, list) {
+ if (vnse_bind->vbind == vbind)
+ return CMD_SUCCESS;
+ }
+
+ vnse_bind = talloc(vty_nsi, struct vty_nse_bind);
+ if (!vnse_bind)
+ return CMD_WARNING;
+ vnse_bind->vbind = vbind;
+
+ llist_add_tail(&vnse_bind->list, &ip_sns_default_binds);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ns_ip_sns_default_bind, cfg_no_ns_ip_sns_default_bind_cmd,
+ "no ip-sns-default bind ID",
+ NO_STR "Defaults for dynamically created NSEs created by IP-SNS in SGSN role\n"
+ "IP SNS binds\n"
+ "Name of NS udp bind whose IP endpoint will be removed as IP-SNS local endpoint.\n")
+{
+ struct vty_bind *vbind;
+ struct vty_nse_bind *vnse_bind;
+ const char *name = argv[0];
+
+ vbind = vty_bind_by_name(name);
+ if (!vbind) {
+ vty_out(vty, "Can not find the given bind '%s'%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "ip-sns-default bind can only be used with UDP bind%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_for_each_entry(vnse_bind, &ip_sns_default_binds, list) {
+ if (vnse_bind->vbind == vbind) {
+ llist_del(&vnse_bind->list);
+ talloc_free(vnse_bind);
+ return CMD_SUCCESS;
+ }
+ }
+
+ vty_out(vty, "Bind '%s' was not an ip-sns-default bind%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+}
+
+DEFUN(cfg_ns_txqueue_max_length, cfg_ns_txqueue_max_length_cmd,
+ "txqueue-max-length <1-4096>",
+ "Set the maximum length of the txqueue (for UDP)\n"
+ "Maximum length of the txqueue\n")
+{
+ struct gprs_ns2_vc_bind *bind;
+ uint32_t max_length = atoi(argv[0]);
+ vty_nsi->txqueue_max_length = max_length;
+
+
+ llist_for_each_entry(bind, &vty_nsi->binding, list) {
+ if (!gprs_ns2_is_ip_bind(bind))
+ continue;
+
+ ns2_ip_set_txqueue_max_length(bind, max_length);
+ }
+
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ns_nse_ip_sns_bind, cfg_ns_nse_ip_sns_bind_cmd,
+ "ip-sns-bind BINDID",
+ "IP SNS binds\n"
+ "Name of NS udp bind whose IP endpoint will be used as IP-SNS local endpoint. Can be given multiple times.\n")
+{
+ struct gprs_ns2_nse *nse = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+ struct vty_bind *vbind;
+ struct vty_nse *vnse;
+ const char *name = argv[0];
+ bool ll_modified = false;
+ bool dialect_modified = false;
+ int rc;
+
+ if (nse->ll == GPRS_NS2_LL_UNDEF) {
+ nse->ll = GPRS_NS2_LL_UDP;
+ ll_modified = true;
+ }
+
+ if (nse->dialect == GPRS_NS2_DIALECT_UNDEF) {
+ if (ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_SNS) < 0)
+ goto err;
+ dialect_modified = true;
+ }
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "Can not mix NS-VC with different link layer%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+ vty_out(vty, "Can not mix NS-VC with different dialects%s", VTY_NEWLINE);
+ goto err;
+ }
+
+ vbind = vty_bind_by_name(name);
+ if (!vbind) {
+ vty_out(vty, "Can not find the given bind '%s'%s", name, VTY_NEWLINE);
+ goto err;
+ }
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "ip-sns-bind can only be used with UDP bind%s",
+ VTY_NEWLINE);
+ goto err;
+ }
+
+ /* the vnse has been created together when creating the nse node. The parent node should check this already! */
+ vnse = vty_nse_by_nsei(nse->nsei);
+ OSMO_ASSERT(vnse);
+
+ rc = vty_nse_add_vbind(vnse, vbind);
+ switch (rc) {
+ case 0:
+ break;
+ case -EALREADY:
+ vty_out(vty, "Failed to add ip-sns-bind %s already present%s", name, VTY_NEWLINE);
+ goto err;
+ case -ENOMEM:
+ vty_out(vty, "Failed to add ip-sns-bind %s out of memory%s", name, VTY_NEWLINE);
+ goto err;
+ default:
+ vty_out(vty, "Failed to add ip-sns-bind %s! %d%s", name, rc, VTY_NEWLINE);
+ goto err;
+ }
+
+ /* the bind might not yet created because "listen" is missing. */
+ bind = gprs_ns2_bind_by_name(vty_nsi, name);
+ if (!bind)
+ return CMD_SUCCESS;
+
+ rc = gprs_ns2_sns_add_bind(nse, bind);
+ switch (rc) {
+ case 0:
+ break;
+ case -EALREADY:
+ vty_out(vty, "Failed to add ip-sns-bind %s already present%s", name, VTY_NEWLINE);
+ goto err;
+ case -ENOMEM:
+ vty_out(vty, "Failed to add ip-sns-bind %s out of memory%s", name, VTY_NEWLINE);
+ goto err;
+ default:
+ vty_out(vty, "Failed to add ip-sns-bind %s! %d%s", name, rc, VTY_NEWLINE);
+ goto err;
+ }
+
+ return CMD_SUCCESS;
+err:
+ if (ll_modified)
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ if (dialect_modified)
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+
+ return CMD_WARNING;
+}
+
+DEFUN(cfg_no_ns_nse_ip_sns_bind, cfg_no_ns_nse_ip_sns_bind_cmd,
+ "no ip-sns-bind BINDID",
+ NO_STR
+ "IP SNS binds\n"
+ "Name of NS udp bind whose IP endpoint will not be used as IP-SNS local endpoint\n")
+{
+ struct gprs_ns2_nse *nse = vty->index;
+ struct gprs_ns2_vc_bind *bind;
+ struct vty_bind *vbind;
+ struct vty_nse *vnse;
+ const char *name = argv[0];
+ int rc;
+
+ if (nse->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "This NSE doesn't support UDP.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+ vty_out(vty, "This NSE doesn't support UDP with dialect ip-sns.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vbind = vty_bind_by_name(name);
+ if (!vbind) {
+ vty_out(vty, "Can not find the given bind '%s'%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (vbind->ll != GPRS_NS2_LL_UDP) {
+ vty_out(vty, "no ip-sns-bind can only be used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* the vnse has been created together when creating the nse node. The parent node should check this already! */
+ vnse = vty_nse_by_nsei(nse->nsei);
+ OSMO_ASSERT(vnse);
+
+ rc = vty_nse_remove_vbind(vnse, vbind);
+ switch(rc) {
+ case 0:
+ break;
+ case -ENOENT:
+ vty_out(vty, "Bind %s is not part of this NSE%s", name, VTY_NEWLINE);
+ return CMD_WARNING;
+ case -EINVAL:
+ vty_out(vty, "no ip-sns-bind can only be used with UDP bind%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ default:
+ return CMD_WARNING;
+ }
+
+ /* the bind might not exists yet */
+ bind = gprs_ns2_bind_by_name(vty_nsi, name);
+ if (bind)
+ gprs_ns2_sns_del_bind(nse, bind);
+
+ if (!vty_nse_check_sns(nse)) {
+ /* clean up nse to allow other nsvc commands */
+ ns2_nse_set_dialect(nse, GPRS_NS2_DIALECT_UNDEF);
+ nse->ll = GPRS_NS2_LL_UNDEF;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* non-config commands */
+void ns2_vty_dump_nsvc(struct vty *vty, struct gprs_ns2_vc *nsvc, bool stats)
+{
+ if (nsvc->nsvci_is_valid)
+ vty_out(vty, " NSVCI %05u: %s %s %s %s %ssince ", nsvc->nsvci,
+ osmo_fsm_inst_state_name(nsvc->fi),
+ nsvc->persistent ? "PERSIST" : "DYNAMIC",
+ gprs_ns2_ll_str(nsvc),
+ ns2_vc_is_unblocked(nsvc) ? "ALIVE" : "DEAD",
+ nsvc->om_blocked ? "(blocked by O&M/vty) " :
+ !ns2_vc_is_unblocked(nsvc) ? "(cause: remote) " : "");
+ else
+ vty_out(vty, " %s %s sig_weight=%u data_weight=%u %s %s %ssince ",
+ osmo_fsm_inst_state_name(nsvc->fi),
+ nsvc->persistent ? "PERSIST" : "DYNAMIC",
+ nsvc->sig_weight, nsvc->data_weight,
+ gprs_ns2_ll_str(nsvc),
+ ns2_vc_is_unblocked(nsvc) ? "ALIVE" : "DEAD",
+ !ns2_vc_is_unblocked(nsvc) ? "(cause: remote) " : "");
+
+ vty_out_uptime(vty, &nsvc->ts_alive_change);
+ vty_out_newline(vty);
+
+ if (stats) {
+ vty_out_rate_ctr_group(vty, " ", nsvc->ctrg);
+ vty_out_stat_item_group(vty, " ", nsvc->statg);
+ }
+}
+
+static void dump_nse(struct vty *vty, const struct gprs_ns2_nse *nse, bool stats, bool persistent_only)
+{
+ struct gprs_ns2_vc *nsvc;
+ unsigned int nsvcs = 0;
+
+ if (persistent_only && !nse->persistent)
+ return;
+
+ vty_out(vty, "NSEI %05u: %s, %s since ", nse->nsei, gprs_ns2_lltype_str(nse->ll),
+ nse->alive ? "ALIVE" : "DEAD");
+ vty_out_uptime(vty, &nse->ts_alive_change);
+ vty_out_newline(vty);
+
+ ns2_sns_dump_vty(vty, " ", nse, stats);
+ llist_for_each_entry(nsvc, &nse->nsvc, list) {
+ nsvcs++;
+ }
+ vty_out(vty, " %u NS-VC:%s", nsvcs, VTY_NEWLINE);
+ llist_for_each_entry(nsvc, &nse->nsvc, list)
+ ns2_vty_dump_nsvc(vty, nsvc, stats);
+}
+
+static void dump_bind(struct vty *vty, const struct gprs_ns2_vc_bind *bind, bool stats)
+{
+ if (bind->dump_vty)
+ bind->dump_vty(bind, vty, stats);
+
+ if (stats) {
+ vty_out_stat_item_group(vty, " ", bind->statg);
+ }
+}
+
+static void dump_ns_bind(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats)
+{
+ struct gprs_ns2_vc_bind *bind;
+
+ llist_for_each_entry(bind, &nsi->binding, list) {
+ dump_bind(vty, bind, stats);
+ }
+}
+
+
+static void dump_ns_entities(struct vty *vty, const struct gprs_ns2_inst *nsi, bool stats, bool persistent_only)
+{
+ struct gprs_ns2_nse *nse;
+
+ llist_for_each_entry(nse, &nsi->nse, list) {
+ dump_nse(vty, nse, stats, persistent_only);
+ }
+}
+
+/* Backwards compatibility, among other things for the TestVTYGbproxy which expects
+ * 'show ns' to output something about binds */
+DEFUN_HIDDEN(show_ns, show_ns_cmd, "show ns",
+ SHOW_STR SHOW_NS_STR)
+{
+ dump_ns_entities(vty, vty_nsi, false, false);
+ dump_ns_bind(vty, vty_nsi, false);
+ if (vty_fr_network && llist_count(&vty_fr_network->links))
+ osmo_fr_network_dump_vty(vty, vty_fr_network);
+ return CMD_SUCCESS;
+}
+
+
+DEFUN(show_ns_binds, show_ns_binds_cmd, "show ns binds [stats]",
+ SHOW_STR SHOW_NS_STR
+ "Display information about the NS protocol binds\n"
+ "Include statistic\n")
+{
+ bool stats = false;
+ if (argc > 0)
+ stats = true;
+
+ dump_ns_bind(vty, vty_nsi, stats);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_ns_entities, show_ns_entities_cmd, "show ns entities [stats]",
+ SHOW_STR SHOW_NS_STR
+ "Display information about the NS protocol entities (NSEs)\n"
+ "Include statistics\n")
+{
+ bool stats = false;
+ if (argc > 0)
+ stats = true;
+
+ dump_ns_entities(vty, vty_nsi, stats, false);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_ns_pers, show_ns_pers_cmd, "show ns persistent",
+ SHOW_STR SHOW_NS_STR
+ "Show only persistent NS\n")
+{
+ dump_ns_entities(vty, vty_nsi, true, true);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]",
+ SHOW_STR SHOW_NS_STR
+ "Select one NSE by its NSE Identifier\n"
+ "Select one NSE by its NS-VC Identifier\n"
+ "The Identifier of selected type\n"
+ "Include Statistics\n")
+{
+ struct gprs_ns2_inst *nsi = vty_nsi;
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc;
+ uint16_t id = atoi(argv[1]);
+ bool show_stats = false;
+
+ if (argc >= 3)
+ show_stats = true;
+
+ if (!strcmp(argv[0], "nsei")) {
+ nse = gprs_ns2_nse_by_nsei(nsi, id);
+ if (!nse) {
+ return CMD_WARNING;
+ }
+
+ dump_nse(vty, nse, show_stats, false);
+ } else {
+ nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
+
+ if (!nsvc) {
+ vty_out(vty, "No such NS Entity%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ ns2_vty_dump_nsvc(vty, nsvc, show_stats);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int nsvc_force_unconf_cb(struct gprs_ns2_vc *nsvc, void *ctx)
+{
+ ns2_vc_force_unconfigured(nsvc);
+ ns2_vc_fsm_start(nsvc);
+ return 0;
+}
+
+DEFUN_HIDDEN(nsvc_force_unconf, nsvc_force_unconf_cmd,
+ "nsvc nsei <0-65535> force-unconfigured",
+ "NS Virtual Connection\n"
+ "The NSEI\n"
+ "Reset the NSVCs back to initial state\n"
+ )
+{
+ struct gprs_ns2_inst *nsi = vty_nsi;
+ struct gprs_ns2_nse *nse;
+
+ uint16_t id = atoi(argv[0]);
+
+ nse = gprs_ns2_nse_by_nsei(nsi, id);
+ if (!nse) {
+ vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!nse->persistent) {
+ gprs_ns2_free_nse(nse);
+ } else if (nse->dialect == GPRS_NS2_DIALECT_SNS) {
+ gprs_ns2_free_nsvcs(nse);
+ } else {
+ /* Perform the operation for all nsvc */
+ gprs_ns2_nse_foreach_nsvc(nse, nsvc_force_unconf_cb, NULL);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(nse_restart_sns, nse_restart_sns_cmd,
+ "nse <0-65535> restart-sns",
+ "NSE specific commands\n"
+ "NS Entity ID (NSEI)\n"
+ "Restart SNS procedure\n")
+{
+ struct gprs_ns2_inst *nsi = vty_nsi;
+ struct gprs_ns2_nse *nse;
+
+ uint16_t id = atoi(argv[0]);
+ nse = gprs_ns2_nse_by_nsei(nsi, id);
+ if (!nse) {
+ vty_out(vty, "Could not find NSE for NSEI %u%s", id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (nse->dialect != GPRS_NS2_DIALECT_SNS) {
+ vty_out(vty, "Given NSEI %u doesn't use IP-SNS%s", id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ gprs_ns2_free_nsvcs(nse);
+ return CMD_SUCCESS;
+}
+
+DEFUN(nsvc_block, nsvc_block_cmd,
+ "nsvc <0-65535> (block|unblock|reset)",
+ "NS Virtual Connection\n"
+ NSVCI_STR
+ "Block a NSVC. As cause code O&M intervention will be used.\n"
+ "Unblock a NSVC. As cause code O&M intervention will be used.\n"
+ "Reset a NSVC. As cause code O&M intervention will be used.\n")
+{
+ struct gprs_ns2_inst *nsi = vty_nsi;
+ struct gprs_ns2_vc *nsvc;
+ int rc;
+
+ uint16_t id = atoi(argv[0]);
+
+ nsvc = gprs_ns2_nsvc_by_nsvci(nsi, id);
+ if (!nsvc) {
+ vty_out(vty, "Could not find NSVCI %05u%s", id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[1], "block")) {
+ rc = ns2_vc_block(nsvc);
+ switch (rc) {
+ case 0:
+ vty_out(vty, "The NS-VC %05u will be blocked.%s", id, VTY_NEWLINE);
+ return CMD_SUCCESS;
+ case -EALREADY:
+ vty_out(vty, "The NS-VC %05u is already blocked.%s", id, VTY_NEWLINE);
+ return CMD_ERR_NOTHING_TODO;
+ default:
+ vty_out(vty, "An unknown error %d happend on NS-VC %05u.%s", rc, id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ } else if (!strcmp(argv[1], "unblock")) {
+ rc = ns2_vc_unblock(nsvc);
+ switch (rc) {
+ case 0:
+ vty_out(vty, "The NS-VC %05u will be unblocked.%s", id, VTY_NEWLINE);
+ return CMD_SUCCESS;
+ case -EALREADY:
+ vty_out(vty, "The NS-VC %05u is already unblocked.%s", id, VTY_NEWLINE);
+ return CMD_ERR_NOTHING_TODO;
+ default:
+ vty_out(vty, "An unknown error %d happend on NS-VC %05u.%s", rc, id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ } else {
+ ns2_vc_reset(nsvc);
+ vty_out(vty, "The NS-VC %05u has been resetted.%s", id, VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void log_set_nse_filter(struct log_target *target,
+ struct gprs_ns2_nse *nse)
+{
+ if (nse) {
+ target->filter_map |= (1 << LOG_FLT_GB_NSE);
+ target->filter_data[LOG_FLT_GB_NSE] = nse;
+ } else if (target->filter_data[LOG_FLT_GB_NSE]) {
+ target->filter_map = ~(1 << LOG_FLT_GB_NSE);
+ target->filter_data[LOG_FLT_GB_NSE] = NULL;
+ }
+}
+
+static void log_set_nsvc_filter(struct log_target *target,
+ struct gprs_ns2_vc *nsvc)
+{
+ if (nsvc) {
+ target->filter_map |= (1 << LOG_FLT_GB_NSVC);
+ target->filter_data[LOG_FLT_GB_NSVC] = nsvc;
+ } else if (target->filter_data[LOG_FLT_GB_NSVC]) {
+ target->filter_map = ~(1 << LOG_FLT_GB_NSVC);
+ target->filter_data[LOG_FLT_GB_NSVC] = NULL;
+ }
+}
+
+DEFUN(logging_fltr_nse,
+ logging_fltr_nse_cmd,
+ "logging filter nse nsei <0-65535>",
+ LOGGING_STR FILTER_STR
+ "Filter based on NS Entity\n"
+ "Identify NSE by NSEI\n"
+ "Numeric identifier\n")
+{
+ struct log_target *tgt;
+ struct gprs_ns2_nse *nse;
+ uint16_t id = atoi(argv[0]);
+
+ log_tgt_mutex_lock();
+ tgt = osmo_log_vty2tgt(vty);
+ if (!tgt) {
+ log_tgt_mutex_unlock();
+ return CMD_WARNING;
+ }
+
+ nse = gprs_ns2_nse_by_nsei(vty_nsi, id);
+ if (!nse) {
+ vty_out(vty, "No NSE by that identifier%s", VTY_NEWLINE);
+ log_tgt_mutex_unlock();
+ return CMD_WARNING;
+ }
+
+ log_set_nse_filter(tgt, nse);
+ log_tgt_mutex_unlock();
+ return CMD_SUCCESS;
+}
+
+/* TODO: add filter for single connection by description */
+DEFUN(logging_fltr_nsvc,
+ logging_fltr_nsvc_cmd,
+ "logging filter nsvc nsvci <0-65535>",
+ LOGGING_STR FILTER_STR
+ "Filter based on NS Virtual Connection\n"
+ "Identify NS-VC by NSVCI\n"
+ "Numeric identifier\n")
+{
+ struct log_target *tgt;
+ struct gprs_ns2_vc *nsvc;
+ uint16_t id = atoi(argv[0]);
+
+ log_tgt_mutex_lock();
+ tgt = osmo_log_vty2tgt(vty);
+ if (!tgt) {
+ log_tgt_mutex_unlock();
+ return CMD_WARNING;
+ }
+
+ nsvc = gprs_ns2_nsvc_by_nsvci(vty_nsi, id);
+ if (!nsvc) {
+ vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE);
+ log_tgt_mutex_unlock();
+ return CMD_WARNING;
+ }
+
+ log_set_nsvc_filter(tgt, nsvc);
+ log_tgt_mutex_unlock();
+ return CMD_SUCCESS;
+}
+
+/*! initialized a reduced vty interface which excludes the configuration nodes besides timeouts.
+ * This can be used by the PCU which can be only configured by the BTS/BSC and not by the vty.
+ * \param[in] nsi NS instance on which we operate
+ * \return 0 on success.
+ */
+int gprs_ns2_vty_init_reduced(struct gprs_ns2_inst *nsi)
+{
+ vty_nsi = nsi;
+ INIT_LLIST_HEAD(&binds);
+ INIT_LLIST_HEAD(&nses);
+ INIT_LLIST_HEAD(&ip_sns_default_binds);
+
+ vty_fr_network = osmo_fr_network_alloc(nsi);
+ if (!vty_fr_network)
+ return -ENOMEM;
+
+ install_lib_element_ve(&show_ns_cmd);
+ install_lib_element_ve(&show_ns_binds_cmd);
+ install_lib_element_ve(&show_ns_entities_cmd);
+ install_lib_element_ve(&show_ns_pers_cmd);
+ install_lib_element_ve(&show_nse_cmd);
+ install_lib_element_ve(&logging_fltr_nse_cmd);
+ install_lib_element_ve(&logging_fltr_nsvc_cmd);
+
+ install_lib_element(ENABLE_NODE, &nsvc_force_unconf_cmd);
+ install_lib_element(ENABLE_NODE, &nsvc_block_cmd);
+ install_lib_element(ENABLE_NODE, &nse_restart_sns_cmd);
+
+ install_lib_element(CFG_LOG_NODE, &logging_fltr_nse_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
+
+ install_lib_element(CONFIG_NODE, &cfg_ns_cmd);
+
+ install_node(&ns_node, config_write_ns);
+ /* TODO: convert into osmo timer */
+ install_lib_element(L_NS_NODE, &cfg_ns_timer_cmd);
+
+ return 0;
+}
+
+int gprs_ns2_vty_init(struct gprs_ns2_inst *nsi)
+{
+ int rc = gprs_ns2_vty_init_reduced(nsi);
+ if (rc)
+ return rc;
+
+ install_lib_element(L_NS_NODE, &cfg_ns_nsei_cmd);
+ install_lib_element(L_NS_NODE, &cfg_no_ns_nsei_cmd);
+ install_lib_element(L_NS_NODE, &cfg_ns_bind_cmd);
+ install_lib_element(L_NS_NODE, &cfg_no_ns_bind_cmd);
+
+ install_lib_element(L_NS_NODE, &cfg_ns_ip_sns_default_bind_cmd);
+ install_lib_element(L_NS_NODE, &cfg_no_ns_ip_sns_default_bind_cmd);
+
+ install_lib_element(L_NS_NODE, &cfg_ns_txqueue_max_length_cmd);
+
+ install_node(&ns_bind_node, NULL);
+ install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_listen_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_listen_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_dscp_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_dscp_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_priority_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_ip_sns_weight_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_ipaccess_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_ipaccess_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_fr_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_fr_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_ns_bind_accept_sns_cmd);
+ install_lib_element(L_NS_BIND_NODE, &cfg_no_ns_bind_accept_sns_cmd);
+
+ install_node(&ns_nse_node, NULL);
+ install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_nsvc_fr_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_nsvci_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_nsvc_fr_dlci_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_nsvc_udp_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_nsvc_udp_weights_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_nsvc_udp_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_nsvc_ipa_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_nsvc_ipa_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_ip_sns_remote_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_ip_sns_remote_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_ns_nse_ip_sns_bind_cmd);
+ install_lib_element(L_NS_NSE_NODE, &cfg_no_ns_nse_ip_sns_bind_cmd);
+
+ return 0;
+}
diff --git a/src/gb/gprs_ns_vty.c b/src/gb/gprs_ns_vty.c
index 9cffb71d..f27bf6a8 100644
--- a/src/gb/gprs_ns_vty.c
+++ b/src/gb/gprs_ns_vty.c
@@ -90,6 +90,32 @@ static int config_write_ns(struct vty *vty)
vty_out(vty, "ns%s", VTY_NEWLINE);
+ /* global configuration must be written first, as some of it may be
+ * relevant when creating the NSE/NSVC later below */
+
+ if (vty_nsi->nsip.local_ip) {
+ ia.s_addr = osmo_htonl(vty_nsi->nsip.local_ip);
+ vty_out(vty, " encapsulation udp local-ip %s%s",
+ inet_ntoa(ia), VTY_NEWLINE);
+ }
+ if (vty_nsi->nsip.local_port)
+ vty_out(vty, " encapsulation udp local-port %u%s",
+ vty_nsi->nsip.local_port, VTY_NEWLINE);
+ if (vty_nsi->nsip.dscp)
+ vty_out(vty, " encapsulation udp dscp %d%s",
+ vty_nsi->nsip.dscp, VTY_NEWLINE);
+
+ vty_out(vty, " encapsulation udp use-reset-block-unblock %s%s",
+ vty_nsi->nsip.use_reset_block_unblock ? "enabled" : "disabled", VTY_NEWLINE);
+
+ vty_out(vty, " encapsulation framerelay-gre enabled %u%s",
+ vty_nsi->frgre.enabled ? 1 : 0, VTY_NEWLINE);
+ if (vty_nsi->frgre.local_ip) {
+ ia.s_addr = osmo_htonl(vty_nsi->frgre.local_ip);
+ vty_out(vty, " encapsulation framerelay-gre local-ip %s%s",
+ inet_ntoa(ia), VTY_NEWLINE);
+ }
+
llist_for_each_entry(nsvc, &vty_nsi->gprs_nsvcs, list) {
if (!nsvc->persistent)
continue;
@@ -130,26 +156,6 @@ static int config_write_ns(struct vty *vty)
get_value_string(gprs_ns_timer_strs, i),
vty_nsi->timeout[i], VTY_NEWLINE);
- if (vty_nsi->nsip.local_ip) {
- ia.s_addr = osmo_htonl(vty_nsi->nsip.local_ip);
- vty_out(vty, " encapsulation udp local-ip %s%s",
- inet_ntoa(ia), VTY_NEWLINE);
- }
- if (vty_nsi->nsip.local_port)
- vty_out(vty, " encapsulation udp local-port %u%s",
- vty_nsi->nsip.local_port, VTY_NEWLINE);
- if (vty_nsi->nsip.dscp)
- vty_out(vty, " encapsulation udp dscp %d%s",
- vty_nsi->nsip.dscp, VTY_NEWLINE);
-
- vty_out(vty, " encapsulation framerelay-gre enabled %u%s",
- vty_nsi->frgre.enabled ? 1 : 0, VTY_NEWLINE);
- if (vty_nsi->frgre.local_ip) {
- ia.s_addr = osmo_htonl(vty_nsi->frgre.local_ip);
- vty_out(vty, " encapsulation framerelay-gre local-ip %s%s",
- inet_ntoa(ia), VTY_NEWLINE);
- }
-
return CMD_SUCCESS;
}
@@ -286,7 +292,7 @@ DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd,
nsvc = gprs_nsvc_by_nsei(vty_nsi, nsei);
if (!nsvc) {
- nsvc = gprs_nsvc_create(vty_nsi, nsvci);
+ nsvc = gprs_nsvc_create2(vty_nsi, nsvci, 1, 1);
nsvc->nsei = nsei;
}
nsvc->nsvci = nsvci;
@@ -312,6 +318,7 @@ DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd,
vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE);
return CMD_WARNING;
}
+ nsvc->ip.bts_addr.sin_family = AF_INET;
inet_aton(argv[1], &nsvc->ip.bts_addr.sin_addr);
return CMD_SUCCESS;
@@ -500,6 +507,21 @@ DEFUN(cfg_nsip_dscp, cfg_nsip_dscp_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_nsip_res_block_unblock, cfg_nsip_res_block_unblock_cmd,
+ "encapsulation udp use-reset-block-unblock (enabled|disabled)",
+ ENCAPS_STR "NS over UDP Encapsulation\n"
+ "Use NS-{RESET,BLOCK,UNBLOCK} procedures in violation of 3GPP TS 48.016\n"
+ "Enable NS-{RESET,BLOCK,UNBLOCK}\n"
+ "Disable NS-{RESET,BLOCK,UNBLOCK}\n")
+{
+ if (!strcmp(argv[0], "enabled"))
+ vty_nsi->nsip.use_reset_block_unblock = true;
+ else
+ vty_nsi->nsip.use_reset_block_unblock = false;
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd,
"encapsulation framerelay-gre local-ip A.B.C.D",
ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n"
@@ -622,31 +644,32 @@ int gprs_ns_vty_init(struct gprs_ns_inst *nsi)
return 0;
vty_elements_installed = true;
- install_element_ve(&show_ns_cmd);
- install_element_ve(&show_ns_stats_cmd);
- install_element_ve(&show_ns_pers_cmd);
- install_element_ve(&show_nse_cmd);
- install_element_ve(&logging_fltr_nsvc_cmd);
+ install_lib_element_ve(&show_ns_cmd);
+ install_lib_element_ve(&show_ns_stats_cmd);
+ install_lib_element_ve(&show_ns_pers_cmd);
+ install_lib_element_ve(&show_nse_cmd);
+ install_lib_element_ve(&logging_fltr_nsvc_cmd);
- install_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd);
- install_element(CONFIG_NODE, &cfg_ns_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_ns_cmd);
install_node(&ns_node, config_write_ns);
- install_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
- install_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
- install_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
- install_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd);
- install_element(L_NS_NODE, &cfg_nse_encaps_cmd);
- install_element(L_NS_NODE, &cfg_nse_remoterole_cmd);
- install_element(L_NS_NODE, &cfg_no_nse_cmd);
- install_element(L_NS_NODE, &cfg_ns_timer_cmd);
- install_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);
- install_element(L_NS_NODE, &cfg_nsip_local_port_cmd);
- install_element(L_NS_NODE, &cfg_nsip_dscp_cmd);
- install_element(L_NS_NODE, &cfg_frgre_enable_cmd);
- install_element(L_NS_NODE, &cfg_frgre_local_ip_cmd);
-
- install_element(ENABLE_NODE, &nsvc_nsei_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nse_nsvci_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nse_remoteip_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nse_remoteport_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nse_encaps_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nse_remoterole_cmd);
+ install_lib_element(L_NS_NODE, &cfg_no_nse_cmd);
+ install_lib_element(L_NS_NODE, &cfg_ns_timer_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nsip_local_ip_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nsip_local_port_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nsip_dscp_cmd);
+ install_lib_element(L_NS_NODE, &cfg_nsip_res_block_unblock_cmd);
+ install_lib_element(L_NS_NODE, &cfg_frgre_enable_cmd);
+ install_lib_element(L_NS_NODE, &cfg_frgre_local_ip_cmd);
+
+ install_lib_element(ENABLE_NODE, &nsvc_nsei_cmd);
return 0;
}
diff --git a/src/gb/libosmogb.map b/src/gb/libosmogb.map
index ad139c1c..e5a5c8fd 100644
--- a/src/gb/libosmogb.map
+++ b/src/gb/libosmogb.map
@@ -2,7 +2,25 @@ LIBOSMOGB_1.0 {
global:
bssgp_cause_str;
bssgp_create_cell_id;
+bssgp_create_rim_ri;
+bssgp_dec_app_err_cont_nacc;
+bssgp_dec_ran_inf_ack_rim_cont;
+bssgp_dec_ran_inf_err_rim_cont;
+bssgp_dec_ran_inf_req_app_cont_nacc;
+bssgp_dec_ran_inf_req_rim_cont;
+bssgp_dec_ran_inf_app_cont_nacc;
+bssgp_dec_ran_inf_app_err_rim_cont;
+bssgp_dec_ran_inf_rim_cont;
bssgp_pdu_str;
+bssgp_enc_app_err_cont_nacc;
+bssgp_enc_ran_inf_ack_rim_cont;
+bssgp_enc_ran_inf_err_rim_cont;
+bssgp_enc_ran_inf_req_app_cont_nacc;
+bssgp_enc_ran_inf_req_rim_cont;
+bssgp_enc_ran_inf_app_cont_nacc;
+bssgp_enc_ran_inf_app_err_rim_cont;
+bssgp_enc_ran_inf_rim_cont;
+bssgp_encode_rim_pdu;
bssgp_fc_in;
bssgp_fc_init;
bssgp_fc_ms_init;
@@ -12,9 +30,20 @@ bssgp_msgb_alloc;
bssgp_msgb_copy;
bssgp_msgb_tlli_put;
bssgp_msgb_ra_put;
+bssgp_nacc_cause_strs;
bssgp_parse_cell_id;
+bssgp_parse_rim_pdu;
+bssgp_parse_rim_ri;
+bssgp_parse_rim_ra;
+bssgp_ran_inf_app_id_strs;
+bssgp_rim_routing_info_discr_strs;
+bssgp_rim_ri_name_buf;
+bssgp_rim_ri_name;
+bssgp_set_bssgp_callback;
bssgp_tx_bvc_block;
bssgp_tx_bvc_reset;
+bssgp_tx_bvc_reset2;
+bssgp_tx_bvc_reset_nsei_bvci;
bssgp_tx_bvc_unblock;
bssgp_tx_fc_bvc;
bssgp_tx_fc_ms;
@@ -27,6 +56,8 @@ bssgp_tx_radio_status_tmsi;
bssgp_tx_resume;
bssgp_tx_resume_ack;
bssgp_tx_resume_nack;
+bssgp_tx_rim;
+bssgp_tx_rim_encoded;
bssgp_tx_simple_bvci;
bssgp_tx_status;
bssgp_tx_suspend;
@@ -42,6 +73,45 @@ bssgp_tx_paging;
bssgp_vty_init;
bssgp_nsi;
+bssgp2_nsi_tx_ptp;
+bssgp2_nsi_tx_sig;
+bssgp2_dec_fc_bvc;
+bssgp2_dec_fc_ms;
+bssgp2_enc_bvc_block;
+bssgp2_enc_bvc_block_ack;
+bssgp2_enc_bvc_unblock;
+bssgp2_enc_bvc_unblock_ack;
+bssgp2_enc_bvc_reset;
+bssgp2_enc_bvc_reset_ack;
+bssgp2_enc_fc_bvc;
+bssgp2_enc_fc_bvc_ack;
+bssgp2_enc_fc_ms;
+bssgp2_enc_fc_ms_ack;
+bssgp2_enc_flush_ll;
+bssgp2_enc_status;
+
+bssgp_bvc_fsm_alloc_sig_bss;
+bssgp_bvc_fsm_alloc_ptp_bss;
+bssgp_bvc_fsm_alloc_sig_sgsn;
+bssgp_bvc_fsm_alloc_ptp_sgsn;
+bssgp_bvc_fsm_set_ops;
+bssgp_bvc_fsm_is_unblocked;
+bssgp_bvc_fsm_get_block_cause;
+bssgp_bvc_fsm_get_features_advertised;
+bssgp_bvc_fsm_get_features_received;
+bssgp_bvc_fsm_get_features_negotiated;
+bssgp_bvc_fsm_set_max_pdu_len;
+bssgp_bvc_fsm_get_max_pdu_len;
+
+osmo_fr_network_alloc;
+osmo_fr_network_free;
+osmo_fr_link_alloc;
+osmo_fr_link_free;
+osmo_fr_dlc_alloc;
+osmo_fr_rx;
+osmo_fr_tx_dlc;
+osmo_fr_role_names;
+
gprs_ns_signal_ns_names;
gprs_ns_pdu_strings;
gprs_ns_cause_str;
@@ -70,7 +140,66 @@ gprs_ns_ll_copy;
gprs_ns_ll_clear;
gprs_ns_msgb_alloc;
-gprs_nsvc_create;
+gprs_ns2_aff_cause_prim_strs;
+gprs_ns2_bind_by_name;
+gprs_ns2_cause_strs;
+gprs_ns2_create_nse;
+gprs_ns2_create_nse2;
+gprs_ns2_find_vc_by_sockaddr;
+gprs_ns2_free;
+gprs_ns2_free_bind;
+gprs_ns2_free_binds;
+gprs_ns2_free_nse;
+gprs_ns2_free_nses;
+gprs_ns2_free_nsvc;
+gprs_ns2_free_nsvcs;
+gprs_ns2_frgre_bind;
+gprs_ns2_fr_bind;
+gprs_ns2_fr_bind_netif;
+gprs_ns2_fr_bind_by_netif;
+gprs_ns2_fr_connect;
+gprs_ns2_fr_nsvc_by_dlci;
+gprs_ns2_fr_nsvc_dlci;
+gprs_ns2_is_fr_bind;
+gprs_ns2_find_vc_by_dlci;
+gprs_ns2_instantiate;
+gprs_ns2_ip_bind;
+gprs_ns2_ip_bind_by_sockaddr;
+gprs_ns2_ip_bind_set_dscp;
+gprs_ns2_ip_bind_set_priority;
+gprs_ns2_ip_bind_set_sns_weight;
+gprs_ns2_ip_bind_sockaddr;
+gprs_ns2_ip_connect;
+gprs_ns2_ip_connect2;
+gprs_ns2_ip_connect_inactive;
+gprs_ns2_ip_vc_local;
+gprs_ns2_ip_vc_remote;
+gprs_ns2_ip_vc_equal;
+gprs_ns2_is_frgre_bind;
+gprs_ns2_is_ip_bind;
+gprs_ns2_ll_str;
+gprs_ns2_ll_str_buf;
+gprs_ns2_ll_str_c;
+gprs_ns2_lltype_strs;
+gprs_ns2_nse_by_nsei;
+gprs_ns2_nse_foreach_nsvc;
+gprs_ns2_nse_nsei;
+gprs_ns2_nse_sns_remote;
+gprs_ns2_nsvc_by_nsvci;
+gprs_ns2_nsvc_by_sockaddr;
+gprs_ns2_nsvc_state_name;
+gprs_ns2_prim_strs;
+gprs_ns2_recv_prim;
+gprs_ns2_reset_persistent_nsvcs;
+gprs_ns2_start_alive_all_nsvcs;
+gprs_ns2_sns_add_bind;
+gprs_ns2_sns_add_endpoint;
+gprs_ns2_sns_del_bind;
+gprs_ns2_sns_del_endpoint;
+gprs_ns2_vty_init;
+gprs_ns2_vty_init_reduced;
+
+gprs_nsvc_create2;
gprs_nsvc_delete;
gprs_nsvc_reset;
gprs_nsvc_by_nsvci;
@@ -84,5 +213,7 @@ bssgp_bvc_ctx_free;
btsctx_by_bvci_nsei;
btsctx_by_raid_cid;
+osmo_pdef_bssgp;
+
local: *;
};
diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am
index 0aa0de37..e6b687d2 100644
--- a/src/gsm/Makefile.am
+++ b/src/gsm/Makefile.am
@@ -1,10 +1,10 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
-LIBVERSION=13:0:0
+LIBVERSION=20:0:0
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
-AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN}
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
if ENABLE_PSEUDOTALLOC
AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
@@ -15,6 +15,8 @@ noinst_HEADERS = milenage/aes.h milenage/aes_i.h milenage/aes_wrap.h \
milenage/common.h milenage/crypto.h milenage/includes.h \
milenage/milenage.h
+noinst_HEADERS += tuak/KeccakP-1600-3gpp.h tuak/tuak.h
+
noinst_LTLIBRARIES = libgsmint.la
lib_LTLIBRARIES = libosmogsm.la
@@ -25,30 +27,41 @@ libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \
gsm48_ie.c gsm0808.c sysinfo.c \
gprs_cipher_core.c gprs_rlc.c gsm0480.c abis_nm.c gsm0502.c \
gsm0411_utils.c gsm0411_smc.c gsm0411_smr.c gsm0414.c \
- lapd_core.c lapdm.c kasumi.c gsm29205.c gsm_04_08_gprs.c \
- auth_core.c auth_comp128v1.c auth_comp128v23.c \
+ lapdm.c kasumi.c gsm29205.c gsm_04_08_gprs.c \
+ auth_core.c auth_comp128v1.c auth_comp128v23.c auth_xor.c auth_xor_2g.c \
auth_milenage.c milenage/aes-encblock.c gea.c \
milenage/aes-internal.c milenage/aes-internal-enc.c \
milenage/milenage.c gan.c ipa.c gsm0341.c apn.c \
- gsup.c gsup_sms.c gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \
- gsm23003.c mncc.c bts_features.c oap_client.c \
- gsm29118.c gsm48_rest_octets.c cbsp.c gsm48049.c
+ tuak/KeccakP-1600-3gpp.c tuak/tuak.c auth_tuak.c \
+ gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \
+ gsm23003.c gsm23236.c mncc.c bts_features.c oap_client.c \
+ gsm29118.c gsm48_rest_octets.c cbsp.c gsm48049.c \
+ gad.c bsslap.c bssmap_le.c kdf.c iuup.c gsm44021.c gsm44068.c rlp.c
+if !EMBEDDED
+libgsmint_la_SOURCES += gsup.c gsup_sms.c
+endif # !EMBEDDED
+
libgsmint_la_LDFLAGS = -no-undefined
-libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la
+libgsmint_la_LIBADD = $(top_builddir)/src/core/libosmocore.la $(top_builddir)/src/isdn/libosmoisdn.la
libosmogsm_la_SOURCES =
libosmogsm_la_LDFLAGS = $(LTLDFLAGS_OSMOGSM) -version-info $(LIBVERSION) -no-undefined
libosmogsm_la_LIBADD = libgsmint.la $(TALLOC_LIBS)
if ENABLE_GNUTLS
-AM_CPPFLAGS += $(LIBGNUTLS_CFLAGS)
+AM_CFLAGS += $(LIBGNUTLS_CFLAGS)
libosmogsm_la_LIBADD += $(LIBGNUTLS_LIBS)
+else
+noinst_HEADERS += kdf/sha1.h kdf/sha256.h kdf/common.h kdf/sha1_i.h kdf/sha256_i.h
+libgsmint_la_SOURCES += kdf/sha256.c kdf/sha256-internal.c \
+ kdf/sha1.c kdf/sha1-internal.c
endif
EXTRA_DIST = libosmogsm.map
+EXTRA_libosmogsm_la_DEPENDENCIES = libosmogsm.map
# Convolutional codes generation
gsm0503_conv.c: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py
- $(AM_V_GEN)python $(top_srcdir)/utils/conv_gen.py gen_codes gsm
+ $(AM_V_GEN)python3 $(top_srcdir)/utils/conv_gen.py gen_codes gsm
CLEANFILES = gsm0503_conv.c
diff --git a/src/gsm/a5.c b/src/gsm/a5.c
index 223d3ad8..d223cb76 100644
--- a/src/gsm/a5.c
+++ b/src/gsm/a5.c
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*! \addtogroup a5
diff --git a/src/gsm/abis_nm.c b/src/gsm/abis_nm.c
index 3fb8f0f5..c4060e01 100644
--- a/src/gsm/abis_nm.c
+++ b/src/gsm/abis_nm.c
@@ -589,6 +589,7 @@ const struct tlv_definition abis_nm_att_tlvdef = {
/*! org.osmocom GSM A-bis OML TLV parser definition */
const struct tlv_definition abis_nm_osmo_att_tlvdef = {
.def = {
+ [NM_ATT_OSMO_NS_LINK_CFG] = { TLV_TYPE_TL16V },
[NM_ATT_OSMO_REDUCEPOWER] = { TLV_TYPE_TV },
},
};
@@ -609,6 +610,10 @@ const struct value_string abis_nm_obj_class_names[] = {
{ NM_OC_RADIO_CARRIER, "RADIO-CARRIER" },
{ NM_OC_BASEB_TRANSC, "BASEBAND-TRANSCEIVER" },
{ NM_OC_CHANNEL, "CHANNEL" },
+ { NM_OC_IPAC_E1_TRUNK, "IPAC-E1-TRUNK" },
+ { NM_OC_IPAC_E1_PORT, "IPAC-E1-PORT" },
+ { NM_OC_IPAC_E1_CHAN, "IPAC-E1-CHAN" },
+ { NM_OC_IPAC_CLK_MODULE,"IPAC-CLK-MODULE" },
{ NM_OC_BS11_ADJC, "ADJC" },
{ NM_OC_BS11_HANDOVER, "HANDOVER" },
{ NM_OC_BS11_PWR_CTRL, "POWER-CONTROL" },
@@ -698,10 +703,104 @@ static const enum abis_nm_chan_comb chcomb4pchan[] = {
[GSM_PCHAN_UNKNOWN] = 0xff,
[GSM_PCHAN_CCCH_SDCCH4_CBCH] = NM_CHANC_BCCH_CBCH,
[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = NM_CHANC_SDCCH_CBCH,
- [GSM_PCHAN_TCH_F_TCH_H_PDCH] = NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH,
+ [GSM_PCHAN_OSMO_DYN] = NM_CHANC_OSMO_DYN,
/* FIXME: bounds check */
};
+const struct value_string abis_nm_ipacc_freq_band_desc[] = {
+ { NM_IPAC_F_FREQ_BAND_PGSM, "P-GSM" },
+ { NM_IPAC_F_FREQ_BAND_EGSM, "E-GSM" },
+ { NM_IPAC_F_FREQ_BAND_RGSM, "R-GSM" },
+ { NM_IPAC_F_FREQ_BAND_DCS, "DCS-1800" },
+ { NM_IPAC_F_FREQ_BAND_PCS, "PCS-1900" },
+ { NM_IPAC_F_FREQ_BAND_850, "850" },
+ { NM_IPAC_F_FREQ_BAND_480, "480" },
+ { NM_IPAC_F_FREQ_BAND_450, "450" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_ciph_algo_desc[] = {
+ { NM_IPAC_F_CIPH_ALGO_A51, "A5/1" },
+ { NM_IPAC_F_CIPH_ALGO_A52, "A5/2" },
+ { NM_IPAC_F_CIPH_ALGO_A53, "A5/3" },
+ { NM_IPAC_F_CIPH_ALGO_A54, "A5/4" },
+ { NM_IPAC_F_CIPH_ALGO_A55, "A5/5" },
+ { NM_IPAC_F_CIPH_ALGO_A56, "A5/6" },
+ { NM_IPAC_F_CIPH_ALGO_A57, "A5/7" },
+ { NM_IPAC_F_CIPH_ALGO_A58, "A5/8" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_chant_desc[] = {
+ { NM_IPAC_F_CHANT_TCHF, "TCH/F" },
+ { NM_IPAC_F_CHANT_TCHH, "TCH/H" },
+ { NM_IPAC_F_CHANT_SDCCH8, "SDCCH/8" },
+ { NM_IPAC_F_CHANT_BCCH, "BCCH" },
+ { NM_IPAC_F_CHANT_BCCH_SDCCH4, "BCCH+SDCCH/4" },
+ { NM_IPAC_F_CHANT_BCH, "BCH" },
+ { NM_IPAC_F_CHANT_BCCH_SDCCH4_CBCH, "BCCH+SDCCH/4+CBCH" },
+ { NM_IPAC_F_CHANT_SDCCH8_CBCH, "SDCCH/8+CBCH" },
+ { NM_IPAC_F_CHANT_PDCHF, "PDCH/F" },
+ { NM_IPAC_F_CHANT_TCHF_PDCHF, "TCH/F or PDCH/F" },
+ { NM_IPAC_F_CHANT_TCHH_PDCHH, "TCH/H+PDCH/H" },
+ { NM_IPAC_F_CHANT_TCHF_TCHH, "TCH/F or TCH/H" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_chanm_desc[] = {
+ { NM_IPAC_F_CHANM_SPEECH_FS, "FR1/FS" },
+ { NM_IPAC_F_CHANM_SPEECH_EFS, "FR2/EFS" },
+ { NM_IPAC_F_CHANM_SPEECH_AFS, "FR3/AFS" },
+ { NM_IPAC_F_CHANM_SPEECH_HS, "HR1/HS" },
+ { NM_IPAC_F_CHANM_SPEECH_AHS, "HR3/AHS" },
+ { NM_IPAC_F_CHANM_CSD_NT_4k8, "CSD NT 4.8 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_NT_9k6, "CSD NT 9.6 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_NT_14k4, "CSD NT 14.4 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_1200_75, "CSD T 1200/75 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_600, "CSD T 600 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_1k2, "CSD T 1k2 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_2k4, "CSD T 2.4 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_4k8, "CSD T 4.8 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_9k6, "CSD T 9.6 kbit/s" },
+ { NM_IPAC_F_CHANM_CSD_T_14k4, "CSD T 14.4 kbit/s" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_gprs_coding_desc[] = {
+ { NM_IPAC_F_GPRS_CODING_CS1, "CS1" },
+ { NM_IPAC_F_GPRS_CODING_CS2, "CS2" },
+ { NM_IPAC_F_GPRS_CODING_CS3, "CS3" },
+ { NM_IPAC_F_GPRS_CODING_CS4, "CS4" },
+ { NM_IPAC_F_GPRS_CODING_MCS1, "MCS1" },
+ { NM_IPAC_F_GPRS_CODING_MCS2, "MCS2" },
+ { NM_IPAC_F_GPRS_CODING_MCS3, "MCS3" },
+ { NM_IPAC_F_GPRS_CODING_MCS4, "MCS4" },
+ { NM_IPAC_F_GPRS_CODING_MCS5, "MCS5" },
+ { NM_IPAC_F_GPRS_CODING_MCS6, "MCS6" },
+ { NM_IPAC_F_GPRS_CODING_MCS7, "MCS7" },
+ { NM_IPAC_F_GPRS_CODING_MCS8, "MCS8" },
+ { NM_IPAC_F_GPRS_CODING_MCS9, "MCS9" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_rtp_feat_desc[] = {
+ { NM_IPAC_F_RTP_FEAT_COMPR_CONTROL, "Compression Control" },
+ { NM_IPAC_F_RTP_FEAT_IR_8k, "Intermediate Rate 8 kbit/s" },
+ { NM_IPAC_F_RTP_FEAT_IR_16k, "Intermediate Rate 16 kbit/s" },
+ { NM_IPAC_F_RTP_FEAT_IR_32k, "Intermediate Rate 32 kbit/s" },
+ { NM_IPAC_F_RTP_FEAT_IR_64k, "Intermediate Rate 64 kbit/s" },
+ { NM_IPAC_F_RTP_FEAT_MULTIPLEX_RTP, "RTP Multiplex" },
+ { NM_IPAC_F_RTP_FEAT_MULTIPLEX_SRTP, "SRTP Multiplex" },
+ { 0, NULL }
+};
+
+const struct value_string abis_nm_ipacc_rsl_feat_desc[] = {
+ { NM_IPAC_F_RSL_FEAT_PHYSICAL_CONTEXT, "Physical Context" },
+ { NM_IPAC_F_RSL_FEAT_DYN_PDCH_ACT, "Dynamic PDCH Activation" },
+ { NM_IPAC_F_RSL_FEAT_RTP_PT2, "RTP Payload Type 2" },
+ { 0, NULL }
+};
+
/*! Pack 3GPP TS 12.21 § 8.8.2 Failure Event Report into msgb */
struct msgb *abis_nm_fail_evt_rep(enum abis_nm_event_type t,
enum abis_nm_severity s,
diff --git a/src/gsm/apn.c b/src/gsm/apn.c
index a7074eab..4800702c 100644
--- a/src/gsm/apn.c
+++ b/src/gsm/apn.c
@@ -24,8 +24,8 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include <talloc.h>
+#include <osmocom/core/talloc.h>
#include <osmocom/gsm/apn.h>
#define APN_OI_GPRS_FMT "mnc%03u.mcc%03u.gprs"
diff --git a/src/gsm/auth_comp128v1.c b/src/gsm/auth_comp128v1.c
index 493ebfd0..ded3f8a6 100644
--- a/src/gsm/auth_comp128v1.c
+++ b/src/gsm/auth_comp128v1.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/crypt/auth.h>
@@ -31,9 +27,10 @@
*/
static int c128v1_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand)
{
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_COMP128v1);
comp128v1(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
vec->auth_types = OSMO_AUTH_TYPE_GSM;
diff --git a/src/gsm/auth_comp128v23.c b/src/gsm/auth_comp128v23.c
index 279d2b74..f942bc02 100644
--- a/src/gsm/auth_comp128v23.c
+++ b/src/gsm/auth_comp128v23.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/crypt/auth.h>
@@ -33,9 +29,10 @@
*/
static int c128v2_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand)
{
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_COMP128v2);
comp128v2(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
vec->auth_types = OSMO_AUTH_TYPE_GSM;
@@ -50,9 +47,10 @@ static struct osmo_auth_impl c128v2_alg = {
};
static int c128v3_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand)
{
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_COMP128v3);
comp128v3(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
vec->auth_types = OSMO_AUTH_TYPE_GSM;
diff --git a/src/gsm/auth_core.c b/src/gsm/auth_core.c
index 9e750a01..6972cb77 100644
--- a/src/gsm/auth_core.c
+++ b/src/gsm/auth_core.c
@@ -1,4 +1,4 @@
-/* (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2010-2023 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
@@ -40,6 +36,35 @@
static LLIST_HEAD(osmo_auths);
+/* generate auth_data2 from auth_data (for legacy API/ABI compatibility */
+static int auth_data2auth_data2(struct osmo_sub_auth_data2 *out, const struct osmo_sub_auth_data *in)
+{
+ out->type = in->type;
+ out->algo = in->algo;
+ switch (in->type) {
+ case OSMO_AUTH_TYPE_NONE:
+ return 0;
+ case OSMO_AUTH_TYPE_GSM:
+ memcpy(out->u.gsm.ki, in->u.gsm.ki, sizeof(out->u.gsm.ki));
+ break;
+ case OSMO_AUTH_TYPE_UMTS:
+ memcpy(out->u.umts.opc, in->u.umts.opc, sizeof(in->u.umts.opc));
+ out->u.umts.opc_len = sizeof(in->u.umts.opc);
+ memcpy(out->u.umts.k, in->u.umts.k, sizeof(in->u.umts.k));
+ out->u.umts.k_len = sizeof(in->u.umts.k);
+ memcpy(out->u.umts.amf, in->u.umts.amf, sizeof(out->u.umts.amf));
+ out->u.umts.sqn = in->u.umts.sqn;
+ out->u.umts.opc_is_op = in->u.umts.opc_is_op;
+ out->u.umts.ind_bitlen = in->u.umts.ind_bitlen;
+ out->u.umts.ind = in->u.umts.ind;
+ out->u.umts.sqn_ms = in->u.umts.sqn_ms;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
static struct osmo_auth_impl *selected_auths[_OSMO_AUTH_ALG_NUM];
/*! Register an authentication algorithm implementation with the core
@@ -142,6 +167,42 @@ int osmo_auth_3g_from_2g(struct osmo_auth_vector *vec)
}
/*! Generate authentication vector
+ * \param[out] vec Generated authentication vector. See below!
+ * \param[in] aud Subscriber-specific key material
+ * \param[in] _rand Random challenge to be used
+ * \returns 0 on success, negative error on failure
+ *
+ * This function performs the core cryptographic function of the AUC,
+ * computing authentication triples/quintuples based on the permanent
+ * subscriber data and a random value. The result is what is forwarded
+ * by the AUC via HLR and VLR to the MSC which will then be able to
+ * invoke authentication with the MS.
+ *
+ * Contrary to the older osmo_auth_gen_vec(), the caller must specify
+ * the desired RES length in the vec->res_len field prior to calling
+ * this function. The requested length must match the capabilities of
+ * the chosen algorithm (e.g. 4/8 for MILENAGE).
+ */
+int osmo_auth_gen_vec2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *_rand)
+{
+ struct osmo_auth_impl *impl = selected_auths[aud->algo];
+ int rc;
+
+ if (!impl)
+ return -ENOENT;
+
+ rc = impl->gen_vec(vec, aud, _rand);
+ if (rc < 0)
+ return rc;
+
+ memcpy(vec->rand, _rand, sizeof(vec->rand));
+
+ return 0;
+}
+
+/*! Generate authentication vector
* \param[out] vec Generated authentication vector
* \param[in] aud Subscriber-specific key material
* \param[in] _rand Random challenge to be used
@@ -157,13 +218,58 @@ int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
struct osmo_sub_auth_data *aud,
const uint8_t *_rand)
{
+ struct osmo_sub_auth_data2 aud2;
+ int rc;
+
+ if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ /* old API callers are not expected to initialize this struct field,
+ * and always expect an 8-byte RES value */
+ vec->res_len = 8;
+ }
+
+ rc = auth_data2auth_data2(&aud2, aud);
+ if (rc < 0)
+ return rc;
+
+ rc = osmo_auth_gen_vec2(vec, &aud2, _rand);
+ if (aud->type == OSMO_AUTH_TYPE_UMTS)
+ aud->u.umts.sqn = aud2.u.umts.sqn;
+
+ return rc;
+}
+
+/*! Generate authentication vector and re-sync sequence
+ * \param[out] vec Generated authentication vector. See below!
+ * \param[in] aud Subscriber-specific key material
+ * \param[in] auts AUTS value sent by the SIM/MS
+ * \param[in] rand_auts RAND value sent by the SIM/MS
+ * \param[in] _rand Random challenge to be used to generate vector
+ * \returns 0 on success, negative error on failure
+ *
+ * This function performs a special variant of the core cryptographic
+ * function of the AUC: computing authentication triples/quintuples
+ * based on the permanent subscriber data, a random value as well as the
+ * AUTS and RAND values returned by the SIM/MS. This special variant is
+ * needed if the sequence numbers between MS and AUC have for some
+ * reason become different.
+ *
+ * Contrary to the older osmo_auth_gen_vec_auts(), the caller must specify
+ * the desired RES length in the vec->res_len field prior to calling
+ * this function. The requested length must match the capabilities of
+ * the chosen algorithm (e.g. 4/8 for MILENAGE).
+ */
+int osmo_auth_gen_vec_auts2(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand)
+{
struct osmo_auth_impl *impl = selected_auths[aud->algo];
int rc;
- if (!impl)
+ if (!impl || !impl->gen_vec_auts)
return -ENOENT;
- rc = impl->gen_vec(vec, aud, _rand);
+ rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
if (rc < 0)
return rc;
@@ -192,19 +298,26 @@ int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
const uint8_t *auts, const uint8_t *rand_auts,
const uint8_t *_rand)
{
- struct osmo_auth_impl *impl = selected_auths[aud->algo];
+ struct osmo_sub_auth_data2 aud2;
int rc;
- if (!impl || !impl->gen_vec_auts)
- return -ENOENT;
+ if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ /* old API callers are not expected to initialize this struct field,
+ * and always expect an 8-byte RES value */
+ vec->res_len = 8;
+ }
- rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
+ rc = auth_data2auth_data2(&aud2, aud);
if (rc < 0)
return rc;
- memcpy(vec->rand, _rand, sizeof(vec->rand));
+ rc = osmo_auth_gen_vec_auts2(vec, &aud2, auts, rand_auts, _rand);
+ if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ aud->u.umts.sqn = aud2.u.umts.sqn;
+ aud->u.umts.sqn_ms = aud2.u.umts.sqn_ms;
+ }
- return 0;
+ return rc;
}
static const struct value_string auth_alg_vals[] = {
@@ -212,8 +325,10 @@ static const struct value_string auth_alg_vals[] = {
{ OSMO_AUTH_ALG_COMP128v1, "COMP128v1" },
{ OSMO_AUTH_ALG_COMP128v2, "COMP128v2" },
{ OSMO_AUTH_ALG_COMP128v3, "COMP128v3" },
- { OSMO_AUTH_ALG_XOR, "XOR" },
+ { OSMO_AUTH_ALG_XOR_3G, "XOR-3G" },
{ OSMO_AUTH_ALG_MILENAGE, "MILENAGE" },
+ { OSMO_AUTH_ALG_XOR_2G, "XOR-2G" },
+ { OSMO_AUTH_ALG_TUAK, "TUAK" },
{ 0, NULL }
};
@@ -249,4 +364,33 @@ void osmo_auth_c3(uint8_t kc[], const uint8_t ck[], const uint8_t ik[])
kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
}
+/*! Derive GSM SRES from UMTS [X]RES (auth function c2 from 3GPP TS 33.103 Section 6.8.1.2
+ * \param[out] sres GSM SRES value, 4 byte target buffer
+ * \param[in] res UMTS XRES, 4..16 bytes input buffer
+ * \param[in] res_len length of res parameter (in bytes)
+ * \param[in] sres_deriv_func SRES derivation function (1 or 2, see 3GPP TS 55.205 Section 4
+ */
+void osmo_auth_c2(uint8_t sres[4], const uint8_t *res, size_t res_len, uint8_t sres_deriv_func)
+{
+ uint8_t xres[16];
+
+ OSMO_ASSERT(sres_deriv_func == 1 || sres_deriv_func == 2);
+ OSMO_ASSERT(res_len <= sizeof(xres));
+
+ memcpy(xres, res, res_len);
+
+ /* zero-pad the end, if XRES is < 16 bytes */
+ if (res_len < sizeof(xres))
+ memset(xres+res_len, 0, sizeof(xres)-res_len);
+
+ if (sres_deriv_func == 1) {
+ /* SRES derivation function #1 */
+ for (unsigned int i = 0; i < 4; i++)
+ sres[i] = xres[i] ^ xres[4+i] ^ xres[8+i] ^ xres[12+i];
+ } else {
+ /* SRES derivation function #2 */
+ memcpy(sres, xres, 4);
+ }
+}
+
/*! @} */
diff --git a/src/gsm/auth_milenage.c b/src/gsm/auth_milenage.c
index 95891000..2bd05a6d 100644
--- a/src/gsm/auth_milenage.c
+++ b/src/gsm/auth_milenage.c
@@ -17,12 +17,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
+#include <errno.h>
#include <osmocom/crypt/auth.h>
#include <osmocom/core/bits.h>
#include "milenage/common.h"
@@ -32,7 +29,7 @@
* @{
*/
-static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data *aud, uint8_t *gen_opc)
+static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data2 *aud, uint8_t *gen_opc)
{
int rc;
@@ -47,7 +44,7 @@ static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data *aud, ui
}
static int milenage_gen_vec(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *_rand)
{
size_t res_len = sizeof(vec->res);
@@ -57,7 +54,15 @@ static int milenage_gen_vec(struct osmo_auth_vector *vec,
uint8_t sqn[6];
uint64_t ind_mask;
uint64_t seq_1;
- int rc;
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_MILENAGE);
+
+ if (aud->u.umts.k_len != 16)
+ return -EINVAL;
+ if (aud->u.umts.opc_len != 16)
+ return -EINVAL;
+ if (vec->res_len != 4 && vec->res_len != 8)
+ return -EINVAL;
opc = gen_opc_if_needed(aud, gen_opc);
if (!opc)
@@ -131,10 +136,9 @@ static int milenage_gen_vec(struct osmo_auth_vector *vec,
milenage_generate(opc, aud->u.umts.amf, aud->u.umts.k,
sqn, _rand,
vec->autn, vec->ik, vec->ck, vec->res, &res_len);
- vec->res_len = res_len;
- rc = gsm_milenage(opc, aud->u.umts.k, _rand, vec->sres, vec->kc);
- if (rc < 0)
- return rc;
+
+ osmo_auth_c3(vec->kc, vec->ck, vec->ik);
+ osmo_auth_c2(vec->sres, vec->res, vec->res_len, 1);
vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
@@ -145,7 +149,7 @@ static int milenage_gen_vec(struct osmo_auth_vector *vec,
}
static int milenage_gen_vec_auts(struct osmo_auth_vector *vec,
- struct osmo_sub_auth_data *aud,
+ struct osmo_sub_auth_data2 *aud,
const uint8_t *auts, const uint8_t *rand_auts,
const uint8_t *_rand)
{
@@ -154,6 +158,13 @@ static int milenage_gen_vec_auts(struct osmo_auth_vector *vec,
const uint8_t *opc;
int rc;
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_MILENAGE);
+
+ if (aud->u.umts.k_len != 16)
+ return -EINVAL;
+ if (aud->u.umts.opc_len != 16)
+ return -EINVAL;
+
opc = gen_opc_if_needed(aud, gen_opc);
rc = milenage_auts(opc, aud->u.umts.k, rand_auts, auts, sqn_out);
diff --git a/src/gsm/auth_tuak.c b/src/gsm/auth_tuak.c
new file mode 100644
index 00000000..05fbf691
--- /dev/null
+++ b/src/gsm/auth_tuak.c
@@ -0,0 +1,207 @@
+/*! \file auth_tuak.c
+ * GSM/GPRS/3G authentication core infrastructure */
+/*
+ * (C) 2023 by Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* NOTE: TUAK offers a lot of size variability in terms of size of length of MAC_A, MAC_S,
+ * but this is not used within 3GPP. The different sizes of Kc and RES are handled via
+ * osmo_sub_auth_data2. */
+
+#include <errno.h>
+#include <osmocom/crypt/auth.h>
+#include <osmocom/core/bits.h>
+#include "tuak/tuak.h"
+
+/*! \addtogroup auth
+ * @{
+ */
+
+static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data2 *aud, uint8_t *gen_opc)
+{
+ int rc;
+
+ /* Check if we only know OP and compute OPC if required */
+ if (aud->type == OSMO_AUTH_TYPE_UMTS && aud->u.umts.opc_is_op) {
+ rc = tuak_opc_gen(gen_opc, aud->u.umts.k, aud->u.umts.k_len, aud->u.umts.opc);
+ if (rc < 0)
+ return NULL;
+ return gen_opc;
+ }
+
+ return aud->u.umts.opc;
+}
+
+static int tuak_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *_rand)
+{
+ size_t res_len = vec->res_len;
+ uint64_t next_sqn;
+ uint8_t gen_opc[32];
+ const uint8_t *opc;
+ uint8_t sqn[6];
+ uint64_t ind_mask;
+ uint64_t seq_1;
+
+ switch (vec->res_len) {
+ case 4:
+ case 8:
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_TUAK);
+
+ if (aud->u.umts.k_len != 16 && aud->u.umts.k_len != 32)
+ return -EINVAL;
+ if (aud->u.umts.opc_len != 32)
+ return -EINVAL;
+
+ opc = gen_opc_if_needed(aud, gen_opc);
+ if (!opc)
+ return -1;
+
+ /* Determine next SQN, according to 3GPP TS 33.102:
+ * SQN consists of SEQ and a lower significant part of IND bits:
+ *
+ * |----------SEQ------------|
+ * |------------------------SQN-----------|
+ * |-----IND----|
+ *
+ * The IND part is used as "slots": e.g. a given HLR client will always
+ * get the same IND part, called ind here, with incrementing SEQ. In
+ * the USIM, each IND slot enforces that its SEQ are used in ascending
+ * order -- as long as that constraint is satisfied, the SQN may jump
+ * forwards and backwards. For example, for ind_bitlen == 5, asking the
+ * USIM for SQN = 32, 64, 33 is allowed, because 32 and 64 are
+ * SEQ || (ind == 0), and though 33 is below 64, it is ind == 1 and
+ * allowed. Not allowed would be 32, 96, 64, because 64 would go
+ * backwards after 96, both being ind == 0.
+ *
+ * From the last used SQN, we want to increment SEQ + 1, and then pick
+ * the matching IND part.
+ *
+ * IND size is suggested in TS 33.102 as 5 bits. SQN is 48 bits long.
+ * If ind_bitlen is passed too large here, the algorithms will break
+ * down. But at which point should we return an error? A sane limit
+ * seems to be ind_bitlen == 10, but to protect against failure,
+ * limiting ind_bitlen to 28 is enough, 28 being the number of bits
+ * suggested for the delta in 33.102, which is discussed to still
+ * require 2^15 > 32000 authentications to wrap the SQN back to the
+ * start.
+ *
+ * Note that if a caller with ind == 1 generates N vectors, the SQN
+ * stored after this will reflect SEQ + N. If then another caller with
+ * ind == 2 generates another N vectors, this will then use SEQ + N
+ * onwards and end up with SEQ + N + N. In other words, most of each
+ * SEQ's IND slots will remain unused. When looking at SQN being 48
+ * bits wide, after dropping ind_bitlen (say 5) from it, we will still
+ * have a sequence range of 2^43 = 8.8e12, eight trillion sequences,
+ * which is large enough to not bother further. With the maximum
+ * ind_bitlen of 28 enforced below, we still get more than 1 million
+ * sequences, which is also sufficiently large.
+ *
+ * An ind_bitlen of zero may be passed from legacy callers that are not
+ * aware of the IND extension. For these, below algorithm works out as
+ * before, simply incrementing SQN by 1.
+ *
+ * This is also a mechanism for tools like the osmo-auc-gen to directly
+ * request a given SQN to be used. With ind_bitlen == 0 the caller can
+ * be sure that this code will increment SQN by exactly one before
+ * generating a tuple, thus a caller would simply pass
+ * { .ind_bitlen = 0, .ind = 0, .sqn = (desired_sqn - 1) }
+ */
+
+ if (aud->u.umts.ind_bitlen > OSMO_MILENAGE_IND_BITLEN_MAX)
+ return -2;
+
+ seq_1 = 1LL << aud->u.umts.ind_bitlen;
+ ind_mask = ~(seq_1 - 1);
+
+ /* the ind index must not affect the SEQ part */
+ if (aud->u.umts.ind >= seq_1)
+ return -3;
+
+ /* keep the incremented SQN local until gsm_milenage() succeeded. */
+ next_sqn = ((aud->u.umts.sqn + seq_1) & ind_mask) + aud->u.umts.ind;
+
+ osmo_store64be_ext(next_sqn, sqn, 6);
+
+ tuak_generate(opc, aud->u.umts.amf, aud->u.umts.k, aud->u.umts.k_len,
+ sqn, _rand, vec->autn, vec->ik, vec->ck, vec->res, &res_len);
+
+ /* generate the GSM Kc + SRES values using C2 + C3 functions */
+ osmo_auth_c3(vec->kc, vec->ck, vec->ik);
+ osmo_auth_c2(vec->sres, vec->res, vec->res_len, 1);
+
+ vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
+
+ /* for storage in the caller's AUC database */
+ aud->u.umts.sqn = next_sqn;
+
+ return 0;
+}
+
+static int tuak_gen_vec_auts(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand)
+{
+ uint8_t sqn_out[6];
+ uint8_t gen_opc[32];
+ const uint8_t *opc;
+ int rc;
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_TUAK);
+
+ if (aud->u.umts.k_len != 16 && aud->u.umts.k_len != 32)
+ return -EINVAL;
+ if (aud->u.umts.opc_len != 32)
+ return -EINVAL;
+
+ opc = gen_opc_if_needed(aud, gen_opc);
+
+ rc = tuak_auts(opc, aud->u.umts.k, sizeof(aud->u.umts.k), rand_auts, auts, sqn_out);
+ if (rc < 0)
+ return rc;
+
+ aud->u.umts.sqn_ms = osmo_load64be_ext(sqn_out, 6) >> 16;
+ /* Update our "largest used SQN" from the USIM -- milenage_gen_vec()
+ * below will increment SQN. */
+ aud->u.umts.sqn = aud->u.umts.sqn_ms;
+
+ return tuak_gen_vec(vec, aud, _rand);
+}
+
+static struct osmo_auth_impl tuak_alg = {
+ .algo = OSMO_AUTH_ALG_TUAK,
+ .name = "TUAK (libosmogsm built-in)",
+ .priority = 1000,
+ .gen_vec = &tuak_gen_vec,
+ .gen_vec_auts = &tuak_gen_vec_auts,
+};
+
+static __attribute__((constructor)) void on_dso_load_tuak(void)
+{
+ osmo_auth_register(&tuak_alg);
+}
+
+/*! @} */
diff --git a/src/gsm/auth_xor.c b/src/gsm/auth_xor.c
new file mode 100644
index 00000000..a506a03d
--- /dev/null
+++ b/src/gsm/auth_xor.c
@@ -0,0 +1,191 @@
+/*! \file auth_xor.c
+ * GSM/GPRS/3G authentication core infrastructure */
+/*
+ * (C) 2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2017 by sysmocom s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * Author: Daniel Willmann <dwillmann@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bit64gen.h>
+#include <osmocom/crypt/auth.h>
+
+/*! \addtogroup auth
+ * @{
+ */
+
+static void xor(uint8_t *out, const uint8_t *a, const uint8_t *b, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+/* 3GPP TS 34.108, section 8.1.2.1 */
+static int xor_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *_rand)
+{
+ uint8_t xdout[16], cdout[8];
+ uint8_t ak[6], xmac[8];
+ int i;
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_XOR_3G);
+
+ /* Step 1: xdout = (ki or k) ^ rand */
+ if (aud->type == OSMO_AUTH_TYPE_GSM)
+ xor(xdout, aud->u.gsm.ki, _rand, sizeof(xdout));
+ else if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ if (aud->u.umts.k_len != 16)
+ return -EINVAL;
+ xor(xdout, aud->u.umts.k, _rand, sizeof(xdout));
+ } else
+ return -ENOTSUP;
+
+ /**
+ * Step 2: res = xdout
+ *
+ * Suggested length for res is 128 bits, i.e. 16 bytes,
+ * but also can be in range: 30 < n < 128 bits.
+ */
+ memcpy(vec->res, xdout, sizeof(xdout));
+ vec->res_len = sizeof(xdout);
+
+ /* ck = xdout[1-15,0] */
+ memcpy(vec->ck, xdout + 1, sizeof(xdout) - 1);
+ vec->ck[15] = xdout[0];
+
+ /* ik = xdout[2-15,0-1] */
+ memcpy(vec->ik, xdout + 2, sizeof(xdout) - 2);
+ memcpy(vec->ik + sizeof(xdout) - 2, xdout, 2);
+
+ /* ak = xdout[3-8] */
+ memcpy(ak, xdout + 3, sizeof(ak));
+
+ /**
+ * 3GPP TS 33.102, clause 6.8.1.2, b
+ * sres = c2(res) = res[0-3] ^ res[4-7] ^ res[8-11] ^ res[12-15]
+ */
+ for (i = 0; i < 4; i++) {
+ vec->sres[i] = vec->res[i] ^ vec->res[i + 4];
+ vec->sres[i] ^= vec->res[i + 8] ^ vec->res[i + 12];
+ }
+
+ /**
+ * 3GPP TS 33.102, clause 6.8.1.2, c
+ * kc = c3(ck, ik) = ck[0-7] ^ ck[8-15] ^ ik[0-7] ^ ik[8-15]
+ * FIXME: do we really have CK/IK for GSM?
+ */
+ osmo_auth_c3(vec->kc, vec->ck, vec->ik);
+
+ /* The further part is UMTS specific */
+ if (aud->type != OSMO_AUTH_TYPE_UMTS) {
+ vec->auth_types = OSMO_AUTH_TYPE_GSM;
+ return 0;
+ }
+
+ /**
+ * Step 3: cdout = sqn[0-5] || amf[0-1]
+ * NOTE (for USIM): sqn[0-5] = autn[0-5] ^ ak[0-5]
+ */
+ osmo_store64be_ext(aud->u.umts.sqn, cdout, 6);
+ memcpy(cdout + 6, aud->u.umts.amf, 2);
+
+ /* Step 4: xmac = xdout[0-8] ^ cdout[0-8] */
+ xor(xmac, xdout, cdout, sizeof(xmac));
+
+ /**
+ * Step 5: autn = sqn ^ ak || amf || mac
+ * NOTE: cdout still contains SQN from step 3
+ */
+ xor(vec->autn, cdout, ak, sizeof(ak));
+ memcpy(vec->autn + 6, aud->u.umts.amf, 2);
+ memcpy(vec->autn + 8, xmac, sizeof(xmac));
+
+ vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM;
+
+ return 0;
+}
+
+/* 3GPP TS 34.108, section 8.1.2.2 */
+static int xor_gen_vec_auts(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *auts,
+ const uint8_t *rand_auts,
+ const uint8_t *_rand)
+{
+ uint8_t xdout[16], cdout[8];
+ uint8_t ak[6], xmac[8];
+ uint8_t sqnms[6];
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_XOR_3G);
+
+ /* Step 1: xdout = (ki or k) ^ rand */
+ if (aud->type == OSMO_AUTH_TYPE_GSM)
+ xor(xdout, aud->u.gsm.ki, _rand, sizeof(xdout));
+ else if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+ if (aud->u.umts.k_len != 16)
+ return -EINVAL;
+ xor(xdout, aud->u.umts.k, _rand, sizeof(xdout));
+ } else
+ return -ENOTSUP;
+
+ /* Step 2: ak = xdout[2-8] */
+ memcpy(ak, xdout + 3, 6);
+
+ /* sqnms = auts[0-5] ^ ak[0-5] */
+ xor(sqnms, auts, ak, sizeof(ak));
+
+ /* cdout = sqnms || amf* (dummy) */
+ memcpy(cdout, sqnms, 6);
+ memset(cdout + 6, 0x00, 2);
+
+ /* xmac = xdout[0-7] ^ cdout[0-7] */
+ xor(xmac, xdout, cdout, 8);
+
+ /* Compare the last 64 bits of received AUTS with the locally-generated MAC-S */
+ if (memcmp(auts + 6, xmac, 8))
+ return -1;
+
+ /* Update the "largest used SQN" from the USIM,
+ * milenage_gen_vec() will increment it. */
+ aud->u.umts.sqn_ms = osmo_load64be_ext(sqnms, 6) >> 16;
+ aud->u.umts.sqn = aud->u.umts.sqn_ms;
+
+ return xor_gen_vec(vec, aud, _rand);
+}
+
+static struct osmo_auth_impl xor_alg = {
+ .algo = OSMO_AUTH_ALG_XOR_3G,
+ .name = "XOR-3G (libosmogsm built-in)",
+ .priority = 1000,
+ .gen_vec = &xor_gen_vec,
+ .gen_vec_auts = &xor_gen_vec_auts,
+};
+
+static __attribute__((constructor)) void on_dso_load_xor(void)
+{
+ osmo_auth_register(&xor_alg);
+}
+
+/*! @} */
diff --git a/src/gsm/auth_xor_2g.c b/src/gsm/auth_xor_2g.c
new file mode 100644
index 00000000..367c79d4
--- /dev/null
+++ b/src/gsm/auth_xor_2g.c
@@ -0,0 +1,81 @@
+/*! \file auth_xor.c
+ * GSM XOR-2G algorithm as specified in Annex 4 (A.4.1.2) of 3GPP TS 51.010-1.
+ * This is implemented by typical GSM MS tester */
+
+/*
+ * (C) 2023 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * Author: Daniel Willmann <dwillmann@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/crypt/auth.h>
+
+/*! \addtogroup auth
+ * @{
+ */
+
+static void xor(uint8_t *out, const uint8_t *a, const uint8_t *b, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+/* GSM XOR-2G algorithm as specified in Annex 4 (A.4.1.2) of 3GPP TS 51.010-1. */
+static int xor2g_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data2 *aud,
+ const uint8_t *_rand)
+{
+ uint8_t res1[16];
+
+ OSMO_ASSERT(aud->algo == OSMO_AUTH_ALG_XOR_2G);
+
+ if (aud->type != OSMO_AUTH_TYPE_GSM)
+ return -ENOTSUP;
+
+ /* Step 1: XOR to the challenge RAND, a predefined number Ki, having the same bit length (128 bits) as
+ * RAND. */
+ xor(res1, aud->u.gsm.ki, _rand, sizeof(res1));
+
+ /* Step 2: The most significant 32 bits of RES1 form SRES. */
+ memcpy(vec->sres, res1, 4);
+ /* The next 64 bits of RES1 form Kc */
+ memcpy(vec->kc, res1+4, 8);
+
+ vec->auth_types = OSMO_AUTH_TYPE_GSM;
+ return 0;
+}
+
+static struct osmo_auth_impl xor2g_alg = {
+ .algo = OSMO_AUTH_ALG_XOR_2G,
+ .name = "XOR-2G (libosmogsm built-in)",
+ .priority = 1000,
+ .gen_vec = &xor2g_gen_vec,
+};
+
+static __attribute__((constructor)) void on_dso_load_xor(void)
+{
+ osmo_auth_register(&xor2g_alg);
+}
+
+/*! @} */
diff --git a/src/gsm/bsslap.c b/src/gsm/bsslap.c
new file mode 100644
index 00000000..70ded13f
--- /dev/null
+++ b/src/gsm/bsslap.c
@@ -0,0 +1,325 @@
+/* 3GPP TS 48.071 BSSLAP protocol definitions */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/bsslap.h>
+#include <osmocom/gsm/tlv.h>
+
+#include <osmocom/core/logging.h>
+
+/*! \addtogroup bsslap
+ * @{
+ * \file bsslap.c
+ * Message encoding and decoding for 3GPP TS 48.071 BSSLAP protocol.
+ */
+
+static const struct tlv_definition osmo_bsslap_tlvdef = {
+ .def = {
+ [BSSLAP_IEI_TA] = { TLV_TYPE_TV },
+ [BSSLAP_IEI_CELL_ID] = { TLV_TYPE_FIXED, 2 },
+ [BSSLAP_IEI_CHAN_DESC] = { TLV_TYPE_FIXED, 3 },
+ [BSSLAP_IEI_MEAS_REP] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_CAUSE] = { TLV_TYPE_TV },
+ [BSSLAP_IEI_RRLP_FLAG] = { TLV_TYPE_TV },
+ [BSSLAP_IEI_RRLP] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_CELL_ID_LIST] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_ENH_MEAS_REP] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_LAC] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_FREQ_LIST] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_MS_POWER] = { TLV_TYPE_TV },
+ [BSSLAP_IEI_DELTA_TIMER] = { TLV_TYPE_TV },
+ [BSSLAP_IEI_SERVING_CELL_ID] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_ENCR_KEY] = { TLV_TYPE_FIXED, 8 },
+ [BSSLAP_IEI_CIPH_MODE_SET] = { TLV_TYPE_TV },
+ [BSSLAP_IEI_CHAN_MODE] = { TLV_TYPE_TV, 2 },
+ [BSSLAP_IEI_MR_CONFIG] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_POLLING_REPETITION] = { TLV_TYPE_TV },
+ [BSSLAP_IEI_PACKET_CHAN_DESC] = { TLV_TYPE_FIXED, 4 },
+ [BSSLAP_IEI_TLLI] = { TLV_TYPE_FIXED, 4 },
+ [BSSLAP_IEI_TFI] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_TBF_START_TIME] = { TLV_TYPE_FIXED, 2 },
+ [BSSLAP_IEI_PWRUP_START_TIME] = { TLV_TYPE_TLV },
+ [BSSLAP_IEI_LONG_ENCR_KEY] = { TLV_TYPE_FIXED, 16 },
+ [BSSLAP_IEI_CONCUR_POS_PROC_F] = { TLV_TYPE_TV },
+ },
+};
+
+#define DEC_ERR(RC, MSG_TYPE, IEI, CAUSE, fmt, args...) do { \
+ if (err && !*err) { \
+ *err = talloc_zero(err_ctx, struct osmo_bsslap_err); \
+ **err = (struct osmo_bsslap_err){ \
+ .rc = (RC), \
+ .msg_type = (MSG_TYPE), \
+ .iei = (IEI), \
+ .cause = (CAUSE), \
+ .logmsg = talloc_asprintf(*err, "Error decoding BSSLAP%s%s%s%s%s: " fmt, \
+ (MSG_TYPE) >= 0 ? " " : "", \
+ (MSG_TYPE) >= 0 ? osmo_bsslap_msgt_name(MSG_TYPE) : "", \
+ (IEI) >= 0 ? ": " : "", \
+ (IEI) >= 0 ? osmo_bsslap_iei_name(IEI) : "", \
+ (IEI) >= 0 ? " IE" : "", \
+##args), \
+ }; \
+ } \
+ return RC; \
+ } while(0)
+
+static void osmo_bsslap_ie_enc_cell_id(struct msgb *msg, uint16_t cell_id)
+{
+ msgb_put_u8(msg, BSSLAP_IEI_CELL_ID);
+ msgb_put_u16(msg, cell_id);
+}
+
+static int osmo_bsslap_ie_dec_cell_id(uint16_t *cell_id,
+ enum bsslap_msgt msgt, enum bsslap_iei iei,
+ struct osmo_bsslap_err **err, void *err_ctx,
+ const uint8_t *data, size_t len)
+{
+ if (len != 2)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Expected 2 bytes, got %zu", len);
+ *cell_id = osmo_load16be(data);
+ return 0;
+}
+
+static void osmo_bsslap_ie_enc_ta(struct msgb *msg, uint8_t ta)
+{
+ msgb_put_u8(msg, BSSLAP_IEI_TA);
+ msgb_put_u8(msg, ta);
+}
+
+static int osmo_bsslap_ie_dec_ta(uint8_t *ta,
+ enum bsslap_msgt msgt, enum bsslap_iei iei,
+ struct osmo_bsslap_err **err, void *err_ctx,
+ const uint8_t *data, size_t len)
+{
+ if (len != 1)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Expected 1 byte, got %zu", len);
+ *ta = data[0];
+ return 0;
+}
+
+static void osmo_bsslap_ie_enc_cause(struct msgb *msg, enum bsslap_cause cause)
+{
+ msgb_put_u8(msg, BSSLAP_IEI_CAUSE);
+ msgb_put_u8(msg, cause);
+}
+
+static int osmo_bsslap_ie_dec_cause(enum bsslap_cause *cause,
+ enum bsslap_msgt msgt, enum bsslap_iei iei,
+ struct osmo_bsslap_err **err, void *err_ctx,
+ const uint8_t *data, size_t len)
+{
+ if (len != 1)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Expected 1 byte, got %zu", len);
+ *cause = data[0];
+ return 0;
+}
+
+static void osmo_bsslap_ie_enc_chan_desc(struct msgb *msg, const struct gsm48_chan_desc *chan_desc)
+{
+ struct gsm48_chan_desc *put_chan_desc;
+ msgb_put_u8(msg, BSSLAP_IEI_CHAN_DESC);
+ put_chan_desc = (void*)msgb_put(msg, sizeof(*chan_desc));
+ *put_chan_desc = *chan_desc;
+}
+
+static int osmo_bsslap_ie_dec_chan_desc(struct gsm48_chan_desc *chan_desc,
+ enum bsslap_msgt msgt, enum bsslap_iei iei,
+ struct osmo_bsslap_err **err, void *err_ctx,
+ const uint8_t *data, size_t len)
+{
+ if (len != sizeof(*chan_desc))
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Expected %zu bytes, got %zu",
+ sizeof(*chan_desc), len);
+ *chan_desc = *(struct gsm48_chan_desc*)data;
+ return 0;
+}
+
+/*! Encode BSSLAP PDU and append to msgb (3GPP TS 48.071).
+ * \param[out] msg msgb to append to.
+ * \param[in] pdu PDU data to encode.
+ * \return number of bytes written, negative on error.
+ */
+int osmo_bsslap_enc(struct msgb *msg, const struct bsslap_pdu *pdu)
+{
+ uint8_t *old_tail = msg->tail;
+
+ msgb_put_u8(msg, pdu->msg_type);
+
+ switch (pdu->msg_type) {
+ case BSSLAP_MSGT_TA_REQUEST:
+ /* The TA Request message contains only the message type. */
+ break;
+
+ case BSSLAP_MSGT_TA_RESPONSE:
+ osmo_bsslap_ie_enc_cell_id(msg, pdu->ta_response.cell_id);
+ osmo_bsslap_ie_enc_ta(msg, pdu->ta_response.ta);
+ break;
+
+ case BSSLAP_MSGT_REJECT:
+ osmo_bsslap_ie_enc_cause(msg, pdu->reject);
+ break;
+
+ case BSSLAP_MSGT_RESET:
+ osmo_bsslap_ie_enc_cell_id(msg, pdu->reset.cell_id);
+ osmo_bsslap_ie_enc_ta(msg, pdu->reset.ta);
+ osmo_bsslap_ie_enc_chan_desc(msg, &pdu->reset.chan_desc);
+ osmo_bsslap_ie_enc_cause(msg, pdu->reset.cause);
+ break;
+
+ case BSSLAP_MSGT_ABORT:
+ osmo_bsslap_ie_enc_cause(msg, pdu->abort);
+ break;
+
+ case BSSLAP_MSGT_TA_LAYER3:
+ osmo_bsslap_ie_enc_ta(msg, pdu->ta_layer3.ta);
+ break;
+
+ default:
+ return -ENOTSUP;
+ }
+ return (msg->tail - old_tail);
+}
+
+/*! Decode BSSLAP PDU (3GPP TS 48.071).
+ * \param[out] pdu Write decoded values here.
+ * \param[out] err Returned pointer to error info, dynamically allocated; NULL to not return any.
+ * \param[in] err_ctx Talloc context to allocate err from, if required.
+ * \param[in] data Pointer to BSSLAP PDU raw data.
+ * \param[in] len Data length to decode.
+ * \return 0 on success, negative on error.
+ */
+int osmo_bsslap_dec(struct bsslap_pdu *pdu,
+ struct osmo_bsslap_err **err, void *err_ctx,
+ const uint8_t *data, size_t len)
+{
+ const uint8_t *ies_start;
+ int ies_len;
+ struct tlv_parsed tp;
+
+ memset(pdu, 0x00, sizeof(*pdu));
+ if (err)
+ *err = NULL;
+
+#define DEC_IE_MANDATORY(IEI, DEC_FUN, DEC_FUN_ARG) do { \
+ const struct tlv_p_entry *e; \
+ int rc; \
+ if (!(e = TLVP_GET(&tp, IEI))) \
+ DEC_ERR(-EINVAL, pdu->msg_type, IEI, LCS_CAUSE_DATA_MISSING_IN_REQ, "missing mandatory IE"); \
+ rc = DEC_FUN(DEC_FUN_ARG, pdu->msg_type, IEI, err, err_ctx, e->val, e->len); \
+ if (rc) \
+ DEC_ERR(rc, pdu->msg_type, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \
+ } while (0)
+
+ if (len < 1)
+ DEC_ERR(-EINVAL, -1, -1, LCS_CAUSE_UNSPECIFIED, "PDU too short: %zu b", len);
+
+ pdu->msg_type = data[0];
+
+ if (pdu->msg_type == BSSLAP_MSGT_TA_REQUEST) {
+ /* The TA Request message contains only the message type. */
+ return 0;
+ }
+
+ ies_start = &data[1];
+ ies_len = len - 1;
+
+ if (tlv_parse2(&tp, 1, &osmo_bsslap_tlvdef, ies_start, ies_len, 0, 0) <= 0)
+ DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "failed to parse TLV structure");
+
+ switch (pdu->msg_type) {
+
+ case BSSLAP_MSGT_TA_RESPONSE:
+ DEC_IE_MANDATORY(BSSLAP_IEI_CELL_ID, osmo_bsslap_ie_dec_cell_id, &pdu->ta_response.cell_id);
+ DEC_IE_MANDATORY(BSSLAP_IEI_TA, osmo_bsslap_ie_dec_ta, &pdu->ta_response.ta);
+ return 0;
+
+ case BSSLAP_MSGT_REJECT:
+ DEC_IE_MANDATORY(BSSLAP_IEI_CAUSE, osmo_bsslap_ie_dec_cause, &pdu->reject);
+ return 0;
+
+ case BSSLAP_MSGT_RESET:
+ DEC_IE_MANDATORY(BSSLAP_IEI_CELL_ID, osmo_bsslap_ie_dec_cell_id, &pdu->reset.cell_id);
+ DEC_IE_MANDATORY(BSSLAP_IEI_TA, osmo_bsslap_ie_dec_ta, &pdu->reset.ta);
+ DEC_IE_MANDATORY(BSSLAP_IEI_CHAN_DESC, osmo_bsslap_ie_dec_chan_desc, &pdu->reset.chan_desc);
+ DEC_IE_MANDATORY(BSSLAP_IEI_CAUSE, osmo_bsslap_ie_dec_cause, &pdu->reset.cause);
+ return 0;
+
+ case BSSLAP_MSGT_ABORT:
+ DEC_IE_MANDATORY(BSSLAP_IEI_CAUSE, osmo_bsslap_ie_dec_cause, &pdu->abort);
+ return 0;
+
+ case BSSLAP_MSGT_TA_LAYER3:
+ DEC_IE_MANDATORY(BSSLAP_IEI_TA, osmo_bsslap_ie_dec_ta, &pdu->ta_layer3.ta);
+ return 0;
+
+ default:
+ DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "Unsupported message type");
+ }
+}
+
+const struct value_string osmo_bsslap_msgt_names[] = {
+ { BSSLAP_MSGT_TA_REQUEST, "TA Request" },
+ { BSSLAP_MSGT_TA_RESPONSE, "TA Response" },
+ { BSSLAP_MSGT_REJECT, "Reject" },
+ { BSSLAP_MSGT_RESET, "Reset" },
+ { BSSLAP_MSGT_ABORT, "Abort" },
+ { BSSLAP_MSGT_TA_LAYER3, "TA Layer3" },
+ { BSSLAP_MSGT_MS_POS_CMD, "MS Position Command" },
+ { BSSLAP_MSGT_MS_POS_RESP, "MS Position Response" },
+ { BSSLAP_MSGT_UTDOA_REQ, "U-TDOA Request" },
+ { BSSLAP_MSGT_UTDOA_RESP, "U-TDOA Response" },
+ {}
+};
+
+const struct value_string osmo_bsslap_iei_names[] = {
+ { BSSLAP_IEI_TA, "Timing Advance" },
+ { BSSLAP_IEI_CELL_ID, "Cell Identity" },
+ { BSSLAP_IEI_CHAN_DESC, "Channel Description" },
+ { BSSLAP_IEI_MEAS_REP, "Measurement Report" },
+ { BSSLAP_IEI_CAUSE, "Cause" },
+ { BSSLAP_IEI_RRLP_FLAG, "RRLP Flag" },
+ { BSSLAP_IEI_RRLP, "RRLP" },
+ { BSSLAP_IEI_CELL_ID_LIST, "Cell Identity List" },
+ { BSSLAP_IEI_ENH_MEAS_REP, "Enhanced Measurement Report" },
+ { BSSLAP_IEI_LAC, "Location Area Code" },
+ { BSSLAP_IEI_FREQ_LIST, "Frequency List" },
+ { BSSLAP_IEI_MS_POWER, "MS Power" },
+ { BSSLAP_IEI_DELTA_TIMER, "Delta Timer" },
+ { BSSLAP_IEI_SERVING_CELL_ID, "Serving Cell Identifier" },
+ { BSSLAP_IEI_ENCR_KEY, "Encryption Key" },
+ { BSSLAP_IEI_CIPH_MODE_SET, "Cipher Mode Setting" },
+ { BSSLAP_IEI_CHAN_MODE, "Channel Mode" },
+ { BSSLAP_IEI_MR_CONFIG, "MultiRate Configuration" },
+ { BSSLAP_IEI_POLLING_REPETITION, "Polling Repetition" },
+ { BSSLAP_IEI_PACKET_CHAN_DESC, "Packet Channel Description" },
+ { BSSLAP_IEI_TLLI, "TLLI" },
+ { BSSLAP_IEI_TFI, "TFI" },
+ { BSSLAP_IEI_TBF_START_TIME, "TBF Starting Time" },
+ { BSSLAP_IEI_PWRUP_START_TIME, "Powerup Starting Time" },
+ { BSSLAP_IEI_LONG_ENCR_KEY, "Long Encryption Key" },
+ { BSSLAP_IEI_CONCUR_POS_PROC_F, "Concurrent Positioning Flag" },
+ {}
+};
+
+/*! @} */
diff --git a/src/gsm/bssmap_le.c b/src/gsm/bssmap_le.c
new file mode 100644
index 00000000..1ee45517
--- /dev/null
+++ b/src/gsm/bssmap_le.c
@@ -0,0 +1,937 @@
+/* 3GPP TS 49.031 BSSMAP-LE protocol definitions */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+
+#include <osmocom/core/byteswap.h>
+#include <osmocom/core/endian.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/bssmap_le.h>
+#include <osmocom/gsm/bsslap.h>
+#include <osmocom/gsm/gad.h>
+#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gsm0808.h>
+
+/*! \addtogroup bssmap_le
+ * @{
+ * \file bssmap_le.c
+ * Message encoding and decoding for 3GPP TS 49.031 BSSMAP-LE.
+ */
+
+#define BSSAP_LE_MSG_SIZE BSSMAP_MSG_SIZE
+#define BSSAP_LE_MSG_HEADROOM BSSMAP_MSG_HEADROOM
+
+static const struct tlv_definition osmo_bssmap_le_tlvdef = {
+ .def = {
+ [BSSMAP_LE_IEI_LCS_QoS] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_LCS_PRIORITY] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_LOCATION_TYPE] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_GANSS_LOCATION_TYPE] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_GEO_LOCATION] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_POSITIONING_DATA] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_GANSS_POS_DATA] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_VELOCITY_DATA] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_LCS_CAUSE] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_LCS_CLIENT_TYPE] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_APDU] = { TLV_TYPE_TL16V },
+ [BSSMAP_LE_IEI_NET_ELEM_ID] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_REQ_GPS_ASS_D] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_REQ_GANSS_ASS_D] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_DECIPH_KEYS] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_RET_ERR_REQ] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_RET_ERR_CAUSE] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_SEGMENTATION] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_CLASSMARK3_INFO] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_CAUSE] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_CELL_ID] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_CHOSEN_CHAN] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_IMSI] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_LCS_CAPABILITY] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_PKT_MEAS_REP] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_CELL_ID_LIST] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_IMEI] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_BSS_MLAT_CAP] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_CELL_INFO_LIST] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_BTS_RX_ACC_LVL] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_MLAT_METHOD] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_MLAT_TA] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_MS_SYNC_ACC] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_SHORT_ID_SET] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_RANDOM_ID_SET] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_SHORT_BSS_ID] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_RANDOM_ID] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_SHORT_ID] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_COVERAGE_CLASS] = { TLV_TYPE_TLV },
+ [BSSMAP_LE_IEI_MTA_ACC_SEC_RQD] = { TLV_TYPE_TLV },
+ },
+};
+
+#define DEC_ERR_NO_RETURN(RC, MSG_TYPE, IEI, CAUSE, fmt, args...) do { \
+ if (err && !*err) { \
+ *err = talloc_zero(err_ctx, struct osmo_bssmap_le_err); \
+ **err = (struct osmo_bssmap_le_err){ \
+ .rc = (RC), \
+ .msg_type = (MSG_TYPE), \
+ .iei = (IEI), \
+ .cause = (CAUSE), \
+ }; \
+ (*err)->logmsg = talloc_asprintf(*err, "Error decoding BSSMAP-LE%s%s%s%s%s: " fmt, \
+ (MSG_TYPE) >= 0 ? " " : "", \
+ (MSG_TYPE) >= 0 ? osmo_bssmap_le_msgt_name(MSG_TYPE) : "", \
+ (IEI) >= 0 ? ": " : "", \
+ (IEI) >= 0 ? osmo_bssmap_le_iei_name(IEI) : "", \
+ (IEI) >= 0 ? " IE" : "", \
+ ##args); \
+ } \
+ } while(0)
+
+#define DEC_ERR(RC, MSG_TYPE, IEI, CAUSE, fmt, args...) do { \
+ DEC_ERR_NO_RETURN(RC, MSG_TYPE, IEI, CAUSE, fmt, ##args); \
+ return RC; \
+ } while(0)
+
+#define DEC_IE_MANDATORY(MSG_TYPE, IEI, DEC_FUN, DEC_FUN_ARG) do { \
+ const struct tlv_p_entry *e; \
+ int rc; \
+ if (!(e = TLVP_GET(tp, IEI))) \
+ DEC_ERR(-EINVAL, MSG_TYPE, IEI, LCS_CAUSE_DATA_MISSING_IN_REQ, "missing mandatory IE"); \
+ rc = DEC_FUN(DEC_FUN_ARG, MSG_TYPE, IEI, err, err_ctx, e->val, e->len); \
+ if (rc) \
+ DEC_ERR(rc, MSG_TYPE, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \
+ } while (0)
+
+#define DEC_IE_OPTIONAL_FLAG(MSG_TYPE, IEI, DEC_FUN, DEC_FUN_ARG, PRESENCE_FLAG) do { \
+ const struct tlv_p_entry *e; \
+ int rc; \
+ if ((e = TLVP_GET(tp, IEI))) {\
+ rc = DEC_FUN(DEC_FUN_ARG, MSG_TYPE, IEI, err, err_ctx, e->val, e->len); \
+ if (rc) \
+ DEC_ERR(rc, MSG_TYPE, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \
+ PRESENCE_FLAG = true; \
+ } \
+ } while (0)
+
+#define DEC_IE_OPTIONAL(MSG_TYPE, IEI, DEC_FUN, DEC_FUN_ARG) do { \
+ const struct tlv_p_entry *e; \
+ int rc; \
+ if ((e = TLVP_GET(tp, IEI))) {\
+ rc = DEC_FUN(DEC_FUN_ARG, MSG_TYPE, IEI, err, err_ctx, e->val, e->len); \
+ if (rc) \
+ DEC_ERR(rc, MSG_TYPE, IEI, LCS_CAUSE_UNSPECIFIED, "cannot parse IE"); \
+ } \
+ } while (0)
+
+/*! Encode full BSSMAP-LE Location Type IE, including IEI tag and length.
+ * \param[inout] msg Message buffer to append to.
+ * \param[in] location_type Values to enconde.
+ * \returns length of bytes written to the msgb.
+ */
+uint8_t osmo_bssmap_le_ie_enc_location_type(struct msgb *msg,
+ const struct bssmap_le_location_type *location_type)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+ OSMO_ASSERT(msg);
+ msgb_put_u8(msg, BSSMAP_LE_IEI_LOCATION_TYPE);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+ msgb_put_u8(msg, location_type->location_information);
+
+ switch (location_type->location_information) {
+ case BSSMAP_LE_LOC_INFO_ASSIST_TARGET_MS:
+ case BSSMAP_LE_LOC_INFO_BC_DECIPHER_KEYS:
+ msgb_put_u8(msg, location_type->positioning_method);
+ break;
+ default:
+ break;
+ }
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode BSSMAP-LE Location Type IE value part.
+ * \param[out] lt Buffer to write decoded values to.
+ * \param[in] elem Pointer to the value part, the V of a TLV.
+ * \param[in] len Length, the L of a TLV.
+ * \returns 0 on success, negative on error; lt is always overwritten: cleared on error, populated with values on
+ * success.
+ */
+int osmo_bssmap_le_ie_dec_location_type(struct bssmap_le_location_type *lt,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ memset(lt, 0x00, sizeof(*lt));
+
+ if (!elem || len < 1)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
+
+ lt->location_information = elem[0];
+ switch (lt->location_information) {
+
+ case BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC:
+ if (len != 1)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
+ "location info type 'Current Geographic': length should be 1 byte, got %u", len);
+ lt->positioning_method = BSSMAP_LE_POS_METHOD_OMITTED;
+ return 0;
+
+ case BSSMAP_LE_LOC_INFO_ASSIST_TARGET_MS:
+ case BSSMAP_LE_LOC_INFO_BC_DECIPHER_KEYS:
+ if (len != 2)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
+ "location info type %d: length should be 2 bytes, got %u",
+ lt->location_information, len);
+ lt->positioning_method = elem[1];
+ switch (lt->positioning_method) {
+ case BSSMAP_LE_POS_METHOD_MOBILE_ASSISTED_E_OTD:
+ case BSSMAP_LE_POS_METHOD_MOBILE_BASED_E_OTD:
+ case BSSMAP_LE_POS_METHOD_ASSISTED_GPS:
+ return 0;
+ default:
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
+ "location info type %d: unknown Positioning Method: %d",
+ lt->location_information, lt->positioning_method);
+ }
+
+ default:
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unknown location info type %d",
+ lt->location_information);
+ }
+}
+
+/*! Encode full BSSMAP-LE LCS Client Type IE, including IEI tag and length.
+ * \param[inout] msg Message buffer to append to.
+ * \param[in] client_type Value to enconde.
+ * \returns length of bytes written to the msgb.
+ */
+static uint8_t osmo_bssmap_le_ie_enc_lcs_client_type(struct msgb *msg, enum bssmap_le_lcs_client_type client_type)
+{
+ OSMO_ASSERT(msg);
+ msgb_put_u8(msg, BSSMAP_LE_IEI_LCS_CLIENT_TYPE);
+ /* length */
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, client_type);
+ return 3;
+}
+
+static int osmo_bssmap_le_ie_dec_lcs_client_type(enum bssmap_le_lcs_client_type *client_type,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ *client_type = 0;
+
+ if (!elem || len < 1)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
+
+ *client_type = elem[0];
+
+ switch (*client_type) {
+ case BSSMAP_LE_LCS_CTYPE_VALUE_ADDED_UNSPECIFIED:
+ case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_UNSPECIFIED:
+ case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_BCAST_SERVICE:
+ case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_OAM:
+ case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_ANON_STATS:
+ case BSSMAP_LE_LCS_CTYPE_PLMN_OPER_TGT_MS_SVC:
+ case BSSMAP_LE_LCS_CTYPE_EMERG_SVC_UNSPECIFIED:
+ case BSSMAP_LE_LCS_CTYPE_LI_UNSPECIFIED:
+ return 0;
+ default:
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unknown LCS Client Type: %d", *client_type);
+ }
+}
+
+/*! Encode full BSSMAP-LE LCS Priority IE, including IEI tag and length.
+ * \param[inout] msg Message buffer to append to.
+ * \param[in] priority Value to enconde.
+ * \returns length of bytes written to the msgb.
+ */
+static uint8_t osmo_bssmap_le_ie_enc_lcs_priority(struct msgb *msg, uint8_t priority)
+{
+ OSMO_ASSERT(msg);
+ msgb_put_u8(msg, BSSMAP_LE_IEI_LCS_PRIORITY);
+ /* length */
+ msgb_put_u8(msg, 1);
+ msgb_put_u8(msg, priority);
+ return 3;
+}
+
+static int osmo_bssmap_le_ie_dec_lcs_priority(uint8_t *priority,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ if (!elem || len != 1)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unexpected length");
+
+ *priority = elem[0];
+ return 0;
+}
+
+/*! Encode full BSSMAP-LE LCS QoS IE, including IEI tag and length.
+ * \param[inout] msg Message buffer to append to.
+ * \param[in] priority Value to enconde.
+ * \returns length of bytes written to the msgb.
+ */
+static uint8_t osmo_bssmap_le_ie_enc_lcs_qos(struct msgb *msg, const struct osmo_bssmap_le_lcs_qos *qos)
+{
+ OSMO_ASSERT(msg);
+ msgb_tlv_put(msg, BSSMAP_LE_IEI_LCS_QoS, sizeof(*qos), (const uint8_t *)qos);
+ return 2 + sizeof(*qos);
+}
+
+static int osmo_bssmap_le_ie_dec_lcs_qos(struct osmo_bssmap_le_lcs_qos *qos,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ if (!elem || len != sizeof(*qos))
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "unexpected length");
+
+ memcpy(qos, elem, len);
+ return 0;
+}
+
+/*! Encode the value part of 3GPP TS 49.031 10.13 LCS Cause, without IEI and len.
+ * Identically used in 3GPP TS 48.008 3.2.2.66. Usage example:
+ *
+ * uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_LCS_CAUSE);
+ * int rc = osmo_lcs_cause_enc(msg, &lcs_cause);
+ * if (rc < 0)
+ * goto error;
+ * *l = rc;
+ *
+ * \param[inout] msg Message buffer to append the LCS Cause values to.
+ * \param[in] lcs_cause LCS Cause values to enconde.
+ * \returns length of bytes written to the msgb.
+ */
+int osmo_lcs_cause_enc(struct msgb *msg, const struct lcs_cause_ie *lcs_cause)
+{
+ msgb_put_u8(msg, lcs_cause->cause_val);
+ if (lcs_cause->cause_val == LCS_CAUSE_POS_METH_FAILURE && lcs_cause->diag_val_present) {
+ msgb_put_u8(msg, lcs_cause->diag_val);
+ return 2;
+ }
+ return 1;
+}
+
+/*! Decode the value part of 3GPP TS 49.031 10.13 LCS Cause, without IEI and len.
+ * Identically used in 3GPP TS 48.008 3.2.2.66.
+ *
+ * \param[out] lcs_cause Write decoded LCS Cause values here.
+ * \param[in] data Encoded cause bytes.
+ * \param[in] len Length of data in bytes.
+ * \returns 0 on success, negative on error.
+ */
+int osmo_lcs_cause_dec(struct lcs_cause_ie *lcs_cause,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *data, uint8_t len)
+{
+ memset(lcs_cause, 0x00, sizeof(*lcs_cause));
+
+ if (!data || len < 1)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
+
+ lcs_cause->present = true;
+ lcs_cause->cause_val = data[0];
+ if (len > 1) {
+ lcs_cause->diag_val_present = true;
+ lcs_cause->diag_val = data[1];
+ }
+ if (len > 2)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "expected length <= 2, got %u", len);
+
+ return 0;
+}
+
+static int osmo_bssmap_le_ie_enc_apdu(struct msgb *msg, const struct bsslap_pdu *bsslap)
+{
+ uint8_t *old_tail;
+ void *l;
+ msgb_put_u8(msg, BSSMAP_LE_IEI_APDU);
+ l = msgb_put(msg, 2);
+ old_tail = msg->tail;
+ msgb_put_u8(msg, BSSMAP_LE_APDU_PROT_BSSLAP);
+ int rc = osmo_bsslap_enc(msg, bsslap);
+ if (rc <= 0)
+ return -EINVAL;
+ osmo_store16be(msg->tail - old_tail, l);
+ return 0;
+}
+
+static int osmo_bssmap_le_ie_dec_apdu(struct bsslap_pdu *bsslap,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *data, size_t len)
+{
+ enum bssmap_le_apdu_proto proto;
+ struct osmo_bsslap_err *bsslap_err;
+
+ if (!data || len < 1)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "zero length");
+
+ proto = data[0];
+
+ switch (proto) {
+ case BSSMAP_LE_APDU_PROT_BSSLAP:
+ if (osmo_bsslap_dec(bsslap, &bsslap_err, err_ctx, data + 1, len - 1)) {
+ DEC_ERR_NO_RETURN(bsslap_err ? bsslap_err->rc : -EINVAL,
+ msgt, iei, LCS_CAUSE_UNSPECIFIED,
+ "Error decoding BSSLAP%s%s",
+ bsslap_err && bsslap_err->logmsg ? ": " : "",
+ bsslap_err && bsslap_err->logmsg ? bsslap_err->logmsg : "");
+ (*err)->bsslap_err = bsslap_err;
+ return (*err)->rc;
+ }
+ return 0;
+ case BSSMAP_LE_APDU_PROT_LLP:
+ case BSSMAP_LE_APDU_PROT_SMLCPP:
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Unimplemented APDU type: %d", proto);
+ default:
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Invalid APDU type: %d", proto);
+ }
+}
+
+static int osmo_bssmap_le_ie_dec_cell_id(struct gsm0808_cell_id *cell_id,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ int rc;
+ rc = gsm0808_dec_cell_id(cell_id, elem, len);
+ if (rc <= 0)
+ DEC_ERR(rc, msgt, iei, LCS_CAUSE_UNSPECIFIED, "Error decoding Cell Identifier %s",
+ osmo_hexdump_c(err_ctx, elem, len));
+ return 0;
+}
+
+static int osmo_bssmap_le_ie_dec_imsi(struct osmo_mobile_identity *imsi,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ int rc;
+ rc = osmo_mobile_identity_decode(imsi, elem, len, false);
+ if (rc || imsi->type != GSM_MI_TYPE_IMSI)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
+ "cannot parse IMSI identity %s", osmo_hexdump_c(err_ctx, elem, len));
+ return 0;
+}
+
+static int osmo_bssmap_le_ie_dec_imei(struct osmo_mobile_identity *imei,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ int rc;
+ rc = osmo_mobile_identity_decode(imei, elem, len, false);
+ if (rc || imei->type != GSM_MI_TYPE_IMEI)
+ DEC_ERR(-EINVAL, msgt, iei, LCS_CAUSE_UNSPECIFIED,
+ "cannot parse IMEI identity %s", osmo_hexdump_c(err_ctx, elem, len));
+ return 0;
+}
+
+static int osmo_bssmap_le_ie_dec_gad(union gad_raw *gad,
+ enum bssmap_le_msgt msgt, enum bssmap_le_iei iei,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *elem, uint8_t len)
+{
+ struct osmo_gad_err *gad_err;
+ if (osmo_gad_raw_read(gad, &gad_err, err_ctx, elem, len)) {
+ DEC_ERR_NO_RETURN(gad_err ? gad_err->rc : -EINVAL,
+ msgt, BSSMAP_LE_IEI_GEO_LOCATION, LCS_CAUSE_UNSPECIFIED,
+ "Error decoding GAD%s%s",
+ gad_err && gad_err->logmsg ? ": " : "",
+ gad_err && gad_err->logmsg ? gad_err->logmsg : "");
+ (*err)->gad_err = gad_err;
+ return (*err)->rc;
+ }
+ return 0;
+}
+
+struct osmo_bssap_le_header {
+ uint8_t type;
+ uint8_t length;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/*! Return the BSSMAP-LE msg_type from a BSSAP-LE PDU, e.g. from a msgb_l3().
+ * \param[in] data BSSAP-LE PDU data, starting with BSSAP-LE discriminator.
+ * \param[in] len Length of data in bytes.
+ * \returns bssmap_le_msgt or negative on error or non-BSSMAP-LE discriminator. */
+enum bssmap_le_msgt osmo_bssmap_le_msgt(const uint8_t *data, uint8_t len)
+{
+ const struct osmo_bssap_le_header *h = (void*)data;
+ if (!data || len < sizeof(struct osmo_bssap_le_header) + 1)
+ return -1;
+ if (h->type != BSSAP_LE_MSG_DISCR_BSSMAP_LE)
+ return -1;
+ return h->data[0];
+}
+
+static int osmo_bssmap_le_enc_reset(struct msgb *msg, enum gsm0808_cause cause)
+{
+ /* The BSSMAP-LE Reset Cause is defined as identical to the 3GPP TS 48.008 Cause. */
+ gsm0808_enc_cause(msg, cause);
+ return 0;
+}
+
+static int osmo_bssmap_le_dec_reset(enum gsm0808_cause *cause,
+ enum bssmap_le_msgt msgt,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const struct tlv_parsed *tp)
+{
+ const struct tlv_p_entry *e;
+
+ if (!(e = TLVP_GET(tp, BSSMAP_LE_IEI_CAUSE)))
+ DEC_ERR(-EINVAL, msgt, BSSMAP_LE_IEI_CAUSE, LCS_CAUSE_DATA_MISSING_IN_REQ, "missing mandatory IE");
+
+ *cause = gsm0808_get_cause(tp);
+ if (*cause < 0)
+ DEC_ERR(-EINVAL, msgt, BSSMAP_LE_IEI_CAUSE, LCS_CAUSE_UNSPECIFIED, "cannot parse IE");
+
+ return 0;
+}
+
+static int osmo_bssmap_le_enc_perform_loc_req(struct msgb *msg, const struct bssmap_le_perform_loc_req *params)
+{
+ osmo_bssmap_le_ie_enc_location_type(msg, &params->location_type);
+
+ gsm0808_enc_cell_id(msg, &params->cell_id);
+
+ if (params->lcs_client_type_present)
+ osmo_bssmap_le_ie_enc_lcs_client_type(msg, params->lcs_client_type);
+
+ if (params->more_items && params->lcs_priority_present)
+ osmo_bssmap_le_ie_enc_lcs_priority(msg, params->lcs_priority);
+
+ if (params->more_items && params->lcs_qos_present)
+ osmo_bssmap_le_ie_enc_lcs_qos(msg, &params->lcs_qos);
+
+ if (params->apdu_present) {
+ int rc = osmo_bssmap_le_ie_enc_apdu(msg, &params->apdu);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (params->imsi.type == GSM_MI_TYPE_IMSI) {
+ uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_IMSI);
+ int rc = osmo_mobile_identity_encode_msgb(msg, &params->imsi, false);
+ if (rc < 0)
+ return rc;
+ *l = rc;
+ }
+
+ if (params->imei.type == GSM_MI_TYPE_IMEI) {
+ uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_IMEI);
+ int rc = osmo_mobile_identity_encode_msgb(msg, &params->imei, false);
+ if (rc < 0)
+ return rc;
+ *l = rc;
+ }
+ return 0;
+}
+
+static int osmo_bssmap_le_dec_perform_loc_req(struct bssmap_le_perform_loc_req *params,
+ enum bssmap_le_msgt msgt,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const struct tlv_parsed *tp)
+{
+ memset(params, 0x00, sizeof(*params));
+
+ DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LOCATION_TYPE, osmo_bssmap_le_ie_dec_location_type,
+ &params->location_type);
+ DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_CELL_ID, osmo_bssmap_le_ie_dec_cell_id,
+ &params->cell_id);
+ DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_CLIENT_TYPE, osmo_bssmap_le_ie_dec_lcs_client_type,
+ &params->lcs_client_type, params->lcs_client_type_present);
+ DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_PRIORITY, osmo_bssmap_le_ie_dec_lcs_priority,
+ &params->lcs_priority, params->lcs_priority_present);
+ DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_LCS_QoS, osmo_bssmap_le_ie_dec_lcs_qos,
+ &params->lcs_qos, params->lcs_qos_present);
+ DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, &params->apdu,
+ params->apdu_present);
+ DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMSI, osmo_bssmap_le_ie_dec_imsi, &params->imsi);
+ DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_IMEI, osmo_bssmap_le_ie_dec_imei, &params->imei);
+
+ if (params->lcs_priority_present || params->lcs_qos_present)
+ params->more_items = true;
+
+ return 0;
+}
+
+static int osmo_bssmap_le_enc_perform_loc_resp(struct msgb *msg, const struct bssmap_le_perform_loc_resp *params)
+{
+ if (params->location_estimate_present) {
+ uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_GEO_LOCATION);
+ int rc = osmo_gad_raw_write(msg, &params->location_estimate);
+ if (rc < 0)
+ return rc;
+ *l = rc;
+ }
+
+ if (params->lcs_cause.present) {
+ uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_LCS_CAUSE);
+ int rc = osmo_lcs_cause_enc(msg, &params->lcs_cause);
+ if (rc < 0)
+ return rc;
+ *l = rc;
+ }
+ return 0;
+}
+
+static int osmo_bssmap_le_dec_perform_loc_resp(struct bssmap_le_perform_loc_resp *params,
+ enum bssmap_le_msgt msgt,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const struct tlv_parsed *tp)
+{
+ memset(params, 0x00, sizeof(*params));
+
+ DEC_IE_OPTIONAL_FLAG(msgt, BSSMAP_LE_IEI_GEO_LOCATION, osmo_bssmap_le_ie_dec_gad, &params->location_estimate,
+ params->location_estimate_present);
+ DEC_IE_OPTIONAL(msgt, BSSMAP_LE_IEI_LCS_CAUSE, osmo_lcs_cause_dec, &params->lcs_cause);
+
+ return 0;
+}
+
+static int osmo_bssmap_le_enc_perform_loc_abort(struct msgb *msg, const struct lcs_cause_ie *params)
+{
+ uint8_t *l = msgb_tl_put(msg, BSSMAP_LE_IEI_LCS_CAUSE);
+ int rc = osmo_lcs_cause_enc(msg, params);
+ if (rc < 0)
+ return rc;
+ *l = rc;
+ return 0;
+}
+
+static int osmo_bssmap_le_dec_perform_loc_abort(struct lcs_cause_ie *params,
+ enum bssmap_le_msgt msgt,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const struct tlv_parsed *tp)
+{
+ memset(params, 0x00, sizeof(*params));
+
+ DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_LCS_CAUSE, osmo_lcs_cause_dec, params);
+ return 0;
+}
+
+static int osmo_bssmap_le_enc_conn_oriented_info(struct msgb *msg,
+ const struct bssmap_le_conn_oriented_info *params)
+{
+ return osmo_bssmap_le_ie_enc_apdu(msg, &params->apdu);
+}
+
+static int osmo_bssmap_le_dec_conn_oriented_info(struct bssmap_le_conn_oriented_info *params,
+ enum bssmap_le_msgt msgt,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const struct tlv_parsed *tp)
+{
+ memset(params, 0x00, sizeof(*params));
+
+ DEC_IE_MANDATORY(msgt, BSSMAP_LE_IEI_APDU, osmo_bssmap_le_ie_dec_apdu, &params->apdu);
+ return 0;
+}
+
+/*! Encode BSSMAP-LE PDU and add to msgb (3GPP TS 49.031).
+ * See also osmo_bssap_le_enc().
+ * \param[out] msg msgb to append to.
+ * \param[in] pdu PDU data to encode.
+ * \return number of bytes written, negative on error.
+ */
+static int osmo_bssmap_le_enc(struct msgb *msg, const struct bssmap_le_pdu *pdu)
+{
+ int rc;
+ uint8_t *old_tail;
+ old_tail = msg->tail;
+
+ msgb_v_put(msg, pdu->msg_type);
+
+ switch (pdu->msg_type) {
+ case BSSMAP_LE_MSGT_RESET:
+ rc = osmo_bssmap_le_enc_reset(msg, pdu->reset);
+ break;
+ case BSSMAP_LE_MSGT_RESET_ACK:
+ /* Consists only of the message type. */
+ rc = 0;
+ break;
+ case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:
+ rc = osmo_bssmap_le_enc_perform_loc_req(msg, &pdu->perform_loc_req);
+ break;
+ case BSSMAP_LE_MSGT_PERFORM_LOC_RESP:
+ rc = osmo_bssmap_le_enc_perform_loc_resp(msg, &pdu->perform_loc_resp);
+ break;
+ case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT:
+ rc = osmo_bssmap_le_enc_perform_loc_abort(msg, &pdu->perform_loc_abort);
+ break;
+ case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
+ rc = osmo_bssmap_le_enc_conn_oriented_info(msg, &pdu->conn_oriented_info);
+ break;
+ default:
+ rc = -ENOTSUP;
+ }
+
+ if (rc < 0)
+ return rc;
+
+ return (msg->tail - old_tail);
+}
+
+/*! Decode BSSMAP-LE PDU (3GPP TS 49.031).
+ * See also osmo_bssap_le_dec().
+ * \param[out] pdu Write decoded values here.
+ * \param[in] data Pointer to BSSMAP-LE PDU raw data.
+ * \param[in] len Data length to decode.
+ * \return NULL upon success, a human readable error message on failure.
+ */
+static int osmo_bssmap_le_dec(struct bssmap_le_pdu *pdu,
+ struct osmo_bssmap_le_err **err, void *err_ctx,
+ const uint8_t *data, size_t len)
+{
+ const uint8_t *ies_start;
+ int ies_len;
+ struct tlv_parsed tp;
+
+ memset(pdu, 0x00, sizeof(*pdu));
+
+ if (len < 1)
+ DEC_ERR(-EINVAL, -1, -1, LCS_CAUSE_UNSPECIFIED, "zero length");
+ pdu->msg_type = data[0];
+
+ /* BSSMAP-LE IEs */
+ ies_start = &data[1];
+ ies_len = len - 1;
+
+ if (tlv_parse(&tp, &osmo_bssmap_le_tlvdef, ies_start, ies_len, 0, 0) < 0)
+ DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "failed to parse TLV structure");
+
+ switch (pdu->msg_type) {
+ case BSSMAP_LE_MSGT_RESET:
+ return osmo_bssmap_le_dec_reset(&pdu->reset, pdu->msg_type, err, err_ctx, &tp);
+ case BSSMAP_LE_MSGT_RESET_ACK:
+ /* Consists only of the message type. */
+ return 0;
+ case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:
+ return osmo_bssmap_le_dec_perform_loc_req(&pdu->perform_loc_req, pdu->msg_type, err, err_ctx, &tp);
+ case BSSMAP_LE_MSGT_PERFORM_LOC_RESP:
+ return osmo_bssmap_le_dec_perform_loc_resp(&pdu->perform_loc_resp, pdu->msg_type, err, err_ctx, &tp);
+ case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT:
+ return osmo_bssmap_le_dec_perform_loc_abort(&pdu->perform_loc_abort, pdu->msg_type, err, err_ctx, &tp);
+ case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
+ return osmo_bssmap_le_dec_conn_oriented_info(&pdu->conn_oriented_info, pdu->msg_type, err, err_ctx,
+ &tp);
+ default:
+ DEC_ERR(-EINVAL, pdu->msg_type, -1, LCS_CAUSE_UNSPECIFIED, "Unsupported BSSMAP-LE message type");
+ }
+}
+
+/*! Encode BSSAP-LE PDU returned in new msgb (3GPP TS 49.031).
+ * By spec, BSSAP-LE contains either BSSMAP-LE or DTAP.
+ * \param[in] pdu PDU data to encode.
+ * \return msgb with encoded data and l2h set to the start.
+ */
+struct msgb *osmo_bssap_le_enc(const struct bssap_le_pdu *pdu)
+{
+ struct msgb *msg;
+ int rc;
+
+ if (pdu->discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE)
+ return NULL;
+
+ msg = msgb_alloc_headroom(BSSAP_LE_MSG_SIZE, BSSAP_LE_MSG_HEADROOM,
+ osmo_bssmap_le_msgt_name(pdu->bssmap_le.msg_type));
+ if (!msg)
+ return NULL;
+
+ rc = osmo_bssmap_le_enc(msg, &pdu->bssmap_le);
+ if (rc <= 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+
+ /* prepend header with final length */
+ msg->l2h = msgb_tv_push(msg, pdu->discr, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Decode BSSAP-LE PDU (3GPP TS 49.031).
+ * \param[out] pdu Write decoded values here.
+ * \param[in] data Pointer to BSSMAP-LE PDU raw data.
+ * \param[in] len Data length to decode.
+ * \return NULL upon success, a human readable error message on failure.
+ */
+int osmo_bssap_le_dec(struct bssap_le_pdu *pdu, struct osmo_bssap_le_err **err, void *err_ctx, struct msgb *msg)
+{
+ struct osmo_bssap_le_header *h;
+ unsigned int check_len;
+ struct osmo_bssmap_le_err *bssmap_le_err = NULL;
+ int rc;
+
+#define BSSAP_LE_DEC_ERR(RC, fmt, args...) do { \
+ if (err && !*err) { \
+ *err = talloc_zero(err_ctx, struct osmo_bssap_le_err); \
+ **err = (struct osmo_bssap_le_err){ \
+ .rc = (RC), \
+ .logmsg = talloc_asprintf(*err, "Error decoding BSSAP-LE: " fmt, ##args), \
+ }; \
+ } \
+ return RC; \
+ } while(0)
+
+ memset(pdu, 0x00, sizeof(*pdu));
+
+ h = msgb_l2(msg);
+ if (!h)
+ BSSAP_LE_DEC_ERR(-EINVAL, "missing msgb_l2() pointer");
+ if (msgb_l2len(msg) < sizeof(*h))
+ BSSAP_LE_DEC_ERR(-EINVAL, "message too short for header");
+ check_len = msgb_l2len(msg) - sizeof(*h);
+ if (h->length < check_len)
+ BSSAP_LE_DEC_ERR(-EINVAL, "message truncated, header length (%u) longer than message (%u)",
+ h->length, check_len);
+
+ switch (h->type) {
+ case BSSAP_LE_MSG_DISCR_BSSMAP_LE:
+ break;
+ default:
+ BSSAP_LE_DEC_ERR(-EINVAL, "unsupported discr %u, only BSSMAP-LE is implemented", h->type);
+ }
+
+ rc = osmo_bssmap_le_dec(&pdu->bssmap_le, err ? &bssmap_le_err : NULL, err_ctx,
+ h->data, h->length);
+ if (rc)
+ BSSAP_LE_DEC_ERR(rc, "%s",
+ (bssmap_le_err && bssmap_le_err->logmsg) ?
+ bssmap_le_err->logmsg : "unknown error in BSSMAP-LE part");
+ return 0;
+}
+
+const struct value_string osmo_bssmap_le_msgt_names[] = {
+ { BSSMAP_LE_MSGT_PERFORM_LOC_REQ, "PERFORM LOCATION REQUEST" },
+ { BSSMAP_LE_MSGT_PERFORM_LOC_RESP, "PERFORM LOCATION RESPONSE" },
+ { BSSMAP_LE_MSGT_PERFORM_LOC_ABORT, "PERFORM LOCATION ABORT" },
+ { BSSMAP_LE_MSGT_PERFORM_LOC_INFO, "PERFORM LOCATION INFO" },
+ { BSSMAP_LE_MSGT_ASSIST_INFO_REQ, "ASSISTANCE INFORMATION REQUEST" },
+ { BSSMAP_LE_MSGT_ASSIST_INFO_RESP, "ASSISTANCE INFORMATION RESPONSE" },
+ { BSSMAP_LE_MSGT_CONN_ORIENTED_INFO, "CONNECTION ORIENTED INFORMATON" },
+ { BSSMAP_LE_MSGT_CONN_LESS_INFO, "CONNECTIONLESS INFORMATION" },
+ { BSSMAP_LE_MSGT_RESET, "RESET" },
+ { BSSMAP_LE_MSGT_RESET_ACK, "RESET ACKNOWLEDGE" },
+ {}
+};
+
+const struct value_string osmo_bssmap_le_iei_names[] = {
+ { BSSMAP_LE_IEI_LCS_QoS, "LCS_QoS" },
+ { BSSMAP_LE_IEI_LCS_PRIORITY, "LCS_PRIORITY" },
+ { BSSMAP_LE_IEI_LOCATION_TYPE, "LOCATION_TYPE" },
+ { BSSMAP_LE_IEI_GANSS_LOCATION_TYPE, "GANSS_LOCATION_TYPE" },
+ { BSSMAP_LE_IEI_GEO_LOCATION, "GEO_LOCATION" },
+ { BSSMAP_LE_IEI_POSITIONING_DATA, "POSITIONING_DATA" },
+ { BSSMAP_LE_IEI_GANSS_POS_DATA, "GANSS_POS_DATA" },
+ { BSSMAP_LE_IEI_VELOCITY_DATA, "VELOCITY_DATA" },
+ { BSSMAP_LE_IEI_LCS_CAUSE, "LCS_CAUSE" },
+ { BSSMAP_LE_IEI_LCS_CLIENT_TYPE, "LCS_CLIENT_TYPE" },
+ { BSSMAP_LE_IEI_APDU, "APDU" },
+ { BSSMAP_LE_IEI_NET_ELEM_ID, "NET_ELEM_ID" },
+ { BSSMAP_LE_IEI_REQ_GPS_ASS_D, "REQ_GPS_ASS_D" },
+ { BSSMAP_LE_IEI_REQ_GANSS_ASS_D, "REQ_GANSS_ASS_D" },
+ { BSSMAP_LE_IEI_DECIPH_KEYS, "DECIPH_KEYS" },
+ { BSSMAP_LE_IEI_RET_ERR_REQ, "RET_ERR_REQ" },
+ { BSSMAP_LE_IEI_RET_ERR_CAUSE, "RET_ERR_CAUSE" },
+ { BSSMAP_LE_IEI_SEGMENTATION, "SEGMENTATION" },
+ { BSSMAP_LE_IEI_CLASSMARK3_INFO, "CLASSMARK3_INFO" },
+ { BSSMAP_LE_IEI_CAUSE, "CAUSE" },
+ { BSSMAP_LE_IEI_CELL_ID, "CELL_ID" },
+ { BSSMAP_LE_IEI_CHOSEN_CHAN, "CHOSEN_CHAN" },
+ { BSSMAP_LE_IEI_IMSI, "IMSI" },
+ { BSSMAP_LE_IEI_LCS_CAPABILITY, "LCS_CAPABILITY" },
+ { BSSMAP_LE_IEI_PKT_MEAS_REP, "PKT_MEAS_REP" },
+ { BSSMAP_LE_IEI_CELL_ID_LIST, "CELL_ID_LIST" },
+ { BSSMAP_LE_IEI_IMEI, "IMEI" },
+ { BSSMAP_LE_IEI_BSS_MLAT_CAP, "BSS_MLAT_CAP" },
+ { BSSMAP_LE_IEI_CELL_INFO_LIST, "CELL_INFO_LIST" },
+ { BSSMAP_LE_IEI_BTS_RX_ACC_LVL, "BTS_RX_ACC_LVL" },
+ { BSSMAP_LE_IEI_MLAT_METHOD, "MLAT_METHOD" },
+ { BSSMAP_LE_IEI_MLAT_TA, "MLAT_TA" },
+ { BSSMAP_LE_IEI_MS_SYNC_ACC, "MS_SYNC_ACC" },
+ { BSSMAP_LE_IEI_SHORT_ID_SET, "SHORT_ID_SET" },
+ { BSSMAP_LE_IEI_RANDOM_ID_SET, "RANDOM_ID_SET" },
+ { BSSMAP_LE_IEI_SHORT_BSS_ID, "SHORT_BSS_ID" },
+ { BSSMAP_LE_IEI_RANDOM_ID, "RANDOM_ID" },
+ { BSSMAP_LE_IEI_SHORT_ID, "SHORT_ID" },
+ { BSSMAP_LE_IEI_COVERAGE_CLASS, "COVERAGE_CLASS" },
+ { BSSMAP_LE_IEI_MTA_ACC_SEC_RQD, "MTA_ACC_SEC_RQD" },
+ {}
+};
+
+/*! Return a human readable string describing a BSSAP-LE PDU.
+ * \param[out] buf String buffer to write to.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] bssap_le Decoded BSSAP-LE PDU data.
+ * \returns number of chars that would be written, like snprintf().
+ */
+int osmo_bssap_le_pdu_to_str_buf(char *buf, size_t buflen, const struct bssap_le_pdu *bssap_le)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ const struct bssmap_le_pdu *bssmap_le;
+
+ switch (bssap_le->discr) {
+ case BSSAP_LE_MSG_DISCR_BSSMAP_LE:
+ bssmap_le = &bssap_le->bssmap_le;
+ OSMO_STRBUF_PRINTF(sb, "BSSMAP-LE %s", osmo_bssmap_le_msgt_name(bssmap_le->msg_type));
+ switch (bssmap_le->msg_type) {
+ case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:
+ if (bssmap_le->perform_loc_req.apdu_present)
+ OSMO_STRBUF_PRINTF(sb, " with BSSLAP %s",
+ osmo_bsslap_msgt_name(bssmap_le->perform_loc_req.apdu.msg_type));
+ break;
+
+ case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
+ OSMO_STRBUF_PRINTF(sb, " with BSSLAP %s",
+ osmo_bsslap_msgt_name(bssmap_le->conn_oriented_info.apdu.msg_type));
+ break;
+
+ default:
+ break;
+ }
+ break;
+ default:
+ OSMO_STRBUF_PRINTF(sb, "BSSAP-LE discr %d not implemented", bssap_le->discr);
+ break;
+ }
+
+ return sb.chars_needed;
+}
+
+/*! Return a human readable string describing a BSSAP-LE PDU.
+ * \param[in] ctx Talloc context to allocate string buffer from.
+ * \param[in] bssap_le Decoded BSSAP-LE PDU data.
+ * \returns string.
+ */
+char *osmo_bssap_le_pdu_to_str_c(void *ctx, const struct bssap_le_pdu *bssap_le)
+{
+ OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_bssap_le_pdu_to_str_buf, bssap_le)
+}
+
+/*! @} */
diff --git a/src/gsm/bts_features.c b/src/gsm/bts_features.c
index e4ff76c8..b6cd82ec 100644
--- a/src/gsm/bts_features.c
+++ b/src/gsm/bts_features.c
@@ -14,13 +14,9 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
*/
+#include <osmocom/core/utils.h>
#include <osmocom/gsm/bts_features.h>
const struct value_string osmo_bts_features_descs[] = {
@@ -39,11 +35,61 @@ const struct value_string osmo_bts_features_descs[] = {
{ BTS_FEAT_SPEECH_F_AMR, "Fullrate speech AMR" },
{ BTS_FEAT_SPEECH_H_AMR, "Halfrate speech AMR" },
{ BTS_FEAT_ETWS_PN, "ETWS Primary Notification via PCH" },
+ { BTS_FEAT_PAGING_COORDINATION, "BSS Paging Coordination" },
+ { BTS_FEAT_IPV6_NSVC, "NSVC IPv6" },
+ { BTS_FEAT_ACCH_REP, "FACCH/SACCH Repetition" },
+ { BTS_FEAT_CCN, "Cell Change Notification (CCN)" },
+ { BTS_FEAT_VAMOS, "VAMOS (Voice services over Adaptive Multi-user channels on One Slot)" },
+ { BTS_FEAT_ABIS_OSMO_PCU, "OsmoPCU over OML link IPA multiplex" },
+ { BTS_FEAT_BCCH_POWER_RED, "BCCH carrier power reduction mode" },
+ { BTS_FEAT_DYN_TS_SDCCH8, "Dynamic Timeslot configuration as SDCCH8" },
+ { BTS_FEAT_ACCH_TEMP_OVP, "FACCH/SACCH Temporary overpower" },
+ { BTS_FEAT_OSMUX, "Osmux (Osmocom RTP multiplexing)" },
+ { BTS_FEAT_VBS, "Voice Broadcast Service" },
+ { BTS_FEAT_VGCS, "Voice Group Call Service" },
{ 0, NULL }
};
-/*! return string representation of a BTS feature */
+/* Ensure that all BTS_FEAT_* entries are present in osmo_bts_features_descs[] */
+osmo_static_assert(ARRAY_SIZE(osmo_bts_features_descs) == _NUM_BTS_FEAT + 1, _bts_features_descs);
+
+/*! return description string of a BTS feature (osmo_bts_features_descs).
+ * To get the plain feature name, use osmo_bts_features_name() instead. */
const char *osmo_bts_feature_name(enum osmo_bts_features feature)
{
return get_value_string(osmo_bts_features_descs, feature);
}
+
+const struct value_string osmo_bts_features_names[] = {
+ { BTS_FEAT_HSCSD, "HSCSD" },
+ { BTS_FEAT_GPRS, "GPRS" },
+ { BTS_FEAT_EGPRS, "EGPRS" },
+ { BTS_FEAT_ECSD, "ECSD" },
+ { BTS_FEAT_HOPPING, "HOPPING" },
+ { BTS_FEAT_MULTI_TSC, "MULTI_TSC" },
+ { BTS_FEAT_OML_ALERTS, "OML_ALERTS" },
+ { BTS_FEAT_AGCH_PCH_PROP, "AGCH_PCH_PROP" },
+ { BTS_FEAT_CBCH, "CBCH" },
+ { BTS_FEAT_SPEECH_F_V1, "SPEECH_F_V1" },
+ { BTS_FEAT_SPEECH_H_V1, "SPEECH_H_V1" },
+ { BTS_FEAT_SPEECH_F_EFR, "SPEECH_F_EFR" },
+ { BTS_FEAT_SPEECH_F_AMR, "SPEECH_F_AMR" },
+ { BTS_FEAT_SPEECH_H_AMR, "SPEECH_H_AMR" },
+ { BTS_FEAT_ETWS_PN, "ETWS_PN" },
+ { BTS_FEAT_PAGING_COORDINATION, "PAGING_COORDINATION" },
+ { BTS_FEAT_IPV6_NSVC, "IPV6_NSVC" },
+ { BTS_FEAT_ACCH_REP, "ACCH_REP" },
+ { BTS_FEAT_CCN, "CCN" },
+ { BTS_FEAT_VAMOS, "VAMOS" },
+ { BTS_FEAT_ABIS_OSMO_PCU, "ABIS_OSMO_PCU" },
+ { BTS_FEAT_BCCH_POWER_RED, "BCCH_PWR_RED" },
+ { BTS_FEAT_DYN_TS_SDCCH8, "DYN_TS_SDCCH8" },
+ { BTS_FEAT_ACCH_TEMP_OVP, "ACCH_TEMP_OVP" },
+ { BTS_FEAT_OSMUX, "OSMUX" },
+ { BTS_FEAT_VBS, "VBS" },
+ { BTS_FEAT_VGCS, "VGCS" },
+ {}
+};
+
+/* Ensure that all BTS_FEAT_* entries are present in osmo_bts_features_names[] */
+osmo_static_assert(ARRAY_SIZE(osmo_bts_features_names) == _NUM_BTS_FEAT + 1, _bts_features_names);
diff --git a/src/gsm/cbsp.c b/src/gsm/cbsp.c
index ccc2df53..a5e58f4c 100644
--- a/src/gsm/cbsp.c
+++ b/src/gsm/cbsp.c
@@ -1,3 +1,4 @@
+/* Cell Broadcast Service Protocol (CBSP, 3GPP TS 48.049): Message encoding, decoding and reception */
/*
* Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
*
@@ -14,10 +15,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
@@ -33,7 +30,7 @@
#include <osmocom/gsm/cbsp.h>
#include <osmocom/gsm/gsm0808_utils.h>
-const __thread char *osmo_cbsp_errstr;
+__thread const char *osmo_cbsp_errstr;
struct msgb *osmo_cbsp_msgb_alloc(void *ctx, const char *name)
{
@@ -129,13 +126,13 @@ static int encode_wperiod(uint32_t secs)
if (secs <= 10)
return secs;
if (secs <= 30)
- return (secs-10)/2;
+ return 10 + (secs-10)/2;
if (secs <= 120)
- return (secs-30)/5;
+ return 30 + (secs-30)/5;
if (secs <= 600)
- return (secs-120)/10;
+ return 120 + (secs-120)/10;
if (secs <= 60*60)
- return (secs-600)/30;
+ return 600 + (secs-600)/30;
osmo_cbsp_errstr = "warning period out of range";
return -1;
}
@@ -176,12 +173,15 @@ static int cbsp_enc_write_repl(struct msgb *msg, const struct osmo_cbsp_write_re
}
} else {
int wperiod = encode_wperiod(in->u.emergency.warning_period);
+ uint8_t *cur;
if (wperiod < 0)
return -EINVAL;
msgb_tv_put(msg, CBSP_IEI_EMERG_IND, in->u.emergency.indicator);
msgb_tv16_put(msg, CBSP_IEI_WARN_TYPE, in->u.emergency.warning_type);
- msgb_tlv_put(msg, CBSP_IEI_WARN_SEC_INFO, sizeof(in->u.emergency.warning_sec_info),
- in->u.emergency.warning_sec_info);
+ /* Tag + fixed length value! */
+ msgb_put_u8(msg, CBSP_IEI_WARN_SEC_INFO);
+ cur = msgb_put(msg, sizeof(in->u.emergency.warning_sec_info));
+ memcpy(cur, in->u.emergency.warning_sec_info, sizeof(in->u.emergency.warning_sec_info));
msgb_tv_put(msg, CBSP_IEI_WARNING_PERIOD, wperiod);
}
return 0;
@@ -348,7 +348,12 @@ static int cbsp_enc_reset_fail(struct msgb *msg, const struct osmo_cbsp_reset_fa
/* 8.1.3.18a KEEP ALIVE */
static int cbsp_enc_keep_alive(struct msgb *msg, const struct osmo_cbsp_keep_alive *in)
{
- msgb_tv_put(msg, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, in->repetition_period);
+ int rperiod = encode_wperiod(in->repetition_period);
+ if (in->repetition_period > 120)
+ return -EINVAL;
+ if (rperiod < 0)
+ return -EINVAL;
+ msgb_tv_put(msg, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, rperiod);
return 0;
}
@@ -532,8 +537,8 @@ static int cbsp_decode_fail_list(struct llist_head *fl, void *ctx,
struct osmo_cbsp_fail_ent *ent = talloc_zero(ctx, struct osmo_cbsp_fail_ent);
unsigned int len_remain = len - (cur - buf);
OSMO_ASSERT(ent);
- ent->id_discr = cur[0];
- rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur+1, len_remain-1);
+ ent->id_discr = *cur++;
+ rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur, len_remain-1);
if (rc < 0) {
osmo_cbsp_errstr = "fail list: error decoding cell_id_union";
return rc;
@@ -634,6 +639,7 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct
struct msgb *in, void *ctx)
{
unsigned int i;
+ int rc;
/* check for mandatory IEs */
if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) ||
@@ -651,8 +657,10 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) {
uint8_t num_of_pages;
@@ -674,6 +682,7 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct
out->u.cbs.category = *TLVP_VAL(tp, CBSP_IEI_CATEGORY);
out->u.cbs.rep_period = tlvp_val16be(tp, CBSP_IEI_REP_PERIOD);
out->u.cbs.num_bcast_req = tlvp_val16be(tp, CBSP_IEI_NUM_BCAST_REQ);
+ out->u.cbs.dcs = *TLVP_VAL(tp, CBSP_IEI_DCS);
num_of_pages = *TLVP_VAL(tp, CBSP_IEI_NUM_OF_PAGES);
if (num_of_pages < 1)
return -EINVAL;
@@ -712,6 +721,8 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct
static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -727,14 +738,18 @@ static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *ou
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
@@ -747,6 +762,8 @@ static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *ou
static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
@@ -762,21 +779,27 @@ static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
}
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
INIT_LLIST_HEAD(&out->cell_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
}
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
@@ -790,6 +813,8 @@ static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
@@ -801,8 +826,10 @@ static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
@@ -815,6 +842,8 @@ static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp
static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
@@ -827,14 +856,18 @@ static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
@@ -847,6 +880,8 @@ static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct
static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
@@ -858,21 +893,27 @@ static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct t
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
INIT_LLIST_HEAD(&out->cell_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
}
if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
@@ -886,6 +927,8 @@ static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct t
static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -893,8 +936,10 @@ static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tl
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
return 0;
@@ -904,6 +949,8 @@ static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tl
static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -911,9 +958,11 @@ static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
}
INIT_LLIST_HEAD(&out->loading_list.list);
- cbsp_decode_loading_list(&out->loading_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
- TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
+ rc = cbsp_decode_loading_list(&out->loading_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
+ TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
return 0;
@@ -923,6 +972,8 @@ static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -930,25 +981,29 @@ static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
}
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
INIT_LLIST_HEAD(&out->loading_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) {
- cbsp_decode_loading_list(&out->loading_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
- TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
+ rc = cbsp_decode_loading_list(&out->loading_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
+ TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
}
- return 0;
+ return rc;
}
/* 8.1.3.10 STATUS QUERY */
static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
@@ -961,8 +1016,10 @@ static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
return 0;
@@ -972,6 +1029,8 @@ static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) ||
@@ -984,9 +1043,11 @@ static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_com
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->num_compl_list.list);
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
return 0;
}
@@ -995,6 +1056,8 @@ static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_com
static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out,
const struct tlv_parsed *tp, struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
@@ -1007,17 +1070,21 @@ static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_fail
out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
INIT_LLIST_HEAD(&out->num_compl_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
- cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
- TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
+ if (rc < 0)
+ return rc;
}
return 0;
}
@@ -1026,14 +1093,19 @@ static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_fail
static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
return -EINVAL;
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
+
return 0;
}
@@ -1041,14 +1113,19 @@ static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *
static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
return -EINVAL;
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
+
return 0;
}
@@ -1056,20 +1133,26 @@ static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const stru
static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
return -EINVAL;
}
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
INIT_LLIST_HEAD(&out->cell_list.list);
if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
}
return 0;
}
@@ -1078,12 +1161,14 @@ static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct
static int cbsp_dec_keep_alive(struct osmo_cbsp_keep_alive *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ uint8_t rperiod;
if (!TLVP_PRES_LEN(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
return -EINVAL;
}
- out->repetition_period = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD);
+ rperiod = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD);
+ out->repetition_period = decode_wperiod(rperiod);
return 0;
}
@@ -1098,6 +1183,8 @@ static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out,
static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) {
@@ -1106,8 +1193,10 @@ static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_pars
}
INIT_LLIST_HEAD(&out->cell_list.list);
- cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
- TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
+ TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
+ if (rc < 0)
+ return rc;
out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND);
@@ -1118,6 +1207,8 @@ static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_pars
static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp,
struct msgb *in, void *ctx)
{
+ int rc;
+
if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
!TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) {
osmo_cbsp_errstr = "missing/short mandatory IE";
@@ -1125,9 +1216,11 @@ static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_pars
}
INIT_LLIST_HEAD(&out->fail_list);
- cbsp_decode_fail_list(&out->fail_list, ctx,
- TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
- TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ rc = cbsp_decode_fail_list(&out->fail_list, ctx,
+ TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
+ TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
+ if (rc < 0)
+ return rc;
out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
return 0;
@@ -1168,6 +1261,7 @@ static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_
* \returns callee-allocated decoded representation of CBSP message; NULL on error */
struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in)
{
+ OSMO_ASSERT(in->l1h != NULL && in->l2h != NULL);
struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded);
const struct cbsp_header *h = msgb_l1(in);
struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */
@@ -1433,6 +1527,10 @@ int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb *
needed = len - msgb_l2len(msg);
if (needed > 0) {
+ if (needed > msgb_tailroom(msg)) {
+ rc = -ENOMEM;
+ goto discard_msg;
+ }
rc = recv(fd, msg->tail, needed, 0);
if (rc == 0)
goto discard_msg;
@@ -1456,12 +1554,7 @@ int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb *
}
}
/* else: complete message received */
- rc = msgb_l2len(msg);
- if (rc == 0) {
- /* drop empty message */
- rc = -EAGAIN;
- goto discard_msg;
- }
+ rc = msgb_length(msg);
if (tmp_msg)
*tmp_msg = NULL;
*rmsg = msg;
@@ -1474,4 +1567,45 @@ discard_msg:
return rc;
}
+/*! call-back function to segment the data at message boundaries.
+ * Returns the size of the next message. If it returns -EAGAIN or a value larger than msgb_length() (message
+ * is incomplete), the caller (e.g. osmo_io) has to wait for more data to be read. */
+int osmo_cbsp_segmentation_cb(struct msgb *msg)
+{
+ const struct cbsp_header *h;
+ int len;
+
+ if (msgb_length(msg) < sizeof(*h))
+ return -EAGAIN;
+
+ h = (const struct cbsp_header *) msg->data;
+ msg->l1h = msg->data;
+ msg->l2h = msg->data + sizeof(*h);
+ /* then read the length as specified in the header */
+ len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
+
+ return sizeof(*h) + len;
+}
+
+/*! value_string[] for enum osmo_cbsp_cause. */
+const struct value_string osmo_cbsp_cause_names[] = {
+ { OSMO_CBSP_CAUSE_PARAM_NOT_RECOGNISED, "Parameter-not-recognised" },
+ { OSMO_CBSP_CAUSE_PARAM_VALUE_INVALID, "Parameter-value-invalid" },
+ { OSMO_CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED, "Message-reference-not-identified" },
+ { OSMO_CBSP_CAUSE_CELL_ID_NOT_VALID, "Cell-identity-not-valid" },
+ { OSMO_CBSP_CAUSE_UNRECOGNISED_MESSAGE, "Unrecognised-message" },
+ { OSMO_CBSP_CAUSE_MISSING_MANDATORY_ELEMENT, "Missing-mandatory-element" },
+ { OSMO_CBSP_CAUSE_BSC_CAPACITY_EXCEEDED, "BSC-capacity-exceeded" },
+ { OSMO_CBSP_CAUSE_CELL_MEMORY_EXCEEDED, "Cell-memory-exceeded" },
+ { OSMO_CBSP_CAUSE_BSC_MEMORY_EXCEEDED, "BSC-memory-exceeded" },
+ { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_SUPPORTED, "Cell-broadcast-not-supported" },
+ { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_OPERATIONAL, "Cell-broadcast-not-operational" },
+ { OSMO_CBSP_CAUSE_INCOMPATIBLE_DRX_PARAM, "Incompatible-DRX-parameter:"},
+ { OSMO_CBSP_CAUSE_EXT_CHAN_NOT_SUPPORTED, "Extended-channel-not-supported"},
+ { OSMO_CBSP_CAUSE_MSG_REF_ALREADY_USED, "Message-reference-already-used"},
+ { OSMO_CBSP_CAUSE_UNSPECIFIED_ERROR, "Unspecified-error"},
+ { OSMO_CBSP_CAUSE_LAI_OR_LAC_NOT_VALID, "LAI-or-LAC-not-valid"},
+ { 0, NULL }
+};
+
#endif /* HAVE_SYS_SOCKET_H */
diff --git a/src/gsm/comp128.c b/src/gsm/comp128.c
index b28a8437..e252b04c 100644
--- a/src/gsm/comp128.c
+++ b/src/gsm/comp128.c
@@ -58,10 +58,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
diff --git a/src/gsm/comp128v23.c b/src/gsm/comp128v23.c
index 550f6a49..0947017c 100644
--- a/src/gsm/comp128v23.c
+++ b/src/gsm/comp128v23.c
@@ -23,10 +23,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
diff --git a/src/gsm/gad.c b/src/gsm/gad.c
new file mode 100644
index 00000000..23b907ac
--- /dev/null
+++ b/src/gsm/gad.c
@@ -0,0 +1,491 @@
+/* 3GPP TS 23.032 GAD: Universal Geographical Area Description */
+/*
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gad.h>
+
+/*! \addtogroup gad
+ * @{
+ * \file gad.c
+ * Message encoding and decoding for 3GPP TS 23.032 GAD: Universal Geographical Area Description.
+ */
+
+const struct value_string osmo_gad_type_names[] = {
+ { GAD_TYPE_ELL_POINT, "Ellipsoid-point" },
+ { GAD_TYPE_ELL_POINT_UNC_CIRCLE, "Ellipsoid-point-with-uncertainty-circle" },
+ { GAD_TYPE_ELL_POINT_UNC_ELLIPSE, "Ellipsoid-point-with-uncertainty-ellipse" },
+ { GAD_TYPE_POLYGON, "Polygon" },
+ { GAD_TYPE_ELL_POINT_ALT, "Ellipsoid-point-with-altitude" },
+ { GAD_TYPE_ELL_POINT_ALT_UNC_ELL, "Ellipsoid-point-with-altitude-and-uncertainty-ellipsoid" },
+ { GAD_TYPE_ELL_ARC, "Ellipsoid-arc" },
+ { GAD_TYPE_HA_ELL_POINT_UNC_ELLIPSE, "High-accuracy-ellipsoid-point-with-uncertainty-ellipse" },
+ { GAD_TYPE_HA_ELL_POINT_ALT_UNC_ELL, "High-accuracy-ellipsoid-point-with-altitude-and-uncertainty-ellipsoid" },
+ {}
+};
+
+/*! Encode a latitude value according to 3GPP TS 23.032.
+ * Useful to clamp a latitude to an actually encodable accuracy:
+ * set_lat = osmo_gad_dec_lat(osmo_gad_enc_lat(orig_lat));
+ * \param[in] deg_1e6 Latitude in micro degrees (degrees * 1e6), -90'000'000 (S) .. 90'000'000 (N).
+ * \returns encoded latitude in host-byte-order (24bit).
+ */
+uint32_t osmo_gad_enc_lat(int32_t deg_1e6)
+{
+ /* N <= ((2**23)/90)*X < N+1
+ * N: encoded latitude
+ * X: latitude in degrees
+ */
+ int32_t sign = 0;
+ int64_t x;
+ deg_1e6 = OSMO_MAX(-90000000, OSMO_MIN(90000000, deg_1e6));
+ if (deg_1e6 < 0) {
+ sign = 1 << 23;
+ deg_1e6 = -deg_1e6;
+ }
+ x = deg_1e6;
+ x <<= 23;
+ x += (1 << 23) - 1;
+ x /= 90 * 1000000;
+ return sign | (x & 0x7fffff);
+}
+
+/*! Decode a latitude value according to 3GPP TS 23.032.
+ * Useful to clamp a latitude to an actually encodable accuracy:
+ * set_lat = osmo_gad_dec_lat(osmo_gad_enc_lat(orig_lat));
+ * \param[in] lat encoded latitude in host-byte-order (24bit).
+ * \returns decoded latitude in micro degrees (degrees * 1e6), -90'000'000 (S) .. 90'000'000 (N).
+ */
+int32_t osmo_gad_dec_lat(uint32_t lat)
+{
+ int64_t sign = 1;
+ int64_t x;
+ if (lat & 0x800000) {
+ sign = -1;
+ lat &= 0x7fffff;
+ }
+ x = lat;
+ x *= 90 * 1000000;
+ x >>= 23;
+ x *= sign;
+ return x;
+}
+
+/*! Encode a longitude value according to 3GPP TS 23.032.
+ * Useful to clamp a longitude to an actually encodable accuracy:
+ * set_lon = osmo_gad_dec_lon(osmo_gad_enc_lon(orig_lon));
+ * \param[in] deg_1e6 Longitude in micro degrees (degrees * 1e6), -180'000'000 (W) .. 180'000'000 (E).
+ * \returns encoded longitude in host-byte-order (24bit).
+ */
+uint32_t osmo_gad_enc_lon(int32_t deg_1e6)
+{
+ /* -180 .. 180 degrees mapped to a signed 24 bit integer.
+ * N <= ((2**24)/360) * X < N+1
+ * N: encoded longitude
+ * X: longitude in degrees
+ */
+ int64_t x;
+ deg_1e6 = OSMO_MAX(-180000000, OSMO_MIN(180000000, deg_1e6));
+ x = deg_1e6;
+ x *= (1 << 24);
+ if (deg_1e6 >= 0)
+ x += (1 << 24) - 1;
+ else
+ x -= (1 << 24) - 1;
+ x /= 360 * 1000000;
+ return (uint32_t)(x & 0xffffff);
+}
+
+/*! Decode a longitude value according to 3GPP TS 23.032.
+ * Normally, encoding and decoding is done via osmo_gad_enc() and osmo_gad_dec() for entire PDUs. But calling this
+ * directly can be useful to clamp a longitude to an actually encodable accuracy:
+ * int32_t set_lon = osmo_gad_dec_lon(osmo_gad_enc_lon(orig_lon));
+ * \param[in] lon Encoded longitude.
+ * \returns Longitude in micro degrees (degrees * 1e6), -180'000'000 (W) .. 180'000'000 (E).
+ */
+int32_t osmo_gad_dec_lon(uint32_t lon)
+{
+ /* -180 .. 180 degrees mapped to a signed 24 bit integer.
+ * N <= ((2**24)/360) * X < N+1
+ * N: encoded longitude
+ * X: longitude in degrees
+ */
+ int32_t slon;
+ int64_t x;
+ if (lon & 0x800000) {
+ /* make the 24bit negative number to a 32bit negative number */
+ slon = lon | 0xff000000;
+ } else {
+ slon = lon;
+ }
+ x = slon;
+ x *= 360 * 1000000;
+ x /= (1 << 24);
+ return x;
+}
+
+/*
+ * r = C((1+x)**K - 1)
+ * C = 10, x = 0.1
+ *
+ * def r(k):
+ * return 10.*(((1+0.1)**k) -1 )
+ * for k in range(128):
+ * print('%d,' % (r(k) * 1000.))
+ */
+static uint32_t table_uncertainty_1e3[128] = {
+ 0, 1000, 2100, 3310, 4641, 6105, 7715, 9487, 11435, 13579, 15937, 18531, 21384, 24522, 27974, 31772, 35949,
+ 40544, 45599, 51159, 57274, 64002, 71402, 79543, 88497, 98347, 109181, 121099, 134209, 148630, 164494, 181943,
+ 201137, 222251, 245476, 271024, 299126, 330039, 364043, 401447, 442592, 487851, 537636, 592400, 652640, 718904,
+ 791795, 871974, 960172, 1057189, 1163908, 1281299, 1410429, 1552472, 1708719, 1880591, 2069650, 2277615,
+ 2506377, 2758014, 3034816, 3339298, 3674227, 4042650, 4447915, 4893707, 5384077, 5923485, 6516834, 7169517,
+ 7887469, 8677216, 9545938, 10501531, 11552685, 12708953, 13980849, 15379933, 16918927, 18611820, 20474002,
+ 22522402, 24775642, 27254206, 29980627, 32979690, 36278659, 39907525, 43899277, 48290205, 53120226, 58433248,
+ 64277573, 70706330, 77777964, 85556760, 94113436, 103525780, 113879358, 125268293, 137796123, 151576735,
+ 166735409, 183409950, 201751945, 221928139, 244121953, 268535149, 295389664, 324929630, 357423593, 393166952,
+ 432484648, 475734112, 523308524, 575640376, 633205414, 696526955, 766180651, 842799716, 927080688, 1019789756,
+ 1121769732, 1233947705, 1357343476, 1493078824, 1642387706, 1806627477,
+};
+
+/*! Decode an uncertainty circle value according to 3GPP TS 23.032.
+ * Useful to clamp a value to an actually encodable accuracy:
+ * set_unc = osmo_gad_dec_unc(osmo_gad_enc_unc(orig_unc));
+ * \param[in] unc Encoded uncertainty value.
+ * \returns Uncertainty value in millimeters.
+ */
+uint32_t osmo_gad_dec_unc(uint8_t unc)
+{
+ return table_uncertainty_1e3[unc & 0x7f];
+}
+
+/*! Encode an uncertainty circle value according to 3GPP TS 23.032.
+ * Normally, encoding and decoding is done via osmo_gad_enc() and osmo_gad_dec() for entire PDUs. But calling this
+ * directly can be useful to clamp a value to an actually encodable accuracy:
+ * uint32_t set_unc = osmo_gad_dec_unc(osmo_gad_enc_unc(orig_unc));
+ * \param[in] mm Uncertainty value in millimeters.
+ * \returns Encoded uncertainty value.
+ */
+uint8_t osmo_gad_enc_unc(uint32_t mm)
+{
+ uint8_t unc;
+ for (unc = 0; unc < ARRAY_SIZE(table_uncertainty_1e3); unc++) {
+ if (table_uncertainty_1e3[unc] > mm)
+ return unc - 1;
+ }
+ return 127;
+}
+
+/* So far we don't encode a high-accuracy uncertainty anywhere, so these static items would flag as compiler warnings
+ * for unused items. As soon as any HA items get used, remove this ifdef. */
+#ifdef GAD_FUTURE
+
+/*
+ * r = C((1+x)**K - 1)
+ * C = 0.3, x = 0.02
+ *
+ * def r(k):
+ * return 0.3*(((1+0.02)**k) -1 )
+ * for k in range(256):
+ * print('%d,' % (r(k) * 1000.))
+ */
+static uint32_t table_ha_uncertainty_1e3[256] = {
+ 0, 6, 12, 18, 24, 31, 37, 44, 51, 58, 65, 73, 80, 88, 95, 103, 111, 120, 128, 137, 145, 154, 163, 173, 182, 192,
+ 202, 212, 222, 232, 243, 254, 265, 276, 288, 299, 311, 324, 336, 349, 362, 375, 389, 402, 417, 431, 445, 460,
+ 476, 491, 507, 523, 540, 556, 574, 591, 609, 627, 646, 665, 684, 703, 724, 744, 765, 786, 808, 830, 853, 876,
+ 899, 923, 948, 973, 998, 1024, 1051, 1078, 1105, 1133, 1162, 1191, 1221, 1252, 1283, 1314, 1347, 1380, 1413,
+ 1447, 1482, 1518, 1554, 1592, 1629, 1668, 1707, 1748, 1788, 1830, 1873, 1916, 1961, 2006, 2052, 2099, 2147,
+ 2196, 2246, 2297, 2349, 2402, 2456, 2511, 2567, 2625, 2683, 2743, 2804, 2866, 2929, 2994, 3060, 3127, 3195,
+ 3265, 3336, 3409, 3483, 3559, 3636, 3715, 3795, 3877, 3961, 4046, 4133, 4222, 4312, 4404, 4498, 4594, 4692,
+ 4792, 4894, 4998, 5104, 5212, 5322, 5435, 5549, 5666, 5786, 5907, 6032, 6158, 6287, 6419, 6554, 6691, 6830,
+ 6973, 7119, 7267, 7418, 7573, 7730, 7891, 8055, 8222, 8392, 8566, 8743, 8924, 9109, 9297, 9489, 9685, 9884,
+ 10088, 10296, 10508, 10724, 10944, 11169, 11399, 11633, 11871, 12115, 12363, 12616, 12875, 13138, 13407, 13681,
+ 13961, 14246, 14537, 14834, 15136, 15445, 15760, 16081, 16409, 16743, 17084, 17431, 17786, 18148, 18517, 18893,
+ 19277, 19669, 20068, 20475, 20891, 21315, 21747, 22188, 22638, 23096, 23564, 24042, 24529, 25025, 25532, 26048,
+ 26575, 27113, 27661, 28220, 28791, 29372, 29966, 30571, 31189, 31818, 32461, 33116, 33784, 34466, 35161, 35871,
+ 36594, 37332, 38085, 38852, 39635, 40434, 41249, 42080, 42927, 43792, 44674, 45573, 46491,
+};
+
+static uint32_t osmo_gad_dec_ha_unc(uint8_t unc)
+{
+ return table_uncertainty_1e3[unc];
+}
+
+static uint8_t osmo_gad_enc_ha_unc(uint32_t mm)
+{
+ uint8_t unc;
+ for (unc = 0; unc < ARRAY_SIZE(table_ha_uncertainty_1e3); unc++) {
+ if (table_uncertainty_1e3[unc] > mm)
+ return unc - 1;
+ }
+ return 255;
+}
+
+#endif /* GAD_FUTURE */
+
+/* Return error code, and, if required, allocate and populate struct osmo_gad_err. */
+#define DEC_ERR(RC, TYPE, fmt, args...) do { \
+ if (err) { \
+ *err = talloc_zero(err_ctx, struct osmo_gad_err); \
+ **err = (struct osmo_gad_err){ \
+ .rc = (RC), \
+ .type = (TYPE), \
+ .logmsg = talloc_asprintf(*err, "Error decoding GAD%s%s: " fmt, \
+ ((int)(TYPE)) >= 0 ? " " : "", \
+ ((int)(TYPE)) >= 0 ? osmo_gad_type_name(TYPE) : "", ##args), \
+ }; \
+ } \
+ return RC; \
+ } while(0)
+
+static int osmo_gad_enc_ell_point_unc_circle(struct gad_raw_ell_point_unc_circle *raw, const struct osmo_gad_ell_point_unc_circle *v)
+{
+ if (v->lat < -90000000 || v->lat > 90000000)
+ return -EINVAL;
+ if (v->lon < -180000000 || v->lon > 180000000)
+ return -EINVAL;
+ *raw = (struct gad_raw_ell_point_unc_circle){
+ .h = { .type = GAD_TYPE_ELL_POINT_UNC_CIRCLE },
+ .unc = osmo_gad_enc_unc(v->unc),
+ };
+ osmo_store32be_ext(osmo_gad_enc_lat(v->lat), raw->lat, 3);
+ osmo_store32be_ext(osmo_gad_enc_lon(v->lon), raw->lon, 3);
+ return sizeof(raw);
+}
+
+static int osmo_gad_dec_ell_point_unc_circle(struct osmo_gad_ell_point_unc_circle *v,
+ struct osmo_gad_err **err, void *err_ctx,
+ const struct gad_raw_ell_point_unc_circle *raw)
+{
+ /* Load 24bit big endian */
+ v->lat = osmo_gad_dec_lat(osmo_load32be_ext_2(raw->lat, 3));
+ v->lon = osmo_gad_dec_lon(osmo_load32be_ext_2(raw->lon, 3));
+
+ if (raw->spare2)
+ DEC_ERR(-EINVAL, raw->h.type, "Bit 8 of Uncertainty code should be zero");
+
+ v->unc = osmo_gad_dec_unc(raw->unc);
+ return 0;
+}
+
+static int osmo_gad_raw_len(const union gad_raw *gad_raw)
+{
+ switch (gad_raw->h.type) {
+ case GAD_TYPE_ELL_POINT:
+ return sizeof(gad_raw->ell_point);
+ case GAD_TYPE_ELL_POINT_UNC_CIRCLE:
+ return sizeof(gad_raw->ell_point_unc_circle);
+ case GAD_TYPE_ELL_POINT_UNC_ELLIPSE:
+ return sizeof(gad_raw->ell_point_unc_ellipse);
+ case GAD_TYPE_POLYGON:
+ if (gad_raw->polygon.h.num_points < 3)
+ return -EINVAL;
+ return sizeof(gad_raw->polygon.h)
+ + gad_raw->polygon.h.num_points * sizeof(gad_raw->polygon.point[0]);
+ case GAD_TYPE_ELL_POINT_ALT:
+ return sizeof(gad_raw->ell_point_alt);
+ case GAD_TYPE_ELL_POINT_ALT_UNC_ELL:
+ return sizeof(gad_raw->ell_point_alt_unc_ell);
+ case GAD_TYPE_ELL_ARC:
+ return sizeof(gad_raw->ell_arc);
+ case GAD_TYPE_HA_ELL_POINT_UNC_ELLIPSE:
+ return sizeof(gad_raw->ha_ell_point_unc_ell);
+ case GAD_TYPE_HA_ELL_POINT_ALT_UNC_ELL:
+ return sizeof(gad_raw->ha_ell_point_alt_unc_ell);
+ default:
+ return -ENOTSUP;
+ }
+}
+
+/*! Append a GAD PDU to the msgb.
+ * Write the correct number of bytes depending on the GAD type and possibly on variable length attributes.
+ * \param[out] msg Append to this msgb.
+ * \param[in] gad_raw GAD data to write.
+ * \returns number of bytes appended to msgb, or negative on failure.
+ */
+int osmo_gad_raw_write(struct msgb *msg, const union gad_raw *gad_raw)
+{
+ int len;
+ uint8_t *dst;
+ len = osmo_gad_raw_len(gad_raw);
+ if (len < 0)
+ return len;
+ dst = msgb_put(msg, len);
+ memcpy(dst, (void*)gad_raw, len);
+ return len;
+}
+
+/*! Read a GAD PDU and validate structure.
+ * Memcpy from data to gad_raw struct, and validate correct length depending on the GAD type and possibly on variable
+ * length attributes.
+ * \param[out] gad_raw Copy GAD PDU here.
+ * \param[out] err Returned pointer to error info, dynamically allocated; NULL to not return any.
+ * \param[in] err_ctx Talloc context to allocate err from, if required.
+ * \param[in] data Encoded GAD bytes buffer.
+ * \param[in] len Length of data in bytes.
+ * \returns 0 on success, negative on error. If returning negative and err was non-NULL, *err is guaranteed to point to
+ * an allocated struct osmo_gad_err.
+ */
+int osmo_gad_raw_read(union gad_raw *gad_raw, struct osmo_gad_err **err, void *err_ctx, const uint8_t *data, uint8_t len)
+{
+ int gad_len;
+ const union gad_raw *src;
+ if (err)
+ *err = NULL;
+ if (len < sizeof(src->h))
+ DEC_ERR(-EINVAL, -1, "GAD data too short for header (%u bytes)", len);
+
+ src = (void*)data;
+ gad_len = osmo_gad_raw_len(src);
+ if (gad_len < 0)
+ DEC_ERR(-EINVAL, src->h.type, "GAD data invalid (rc=%d)", gad_len);
+ if (gad_len != len)
+ DEC_ERR(-EINVAL, src->h.type, "GAD data with unexpected length: expected %d bytes, got %u",
+ gad_len, len);
+
+ memcpy((void*)gad_raw, data, gad_len);
+ return 0;
+}
+
+/*! Write GAD values with consistent units to raw GAD PDU representation.
+ * \param[out] gad_raw Write to this buffer.
+ * \param[in] gad GAD values to encode.
+ * \returns number of bytes written, or negative on failure.
+ */
+int osmo_gad_enc(union gad_raw *gad_raw, const struct osmo_gad *gad)
+{
+ switch (gad->type) {
+ case GAD_TYPE_ELL_POINT_UNC_CIRCLE:
+ return osmo_gad_enc_ell_point_unc_circle(&gad_raw->ell_point_unc_circle, &gad->ell_point_unc_circle);
+ default:
+ return -ENOTSUP;
+ }
+}
+
+/*! Decode GAD raw PDU to values with consistent units.
+ * \param[out] gad Decoded GAD values are written here.
+ * \param[out] err Returned pointer to error info, dynamically allocated; NULL to not return any.
+ * \param[in] err_ctx Talloc context to allocate err from, if required.
+ * \param[in] raw Raw GAD data in network-byte-order.
+ * \returns 0 on success, negative on error. If returning negative and err was non-NULL, *err is guaranteed to point to
+ * an allocated struct osmo_gad_err.
+ */
+int osmo_gad_dec(struct osmo_gad *gad, struct osmo_gad_err **err, void *err_ctx, const union gad_raw *raw)
+{
+ *gad = (struct osmo_gad){
+ .type = raw->h.type,
+ };
+ switch (raw->h.type) {
+ case GAD_TYPE_ELL_POINT_UNC_CIRCLE:
+ return osmo_gad_dec_ell_point_unc_circle(&gad->ell_point_unc_circle, err, err_ctx,
+ &raw->ell_point_unc_circle);
+ default:
+ DEC_ERR(-ENOTSUP, raw->h.type, "unsupported GAD type");
+ }
+}
+
+/*! Return a human readable representation of a raw GAD PDU.
+ * Convert to GAD values and feed the result to osmo_gad_to_str_buf().
+ * \param[out] buf Buffer to write string to.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] gad Location data.
+ * \returns number of chars that would be written, like snprintf().
+ */
+int osmo_gad_raw_to_str_buf(char *buf, size_t buflen, const union gad_raw *raw)
+{
+ struct osmo_gad gad;
+ if (osmo_gad_dec(&gad, NULL, NULL, raw)) {
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ OSMO_STRBUF_PRINTF(sb, "invalid");
+ return sb.chars_needed;
+ }
+ return osmo_gad_to_str_buf(buf, buflen, &gad);
+}
+
+/*! Return a human readable representation of a raw GAD PDU.
+ * Convert to GAD values and feed the result to osmo_gad_to_str_buf().
+ * \param[in] ctx Talloc ctx to allocate string buffer from.
+ * \param[in] raw GAD data in network-byte-order.
+ * \returns resulting string, dynamically allocated.
+ */
+char *osmo_gad_raw_to_str_c(void *ctx, const union gad_raw *raw)
+{
+ OSMO_NAME_C_IMPL(ctx, 128, "ERROR", osmo_gad_raw_to_str_buf, raw)
+}
+
+/*! Return a human readable representation of GAD (location estimate) values.
+ * \param[out] buf Buffer to write string to.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] gad Location data.
+ * \returns number of chars that would be written, like snprintf().
+ */
+int osmo_gad_to_str_buf(char *buf, size_t buflen, const struct osmo_gad *gad)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+
+ if (!gad) {
+ OSMO_STRBUF_PRINTF(sb, "null");
+ return sb.chars_needed;
+ }
+
+ OSMO_STRBUF_PRINTF(sb, "%s{", osmo_gad_type_name(gad->type));
+
+ switch (gad->type) {
+ case GAD_TYPE_ELL_POINT:
+ OSMO_STRBUF_PRINTF(sb, "lat=");
+ OSMO_STRBUF_APPEND(sb, osmo_int_to_float_str_buf, gad->ell_point.lat, 6);
+ OSMO_STRBUF_PRINTF(sb, ",lon=");
+ OSMO_STRBUF_APPEND(sb, osmo_int_to_float_str_buf, gad->ell_point.lon, 6);
+ break;
+
+ case GAD_TYPE_ELL_POINT_UNC_CIRCLE:
+ OSMO_STRBUF_PRINTF(sb, "lat=");
+ OSMO_STRBUF_APPEND(sb, osmo_int_to_float_str_buf, gad->ell_point_unc_circle.lat, 6);
+ OSMO_STRBUF_PRINTF(sb, ",lon=");
+ OSMO_STRBUF_APPEND(sb, osmo_int_to_float_str_buf, gad->ell_point_unc_circle.lon, 6);
+ OSMO_STRBUF_PRINTF(sb, ",unc=");
+ OSMO_STRBUF_APPEND(sb, osmo_int_to_float_str_buf, gad->ell_point_unc_circle.unc, 3);
+ OSMO_STRBUF_PRINTF(sb, "m");
+ break;
+
+ default:
+ OSMO_STRBUF_PRINTF(sb, "to-str-not-implemented");
+ break;
+ }
+
+ OSMO_STRBUF_PRINTF(sb, "}");
+ return sb.chars_needed;
+}
+
+/*! Return a human readable representation of GAD (location estimate) values.
+ * \param[in] ctx Talloc ctx to allocate string buffer from.
+ * \param[in] val Value to convert to float.
+ * \returns resulting string, dynamically allocated.
+ */
+char *osmo_gad_to_str_c(void *ctx, const struct osmo_gad *gad)
+{
+ OSMO_NAME_C_IMPL(ctx, 128, "ERROR", osmo_gad_to_str_buf, gad)
+}
+
+/*! @} */
diff --git a/src/gsm/gea.c b/src/gsm/gea.c
index 5756bb08..aedc898e 100644
--- a/src/gsm/gea.c
+++ b/src/gsm/gea.c
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <osmocom/core/bits.h>
diff --git a/src/gsm/gprs_cipher_core.c b/src/gsm/gprs_cipher_core.c
index 7f2b1a50..97e581db 100644
--- a/src/gsm/gprs_cipher_core.c
+++ b/src/gsm/gprs_cipher_core.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
diff --git a/src/gsm/gprs_gea.c b/src/gsm/gprs_gea.c
index 73147886..eae1cf12 100644
--- a/src/gsm/gprs_gea.c
+++ b/src/gsm/gprs_gea.c
@@ -16,10 +16,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <osmocom/crypt/gprs_cipher.h>
diff --git a/src/gsm/gprs_rlc.c b/src/gsm/gprs_rlc.c
index bdfc8eac..4f02a7a9 100644
--- a/src/gsm/gprs_rlc.c
+++ b/src/gsm/gprs_rlc.c
@@ -13,9 +13,8 @@
#include <string.h>
#include <osmocom/core/utils.h>
-#include <osmocom/gprs/gprs_rlc.h>
#include <osmocom/coding/gsm0503_coding.h>
-#include <osmocom/gprs/protocol/gsm_04_60.h>
+#include <osmocom/gsm/protocol/gsm_44_060.h>
#define EGPRS_CPS_TYPE1_TBL_SZ 29
#define EGPRS_CPS_TYPE2_TBL_SZ 8
diff --git a/src/gsm/gsm0341.c b/src/gsm/gsm0341.c
index 89f5de3f..a6238b36 100644
--- a/src/gsm/gsm0341.c
+++ b/src/gsm/gsm0341.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
diff --git a/src/gsm/gsm0411_smc.c b/src/gsm/gsm0411_smc.c
index 50d0f3ec..a7d5e11c 100644
--- a/src/gsm/gsm0411_smc.c
+++ b/src/gsm/gsm0411_smc.c
@@ -51,8 +51,9 @@
*
*/
-#include <string.h>
+#include <sys/types.h>
#include <inttypes.h>
+#include <string.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
diff --git a/src/gsm/gsm0411_smr.c b/src/gsm/gsm0411_smr.c
index 21d28c51..03cf0051 100644
--- a/src/gsm/gsm0411_smr.c
+++ b/src/gsm/gsm0411_smr.c
@@ -49,9 +49,10 @@
*/
+#include <sys/types.h>
+#include <inttypes.h>
#include <string.h>
#include <errno.h>
-#include <inttypes.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
@@ -130,7 +131,7 @@ const struct value_string gsm411_rp_cause_strs[] = {
{ GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" },
{ GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" },
{ GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" },
- { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" },
+ { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existent" },
{ GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" },
{ GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" },
{ GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" },
diff --git a/src/gsm/gsm0411_utils.c b/src/gsm/gsm0411_utils.c
index ccefe546..470b017f 100644
--- a/src/gsm/gsm0411_utils.c
+++ b/src/gsm/gsm0411_utils.c
@@ -27,7 +27,7 @@
*
*/
-#include "../../config.h"
+#include "config.h"
#include <time.h>
#include <string.h>
@@ -293,7 +293,7 @@ enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
* \param[in] type GSM340_TYPE_*
* \param[in] plan Numbering Plan
* \param[in] number string containing number
- * \reurns number of bytes of \a oa that have been used */
+ * \returns number of bytes of \a oa that have been used */
int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
uint8_t plan, const char *number)
{
@@ -348,7 +348,7 @@ int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
* \param[in] proto Protocol
* \param[in] trans Transaction
* \param[in] msg_type Message Type
- * \retrns 0 */
+ * \returns 0 */
int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
uint8_t msg_type)
{
diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c
index 3ae591a6..7a7f71f0 100644
--- a/src/gsm/gsm0480.c
+++ b/src/gsm/gsm0480.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/gsm48.h>
@@ -821,6 +817,7 @@ struct msgb *gsm0480_gen_ussd_resp_7bit(uint8_t invoke_id, const char *text)
* not only the FACILITY value, but the full L3 message including message header
* and FACILITY IE Tag+Length.
*/
+OSMO_DEPRECATED("Use gsm0480_gen_ussd_resp_7bit() instead")
struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text)
{
struct msgb *msg;
diff --git a/src/gsm/gsm0502.c b/src/gsm/gsm0502.c
index 1a71e617..e4a761da 100644
--- a/src/gsm/gsm0502.c
+++ b/src/gsm/gsm0502.c
@@ -2,6 +2,8 @@
* Paging helper code */
/*
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
+ *
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
@@ -32,7 +34,7 @@
#include <inttypes.h>
unsigned int
-gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi)
+gsm0502_calc_paging_group(const struct gsm48_control_channel_descr *chan_desc, uint64_t imsi)
{
int ccch_conf;
int bs_cc_chans;
@@ -177,7 +179,6 @@ uint32_t gsm0502_fn_remap(uint32_t fn, enum gsm0502_fn_remap_channel channel)
uint8_t fn_cycle;
uint8_t i;
int sub = -1;
- uint32_t fn_map;
struct fn_remap_table *table;
OSMO_ASSERT(channel < ARRAY_SIZE(fn_remap_table_ptr));
@@ -193,11 +194,104 @@ uint32_t gsm0502_fn_remap(uint32_t fn, enum gsm0502_fn_remap_channel channel)
}
if (sub == -1) {
- LOGP(DLGLOBAL, LOGL_ERROR, "could not remap frame number!, fn=%"PRIu32"\n", fn);
+ LOGP(DLGLOBAL, LOGL_ERROR, "could not remap frame number!, fn=%" PRIu32 "\n", fn);
return fn;
}
- fn_map = (fn + GSM_MAX_FN - sub) % GSM_MAX_FN;
+ return GSM_TDMA_FN_SUB(fn, sub);
+}
+
+/* Magic numbers (RNTABLE) for pseudo-random hopping sequence generation. */
+static const 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,
+};
- return fn_map;
+/*! Hopping sequence generation as per 3GPP TS 45.002, section 6.2.3.
+ * \param[in] t GSM time (TDMA frame number, T1/T2/T3).
+ * \param[in] hsn Hopping Sequence Number.
+ * \param[in] maio Mobile Allocation Index Offset.
+ * \param[in] n number of entries in mobile allocation (arfcn table).
+ * \param[in] ma array of ARFCNs (sorted in ascending order)
+ * representing the Mobile Allocation.
+ * \returns ARFCN to use for given input parameters at time 't'
+ * or Mobile Allocation Index if ma == NULL.
+ */
+uint16_t gsm0502_hop_seq_gen(const struct gsm_time *t,
+ uint8_t hsn, uint8_t maio,
+ size_t n, const uint16_t *ma)
+{
+ unsigned int mai;
+
+ if (hsn == 0) {
+ /* cyclic hopping */
+ mai = (t->fn + maio) % n;
+ } else {
+ /* pseudo random hopping */
+ int m, mp, tp, s, pnm;
+
+ pnm = (n >> 0) | (n >> 1)
+ | (n >> 2) | (n >> 3)
+ | (n >> 4) | (n >> 5)
+ | (n >> 6);
+
+ 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 ma ? ma[mai] : mai;
+}
+
+#define CB_FCCH -1
+#define CB_SCH -2
+#define CB_BCCH -3
+#define CB_IDLE -4
+
+/* Clause 7 Table 3 and Figure 8a */
+static const int ccch_block_table[51] = {
+ CB_FCCH, CB_SCH, /* 0..1 */
+ CB_BCCH, CB_BCCH, CB_BCCH, CB_BCCH, /* 2..5: BCCH */
+ 0, 0, 0, 0, /* 6..9: B0 */
+ CB_FCCH, CB_SCH, /* 10..11 */
+ 1, 1, 1, 1, /* 12..15: B1 */
+ 2, 2, 2, 2, /* 16..19: B2 */
+ CB_FCCH, CB_SCH, /* 20..21 */
+ 3, 3, 3, 3, /* 22..25: B3 */
+ 4, 4, 4, 4, /* 26..29: B4 */
+ CB_FCCH, CB_SCH, /* 30..31 */
+ 5, 5, 5, 5, /* 32..35: B5 */
+ 6, 6, 6, 6, /* 36..39: B6 */
+ CB_FCCH, CB_SCH, /* 40..41 */
+ 7, 7, 7, 7, /* 42..45: B7 */
+ 8, 8, 8, 8, /* 46..49: B8 */
+ CB_IDLE /* 50: Idle */
+};
+
+/*! Calculate CCCH block number from the given TDMA frame number.
+ * \param[in] fn TDMA frame number (of first or last burst).
+ * \returns CCCH block number 0..8 or a negative value,
+ * if the given frame number cannot carry CCCH.
+ */
+int gsm0502_fn2ccch_block(uint32_t fn)
+{
+ return ccch_block_table[fn % ARRAY_SIZE(ccch_block_table)];
}
diff --git a/src/gsm/gsm0808.c b/src/gsm/gsm0808.c
index 514d7f22..529dbdfe 100644
--- a/src/gsm/gsm0808.c
+++ b/src/gsm/gsm0808.c
@@ -15,17 +15,20 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
+#include "config.h"
+
+#include <string.h>
+
#include <osmocom/core/byteswap.h>
+#include <osmocom/core/endian.h>
#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/gsm0808_lcs.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gad.h>
/*! \addtogroup gsm0808
* @{
@@ -34,6 +37,9 @@
* message generation/encoding.
*/
+/*! Char buffer to return strings from functions */
+static __thread char str_buff[512];
+
/*! Create "Complete L3 Info" for AoIP, legacy implementation.
* Instead use gsm0808_create_layer3_aoip2(), which is capable of three-digit MNC with leading zeros.
* \param[in] msg_l3 msgb containing Layer 3 Message
@@ -96,13 +102,19 @@ struct msgb *gsm0808_create_layer3_2(const struct msgb *msg_l3, const struct osm
msgb_l3len(msg_l3), msg_l3->l3h);
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* push the bssmap header */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create "Complete L3 Info" for A, legacy implementation.
@@ -220,22 +232,32 @@ struct msgb *gsm0808_create_clear_command2(uint8_t cause, bool csfb_ind)
return msg;
}
-/*! Create BSSMAP Cipher Mode Command message
+/*! Superseded by gsm0808_create_cipher2() to include Kc128.
+ * Create BSSMAP Cipher Mode Command message (without Kc128).
* \param[in] ei Mandatory Encryption Information
+ * \param[in] kc128 optional kc128 key for A5/4
* \param[in] cipher_response_mode optional 1-byte Cipher Response Mode
* \returns callee-allocated msgb with BSSMAP Cipher Mode Command message */
struct msgb *gsm0808_create_cipher(const struct gsm0808_encrypt_info *ei,
const uint8_t *cipher_response_mode)
{
+ struct gsm0808_cipher_mode_command cmc = {
+ .ei = *ei,
+ .cipher_response_mode_present = (cipher_response_mode != NULL),
+ .cipher_response_mode = (cipher_response_mode ? *cipher_response_mode : 0),
+ };
+ return gsm0808_create_cipher2(&cmc);
+}
+
+/*! Create BSSMAP Cipher Mode Command message.
+ * \param[in] cmc Information to encode.
+ * \returns callee-allocated msgb with BSSMAP Cipher Mode Command message */
+struct msgb *gsm0808_create_cipher2(const struct gsm0808_cipher_mode_command *cmc)
+{
/* See also: 3GPP TS 48.008 3.2.1.30 CIPHER MODE COMMAND */
struct msgb *msg;
- /* Mandatory emelent! */
- OSMO_ASSERT(ei);
-
- msg =
- msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
- "cipher-mode-command");
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "cipher-mode-command");
if (!msg)
return NULL;
@@ -243,12 +265,16 @@ struct msgb *gsm0808_create_cipher(const struct gsm0808_encrypt_info *ei,
msgb_v_put(msg, BSS_MAP_MSG_CIPHER_MODE_CMD);
/* Encryption Information 3.2.2.10 */
- gsm0808_enc_encrypt_info(msg, ei);
+ gsm0808_enc_encrypt_info(msg, &cmc->ei);
/* Cipher Response Mode 3.2.2.34 */
- if (cipher_response_mode)
+ if (cmc->cipher_response_mode_present)
msgb_tv_put(msg, GSM0808_IE_CIPHER_RESPONSE_MODE,
- *cipher_response_mode);
+ cmc->cipher_response_mode);
+
+ /* Kc128 3.2.2.109 */
+ if (cmc->kc128_present)
+ gsm0808_enc_kc128(msg, cmc->kc128);
/* pre-pend the header */
msg->l3h =
@@ -277,8 +303,9 @@ struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id)
msgb_l3len(layer3), layer3->l3h);
}
- /* and the optional BSS message */
- msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, alg_id);
+ /* Optional Chosen Encryption Algorithm IE */
+ if (alg_id > 0)
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, alg_id);
/* pre-pend the header */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
@@ -354,7 +381,7 @@ struct msgb *gsm0808_create_lcls_conn_ctrl(enum gsm0808_lcls_config config,
if (config != GSM0808_LCLS_CFG_NA)
msgb_tv_put(msg, GSM0808_IE_LCLS_CONFIG, config);
if (control != GSM0808_LCLS_CSC_NA)
- msgb_tv_put(msg, GSM0808_IE_LCLS_CONFIG, control);
+ msgb_tv_put(msg, GSM0808_IE_LCLS_CONN_STATUS_CTRL, control);
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
@@ -399,7 +426,7 @@ struct msgb *gsm0808_create_lcls_notification(enum gsm0808_lcls_status status, b
/*! Create BSSMAP Classmark Request message
* \returns callee-allocated msgb with BSSMAP Classmark Request message */
-struct msgb *gsm0808_create_classmark_request()
+struct msgb *gsm0808_create_classmark_request(void)
{
struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
"classmark-request");
@@ -438,8 +465,9 @@ struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len
/*! Create BSSMAP SAPI N Reject message
* \param[in] link_id Link Identifier
+ * \param[in] cause BSSAP Cause value (see 3GPP TS 48.008, section 3.2.2.5)
* \returns callee-allocated msgb with BSSMAP SAPI N Reject message */
-struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
+struct msgb *gsm0808_create_sapi_reject_cause(uint8_t link_id, uint16_t cause)
{
struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
"bssmap: sapi 'n' reject");
@@ -447,14 +475,24 @@ struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
return NULL;
msgb_v_put(msg, BSS_MAP_MSG_SAPI_N_REJECT);
- msgb_v_put(msg, link_id);
- msgb_v_put(msg, GSM0808_CAUSE_BSS_NOT_EQUIPPED);
+ msgb_tv_put(msg, GSM0808_IE_DLCI, link_id);
+ gsm0808_enc_cause(msg, cause);
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
}
+/*! Create BSSMAP SAPI N Reject message (with hard-coded cause "BSS not equipped").
+ * DEPRECATED: use gsm0808_create_sapi_reject_cause() instead.
+ * \param[in] link_id Link Identifier
+ * \param[in] cause BSSAP Cause value (see 3GPP TS 48.008, section 3.2.2.5)
+ * \returns callee-allocated msgb with BSSMAP SAPI N Reject message */
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
+{
+ return gsm0808_create_sapi_reject_cause(link_id, GSM0808_CAUSE_BSS_NOT_EQUIPPED);
+}
+
/*! Create BSSMAP Assignment Request message, 3GPP TS 48.008 §3.2.1.1.
* This is identical to gsm0808_create_ass(), but adds KC and LCLS IEs.
* \param[in] ct Channel Type
@@ -474,8 +512,6 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
{
/* See also: 3GPP TS 48.008 3.2.1.1 ASSIGNMENT REQUEST */
struct msgb *msg;
- uint16_t cic_sw;
- uint32_t ci_sw;
/* Mandatory emelent! */
OSMO_ASSERT(ct);
@@ -493,11 +529,8 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
gsm0808_enc_channel_type(msg, ct);
/* Circuit Identity Code 3.2.2.2 */
- if (cic) {
- cic_sw = osmo_htons(*cic);
- msgb_tv_fixed_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE,
- sizeof(cic_sw), (uint8_t *) & cic_sw);
- }
+ if (cic)
+ msgb_tv16_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE, *cic);
/* AoIP: AoIP Transport Layer Address (MGW) 3.2.2.102 */
if (ss) {
@@ -505,14 +538,17 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
}
/* AoIP: Codec List (MSC Preferred) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* AoIP: Call Identifier 3.2.2.105 */
if (ci) {
- ci_sw = osmo_htonl(*ci);
- msgb_tv_fixed_put(msg, GSM0808_IE_CALL_ID, sizeof(ci_sw),
- (uint8_t *) & ci_sw);
+ /* NOTE: 3GPP TS 48.008, section 3.2.2.105 specifies that
+ * the least significant byte shall be transmitted first. */
+ msgb_v_put(msg, GSM0808_IE_CALL_ID);
+ osmo_store32le(*ci, msgb_put(msg, sizeof(*ci)));
}
if (kc)
@@ -526,6 +562,10 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP Assignment Request message, 3GPP TS 48.008 §3.2.1.1.
@@ -577,7 +617,8 @@ struct msgb *gsm0808_create_ass_compl2(uint8_t rr_cause, uint8_t chosen_channel,
msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, chosen_channel);
/* write chosen encryption algorithm 3.2.2.44 */
- msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, encr_alg_id);
+ if (encr_alg_id > 0)
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, encr_alg_id);
/* write circuit pool 3.2.2.45 */
/* write speech version chosen: 3.2.2.51 when BTS picked it */
@@ -589,12 +630,16 @@ struct msgb *gsm0808_create_ass_compl2(uint8_t rr_cause, uint8_t chosen_channel,
gsm0808_enc_aoip_trasp_addr(msg, ss);
/* AoIP: Speech Codec (Chosen) 3.2.2.104 */
- if (sc)
- gsm0808_enc_speech_codec(msg, sc);
+ if (sc) {
+ if (gsm0808_enc_speech_codec2(msg, sc) < 0)
+ goto exit_free;
+ }
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* FIXME: write LSA identifier 3.2.2.15 - see 3GPP TS 43.073 */
@@ -605,6 +650,10 @@ struct msgb *gsm0808_create_ass_compl2(uint8_t rr_cause, uint8_t chosen_channel,
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP Assignment Completed message
@@ -666,13 +715,19 @@ struct msgb *gsm0808_create_ass_fail(uint8_t cause, const uint8_t *rr_cause,
/* Circuit pool list 3.2.2.46 */
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* update the size */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP Assignment Failure message
@@ -774,7 +829,7 @@ struct msgb *gsm0808_create_paging(const char *imsi, const uint32_t *tmsi,
{
struct gsm0808_cell_id_list2 cil2 = {};
- /* Mandatory emelents! */
+ /* Mandatory elements! */
OSMO_ASSERT(cil);
if (cil->id_list_len > GSM0808_CELL_ID_LIST2_MAXLEN)
@@ -816,6 +871,11 @@ static uint8_t put_old_bss_to_new_bss_information(struct msgb *msg,
msgb_tlv_put(msg, GSM0808_FE_IE_CURRENT_CHANNEL_TYPE_2, 2, val);
}
+ if (i->last_eutran_plmn_id_present) {
+ msgb_put_u8(msg, GSM0808_FE_IE_LAST_USED_EUTRAN_PLMN_ID);
+ osmo_plmn_to_bcd(msgb_put(msg, 3), &i->last_eutran_plmn_id);
+ }
+
*tlv_len = (uint8_t) (msg->tail - old_tail);
return *tlv_len + 2;
}
@@ -938,7 +998,7 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
}
/* Chosen Encryption Algorithm (Serving) 3.2.2.44 */
- if (params->chosen_encryption_algorithm_serving)
+ if (params->chosen_encryption_algorithm_serving > 0)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, params->chosen_encryption_algorithm_serving);
/* Old BSS to New BSS Information 3.2.2.58 */
@@ -960,8 +1020,10 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
if (params->aoip_transport_layer)
gsm0808_enc_aoip_trasp_addr(msg, params->aoip_transport_layer);
- if (params->codec_list_msc_preferred)
- gsm0808_enc_speech_codec_list(msg, params->codec_list_msc_preferred);
+ if (params->codec_list_msc_preferred) {
+ if (gsm0808_enc_speech_codec_list2(msg, params->codec_list_msc_preferred) < 0)
+ goto exit_free;
+ }
if (params->call_id_present) {
uint8_t val[4];
@@ -969,6 +1031,9 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
msgb_tv_fixed_put(msg, GSM0808_IE_CALL_ID, 4, val);
}
+ if (params->more_items && params->kc128_present)
+ gsm0808_enc_kc128(msg, params->kc128);
+
if (params->global_call_reference && params->global_call_reference_len) {
msgb_tlv_put(msg, GSM0808_IE_GLOBAL_CALL_REF,
params->global_call_reference_len, params->global_call_reference);
@@ -978,6 +1043,10 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP HANDOVER REQUEST ACKNOWLEDGE message, 3GPP TS 48.008 3.2.1.10.
@@ -1001,7 +1070,7 @@ struct msgb *gsm0808_create_handover_request_ack2(const struct gsm0808_handover_
if (params->chosen_channel_present)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, params->chosen_channel);
- if (params->chosen_encr_alg)
+ if (params->chosen_encr_alg > 0)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, params->chosen_encr_alg);
if (params->chosen_speech_version != 0)
@@ -1010,14 +1079,28 @@ struct msgb *gsm0808_create_handover_request_ack2(const struct gsm0808_handover_
if (params->aoip_transport_layer)
gsm0808_enc_aoip_trasp_addr(msg, params->aoip_transport_layer);
+ /* AoIP: add Codec List (BSS Supported) 3.2.2.103.
+ * (codec_list_bss_supported was added to struct gsm0808_handover_request_ack later than speech_codec_chosen
+ * below, but it needs to come before it in the message coding). */
+ if (params->more_items && params->codec_list_bss_supported.len) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
+
/* AoIP: Speech Codec (Chosen) 3.2.2.104 */
- if (params->speech_codec_chosen_present)
- gsm0808_enc_speech_codec(msg, &params->speech_codec_chosen);
+ if (params->speech_codec_chosen_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->speech_codec_chosen) < 0)
+ goto exit_free;
+ }
/* prepend header with final length */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Same as gsm0808_create_handover_request_ack2() but with less parameters.
@@ -1069,7 +1152,7 @@ struct msgb *gsm0808_create_handover_command(const struct gsm0808_handover_comma
/*! Create BSSMAP HANDOVER DETECT message, 3GPP TS 48.008 3.2.1.40.
* Sent from the MT BSC back to the MSC when the MS has sent a handover RACH request and the MT BSC has
* received the Handover Detect message. */
-struct msgb *gsm0808_create_handover_detect()
+struct msgb *gsm0808_create_handover_detect(void)
{
struct msgb *msg;
@@ -1088,7 +1171,7 @@ struct msgb *gsm0808_create_handover_detect()
/*! Create BSSMAP HANDOVER SUCCEEDED message, 3GPP TS 48.008 3.2.1.13.
* Sent from the MSC back to the old BSS to notify that the MS has successfully accessed the new BSS. */
-struct msgb *gsm0808_create_handover_succeeded()
+struct msgb *gsm0808_create_handover_succeeded(void)
{
struct msgb *msg;
@@ -1123,15 +1206,19 @@ struct msgb *gsm0808_create_handover_complete(const struct gsm0808_handover_comp
msgb_tlv_put(msg, GSM0808_IE_RR_CAUSE, 1, &params->rr_cause);
/* AoIP: Speech Codec (Chosen) 3.2.2.104 */
- if (params->speech_codec_chosen_present)
- gsm0808_enc_speech_codec(msg, &params->speech_codec_chosen);
+ if (params->speech_codec_chosen_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->speech_codec_chosen) < 0)
+ goto exit_free;
+ }
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (params->codec_list_bss_supported.len)
- gsm0808_enc_speech_codec_list(msg, &params->codec_list_bss_supported);
+ if (params->codec_list_bss_supported.len) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
/* Chosen Encryption Algorithm 3.2.2.44 */
- if (params->chosen_encr_alg_present)
+ if (params->chosen_encr_alg_present && params->chosen_encr_alg > 0)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, params->chosen_encr_alg);
/* LCLS-BSS-Status 3.2.2.119 */
@@ -1142,6 +1229,10 @@ struct msgb *gsm0808_create_handover_complete(const struct gsm0808_handover_comp
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP HANDOVER FAILURE message, 3GPP TS 48.008 3.2.1.16.
@@ -1165,13 +1256,19 @@ struct msgb *gsm0808_create_handover_failure(const struct gsm0808_handover_failu
msgb_tlv_put(msg, GSM0808_IE_RR_CAUSE, 1, &params->rr_cause);
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (params->codec_list_bss_supported.len)
- gsm0808_enc_speech_codec_list(msg, &params->codec_list_bss_supported);
+ if (params->codec_list_bss_supported.len) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
/* prepend header with final length */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP HANDOVER PERFORMED message, 3GPP TS 48.008 3.2.1.25.
@@ -1199,7 +1296,7 @@ struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_per
msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, params->chosen_channel);
/* Chosen Encryption Algorithm 3.2.2.44 */
- if (params->chosen_encr_alg_present)
+ if (params->chosen_encr_alg_present && params->chosen_encr_alg > 0)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, params->chosen_encr_alg);
/* Speech Version (chosen) 3.2.2.51 */
@@ -1207,8 +1304,10 @@ struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_per
msgb_tv_put(msg, GSM0808_IE_SPEECH_VERSION, params->speech_version_chosen);
/* AoIP: Speech Codec (chosen) 3.2.2.104 */
- if (params->speech_codec_chosen_present)
- gsm0808_enc_speech_codec(msg, &params->speech_codec_chosen);
+ if (params->speech_codec_chosen_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->speech_codec_chosen) < 0)
+ goto exit_free;
+ }
/* LCLS-BSS-Status 3.2.2.119 */
if (params->lcls_bss_status_present)
@@ -1218,6 +1317,65 @@ struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_per
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP COMMON ID message, 3GPP TS 48.008 3.2.1.68.
+ * \param[in] imsi IMSI digits (decimal string).
+ * \param[in] selected_plmn_id Selected PLMN ID to encode, or NULL to not encode this IE.
+ * \param[in] last_used_eutran_plnm_id Last used E-UTRAN PLMN ID to encode, or NULL to not encode this IE.
+ * \returns callee-allocated msgb with BSSMAP COMMON ID message, or NULL if encoding failed. */
+struct msgb *gsm0808_create_common_id(const char *imsi,
+ const struct osmo_plmn_id *selected_plmn_id,
+ const struct osmo_plmn_id *last_used_eutran_plnm_id)
+{
+ struct msgb *msg;
+ uint8_t *out;
+ struct osmo_mobile_identity mi;
+ int rc;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "COMMON-ID");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_COMMON_ID);
+
+ /* mandatory IMSI 3.2.2.6 */
+ mi = (struct osmo_mobile_identity){ .type = GSM_MI_TYPE_IMSI };
+ OSMO_STRLCPY_ARRAY(mi.imsi, imsi);
+ out = msgb_tl_put(msg, GSM0808_IE_IMSI);
+ rc = osmo_mobile_identity_encode_msgb(msg, &mi, false);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ /* write the MI value length */
+ *out = rc;
+
+ /* not implemented: SNA Access Information */
+
+ /* Selected PLMN ID */
+ if (selected_plmn_id) {
+ msgb_v_put(msg, GSM0808_IE_SELECTED_PLMN_ID);
+ out = msgb_put(msg, 3);
+ osmo_plmn_to_bcd(out, selected_plmn_id);
+ }
+
+ /* Last used E-UTRAN PLMN ID */
+ if (last_used_eutran_plnm_id) {
+ msgb_v_put(msg, GSM0808_IE_LAST_USED_EUTRAN_PLMN_ID);
+ out = msgb_put(msg, 3);
+ osmo_plmn_to_bcd(out, last_used_eutran_plnm_id);
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
}
/*! Prepend a DTAP header to given Message Buffer
@@ -1258,6 +1416,763 @@ struct msgb *gsm0808_create_dtap(struct msgb *msg_l3, uint8_t link_id)
return msg;
}
+struct msgb *gsm0808_create_perform_location_request(const struct gsm0808_perform_location_request *params)
+{
+ struct msgb *msg;
+ uint8_t *out;
+ int rc;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-PERFORM-LOCATION-REQUEST");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_PERFORM_LOCATION_RQST);
+
+ /* Location Type 3.2.2.63 */
+ osmo_bssmap_le_ie_enc_location_type(msg, &params->location_type);
+
+ if (params->imsi.type == GSM_MI_TYPE_IMSI) {
+ /* IMSI 3.2.2.6 */
+ out = msgb_tl_put(msg, GSM0808_IE_IMSI);
+ rc = osmo_mobile_identity_encode_msgb(msg, &params->imsi, false);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ /* write the MI value length */
+ *out = rc;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_perform_location_response(const struct gsm0808_perform_location_response *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-PERFORM-LOCATION-RESPONSE");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE);
+
+ if (params->location_estimate_present) {
+ uint8_t *l = msgb_tl_put(msg, GSM0808_IE_LOCATION_ESTIMATE);
+ int rc = osmo_gad_raw_write(msg, &params->location_estimate);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ *l = rc;
+ }
+
+ if (params->lcs_cause.present) {
+ uint8_t *l = msgb_tl_put(msg, GSM0808_IE_LCS_CAUSE);
+ int rc = osmo_lcs_cause_enc(msg, &params->lcs_cause);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ *l = rc;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+int gsm0808_enc_lcs_cause(struct msgb *msg, const struct lcs_cause_ie *lcs_cause)
+{
+ uint8_t *l = msgb_tl_put(msg, GSM0808_IE_LCS_CAUSE);
+ int rc = osmo_lcs_cause_enc(msg, lcs_cause);
+ if (rc <= 0)
+ return rc;
+ *l = rc;
+ return rc + 2;
+}
+
+struct msgb *gsm0808_create_perform_location_abort(const struct lcs_cause_ie *lcs_cause)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-PERFORM-LOCATION-ABORT");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_PERFORM_LOCATION_ABORT);
+
+ gsm0808_enc_lcs_cause(msg, lcs_cause);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS SETUP message, 3GPP TS 48.008 3.2.1.50.
+ * Sent from the MSC to the BSC to request VGCS/VBS call. */
+struct msgb *gsm0808_create_vgcs_vbs_setup(const struct gsm0808_vgcs_vbs_setup *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-SETUP");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_SETUP);
+
+ /* Group Call Reference, 3.2.2.55 */
+ gsm0808_enc_group_callref(msg, &params->callref);
+
+ /* Priority, 3.2.2.18 */
+ if (params->priority_present)
+ gsm0808_enc_priority(msg, &params->priority);
+
+ /* VGCS Feature Flags, 3.2.2.88 */
+ if (params->vgcs_feature_flags_present)
+ gsm0808_enc_vgcs_feature_flags(msg, &params->flags);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS SETUP ACK message, 3GPP TS 48.008 3.2.1.51.
+ * Sent from the BSC to the MSC to confirm VGCS/VBS call. */
+struct msgb *gsm0808_create_vgcs_vbs_setup_ack(const struct gsm0808_vgcs_vbs_setup_ack *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-SETUP-ACK");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_SETUP_ACK);
+
+ /* VGCS Feature Flags, 3.2.2.88 */
+ if (params->vgcs_feature_flags_present)
+ gsm0808_enc_vgcs_feature_flags(msg, &params->flags);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS SETUP REFUSE message, 3GPP TS 48.008 3.2.1.52.
+ * Sent from the BSC to the MSC to reject VGCS/VBS call. */
+struct msgb *gsm0808_create_vgcs_vbs_setup_refuse(enum gsm0808_cause cause)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-SETUP-REFUSE");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, cause);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT REQUEST message, 3GPP TS 48.008 3.2.1.53.
+ * Sent from the MSC to the BSC to assign radio resources for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_req(const struct gsm0808_vgcs_vbs_assign_req *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-REQUEST");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST);
+
+ /* Channel Type, 3.2.2.11 */
+ gsm0808_enc_channel_type(msg, &params->channel_type);
+
+ /* Assignment Requrirement, 3.2.2.52 */
+ gsm0808_enc_assign_req(msg, params->ass_req);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Group Call Reference, 3.2.2.55 */
+ gsm0808_enc_group_callref(msg, &params->callref);
+
+ /* Priority, 3.2.2.18 */
+ if (params->priority_present)
+ gsm0808_enc_priority(msg, &params->priority);
+
+ /* Circuit Identity Code, 3.2.2.2 */
+ if (params->cic_present)
+ msgb_tv16_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE, params->cic);
+
+ /* Downlink DTX Flag, 3.2.2.26 */
+ if (params->downlink_dtx_flag_present)
+ msgb_tv_put(msg, GSM0808_IE_DOWNLINK_DTX_FLAG, params->downlink_dtx_flag);
+
+ /* Encryption Information, 3.2.2.10 */
+ if (params->encryption_information_present)
+ gsm0808_enc_encrypt_info(msg, &params->encryption_information);
+
+ /* VSTK_RAND Imformation, 3.2.2.83 */
+ if (params->vstk_rand_present)
+ msgb_tlv_put(msg, GSM0808_IE_VSTK_RAND_INFO, sizeof(params->vstk_rand), params->vstk_rand);
+
+ /* VSTK Information, 3.2.2.84 */
+ if (params->vstk_present)
+ msgb_tlv_put(msg, GSM0808_IE_VSTK_INFO, sizeof(params->vstk), params->vstk);
+
+ /* Cell Identifier List Segment, 3.2.2.27a */
+ if (params->cils_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEGMENT, &params->cils);
+
+ /* AoIP Transport Layer Address (MGW), 3.2.2.102 */
+ if (params->aoip_transport_layer_present)
+ gsm0808_enc_aoip_trasp_addr(msg, &params->aoip_transport_layer);
+
+ /* Call Identifier, 3.2.2.105 */
+ if (params->call_id_present) {
+ /* NOTE: 3GPP TS 48.008, section 3.2.2.105 specifies that
+ * the least significant byte shall be transmitted first. */
+ msgb_v_put(msg, GSM0808_IE_CALL_ID);
+ osmo_store32le(params->call_id, msgb_put(msg, sizeof(uint32_t)));
+ }
+
+ /* Codec List (MSC Preferred) 3.2.2.103 */
+ if (params->codec_list_present) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_msc_preferred) < 0)
+ goto exit_free;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT RESULT message, 3GPP TS 48.008 3.2.1.54.
+ * Sent from the BSC to the MSC to indicate assignment/deassingment of radio resources for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_res(const struct gsm0808_vgcs_vbs_assign_res *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-RESULT");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT);
+
+ /* Channel Type, 3.2.2.11 */
+ gsm0808_enc_channel_type(msg, &params->channel_type);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Chosen Channel, 3.2.2.33 */
+ if (params->chosen_channel_present)
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, params->chosen_channel);
+
+ /* Circuit Identity Code, 3.2.2.2 */
+ if (params->cic_present)
+ msgb_tv16_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE, params->cic);
+
+ /* Circuit Pool, 3.2.2.45 */
+ if (params->circuit_pool_present)
+ msgb_tv_put(msg, GSM0808_IE_CIRCUIT_POOL, params->circuit_pool);
+
+ /* AoIP Transport Layer Address (BSS), 3.2.2.102 */
+ if (params->aoip_transport_layer_present)
+ gsm0808_enc_aoip_trasp_addr(msg, &params->aoip_transport_layer);
+
+ /* Codec (MSC Chosen) 3.2.2.103 */
+ if (params->codec_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->codec_msc_chosen) < 0)
+ goto exit_free;
+ }
+
+ /* Call Identifier, 3.2.2.105 */
+ if (params->call_id_present) {
+ /* NOTE: 3GPP TS 48.008, section 3.2.2.105 specifies that
+ * the least significant byte shall be transmitted first. */
+ msgb_v_put(msg, GSM0808_IE_CALL_ID);
+ osmo_store32le(params->call_id, msgb_put(msg, sizeof(uint32_t)));
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT FAILURE message, 3GPP TS 48.008 3.2.1.55.
+ * Sent from the BSC to the MSC to indicate assignment failure for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_fail(const struct gsm0808_vgcs_vbs_assign_fail *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-RESULT");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Circuit Pool, 3.2.2.45 */
+ if (params->circuit_pool_present)
+ msgb_tv_put(msg, GSM0808_IE_CIRCUIT_POOL, params->circuit_pool);
+
+ /* Circuit Pool List, 3.2.2.46 */
+ if (params->circuit_pool_present)
+ msgb_tlv_put(msg, GSM0808_IE_CIRCUIT_POOL_LIST, params->cpl.list_len, params->cpl.pool);
+
+ /* Codec List (BSS Supported) 3.2.2.103 */
+ if (params->codec_list_present) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP VGCS/VBS QUEUING INDICATION message, 3GPP TS 48.008 3.2.1.56.
+ * Sent from the BSC to the MSC to indicate delay in assignment for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_queuing_ind(void)
+{
+ struct msgb *msg;
+ uint8_t val = BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-QUEUING-INDICATION");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msg->data;
+ msgb_tlv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 1, &val);
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK REQUEST message, 3GPP TS 48.008 3.2.1.57.
+ * Sent from the BSC to the MSC to indicate that a mobile requested access to uplink. */
+struct msgb *gsm0808_create_uplink_request(const struct gsm0808_uplink_request *params)
+{
+ struct msgb *msg;
+ int rc;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REQUEST");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RQST);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* Cell Identifier, 3.2.2.17 */
+ if (params->cell_identifier_present)
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Layer 3 Information, 3.2.2.24 */
+ if (params->l3_present)
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, params->l3.l3_len, params->l3.l3);
+
+ /* Mobile Identity, 3.2.2.41 */
+ if (params->mi_present) {
+ rc = osmo_mobile_identity_encode_msgb(msg, &params->mi, false);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK REQUEST ACKNOWLEDGE message, 3GPP TS 48.008 3.2.1.58.
+ * Sent from the MSC to the BSC to indicate that access to uplink was granted. */
+struct msgb *gsm0808_create_uplink_request_ack(const struct gsm0808_uplink_request_ack *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REQUEST-ACKNOWLEDGE");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* Emergency set indication, 3.2.2.90 */
+ if (params->emerg_set_ind_present)
+ msgb_v_put(msg, GSM0808_IE_EMERGENCY_SET_INDICATION);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK CONFIRM message, 3GPP TS 48.008 3.2.1.59.
+ * Sent from the BSC to the MSC to indicate that access to uplink was has been successfully established. */
+struct msgb *gsm0808_create_uplink_request_cnf(const struct gsm0808_uplink_request_cnf *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REQUEST-CONFIRM");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* Layer 3 Information, 3.2.2.24 */
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, params->l3.l3_len, params->l3.l3);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK APPLICATION DATA message, 3GPP TS 48.008 3.2.1.59a.
+ * Sent from the BSC to the MSC to pass L3 info from the talker. */
+struct msgb *gsm0808_create_uplink_app_data(const struct gsm0808_uplink_app_data *params)
+{
+ struct msgb *msg;
+ uint8_t val;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-APPLICATION-DATA");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_APP_DATA);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Layer 3 Information, 3.2.2.24 */
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, params->l3.l3_len, params->l3.l3);
+
+ /* Application Data Information, 3.2.2.100 */
+ val = params->bt_ind;
+ msgb_tlv_put(msg, GSM0808_IE_APP_DATA_INFO, 1, &val);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK RELEASE INDICATION message, 3GPP TS 48.008 3.2.1.60.
+ * Sent from the BSC to the MSC to indicate that the uplink has been released. */
+struct msgb *gsm0808_create_uplink_release_ind(const struct gsm0808_uplink_release_ind *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-RELEASE-INDICATION");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RELEASE_INDICATION);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK REJECT COMMAND message, 3GPP TS 48.008 3.2.1.61.
+ * Sent from the MSC to the BSC to indicate that the uplink is not available for allocation. */
+struct msgb *gsm0808_create_uplink_reject_cmd(const struct gsm0808_uplink_reject_cmd *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REJECT-COMMAND");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_REJECT_CMD);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->current_talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->current_talker_priority);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->rejected_talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->rejected_talker_priority);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK RELEASE COMMAND message, 3GPP TS 48.008 3.2.1.62.
+ * Sent from the MSC to the BSC to indicate that the uplink is available for allocation. */
+struct msgb *gsm0808_create_uplink_release_cmd(const enum gsm0808_cause cause)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-RELEASE-COMMAND");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RELEASE_CMD);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, cause);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK SEIZED COMMAND message, 3GPP TS 48.008 3.2.1.62.
+ * Sent from the MSC to the BSC to indicate that the uplink is no longer available for allocation. */
+struct msgb *gsm0808_create_uplink_seized_cmd(const struct gsm0808_uplink_seized_cmd *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-SEIZED-COMMAND");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_SEIZED_CMD);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* Emergency set indication, 3.2.2.90 */
+ if (params->emerg_set_ind_present)
+ msgb_v_put(msg, GSM0808_IE_EMERGENCY_SET_INDICATION);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS ADDITIONAL INFORMATION message, 3GPP TS 48.008 3.2.1.78.
+ * Sent from the MSC to the BSC to transfer talker identity. */
+struct msgb *gsm0808_create_vgcs_additional_info(const struct gsm0808_talker_identity *ti)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS-ADDITIONAL-INFO");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_ADDL_INFO);
+
+ /* Talker Identity, 3.2.2.91 */
+ gsm0808_enc_talker_identity(msg, ti);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS AREA CELL INFO message, 3GPP TS 48.008 3.2.1.79.
+ * Sent from the BSC to the MSC to transfer additional infos about cells. */
+struct msgb *gsm0808_create_vgcs_vbs_area_cell_info(const struct gsm0808_vgcs_vbs_area_cell_info *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-AREA-CELL-INFO");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST);
+
+ /* Cell Identifier List Segment, 3.2.2.27a */
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEGMENT, &params->cils);
+
+ /* Assignment Requrirement, 3.2.2.52 */
+ if (params->ass_req_present)
+ gsm0808_enc_assign_req(msg, params->ass_req);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT STATUS message, 3GPP TS 48.008 3.2.1.80.
+ * Sent from the BSC to the MSC to indicate assignment status for each cell. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_stat(const struct gsm0808_vgcs_vbs_assign_stat *params)
+{
+ struct msgb *msg;
+ uint8_t val;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-STATUS");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_STATUS);
+
+ /* Cell Identifier List Segment, 3.2.2.27b */
+ if (params->cils_est_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_EST_CELLS, &params->cils_est);
+ /* Cell Identifier List Segment, 3.2.2.27c */
+ if (params->cils_tbe_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_CELLS_TBE, &params->cils_tbe);
+ /* Cell Identifier List Segment, 3.2.2.27e */
+ if (params->cils_rel_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_REL_CELLS, &params->cils_rel);
+ /* Cell Identifier List Segment, 3.2.2.27f */
+ if (params->cils_ne_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_NE_CELLS, &params->cils_ne);
+
+ /* VGCS/VBS Cell Status, 3.2.2.94 */
+ if (params->cell_status_present) {
+ val = params->cell_status;
+ msgb_tlv_put(msg, GSM0808_IE_VGCS_VBS_CELL_STATUS, 1, &val);
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS SMS message, 3GPP TS 48.008 3.2.1.81.
+ * Sent from the MSC to the BSC to send an SMS to VGCS. */
+struct msgb *gsm0808_create_vgcs_sms(const struct gsm0808_sms_to_vgcs *sms)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS-SMS");
+ if (!msg)
+ return NULL;
+
+ /* SMS to VGCS, 3.2.2.92 */
+ msgb_tlv_put(msg, GSM0808_IE_VGCS_VBS_CELL_STATUS, sms->sms_len, sms->sms);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS/VBS) NOTIFICATION DATA message, 3GPP TS 48.008 3.2.1.82.
+ * Sent from the MSC to the BSC to send application specific data. */
+struct msgb *gsm0808_create_notification_data(const struct gsm0808_notification_data *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS-SMS");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_NOTIFICATION_DATA);
+
+ /* Application Data, 3.2.2.98 */
+ msgb_tlv_put(msg, GSM0808_IE_APP_DATA, params->app_data.data_len, params->app_data.data);
+
+ /* Data Identity, 3.2.2.99 */
+ gsm0808_enc_data_identity(msg, &params->data_ident);
+
+ /* MSISDN, 3.2.2.101 */
+ if (params->msisdn_present)
+ gsm0808_enc_msisdn(msg, params->msisdn);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/* Note that EMERGENCY RESET INDICATION and EMERGENCY RESET COMMAND cannot be implemented, due to lack of
+ * message type information in the specifications. */
+
/* As per 3GPP TS 48.008 version 11.7.0 Release 11 */
static const struct tlv_definition bss_att_tlvdef = {
.def = {
@@ -1329,6 +2244,7 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0808_IE_LOCATION_ESTIMATE] = { TLV_TYPE_TLV },
[GSM0808_IE_POSITIONING_DATA] = { TLV_TYPE_TLV },
[GSM0808_IE_LCS_CAUSE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LCS_CLIENT_TYPE] = { TLV_TYPE_TLV },
[GSM0808_IE_APDU] = { TLV_TYPE_TLV },
[GSM0808_IE_NETWORK_ELEMENT_IDENTITY] = { TLV_TYPE_TLV },
[GSM0808_IE_GPS_ASSISTANCE_DATA] = { TLV_TYPE_TLV },
@@ -1345,6 +2261,7 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0800_IE_INTER_SYSTEM_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_SNA_ACCESS_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_VSTK_RAND_INFO] = { TLV_TYPE_TLV },
+ [GSM0808_IE_VSTK_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_PAGING_INFO] = { TLV_TYPE_TV },
[GSM0808_IE_IMEI] = { TLV_TYPE_TLV },
[GSM0808_IE_VELOCITY_ESTIMATE] = { TLV_TYPE_TLV },
@@ -1387,6 +2304,11 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0808_IE_CN_TO_MS_TRANSP_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_SELECTED_PLMN_ID] = { TLV_TYPE_FIXED, 3 },
[GSM0808_IE_LAST_USED_EUTRAN_PLMN_ID] = { TLV_TYPE_FIXED, 3 },
+ [GSM0808_IE_OLD_LAI] = { TLV_TYPE_FIXED, 5 },
+ [GSM0808_IE_ATTACH_INDICATOR] = { TLV_TYPE_T },
+ [GSM0808_IE_SELECTED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
+ [GSM0808_IE_PS_REGISTERED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
+ [GSM0808_IE_CS_REGISTERED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
/* Osmocom extensions */
[GSM0808_IE_OSMO_OSMUX_SUPPORT] = { TLV_TYPE_T },
@@ -1399,6 +2321,38 @@ const struct tlv_definition *gsm0808_att_tlvdef(void)
return &bss_att_tlvdef;
}
+/* As per 3GPP TS 48.008 version 16.0.0 Release 16 § 3.2.2.58 Old BSS to New BSS Information */
+const struct tlv_definition gsm0808_old_bss_to_new_bss_info_att_tlvdef = {
+ .def = {
+ [GSM0808_FE_IE_EXTRA_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_CURRENT_CHANNEL_TYPE_2] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_TARGET_CELL_RADIO_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_GPRS_SUSPEND_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_MULTIRATE_CONFIGURATION_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_DUAL_TRANSFER_MODE_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_INTER_RAT_HANDOVER_INFO] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_CDMA2000_CAPABILITY_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_DOWNLINK_CELL_LOAD_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_UPLINK_CELL_LOAD_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_CELL_LOAD_INFORMATION_GROUP] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_CELL_LOAD_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_PS_INDICATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_DTM_HANDOVER_COMMAND_INDICATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_D_RNTI] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_IRAT_MEASUREMENT_CONFIGURATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_SOURCE_CELL_ID] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_IRAT_MEASUREMENT_CONFIGURATION_EXTENDED_E_ARFCNS] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_VGCS_TALKER_MODE] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_LAST_USED_EUTRAN_PLMN_ID] = { TLV_TYPE_FIXED, 3 },
+ },
+};
+
+const struct value_string gsm0406_dlci_sapi_names[] = {
+ { DLCI_SAPI_RR_MM_CC, "RR/MM/CC" },
+ { DLCI_SAPI_SMS, "SMS" },
+ { 0, NULL }
+};
+
static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_ASSIGMENT_RQST, "ASSIGNMENT REQ" },
{ BSS_MAP_MSG_ASSIGMENT_COMPLETE, "ASSIGNMENT COMPL" },
@@ -1480,7 +2434,9 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST, "VGCS/VBS ASSIGN REQ" },
{ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT, "VGCS/VBS ASSIGN RES" },
{ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE, "VGCS/VBS ASSIGN FAIL" },
+ { BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_STATUS, "VGCS/VBS ASSIGN STATUS" },
{ BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION, "VGCS/VBS QUEUING IND" },
+ { BSS_MAP_MSG_VGCS_VBS_AREA_CELL_INFO, "VGCS/VBS AREA CELL INFO" },
{ BSS_MAP_MSG_UPLINK_RQST, "UPLINK REQ" },
{ BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE, "UPLINK REQ ACK" },
{ BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION, "UPLINK REQ CONF" },
@@ -1489,11 +2445,12 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_UPLINK_RELEASE_CMD, "UPLINK REL CMD" },
{ BSS_MAP_MSG_UPLINK_SEIZED_CMD, "UPLINK SEIZED CMD" },
{ BSS_MAP_MSG_VGCS_ADDL_INFO, "VGCS ADDL INFO" },
+ { BSS_MAP_MSG_VGCS_SMS, "VGCS SMS" },
{ BSS_MAP_MSG_NOTIFICATION_DATA, "NOTIF DATA" },
{ BSS_MAP_MSG_UPLINK_APP_DATA, "UPLINK APP DATA" },
{ BSS_MAP_MSG_LCLS_CONNECT_CTRL, "LCLS-CONNECT-CONTROL" },
- { BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK, "CLS-CONNECT-CONTROL-ACK" },
+ { BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK, "LCLS-CONNECT-CONTROL-ACK" },
{ BSS_MAP_MSG_LCLS_NOTIFICATION, "LCLS-NOTIFICATION" },
{ 0, NULL }
@@ -1527,6 +2484,7 @@ const struct value_string gsm0808_speech_codec_type_names[] = {
{ GSM0808_SCT_HR3, "HR3" },
{ GSM0808_SCT_HR4, "HR4" },
{ GSM0808_SCT_HR6, "HR6" },
+ { GSM0808_SCT_EXT, "Codec Extension" },
{ GSM0808_SCT_CSD, "CSD" },
{ 0, NULL }
};
@@ -1648,6 +2606,55 @@ const char *gsm0808_cause_name(enum gsm0808_cause cause)
return get_value_string(gsm0808_cause_names, cause);
}
+enum gsm0808_cause gsm0808_get_cause(const struct tlv_parsed *tp)
+{
+ const uint8_t *buf = TLVP_VAL_MINLEN(tp, GSM0808_IE_CAUSE, 1);
+
+ if (!buf)
+ return -EBADMSG;
+
+ if (TLVP_LEN(tp, GSM0808_IE_CAUSE) > 1) {
+ if (!gsm0808_cause_ext(buf[0]))
+ return -EINVAL;
+ return buf[1];
+ }
+
+ return buf[0];
+}
+
+const char *gsm0808_diagnostics_octet_location_str(uint8_t pointer)
+{
+ switch (pointer) {
+ case 0:
+ return "Error location not determined";
+ case 1:
+ return "The first octet of the message received (i.e. the message type) was found erroneous (unknown)";
+ case 0xfd:
+ return "The first octet of the BSSAP header (Discrimination) was found erroneous";
+ case 0xfe:
+ return "(DTAP only) The DLCI (second) octet of the BSSAP header was found erroneous";
+ case 0xff:
+ return "The last octet of the BSSAP header (length indicator) was found erroneous";
+ default:
+ snprintf(str_buff, sizeof(str_buff), "The %d octet of the message received was found erroneous", pointer);
+ return str_buff;
+ }
+}
+
+const char *gsm0808_diagnostics_bit_location_str(uint8_t bit_pointer)
+{
+ if (bit_pointer == 0) {
+ return "No particular part of the octet is indicated";
+ } else if (bit_pointer > 8) {
+ return "Reserved value";
+ }
+
+ snprintf(str_buff, sizeof(str_buff),
+ "An error was provoked by the field whose most significant bit is in bit position %d",
+ bit_pointer);
+ return str_buff;
+}
+
const struct value_string gsm0808_lcls_config_names[] = {
{ GSM0808_LCLS_CFG_BOTH_WAY, "Connect both-way" },
{ GSM0808_LCLS_CFG_BOTH_WAY_AND_BICAST_UL,
@@ -1684,4 +2691,101 @@ const struct value_string gsm0808_lcls_status_names[] = {
{ 0, NULL }
};
+/* Convert one S0-S15 bit to its set of AMR modes, for HR AMR and FR AMR.
+ * This is 3GPP TS 28.062 Table 7.11.3.1.3-2: "Preferred Configurations", with some configurations removed as specified
+ * in 3GPP TS 48.008 3.2.2.103:
+ *
+ * FR_AMR is coded ‘0011’.
+ * S11, S13 and S15 are reserved and coded with zeroes.
+ *
+ * HR_AMR is coded ‘0100’.
+ * S6 - S7 and S11 – S15 are reserved and coded with zeroes.
+ *
+ * Meaning: for FR, exclude all Optimisation Mode configurations.
+ * For HR, exclude all that are not supported by HR AMR -- drop all that include at least one of
+ * 10.2 or 12.2.
+ *
+ * Also, for HR, drop 12.2k from S1.
+ *
+ * The first array dimension is 0 for half rate and 1 for full rate.
+ * The second array dimension is the configuration number (0..15) aka Sn.
+ * The values are bitmask combinations of (1 << GSM0808_AMR_MODE_nnnn).
+ *
+ * For example, accumulate all modes that are possible in a given my_s15_s0:
+ *
+ * uint8_t modes = 0;
+ * for (s_bit = 0; s_bit < 15; s_bit++)
+ * if (my_s15_s0 & (1 << s_bit))
+ * modes |= gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][s_bit];
+ * for (i = 0; i < 8; i++)
+ * if (modes & (1 << i))
+ * printf(" %s", gsm0808_amr_mode_name(i));
+ */
+const uint8_t gsm0808_amr_modes_from_cfg[2][16] = {
+#define B(X) (1 << (GSM0808_AMR_MODE_##X))
+ /* HR */
+ {
+ /* Sn = modes */
+ [0] = B(4_75),
+ [1] = B(4_75) | B(5_90) | B(7_40),
+ [2] = B(5_90),
+ [3] = B(6_70),
+ [4] = B(7_40),
+ [5] = B(7_95),
+ [6] = 0,
+ [7] = 0,
+
+ [8] = B(4_75) | B(5_90),
+ [9] = B(4_75) | B(5_90) | B(6_70),
+ [10] = B(4_75) | B(5_90) | B(6_70) | B(7_40),
+ [11] = 0,
+ [12] = 0,
+ [13] = 0,
+ [14] = 0,
+ [15] = 0,
+ },
+ /* FR */
+ {
+ /* Sn = modes */
+ [0] = B(4_75),
+ [1] = B(4_75) | B(5_90) | B(7_40) | B(12_2),
+ [2] = B(5_90),
+ [3] = B(6_70),
+ [4] = B(7_40),
+ [5] = B(7_95),
+ [6] = B(10_2),
+ [7] = B(12_2),
+
+ [8] = B(4_75) | B(5_90),
+ [9] = B(4_75) | B(5_90) | B(6_70),
+ [10] = B(4_75) | B(5_90) | B(6_70) | B(7_40),
+ [11] = 0,
+ [12] = B(4_75) | B(5_90) | B(6_70) | B(10_2),
+ [13] = 0,
+ [14] = B(4_75) | B(5_90) | B(7_95) | B(12_2),
+ [15] = 0,
+ }
+};
+
+/* AMR mode names from GSM0808_AMR_MODE_*, for use with gsm0808_amr_modes_from_cfg.
+ *
+ * For example:
+ * printf("S9: ");
+ * uint8_t s9_modes = gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][9];
+ * for (bit = 0; bit < 8; bit++)
+ * if (s9_modes & (1 << bit))
+ * printf("%s,", gsm0808_amr_mode_name(bit));
+ */
+const struct value_string gsm0808_amr_mode_names[] = {
+ { GSM0808_AMR_MODE_4_75, "4.75" },
+ { GSM0808_AMR_MODE_5_15, "5.15" },
+ { GSM0808_AMR_MODE_5_90, "5.90" },
+ { GSM0808_AMR_MODE_6_70, "6.70" },
+ { GSM0808_AMR_MODE_7_40, "7.40" },
+ { GSM0808_AMR_MODE_7_95, "7.95" },
+ { GSM0808_AMR_MODE_10_2, "10.2" },
+ { GSM0808_AMR_MODE_12_2, "12.2" },
+ {}
+};
+
/*! @} */
diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c
index 7416d8f5..c9f26d35 100644
--- a/src/gsm/gsm0808_utils.c
+++ b/src/gsm/gsm0808_utils.c
@@ -78,7 +78,7 @@ uint8_t gsm0808_enc_cause(struct msgb *msg, uint16_t cause)
/*! Encode TS 08.08 AoIP transport address IE
* \param[out] msg Message Buffer to which to append IE
* \param[in] ss Socket Address to be used in IE
- * \returns number of bytes added to \a msg */
+ * \returns number of bytes added to \a msg; 0 if msg is too small */
uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
const struct sockaddr_storage *ss)
{
@@ -87,16 +87,25 @@ uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
struct sockaddr_in6 *sin6;
uint16_t port = 0;
uint8_t *ptr;
- uint8_t *old_tail;
- uint8_t *tlv_len;
+ const uint8_t len_tl = 2;
+ uint8_t len_v = sizeof(port);
- OSMO_ASSERT(msg);
- OSMO_ASSERT(ss);
OSMO_ASSERT(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
+ switch (ss->ss_family) {
+ case AF_INET:
+ len_v += IP_V4_ADDR_LEN;
+ break;
+ case AF_INET6:
+ len_v += IP_V6_ADDR_LEN;
+ break;
+ }
+
+ if (msgb_tailroom(msg) < len_tl + len_v)
+ return 0;
+
msgb_put_u8(msg, GSM0808_IE_AOIP_TRASP_ADDR);
- tlv_len = msgb_put(msg,1);
- old_tail = msg->tail;
+ msgb_put_u8(msg, len_v);
switch (ss->ss_family) {
case AF_INET:
@@ -114,9 +123,7 @@ uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
}
msgb_put_u16(msg, port);
-
- *tlv_len = (uint8_t) (msg->tail - old_tail);
- return *tlv_len + 2;
+ return len_tl + len_v;
}
/*! Decode TS 08.08 AoIP transport address IE
@@ -132,7 +139,6 @@ int gsm0808_dec_aoip_trasp_addr(struct sockaddr_storage *ss,
struct sockaddr_in6 sin6;
const uint8_t *old_elem = elem;
- OSMO_ASSERT(ss);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -180,7 +186,6 @@ int gsm0808_dec_aoip_trasp_addr(struct sockaddr_storage *ss,
* \returns number of bytes parsed */
int gsm0808_dec_osmux_cid(uint8_t *cid, const uint8_t *elem, uint8_t len)
{
- OSMO_ASSERT(cid);
if (!elem)
return -EINVAL;
if (len != 1)
@@ -204,21 +209,21 @@ static void decode_lai(const uint8_t *data, struct osmo_location_area_id *decode
gsm48_decode_lai2(&lai, decoded);
}
-/* Helper function for gsm0808_enc_speech_codec()
- * and gsm0808_enc_speech_codec_list() */
-static uint8_t enc_speech_codec(struct msgb *msg,
- const struct gsm0808_speech_codec *sc)
+/* Helper function for gsm0808_enc_speech_codec[_list]().
+ * Returns number of bytes appended; negative on error. */
+static int enc_speech_codec(struct msgb *msg,
+ const struct gsm0808_speech_codec *sc)
{
/* See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
uint8_t header = 0;
uint8_t *old_tail;
- bool type_extended = false;
+ bool type_extended;
/* Note: Extended codec types are codec types that require 8 instead
* of 4 bit to fully specify the selected codec. In the following,
* we check if we work with an extended type or not. We also check
* if the codec type is valid at all. */
- switch(sc->type) {
+ switch (sc->type) {
case GSM0808_SCT_FR1:
case GSM0808_SCT_FR2:
case GSM0808_SCT_FR3:
@@ -235,8 +240,7 @@ static uint8_t enc_speech_codec(struct msgb *msg,
break;
default:
/* Invalid codec type specified */
- OSMO_ASSERT(false);
- break;
+ return -EINVAL;
}
old_tail = msg->tail;
@@ -251,11 +255,10 @@ static uint8_t enc_speech_codec(struct msgb *msg,
header |= (1 << 4);
if (type_extended) {
- header |= 0x0f;
+ header |= GSM0808_SCT_EXT;
msgb_put_u8(msg, header);
msgb_put_u8(msg, sc->type);
} else {
- OSMO_ASSERT(sc->type < 0x0f);
header |= sc->type;
msgb_put_u8(msg, header);
}
@@ -273,11 +276,13 @@ static uint8_t enc_speech_codec(struct msgb *msg,
case GSM0808_SCT_FR5:
case GSM0808_SCT_HR4:
case GSM0808_SCT_CSD:
- OSMO_ASSERT((sc->cfg & 0xff00) == 0);
+ if (sc->cfg >> 8)
+ return -EINVAL;
msgb_put_u8(msg, (uint8_t) sc->cfg & 0xff);
break;
default:
- OSMO_ASSERT(sc->cfg == 0);
+ if (sc->cfg != 0)
+ return -EINVAL;
break;
}
@@ -287,27 +292,38 @@ static uint8_t enc_speech_codec(struct msgb *msg,
/*! Encode TS 08.08 Speech Codec IE
* \param[out] msg Message Buffer to which IE will be appended
* \param[in] sc Speech Codec to be encoded into IE
- * \returns number of bytes appended to \a msg */
-uint8_t gsm0808_enc_speech_codec(struct msgb *msg,
- const struct gsm0808_speech_codec *sc)
+ * \returns number of bytes appended to \a msg; negative on error */
+int gsm0808_enc_speech_codec2(struct msgb *msg,
+ const struct gsm0808_speech_codec *sc)
{
/*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
- uint8_t *old_tail;
uint8_t *tlv_len;
-
- OSMO_ASSERT(msg);
- OSMO_ASSERT(sc);
+ int rc;
msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC);
tlv_len = msgb_put(msg, 1);
- old_tail = msg->tail;
- enc_speech_codec(msg, sc);
+ rc = enc_speech_codec(msg, sc);
+ if (rc < 0)
+ return rc;
- *tlv_len = (uint8_t) (msg->tail - old_tail);
+ *tlv_len = rc;
return *tlv_len + 2;
}
+/*! Deprecated: gsm0808_enc_speech_codec2() wrapper for backwards compatibility.
+ * Mimics the old behavior: crash on missing and/or invalid input. */
+uint8_t gsm0808_enc_speech_codec(struct msgb *msg,
+ const struct gsm0808_speech_codec *sc)
+{
+ int rc;
+
+ rc = gsm0808_enc_speech_codec2(msg, sc);
+ OSMO_ASSERT(rc > 0);
+
+ return rc;
+}
+
/*! Decode TS 08.08 Speech Codec IE
* \param[out] sc Caller-allocated memory for Speech Codec
* \param[in] elem IE value to be decoded
@@ -320,7 +336,6 @@ int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
uint8_t header;
const uint8_t *old_elem = elem;
- OSMO_ASSERT(sc);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -332,7 +347,7 @@ int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
/* An extended codec type needs at least two fields,
* bail if the input data length is not sufficient. */
- if ((header & 0x0F) == 0x0F && len < 2)
+ if ((header & 0x0F) == GSM0808_SCT_EXT && len < 2)
return -EINVAL;
elem++;
@@ -347,7 +362,7 @@ int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
if (header & (1 << 4))
sc->tf = true;
- if ((header & 0x0F) != 0x0F) {
+ if ((header & 0x0F) != GSM0808_SCT_EXT) {
sc->type = (header & 0x0F);
} else {
sc->type = *elem;
@@ -392,35 +407,46 @@ int gsm0808_dec_speech_codec(struct gsm0808_speech_codec *sc,
/*! Encode TS 08.08 Speech Codec list
* \param[out] msg Message Buffer to which IE is to be appended
* \param[in] scl Speech Codec List to be encoded into IE
- * \returns number of bytes added to \a msg */
-uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg,
- const struct gsm0808_speech_codec_list *scl)
+ * \returns number of bytes added to \a msg; negative on error */
+int gsm0808_enc_speech_codec_list2(struct msgb *msg,
+ const struct gsm0808_speech_codec_list *scl)
{
/*! See also 3GPP TS 48.008 3.2.2.103 Speech Codec List */
uint8_t *old_tail;
uint8_t *tlv_len;
unsigned int i;
- uint8_t rc;
unsigned int bytes_used = 0;
- OSMO_ASSERT(msg);
- OSMO_ASSERT(scl);
-
msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC_LIST);
tlv_len = msgb_put(msg, 1);
old_tail = msg->tail;
for (i = 0; i < scl->len; i++) {
- rc = enc_speech_codec(msg, &scl->codec[i]);
- OSMO_ASSERT(rc >= 1);
+ int rc = enc_speech_codec(msg, &scl->codec[i]);
+ if (rc < 1)
+ return rc;
bytes_used += rc;
- OSMO_ASSERT(bytes_used <= 255);
+ if (bytes_used > 0xff)
+ return -EOVERFLOW;
}
*tlv_len = (uint8_t) (msg->tail - old_tail);
return *tlv_len + 2;
}
+/*! Deprecated: gsm0808_enc_speech_codec_list2() wrapper for backwards compatibility.
+ * Mimics the old behavior: crash on missing and/or invalid input. */
+uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg,
+ const struct gsm0808_speech_codec_list *scl)
+{
+ int rc;
+
+ rc = gsm0808_enc_speech_codec_list2(msg, scl);
+ OSMO_ASSERT(rc > 0);
+
+ return rc;
+}
+
/*! Decode TS 08.08 Speech Codec list IE
* \param[out] scl Caller-provided memory to store codec list
* \param[in] elem IE value to be decoded
@@ -435,7 +461,6 @@ int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl,
int rc;
uint8_t decoded = 0;
- OSMO_ASSERT(scl);
if (!elem)
return -EINVAL;
@@ -472,16 +497,8 @@ uint8_t gsm0808_enc_channel_type(struct msgb *msg,
uint8_t *old_tail;
uint8_t *tlv_len;
- OSMO_ASSERT(msg);
- OSMO_ASSERT(ct);
OSMO_ASSERT(ct->perm_spch_len <= CHANNEL_TYPE_ELEMENT_MAXLEN - 2);
- /* FIXME: Implement encoding support for Data
- * and Speech + CTM Text Telephony */
- if ((ct->ch_indctr & 0x0f) != GSM0808_CHAN_SPEECH
- && (ct->ch_indctr & 0x0f) != GSM0808_CHAN_SIGN)
- OSMO_ASSERT(false);
-
msgb_put_u8(msg, GSM0808_IE_CHANNEL_TYPE);
tlv_len = msgb_put(msg, 1);
old_tail = msg->tail;
@@ -489,12 +506,45 @@ uint8_t gsm0808_enc_channel_type(struct msgb *msg,
msgb_put_u8(msg, ct->ch_indctr & 0x0f);
msgb_put_u8(msg, ct->ch_rate_type);
- for (i = 0; i < ct->perm_spch_len; i++) {
- byte = ct->perm_spch[i];
+ switch (ct->ch_indctr) {
+ case GSM0808_CHAN_DATA:
+ byte = ct->data_rate;
+
+ if (!ct->data_transparent)
+ byte |= 0x40; /* Set T/NT */
+
+ if (ct->data_rate_allowed_is_set) {
+ OSMO_ASSERT(!ct->data_transparent);
+ byte |= 0x80; /* Set ext */
+ msgb_put_u8(msg, byte);
- if (i < ct->perm_spch_len - 1)
- byte |= 0x80;
+ byte = ct->data_rate_allowed;
+ if (ct->data_asym_pref_is_set) {
+ byte |= 0x80; /* Set ext */
+ msgb_put_u8(msg, byte);
+
+ /* Set asymmetry indication, rest is spare */
+ byte = ct->data_asym_pref << 5;
+ }
+ }
msgb_put_u8(msg, byte);
+ break;
+ case GSM0808_CHAN_SPEECH:
+ case GSM0808_CHAN_SPEECH_CTM_TEXT_TELEPHONY:
+ for (i = 0; i < ct->perm_spch_len; i++) {
+ byte = ct->perm_spch[i];
+
+ if (i < ct->perm_spch_len - 1)
+ byte |= 0x80;
+ msgb_put_u8(msg, byte);
+ }
+ break;
+ case GSM0808_CHAN_SIGN:
+ /* Octet 5 is spare */
+ msgb_put_u8(msg, 0);
+ break;
+ default:
+ OSMO_ASSERT(false);
}
*tlv_len = (uint8_t) (msg->tail - old_tail);
@@ -514,7 +564,6 @@ int gsm0808_dec_channel_type(struct gsm0808_channel_type *ct,
uint8_t byte;
const uint8_t *old_elem = elem;
- OSMO_ASSERT(ct);
if (!elem)
return -EINVAL;
if (len < 3 || len > 11)
@@ -524,17 +573,60 @@ int gsm0808_dec_channel_type(struct gsm0808_channel_type *ct,
ct->ch_indctr = (*elem) & 0x0f;
elem++;
- ct->ch_rate_type = (*elem) & 0x0f;
+ ct->ch_rate_type = *elem;
elem++;
- for (i = 0; i < ARRAY_SIZE(ct->perm_spch); i++) {
+ switch (ct->ch_indctr) {
+ case GSM0808_CHAN_DATA:
byte = *elem;
elem++;
- ct->perm_spch[i] = byte & 0x7f;
- if ((byte & 0x80) == 0x00)
- break;
+ ct->data_transparent = !(byte & 0x40); /* T/NT */
+ ct->data_rate = byte & 0x3f;
+
+ /* Optional extension for non-transparent service */
+ if (byte & 0x80) {
+ if (ct->data_transparent)
+ return -EINVAL;
+ if (elem - old_elem >= len)
+ return -EOVERFLOW;
+ byte = *elem;
+ elem++;
+
+ ct->data_rate_allowed_is_set = true;
+ ct->data_rate_allowed = byte & 0x7f;
+
+ /* Optional extension */
+ if (byte & 0x80) {
+ if (elem - old_elem >= len)
+ return -EOVERFLOW;
+ byte = *elem;
+ elem++;
+
+ ct->data_asym_pref_is_set = true;
+ ct->data_asym_pref = byte & 0x60 >> 5;
+ }
+ }
+ break;
+ case GSM0808_CHAN_SPEECH:
+ case GSM0808_CHAN_SPEECH_CTM_TEXT_TELEPHONY:
+ for (i = 0; i < ARRAY_SIZE(ct->perm_spch); i++) {
+ if (elem - old_elem >= len)
+ return -EOVERFLOW;
+
+ byte = *elem;
+ elem++;
+ ct->perm_spch[i] = byte & 0x7f;
+ if ((byte & 0x80) == 0x00)
+ break;
+ }
+ ct->perm_spch_len = i + 1;
+ break;
+ case GSM0808_CHAN_SIGN:
+ /* Octet 5 is spare */
+ break;
+ default:
+ return -ENOTSUP;
}
- ct->perm_spch_len = i + 1;
return (int)(elem - old_elem);
}
@@ -703,8 +795,6 @@ uint8_t gsm0808_enc_encrypt_info(struct msgb *msg,
uint8_t *old_tail;
uint8_t *tlv_len;
- OSMO_ASSERT(msg);
- OSMO_ASSERT(ei);
OSMO_ASSERT(ei->key_len <= ARRAY_SIZE(ei->key));
OSMO_ASSERT(ei->perm_algo_len <= ENCRY_INFO_PERM_ALGO_MAXLEN);
@@ -721,6 +811,8 @@ uint8_t gsm0808_enc_encrypt_info(struct msgb *msg,
}
msgb_put_u8(msg, perm_algo);
+ /* FIXME: 48.008 3.2.2.10 Encryption Information says:
+ * "When present, the key shall be 8 octets long." */
ptr = msgb_put(msg, ei->key_len);
memcpy(ptr, ei->key, ei->key_len);
@@ -741,7 +833,6 @@ int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
unsigned int perm_algo_len = 0;
const uint8_t *old_elem = elem;
- OSMO_ASSERT(ei);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -760,6 +851,8 @@ int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
}
ei->perm_algo_len = perm_algo_len;
+ /* FIXME: 48.008 3.2.2.10 Encryption Information says:
+ * "When present, the key shall be 8 octets long." */
ei->key_len = len - 1;
memcpy(ei->key, elem, ei->key_len);
elem+=ei->key_len;
@@ -767,6 +860,80 @@ int gsm0808_dec_encrypt_info(struct gsm0808_encrypt_info *ei,
return (int)(elem - old_elem);
}
+/*! Encode TS 48.008 Kc128 IE.
+ * \param[out] msg Message Buffer to which IE is to be appended.
+ * \param[in] kc128 Pointer to 16 bytes of Kc128 key data.
+ * \returns number of bytes appended to msg */
+int gsm0808_enc_kc128(struct msgb *msg, const uint8_t *kc128)
+{
+ uint8_t *start = msg->tail;
+ msgb_tv_fixed_put(msg, GSM0808_IE_KC_128, 16, kc128);
+ return msg->tail - start;
+}
+
+/*! Decode TS 48.008 Kc128 IE.
+ * \param[out] kc128 Target buffer for received Kc128 key, 16 bytes long.
+ * \param[in] elem IE value to be decoded (without IE discriminator).
+ * \param[in] len Length of elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_kc128(uint8_t *kc128, const uint8_t *elem, uint8_t len)
+{
+ if (len != 16)
+ return -EINVAL;
+ memcpy(kc128, elem, 16);
+ return len;
+}
+
+/* Store individual Cell Identifier information in a CGI, without clearing the remaining ones.
+ * This is useful to supplement one CGI with information from more than one Cell Identifier,
+ * which in turn is useful to match Cell Identifiers of differing kinds to each other.
+ * Before first invocation, clear the *dst struct externally, this function does only write those members
+ * that are present in parameter u.
+ */
+static void cell_id_to_cgi(struct osmo_cell_global_id *dst,
+ enum CELL_IDENT discr, const union gsm0808_cell_id_u *u)
+{
+ switch (discr) {
+ case CELL_IDENT_WHOLE_GLOBAL:
+ *dst = u->global;
+ return;
+
+ case CELL_IDENT_WHOLE_GLOBAL_PS:
+ dst->lai = u->global_ps.rai.lac;
+ dst->cell_identity = u->global_ps.cell_identity;
+ return;
+
+ case CELL_IDENT_LAC_AND_CI:
+ dst->lai.lac = u->lac_and_ci.lac;
+ dst->cell_identity = u->lac_and_ci.ci;
+ return;
+
+ case CELL_IDENT_CI:
+ dst->cell_identity = u->ci;
+ return;
+
+ case CELL_IDENT_LAI_AND_LAC:
+ dst->lai = u->lai_and_lac;
+ return;
+
+ case CELL_IDENT_LAC:
+ dst->lai.lac = u->lac;
+ return;
+
+ case CELL_IDENT_SAI:
+ dst->lai = u->sai.lai;
+ return;
+
+ case CELL_IDENT_NO_CELL:
+ case CELL_IDENT_BSS:
+ case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
+ case CELL_IDENT_UTRAN_RNC:
+ case CELL_IDENT_UTRAN_LAC_RNC:
+ /* No values to set. */
+ return;
+ }
+}
+
/* Return the size of the value part of a cell identifier of given type */
int gsm0808_cell_id_size(enum CELL_IDENT discr)
{
@@ -784,6 +951,10 @@ int gsm0808_cell_id_size(enum CELL_IDENT discr)
case CELL_IDENT_BSS:
case CELL_IDENT_NO_CELL:
return 0;
+ case CELL_IDENT_SAI:
+ return 7;
+ case CELL_IDENT_WHOLE_GLOBAL_PS:
+ return 8;
default:
return -EINVAL;
}
@@ -828,6 +999,20 @@ int gsm0808_decode_cell_id_u(union gsm0808_cell_id_u *out, enum CELL_IDENT discr
case CELL_IDENT_NO_CELL:
/* Does not have any list items */
break;
+ case CELL_IDENT_SAI:
+ if (len < 7)
+ return -EINVAL;
+ decode_lai(buf, &out->sai.lai);
+ out->sai.sac = osmo_load16be(buf + sizeof(struct gsm48_loc_area_id));
+ break;
+ case CELL_IDENT_WHOLE_GLOBAL_PS:
+ /* 3GPP TS 48.018 sec 11.3.9 "Cell Identifier" */
+ if (len < 8)
+ return -EINVAL;
+ decode_lai(buf, (struct osmo_location_area_id *)&out->global_ps.rai); /* rai contains lai + non-decoded rac */
+ out->global_ps.rai.rac = *(buf + sizeof(struct gsm48_loc_area_id));
+ out->global_ps.cell_identity = osmo_load16be(buf + sizeof(struct gsm48_loc_area_id) + 1);
+ break;
default:
/* Remaining cell identification types are not implemented. */
return -EINVAL;
@@ -869,6 +1054,26 @@ void gsm0808_msgb_put_cell_id_u(struct msgb *msg, enum CELL_IDENT id_discr, cons
case CELL_IDENT_NO_CELL:
/* Does not have any list items */
break;
+
+ case CELL_IDENT_SAI: {
+ const struct osmo_service_area_id *id = &u->sai;
+ struct gsm48_loc_area_id lai;
+ gsm48_generate_lai2(&lai, &id->lai);
+ memcpy(msgb_put(msg, sizeof(lai)), &lai, sizeof(lai));
+ msgb_put_u16(msg, id->sac);
+ break;
+ }
+
+ case CELL_IDENT_WHOLE_GLOBAL_PS: {
+ /* 3GPP TS 48.018 sec 11.3.9 "Cell Identifier" */
+ const struct osmo_cell_global_id_ps *id = &u->global_ps;
+ struct gsm48_loc_area_id lai;
+ gsm48_generate_lai2(&lai, &id->rai.lac);
+ memcpy(msgb_put(msg, sizeof(lai)), &lai, sizeof(lai));
+ memcpy(msgb_put(msg, 1), &id->rai.rac, 1);
+ msgb_put_u16(msg, id->cell_identity);
+ break;
+ }
default:
/* Support for other identifier list types is not implemented. */
OSMO_ASSERT(false);
@@ -885,19 +1090,31 @@ uint8_t gsm0808_enc_cell_id_list2(struct msgb *msg,
uint8_t *old_tail;
uint8_t *tlv_len;
unsigned int i;
-
- OSMO_ASSERT(msg);
- OSMO_ASSERT(cil);
+ uint8_t id_discr;
msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST);
tlv_len = msgb_put(msg, 1);
old_tail = msg->tail;
- msgb_put_u8(msg, cil->id_discr & 0x0f);
+ /* CGI-PS is an osmocom-specific type. In here we don't care about the
+ * PS part, only the CS one. */
+ if (cil->id_discr == CELL_IDENT_WHOLE_GLOBAL_PS)
+ id_discr = CELL_IDENT_WHOLE_GLOBAL;
+ else
+ id_discr = cil->id_discr & 0x0f;
+
+ msgb_put_u8(msg, id_discr);
OSMO_ASSERT(cil->id_list_len <= GSM0808_CELL_ID_LIST2_MAXLEN);
- for (i = 0; i < cil->id_list_len; i++)
- gsm0808_msgb_put_cell_id_u(msg, cil->id_discr, &cil->id_list[i]);
+ for (i = 0; i < cil->id_list_len; i++) {
+ if (cil->id_discr == CELL_IDENT_WHOLE_GLOBAL_PS) {
+ union gsm0808_cell_id_u u;
+ cell_id_to_cgi(&u.global, cil->id_discr, &cil->id_list[i]);
+ gsm0808_msgb_put_cell_id_u(msg, CELL_IDENT_WHOLE_GLOBAL, &u);
+ } else {
+ gsm0808_msgb_put_cell_id_u(msg, cil->id_discr, &cil->id_list[i]);
+ }
+ }
*tlv_len = (uint8_t) (msg->tail - old_tail);
return *tlv_len + 2;
@@ -916,9 +1133,6 @@ uint8_t gsm0808_enc_cell_id_list(struct msgb *msg,
uint8_t *tlv_len;
unsigned int i;
- OSMO_ASSERT(msg);
- OSMO_ASSERT(cil);
-
msgb_put_u8(msg, GSM0808_IE_CELL_IDENTIFIER_LIST);
tlv_len = msgb_put(msg, 1);
old_tail = msg->tail;
@@ -1057,6 +1271,31 @@ static int parse_cell_id_lac_list(struct gsm0808_cell_id_list2 *cil, const uint8
return i;
}
+static int parse_cell_id_sai_list(struct gsm0808_cell_id_list2 *cil, const uint8_t *data, size_t remain, size_t *consumed)
+{
+ struct osmo_service_area_id *id;
+ uint16_t *sac_be;
+ size_t lai_offset;
+ int i = 0;
+ const size_t elemlen = sizeof(struct gsm48_loc_area_id) + sizeof(*sac_be);
+
+ *consumed = 0;
+ while (remain >= elemlen) {
+ if (i >= GSM0808_CELL_ID_LIST2_MAXLEN)
+ return -ENOSPC;
+ id = &cil->id_list[i].sai;
+ lai_offset = i * elemlen;
+ decode_lai(&data[lai_offset], &id->lai);
+ sac_be = (uint16_t *)(&data[lai_offset + sizeof(struct gsm48_loc_area_id)]);
+ id->sac = osmo_load16be(sac_be);
+ *consumed += elemlen;
+ remain -= elemlen;
+ i++;
+ }
+
+ return i;
+}
+
/*! Decode Cell Identifier List IE
* \param[out] cil Caller-provided memory to store Cell ID list
* \param[in] elem IE value to be decoded
@@ -1069,7 +1308,6 @@ int gsm0808_dec_cell_id_list2(struct gsm0808_cell_id_list2 *cil,
size_t bytes_elem = 0;
int list_len = 0;
- OSMO_ASSERT(cil);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -1101,6 +1339,12 @@ int gsm0808_dec_cell_id_list2(struct gsm0808_cell_id_list2 *cil,
case CELL_IDENT_NO_CELL:
/* Does not have any list items */
break;
+ case CELL_IDENT_SAI:
+ list_len = parse_cell_id_sai_list(cil, elem, len, &bytes_elem);
+ break;
+ case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
+ case CELL_IDENT_UTRAN_RNC:
+ case CELL_IDENT_UTRAN_LAC_RNC:
default:
/* Remaining cell identification types are not implemented. */
return -EINVAL;
@@ -1131,7 +1375,6 @@ int gsm0808_dec_cell_id_list(struct gsm0808_cell_id_list *cil,
const uint8_t *old_elem = elem;
unsigned int item_count = 0;
- OSMO_ASSERT(cil);
if (!elem)
return -EINVAL;
if (len == 0)
@@ -1289,9 +1532,6 @@ uint8_t gsm0808_enc_cell_id(struct msgb *msg, const struct gsm0808_cell_id *ci)
.id_list_len = 1,
};
- OSMO_ASSERT(msg);
- OSMO_ASSERT(ci);
-
ie_tag = msg->tail;
rc = gsm0808_enc_cell_id_list2(msg, &cil);
@@ -1498,7 +1738,7 @@ int gsm48_mr_cfg_from_gsm0808_sc_cfg(struct gsm48_multi_rate_conf *cfg,
/* Rate 5,15k can never be selected (see table) */
cfg->m5_15 = 0;
- if (s15_s0 & GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20 & 0xff) {
+ if (s15_s0 & GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20) {
/* Table Table 7.11.3.1.3-2 lists one mode that selects 4
* rates at once (Config-NB-Code = 1). The rates selected
* are known to be compatible between GERAN and UTRAN, since
@@ -1565,18 +1805,7 @@ int gsm48_mr_cfg_from_gsm0808_sc_cfg(struct gsm48_multi_rate_conf *cfg,
int gsm0808_get_cipher_reject_cause(const struct tlv_parsed *tp)
{
- const uint8_t *buf = TLVP_VAL_MINLEN(tp, GSM0808_IE_CAUSE, 1);
-
- if (!buf)
- return -EBADMSG;
-
- if (TLVP_LEN(tp, GSM0808_IE_CAUSE) > 1) {
- if (!gsm0808_cause_ext(buf[0]))
- return -EINVAL;
- return buf[1];
- }
-
- return buf[0];
+ return gsm0808_get_cause(tp);
}
/*! Print a human readable name of the cell identifier to the char buffer.
@@ -1603,6 +1832,10 @@ int gsm0808_cell_id_u_name(char *buf, size_t buflen,
return snprintf(buf, buflen, "%s", osmo_lai_name(&u->lai_and_lac));
case CELL_IDENT_WHOLE_GLOBAL:
return snprintf(buf, buflen, "%s", osmo_cgi_name(&u->global));
+ case CELL_IDENT_WHOLE_GLOBAL_PS:
+ return snprintf(buf, buflen, "%s", osmo_cgi_ps_name(&u->global_ps));
+ case CELL_IDENT_SAI:
+ return snprintf(buf, buflen, "%s", osmo_sai_name(&u->sai));
default:
/* For CELL_IDENT_BSS and CELL_IDENT_NO_CELL, just print the discriminator.
* Same for kinds we have no string representation of yet. */
@@ -1610,47 +1843,6 @@ int gsm0808_cell_id_u_name(char *buf, size_t buflen,
}
}
-/* Store individual Cell Identifier information in a CGI, without clearing the remaining ones.
- * This is useful to supplement one CGI with information from more than one Cell Identifier,
- * which in turn is useful to match Cell Identifiers of differing kinds to each other.
- * Before first invocation, clear the *dst struct externally, this function does only write those members
- * that are present in parameter u.
- */
-static void cell_id_to_cgi(struct osmo_cell_global_id *dst,
- enum CELL_IDENT discr, const union gsm0808_cell_id_u *u)
-{
- switch (discr) {
- case CELL_IDENT_WHOLE_GLOBAL:
- *dst = u->global;
- return;
-
- case CELL_IDENT_LAC_AND_CI:
- dst->lai.lac = u->lac_and_ci.lac;
- dst->cell_identity = u->lac_and_ci.ci;
- return;
-
- case CELL_IDENT_CI:
- dst->cell_identity = u->ci;
- return;
-
- case CELL_IDENT_LAI_AND_LAC:
- dst->lai = u->lai_and_lac;
- return;
-
- case CELL_IDENT_LAC:
- dst->lai.lac = u->lac;
- return;
-
- case CELL_IDENT_NO_CELL:
- case CELL_IDENT_BSS:
- case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
- case CELL_IDENT_UTRAN_RNC:
- case CELL_IDENT_UTRAN_LAC_RNC:
- /* No values to set. */
- return;
- }
-}
-
/*! Return true if the common information between the two Cell Identifiers match.
* For example, if a LAC+CI is compared to LAC, return true if the LAC are the same.
* Note that CELL_IDENT_NO_CELL will always return false.
@@ -1773,6 +1965,14 @@ void gsm0808_cell_id_from_cgi(struct gsm0808_cell_id *cid, enum CELL_IDENT id_di
cid->id.global = *cgi;
return;
+ case CELL_IDENT_WHOLE_GLOBAL_PS:
+ cid->id.global_ps = (struct osmo_cell_global_id_ps){
+ .rai = {
+ .lac = cgi->lai,
+ },
+ .cell_identity = cgi->cell_identity,
+ };
+ return;
case CELL_IDENT_LAC_AND_CI:
cid->id.lac_and_ci = (struct osmo_lac_and_ci_id){
.lac = cgi->lai.lac,
@@ -1792,6 +1992,12 @@ void gsm0808_cell_id_from_cgi(struct gsm0808_cell_id *cid, enum CELL_IDENT id_di
cid->id.lac = cgi->lai.lac;
return;
+ case CELL_IDENT_SAI:
+ cid->id.sai = (struct osmo_service_area_id){
+ .lai = cgi->lai,
+ };
+ return;
+
case CELL_IDENT_NO_CELL:
case CELL_IDENT_BSS:
case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
@@ -1816,6 +2022,11 @@ int gsm0808_cell_id_to_cgi(struct osmo_cell_global_id *cgi, const struct gsm0808
*cgi = cid->id.global;
return OSMO_CGI_PART_PLMN | OSMO_CGI_PART_LAC | OSMO_CGI_PART_CI;
+ case CELL_IDENT_WHOLE_GLOBAL_PS:
+ cgi->lai = cid->id.global_ps.rai.lac;
+ cgi->cell_identity = cid->id.global_ps.cell_identity;
+ return OSMO_CGI_PART_PLMN | OSMO_CGI_PART_LAC | OSMO_CGI_PART_CI;
+
case CELL_IDENT_LAC_AND_CI:
cgi->lai.lac = cid->id.lac_and_ci.lac;
cgi->cell_identity = cid->id.lac_and_ci.ci;
@@ -1833,6 +2044,10 @@ int gsm0808_cell_id_to_cgi(struct osmo_cell_global_id *cgi, const struct gsm0808
cgi->lai.lac = cid->id.lac;
return OSMO_CGI_PART_LAC;
+ case CELL_IDENT_SAI:
+ cgi->lai = cid->id.sai.lai;
+ return OSMO_CGI_PART_PLMN | OSMO_CGI_PART_LAC;
+
case CELL_IDENT_NO_CELL:
case CELL_IDENT_BSS:
case CELL_IDENT_UTRAN_PLMN_LAC_RNC:
@@ -1855,28 +2070,17 @@ const struct value_string gsm0808_cell_id_discr_names[] = {
{ CELL_IDENT_UTRAN_PLMN_LAC_RNC, "UTRAN-PLMN-LAC-RNC" },
{ CELL_IDENT_UTRAN_RNC, "UTRAN-RNC" },
{ CELL_IDENT_UTRAN_LAC_RNC, "UTRAN-LAC-RNC" },
+ { CELL_IDENT_SAI, "SAI" },
+ { CELL_IDENT_WHOLE_GLOBAL_PS, "CGI-PS"},
{ 0, NULL }
};
-#define APPEND_THING(func, args...) do { \
- int remain = buflen - (pos - buf); \
- int l = func(pos, remain, ##args); \
- if (l < 0 || l > remain) \
- pos = buf + buflen; \
- else \
- pos += l; \
- if (l > 0) \
- total_len += l; \
- } while(0)
-#define APPEND_STR(fmt, args...) APPEND_THING(snprintf, fmt, ##args)
-#define APPEND_CELL_ID_U(DISCR, U) APPEND_THING(gsm0808_cell_id_u_name, DISCR, U)
-
char *gsm0808_cell_id_name_buf(char *buf, size_t buflen, const struct gsm0808_cell_id *cid)
{
- char *pos = buf;
- int total_len = 0;
- APPEND_STR("%s:", gsm0808_cell_id_discr_name(cid->id_discr));
- APPEND_CELL_ID_U(cid->id_discr, &cid->id);
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+
+ OSMO_STRBUF_PRINTF(sb, "%s:", gsm0808_cell_id_discr_name(cid->id_discr));
+ OSMO_STRBUF_APPEND(sb, gsm0808_cell_id_u_name, cid->id_discr, &cid->id);
return buf;
}
@@ -1923,30 +2127,31 @@ char *gsm0808_cell_id_name_c(const void *ctx, const struct gsm0808_cell_id *cid)
*/
int gsm0808_cell_id_list_name_buf(char *buf, size_t buflen, const struct gsm0808_cell_id_list2 *cil)
{
- char *pos = buf;
- int total_len = 0;
- int i;
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
- APPEND_STR("%s[%u]", gsm0808_cell_id_discr_name(cil->id_discr), cil->id_list_len);
+ OSMO_STRBUF_PRINTF(sb, "%s[%u]",
+ gsm0808_cell_id_discr_name(cil->id_discr),
+ cil->id_list_len);
switch (cil->id_discr) {
case CELL_IDENT_BSS:
case CELL_IDENT_NO_CELL:
- return total_len;
+ return sb.chars_needed;
default:
break;
}
- APPEND_STR(":{");
+ OSMO_STRBUF_PRINTF(sb, ":{");
- for (i = 0; i < cil->id_list_len; i++) {
+ for (unsigned int i = 0; i < cil->id_list_len; i++) {
if (i)
- APPEND_STR(", ");
- APPEND_CELL_ID_U(cil->id_discr, &cil->id_list[i]);
+ OSMO_STRBUF_PRINTF(sb, ", ");
+ OSMO_STRBUF_APPEND(sb, gsm0808_cell_id_u_name,
+ cil->id_discr, &cil->id_list[i]);
}
- APPEND_STR("}");
- return total_len;
+ OSMO_STRBUF_PRINTF(sb, "}");
+ return sb.chars_needed;
}
/*! Return a human-readable representation of \a cil in a static buffer.
@@ -1968,9 +2173,6 @@ char *gsm0808_cell_id_list_name_c(const void *ctx, const struct gsm0808_cell_id_
return buf;
}
-#undef APPEND_STR
-#undef APPEND_CELL_ID_U
-
char *gsm0808_channel_type_name_buf(char *buf, size_t buf_len, const struct gsm0808_channel_type *ct)
{
snprintf(buf, buf_len, "ch_indctr=0x%x ch_rate_type=0x%x perm_spch=%s",
@@ -1993,4 +2195,367 @@ char *gsm0808_channel_type_name_c(const void *ctx, const struct gsm0808_channel_
return gsm0808_channel_type_name_buf(buf, 128, ct);
}
+/*! Encode Group Call Reference IE (3GPP TS 48.008 3.2.2.55).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] gc Group Call Reference to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_group_callref(struct msgb *msg, const struct gsm0808_group_callref *gc)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(gc);
+
+ msgb_put_u8(msg, GSM0808_IE_GROUP_CALL_REFERENCE);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ memcpy(msgb_put(msg, sizeof(*gc)), gc, sizeof(*gc));
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Group Call Reference IE (3GPP TS 48.008 3.2.2.55).
+ * \param[out] gc Group Call Reference structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_group_callref(struct gsm0808_group_callref *gc, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(gc);
+ OSMO_ASSERT(elem);
+
+ if (len != sizeof(*gc))
+ return -EINVAL;
+
+ memcpy(gc, elem, sizeof(*gc));
+
+ return len;
+}
+
+/*! Encode Priority IE (3GPP TS 48.008 3.2.2.18).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] pri Priority to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_priority(struct msgb *msg, const struct gsm0808_priority *pri)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(pri);
+
+ msgb_put_u8(msg, GSM0808_IE_PRIORITY);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ memcpy(msgb_put(msg, sizeof(*pri)), pri, sizeof(*pri));
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Priority IE (3GPP TS 48.008 3.2.2.18).
+ * \param[out] pri Priority structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_priority(struct gsm0808_priority *pri, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(pri);
+ OSMO_ASSERT(elem);
+
+ if (len != sizeof(*pri))
+ return -EINVAL;
+
+ memcpy(pri, elem, sizeof(*pri));
+
+ return len;
+}
+
+/*! Encode VGCS Feature Flags IE (3GPP TS 48.008 3.2.2.88).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] ff VGCS Feature Flags to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_vgcs_feature_flags(struct msgb *msg, const struct gsm0808_vgcs_feature_flags *ff)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(ff);
+
+ msgb_put_u8(msg, GSM0808_IE_VGCS_FEATURE_FLAGS);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ memcpy(msgb_put(msg, sizeof(*ff)), ff, sizeof(*ff));
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode VGCS Feature Flags IE (3GPP TS 48.008 3.2.2.88).
+ * \param[out] ff VGCS Feature Flags structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_vgcs_feature_flags(struct gsm0808_vgcs_feature_flags *ff, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ff);
+ OSMO_ASSERT(elem);
+
+ if (len != sizeof(*ff))
+ return -EINVAL;
+
+ memcpy(ff, elem, sizeof(*ff));
+
+ return len;
+}
+
+/*! Encode Data Identity IE (3GPP TS 48.008 3.2.2.99).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] di Data Identity to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_data_identity(struct msgb *msg, const struct gsm0808_data_identity *ti)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(ti);
+
+ msgb_put_u8(msg, GSM0808_IE_DATA_IDENTITY);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ memcpy(msgb_put(msg, sizeof(*ti)), ti, sizeof(*ti));
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Data Identity IE (3GPP TS 48.008 3.2.2.99).
+ * \param[out] di Data Identity structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_data_identity(struct gsm0808_data_identity *ti, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ti);
+ OSMO_ASSERT(elem);
+
+ if (len < sizeof(*ti))
+ return -EINVAL;
+
+ memcpy(ti, elem, sizeof(*ti));
+
+ return len;
+}
+
+/*! Encode MSISDN IE (3GPP TS 48.008 3.2.2.101).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] msisdn MSISDN to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_msisdn(struct msgb *msg, const char *msisdn)
+{
+ uint8_t *tlv_len;
+ uint8_t bcd_lv[11];
+ int rc;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(msisdn);
+
+ rc = gsm48_encode_bcd_number(bcd_lv, sizeof(bcd_lv), 0, msisdn);
+ if (rc < 1)
+ return 0;
+
+ msgb_put_u8(msg, GSM0808_IE_MSISDN);
+ tlv_len = msgb_put(msg, rc);
+
+ memcpy(tlv_len, bcd_lv, rc);
+
+ *tlv_len = rc - 1;
+ return *tlv_len + 2;
+}
+
+/*! Decode MSISDN IE (3GPP TS 48.008 3.2.2.101).
+ * \param[out] msisdn MSISDN structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_msisdn(char *msisdn, const char *elem, uint8_t len)
+{
+ OSMO_ASSERT(msisdn);
+ OSMO_ASSERT(elem);
+
+ if (len < 1)
+ return -EINVAL;
+
+ gsm48_decode_bcd_number(msisdn, MSISDN_MAXLEN + 1, (uint8_t *)(elem - 1), 0);
+
+ return len;
+}
+
+/*! Encode Talker Identity IE (3GPP TS 48.008 3.2.2.91).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] ti Talker Identity to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_talker_identity(struct msgb *msg, const struct gsm0808_talker_identity *ti)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+ uint8_t *ptr;
+ unsigned int bytes;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(ti);
+
+ msgb_put_u8(msg, GSM0808_IE_TALKER_IDENTITY);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ bytes = (ti->id_bits + 7) >> 3;
+ ptr = msgb_put(msg, 1 + bytes);
+ ptr[0] = -ti->id_bits & 0x7;
+ memcpy(ptr + 1, ti->talker_id, bytes);
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Talker Identity IE (3GPP TS 48.008 3.2.2.91).
+ * \param[out] ti Talker Identity structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_talker_identity(struct gsm0808_talker_identity *ti, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ti);
+ OSMO_ASSERT(elem);
+
+ if (len < 2)
+ return -EINVAL;
+ unsigned int bytes;
+
+ bytes = len - 1;
+ if (bytes > TALKER_IDENTITY_MAXLEN)
+ return -EINVAL;
+ ti->id_bits = (bytes << 3) - (elem[0] & 0x7);
+ memcpy(ti->talker_id, elem + 1, bytes);
+
+ return len;
+}
+
+/*! Encode Assignment Requirements IE (3GPP TS 48.008 3.2.2.52).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] ar Assignment Requirement to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_assign_req(struct msgb *msg, const enum gsm0808_assignment_requirement ar)
+{
+ uint8_t *ptr;
+
+ OSMO_ASSERT(msg);
+
+ msgb_put_u8(msg, GSM0808_IE_ASSIGNMENT_REQUIREMENT);
+
+ ptr = msgb_put(msg, 1);
+ ptr[0] = ar;
+
+ return 2;
+}
+
+/*! Decode Assignment Requirements IE (3GPP TS 48.008 3.2.2.52).
+ * \param[out] ar Assignment Requirements enum to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_assign_req(enum gsm0808_assignment_requirement *ar, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ar);
+ OSMO_ASSERT(elem);
+
+ if (len != 1)
+ return -EINVAL;
+
+ *ar = elem[0];
+
+ return 1;
+}
+
+/*! Encode Cell Identifier List Segment IE (3GPP TS 48.008 3.2.2.27a).
+ * \param[out] msg Message Buffer to which IE is to be appended
+ * \param[in] ie_type Type of IE to use (5 different lists are specified.)
+ * \param[in] ci Cell Identifier List Segment to be encoded
+ * \returns number of bytes appended to \a msg */
+uint8_t gsm0808_enc_cell_id_list_segment(struct msgb *msg, uint8_t ie_type,
+ const struct gsm0808_cell_id_list_segment *ci)
+{
+ uint8_t *old_tail;
+ uint8_t *tlv_len;
+ int rc;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(ci);
+
+ msgb_put_u8(msg, ie_type);
+ tlv_len = msgb_put(msg, 1);
+ old_tail = msg->tail;
+
+ msgb_put_u8(msg, (ci->seq_last << 4) | ci->seq_number);
+
+ rc = gsm0808_enc_cell_id_list2(msg, &ci->cil);
+ if (rc <= 0)
+ return rc;
+
+ *tlv_len = (uint8_t) (msg->tail - old_tail);
+ return *tlv_len + 2;
+}
+
+/*! Decode Cell Identifier List Segment IE (3GPP TS 48.008 3.2.2.27a).
+ * \param[out] ci Cell Identifier List Segment structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_cell_id_list_segment(struct gsm0808_cell_id_list_segment *ci, const uint8_t *elem, uint8_t len)
+{
+ int rc;
+
+ OSMO_ASSERT(ci);
+ OSMO_ASSERT(elem);
+
+ if (len < 1)
+ return -EINVAL;
+
+ ci->seq_last = elem[0] >> 4;
+ ci->seq_number = elem[0] & 0x0f;
+
+ rc = gsm0808_dec_cell_id_list2(&ci->cil, elem + 1, len - 1);
+ if (rc < 0)
+ return rc;
+
+ return len;
+}
+
+/*! Decode Call Identifier IE (3GPP TS 48.008 3.2.2.105).
+ * \param[out] ci Call Identifier structure to store data
+ * \param[in] elem IE value to be decoded.
+ * \param[in] len Length of \a elem in bytes.
+ * \returns number of bytes parsed; negative on error */
+int gsm0808_dec_call_id(uint32_t *ci, const uint8_t *elem, uint8_t len)
+{
+ OSMO_ASSERT(ci);
+ OSMO_ASSERT(elem);
+
+ if (len != 4)
+ return -EINVAL;
+
+ /* The Call Identifier is stored as little endian. */
+ *ci = osmo_load32le(elem);
+
+ return 4;
+}
+
/*! @} */
diff --git a/src/gsm/gsm23003.c b/src/gsm/gsm23003.c
index e20afcbc..1eed41fe 100644
--- a/src/gsm/gsm23003.c
+++ b/src/gsm/gsm23003.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <ctype.h>
@@ -247,6 +243,43 @@ char *osmo_lai_name_c(const void *ctx, const struct osmo_location_area_id *lai)
return osmo_lai_name_buf(buf, 32, lai);
}
+/*! Return MCC-MNC-LAC-RAC as string, in caller-provided output buffer.
+ * \param[out] buf caller-allocated output buffer
+ * \param[in] buf_len size of buf in bytes
+ * \param[in] rai RAI to encode, the rac member is ignored.
+ * \returns buf
+ */
+char *osmo_rai_name2_buf(char *buf, size_t buf_len, const struct osmo_routing_area_id *rai)
+{
+ char plmn[16];
+ snprintf(buf, buf_len, "%s-%u-%u", osmo_plmn_name_buf(plmn, sizeof(plmn),
+ &rai->lac.plmn), rai->lac.lac, rai->rac);
+ return buf;
+}
+
+/*! Return MCC-MNC-LAC-RAC as string, in a static buffer.
+ * \param[in] rai RAI to encode, the rac member is ignored.
+ * \returns Static string buffer.
+ */
+const char *osmo_rai_name2(const struct osmo_routing_area_id *rai)
+{
+ static __thread char buf[32];
+ return osmo_rai_name2_buf(buf, sizeof(buf), rai);
+}
+
+/*! Return MCC-MNC-LAC-RAC as string, in a talloc-allocated output buffer.
+ * \param[in] ctx talloc context from which to allocate output buffer
+ * \param[in] rai RAI to encode, the rac member is ignored.
+ * \returns string representation of lai in dynamically allocated buffer.
+ */
+char *osmo_rai_name2_c(const void *ctx, const struct osmo_routing_area_id *rai)
+{
+ char *buf = talloc_size(ctx, 32);
+ if (!buf)
+ return NULL;
+ return osmo_rai_name2_buf(buf, 32, rai);
+}
+
/*! Return MCC-MNC-LAC-CI as string, in caller-provided output buffer.
* \param[out] buf caller-allocated output buffer
* \param[in] buf_len size of buf in bytes
@@ -291,6 +324,95 @@ char *osmo_cgi_name_c(const void *ctx, const struct osmo_cell_global_id *cgi)
return osmo_cgi_name_buf(buf, 32, cgi);
}
+/*! Return MCC-MNC-LAC-RAC-CI as string, in caller-provided output buffer.
+ * \param[out] buf caller-allocated output buffer
+ * \param[in] buf_len size of buf in bytes
+ * \param[in] cgi_ps CGI-PS to encode.
+ * \returns buf
+ */
+char *osmo_cgi_ps_name_buf(char *buf, size_t buf_len, const struct osmo_cell_global_id_ps *cgi_ps)
+{
+ snprintf(buf, buf_len, "%s-%u", osmo_rai_name2(&cgi_ps->rai), cgi_ps->cell_identity);
+ return buf;
+}
+
+/*! Return MCC-MNC-LAC-RAC-CI as string, in a static buffer.
+ * \param[in] cgi_ps CGI-PS to encode.
+ * \returns Static string buffer.
+ */
+const char *osmo_cgi_ps_name(const struct osmo_cell_global_id_ps *cgi_ps)
+{
+ static __thread char buf[32];
+ return osmo_cgi_ps_name_buf(buf, sizeof(buf), cgi_ps);
+}
+
+/*! Same as osmo_cgi_ps_name(), but uses a different static buffer.
+ * Useful for printing two distinct CGI-PSs in the same printf format.
+ * \param[in] cgi CGI-PS to encode.
+ * \returns Static string buffer.
+ */
+const char *osmo_cgi_ps_name2(const struct osmo_cell_global_id_ps *cgi_ps)
+{
+ static __thread char buf[32];
+ return osmo_cgi_ps_name_buf(buf, sizeof(buf), cgi_ps);
+}
+
+/*! Return MCC-MNC-LAC-RAC-CI as string, in a talloc-allocated output buffer.
+ * \param[in] ctx talloc context from which to allocate output buffer
+ * \param[in] cgi_ps CGI-PS to encode.
+ * \returns string representation of CGI in dynamically-allocated buffer.
+ */
+char *osmo_cgi_ps_name_c(const void *ctx, const struct osmo_cell_global_id_ps *cgi_ps)
+{
+ char *buf = talloc_size(ctx, 32);
+ return osmo_cgi_ps_name_buf(buf, 32, cgi_ps);
+}
+
+/*! Return MCC-MNC-LAC-SAC as string, in caller-provided output buffer.
+ * \param[out] buf caller-allocated output buffer
+ * \param[in] buf_len size of buf in bytes
+ * \param[in] sai SAI to encode.
+ * \returns buf
+ */
+char *osmo_sai_name_buf(char *buf, size_t buf_len, const struct osmo_service_area_id *sai)
+{
+ snprintf(buf, buf_len, "%s-%u", osmo_lai_name(&sai->lai), sai->sac);
+ return buf;
+}
+
+/*! Return MCC-MNC-LAC-SAC as string, in a static buffer.
+ * \param[in] sai SAI to encode.
+ * \returns Static string buffer.
+ */
+const char *osmo_sai_name(const struct osmo_service_area_id *sai)
+{
+ static __thread char buf[32];
+ return osmo_sai_name_buf(buf, sizeof(buf), sai);
+}
+
+/*! Same as osmo_cgi_name(), but uses a different static buffer.
+ * Useful for printing two distinct CGIs in the same printf format.
+ * \param[in] sai SAI to encode.
+ * \returns Static string buffer.
+ */
+const char *osmo_sai_name2(const struct osmo_service_area_id *sai)
+{
+ static __thread char buf[32];
+ return osmo_sai_name_buf(buf, sizeof(buf), sai);
+}
+
+/*! Return MCC-MNC-LAC-SAC as string, in a talloc-allocated output buffer.
+ * \param[in] ctx talloc context from which to allocate output buffer
+ * \param[in] sai SAI to encode.
+ * \returns string representation of CGI in dynamically-allocated buffer.
+ */
+char *osmo_sai_name_c(const void *ctx, const struct osmo_service_area_id *sai)
+{
+ char *buf = talloc_size(ctx, 32);
+ return osmo_sai_name_buf(buf, 32, sai);
+}
+
+
static void to_bcd(uint8_t *bcd, uint16_t val)
{
bcd[2] = val % 10;
@@ -368,14 +490,12 @@ void osmo_plmn_to_bcd(uint8_t *bcd_dst, const struct osmo_plmn_id *plmn)
}
}
-/* Convert given 3-byte BCD buffer to integers and write results to *mcc and
- * *mnc. The first three BCD digits result in the MCC and the remaining ones in
- * the MNC. Return mnc_3_digits as false if the MNC's most significant digit is encoded as 0xF, true
- * otherwise; i.e. true if MNC > 99 or if it is represented with leading zeros instead of 0xF.
+/* Convert given 3-byte BCD buffer to integers and write results to plmn->mcc and plmn->mnc. The first three BCD digits
+ * result in the MCC and the remaining ones in the MNC. Set plmn->mnc_3_digits as false if the MNC's most significant
+ * digit is encoded as 0xF, true otherwise; i.e. true if MNC > 99 or if it is represented with leading zeros instead of
+ * 0xF.
* \param[in] bcd_src 3-byte BCD buffer containing MCC+MNC representations.
- * \param[out] mcc MCC result buffer, or NULL.
- * \param[out] mnc MNC result buffer, or NULL.
- * \param[out] mnc_3_digits Result buffer for 3-digit flag, or NULL.
+ * \param[out] plmn user provided memory to store the result.
*/
void osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn)
{
@@ -406,22 +526,23 @@ void osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn)
*/
int osmo_mnc_from_str(const char *mnc_str, uint16_t *mnc, bool *mnc_3_digits)
{
- long int _mnc = 0;
+ int _mnc = 0;
bool _mnc_3_digits = false;
- char *endptr;
int rc = 0;
if (!mnc_str || !isdigit((unsigned char)mnc_str[0]) || strlen(mnc_str) > 3)
return -EINVAL;
- errno = 0;
- _mnc = strtol(mnc_str, &endptr, 10);
- if (errno)
- rc = -errno;
- else if (*endptr)
+ rc = osmo_str_to_int(&_mnc, mnc_str, 10, 0, 999);
+ /* Heed the API definition to return -EINVAL in case of surplus chars */
+ if (rc == -E2BIG)
return -EINVAL;
- if (_mnc < 0 || _mnc > 999)
- return -ERANGE;
+ /* Heed the API definition to always return negative errno */
+ if (rc > 0)
+ return -rc;
+ if (rc < 0)
+ return rc;
+
_mnc_3_digits = strlen(mnc_str) > 2;
if (mnc)
@@ -484,6 +605,23 @@ int osmo_lai_cmp(const struct osmo_location_area_id *a, const struct osmo_locati
return 0;
}
+/* Compare two RAI.
+ * The order of comparison is MCC, MNC, LAC, RAC. See also osmo_lai_cmp().
+ * \param a[in] "Left" side RAI.
+ * \param b[in] "Right" side RAI.
+ * \returns 0 if the RAI are equal, -1 if a < b, 1 if a > b. */
+int osmo_rai_cmp(const struct osmo_routing_area_id *a, const struct osmo_routing_area_id *b)
+{
+ int rc = osmo_lai_cmp(&a->lac, &b->lac);
+ if (rc)
+ return rc;
+ if (a->rac < b->rac)
+ return -1;
+ if (a->rac > b->rac)
+ return 1;
+ return 0;
+}
+
/* Compare two CGI.
* The order of comparison is MCC, MNC, LAC, CI. See also osmo_lai_cmp().
* \param a[in] "Left" side CGI.
@@ -501,6 +639,23 @@ int osmo_cgi_cmp(const struct osmo_cell_global_id *a, const struct osmo_cell_glo
return 0;
}
+/* Compare two CGI-PS.
+ * The order of comparison is MCC, MNC, LAC, RAC, CI. See also osmo_rai_cmp().
+ * \param a[in] "Left" side CGI-PS.
+ * \param b[in] "Right" side CGI-PS.
+ * \returns 0 if the CGI are equal, -1 if a < b, 1 if a > b. */
+int osmo_cgi_ps_cmp(const struct osmo_cell_global_id_ps *a, const struct osmo_cell_global_id_ps *b)
+{
+ int rc = osmo_rai_cmp(&a->rai, &b->rai);
+ if (rc)
+ return rc;
+ if (a->cell_identity < b->cell_identity)
+ return -1;
+ if (a->cell_identity > b->cell_identity)
+ return 1;
+ return 0;
+}
+
/*! Generate TS 23.003 Section 19.2 Home Network Realm/Domain (text form)
* \param out[out] caller-provided output buffer, at least 33 bytes long
* \param plmn[in] Osmocom representation of PLMN ID (MCC + MNC)
diff --git a/src/gsm/gsm23236.c b/src/gsm/gsm23236.c
new file mode 100644
index 00000000..4a83ec82
--- /dev/null
+++ b/src/gsm/gsm23236.c
@@ -0,0 +1,542 @@
+/*! \file gsm23236.c
+ * Utility function implementations related to 3GPP TS 23.236 */
+/*
+ * (C) 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm23236.h>
+
+/*! Validate that the given NRI is valid for a given nri_bitlen range.
+ * \param[in] nri_v NRI value to validate.
+ * \param[in] nri_bitlen Valid NRI range in nr of bits used; if nri_bitlen > OSMO_NRI_BITLEN_MAX, nri_v is only
+ * checked to not be marked invalid.
+ * \returns 0 if valid, <0 if the NRI is <0, >0 if the NRI surpasses the range.
+ */
+int osmo_nri_v_validate(int16_t nri_v, uint8_t nri_bitlen)
+{
+ if (nri_v < 0)
+ return -1;
+ if (nri_bitlen < OSMO_NRI_BITLEN_MIN)
+ return 1;
+ if (nri_bitlen < OSMO_NRI_BITLEN_MAX && (nri_v >> nri_bitlen))
+ return 1;
+ return 0;
+}
+
+/*! Match NRI value against a list NRI ranges. */
+static bool nri_v_matches_range(const struct osmo_nri_range *range, int16_t nri_v)
+{
+ return range && nri_v >= range->first && nri_v <= range->last;
+}
+
+/*! Return true if the ranges overlap, i.e. one or more NRI values appear in both ranges. */
+static bool nri_range_overlaps_range(const struct osmo_nri_range *a, const struct osmo_nri_range *b)
+{
+ return nri_v_matches_range(b, a->first) || nri_v_matches_range(b, a->last)
+ || nri_v_matches_range(a, b->first) || nri_v_matches_range(a, b->last);
+}
+
+/*! Return true if the ranges overlap or are directly adjacent to each other. */
+static bool nri_range_touches(const struct osmo_nri_range *a, const struct osmo_nri_range *b)
+{
+ /* The first > last check may seem redundant, but ensures integer overflow safety. */
+ return nri_range_overlaps_range(a, b)
+ || (a->first > b->last && a->first == b->last + 1)
+ || (b->first > a->last && b->first == a->last + 1);
+}
+
+/*! Grow target range to also span range 'add'. Only useful for touching ranges, since all values between the two ranges
+ * are also included. */
+static void nri_range_extend(struct osmo_nri_range *target, const struct osmo_nri_range *add)
+{
+ target->first = OSMO_MIN(target->first, add->first);
+ target->last = OSMO_MAX(target->last, add->last);
+}
+
+/*! Return true when the given NRI value appears in the list of NRI ranges.
+ * \param[in] nri_v NRI value to look for.
+ * \param[in] nri_ranges List NRI ranges.
+ * \returns true iff nri_v appears anywhere in nri_ranges.
+ */
+bool osmo_nri_v_matches_ranges(int16_t nri_v, const struct osmo_nri_ranges *nri_ranges)
+{
+ struct osmo_nri_range *range;
+ if (!nri_ranges)
+ return false;
+ llist_for_each_entry(range, &nri_ranges->entries, entry) {
+ if (nri_v_matches_range(range, nri_v))
+ return true;
+ }
+ return false;
+}
+
+/*! Modulo and shift the given NRI value so that it becomes a value present in a list of NRI ranges.
+ * Only range values within nri_bitlen are used.
+ * \param[inout] nri_v The NRI value to limit, e.g. random bits or an increment counter value.
+ * \param[in] nri_ranges List of NRI ranges indicating valid NRI values, where no entries may overlap in range values,
+ * and all entries must be valid (first <= last).
+ * \returns 0 on success, negative on error.
+ */
+int osmo_nri_v_limit_by_ranges(int16_t *nri_v, const struct osmo_nri_ranges *nri_ranges, uint32_t nri_bitlen)
+{
+ struct osmo_nri_range *range;
+ uint32_t total_values = 0;
+ int16_t v = *nri_v;
+ int16_t range_max = (((int16_t)1) << nri_bitlen) - 1;
+
+ if (v < 0 || !nri_ranges)
+ return -1;
+
+ /* Sum up total amount of range values */
+ llist_for_each_entry(range, &nri_ranges->entries, entry) {
+ if (osmo_nri_range_validate(range, 255))
+ return -1;
+ if (range->first > range_max)
+ continue;
+ total_values += OSMO_MIN(range_max, range->last) - range->first + 1;
+ }
+
+ /* Modulo the given NRI value by that, and pick that nth value from the given ranges.
+ * (nri_ranges is pretty much guaranteed to be sorted and range_max checks thus would no longer be needed, but
+ * just check them anyway.) */
+ v %= total_values;
+ llist_for_each_entry(range, &nri_ranges->entries, entry) {
+ uint32_t len;
+ if (range->first > range_max)
+ continue;
+ len = OSMO_MIN(range_max, range->last) - range->first + 1;
+ if (v < len) {
+ *nri_v = range->first + v;
+ return 0;
+ }
+ v -= len;
+ }
+
+ /* Nothing found -- there are no entires or my math is off. */
+ return -1;
+}
+
+/*! Retrieve the Network Resource Indicator bits from a TMSI or p-TMSI.
+ * Useful for MSC pooling as described by 3GPP TS 23.236.
+ * \param[out] nri_v Write the extracted NRI value to this location (if non-NULL). If 0 is returned, it is guaranteed
+ * that nri_v >= 0. On non-zero return code, nri_v == -1.
+ * \param[in] tmsi TMSI value containing NRI bits.
+ * \param[in] nri_bitlen Length of the NRI value in number of bits,
+ * OSMO_NRI_BITLEN_MIN <= nri_bitlen <= * OSMO_NRI_BITLEN_MAX.
+ * \return 0 on success, negative on error (i.e. if nri_bitlen is not in the valid range).
+ */
+int osmo_tmsi_nri_v_get(int16_t *nri_v, uint32_t tmsi, uint8_t nri_bitlen)
+{
+ uint8_t lowest_bit;
+ if (nri_v)
+ *nri_v = -1;
+ if (nri_bitlen < OSMO_NRI_BITLEN_MIN || nri_bitlen > OSMO_NRI_BITLEN_MAX)
+ return -1;
+ /* If not interested in the NRI value, exit here. */
+ if (!nri_v)
+ return 0;
+ /* According to 3GPP TS 23.236, the most significant bit of the NRI is always bit 23.
+ * (So this is not a temporary placeholder 23 we sometimes like to use, it is an actually specified 23!) */
+ lowest_bit = 23 - (nri_bitlen - 1);
+ /* ????xxxxxx??????? -> 0000000????xxxxxx tmsi >> lowest_bit
+ * -> xxxxxx & (bitmask that is nri_bitlen bits wide)
+ */
+ *nri_v = (tmsi >> lowest_bit) & ((((uint32_t)1) << nri_bitlen) - 1);
+ return 0;
+}
+
+/*! Write Network Resource Indicator bits into a TMSI or p-TMSI.
+ * Overwrite the NRI bits with a given NRI value in a TMSI or p-TMSI.
+ * Useful for MSC pooling as described by 3GPP TS 23.236.
+ * \param[inout] tmsi A base TMSI or p-TMSI to replace the NRI value in, result is written back to this location.
+ * \param[in] nri_v The NRI value to place in the tmsi.
+ * \param[in] nri_bitlen Length of the NRI value in number of bits,
+ * OSMO_NRI_BITLEN_MIN <= nri_bitlen <= * OSMO_NRI_BITLEN_MAX.
+ * \return 0 on success, negative on error (i.e. if nri_bitlen is not in the valid range or if tmsi is NULL).
+ */
+int osmo_tmsi_nri_v_set(uint32_t *tmsi, int16_t nri_v, uint8_t nri_bitlen)
+{
+ uint8_t lowest_bit;
+ uint32_t v_mask;
+ if (nri_bitlen < OSMO_NRI_BITLEN_MIN || nri_bitlen > OSMO_NRI_BITLEN_MAX)
+ return -1;
+ if (nri_v < 0)
+ return -1;
+ if (!tmsi)
+ return -1;
+ lowest_bit = 23 - (nri_bitlen - 1);
+ v_mask = ((((uint32_t)1) << nri_bitlen) - 1) << lowest_bit;
+ *tmsi = ((*tmsi) & ~v_mask) | ((((uint32_t)nri_v) << lowest_bit) & v_mask);
+ return 0;
+}
+
+/*! Apply osmo_nri_v_limit_by_ranges() in-place on the NRI value included in a TMSI.
+ * Extract the NRI value from the TMSI, limit that to be part of the ranges given in 'nri_ranges', and place the
+ * resulting NRI value back in the TMSI.
+ * \param[inout] tmsi TMSI value of which to modify the NRI bits, e.g. fresh randomized bits.
+ * \param[in] nri_ranges List of NRI ranges indicating valid NRI values, where no entries may overlap in range values,
+ * and all entries must be valid (first <= last).
+ * \param[in] nri_bitlen Valid NRI range in nr of bits used.
+ * \returns 0 on success, negative on error.
+ */
+int osmo_tmsi_nri_v_limit_by_ranges(uint32_t *tmsi, const struct osmo_nri_ranges *nri_ranges, uint8_t nri_bitlen)
+{
+ int rc;
+ int16_t nri_v;
+ rc = osmo_tmsi_nri_v_get(&nri_v, *tmsi, nri_bitlen);
+ if (rc)
+ return rc;
+ rc = osmo_nri_v_limit_by_ranges(&nri_v, nri_ranges, nri_bitlen);
+ if (rc)
+ return rc;
+ return osmo_tmsi_nri_v_set(tmsi, nri_v, nri_bitlen);
+}
+
+/*! Validate that the given NRI range is valid for a given nri_bitlen range.
+ * \param[in] nri_range NRI value range to validate.
+ * \param[in] nri_bitlen Valid NRI range in nr of bits used. If nri_bitlen > OSMO_NRI_BITLEN_MAX, the NRI range is only
+ * validated to be first <= last and non-negative, not checked to fit a bit length range,
+ * \returns 0 if valid, -1 or 1 if range->first is invalid, -2 or 2 if range->last is invalid, -3 if first > last.
+ */
+int osmo_nri_range_validate(const struct osmo_nri_range *range, uint8_t nri_bitlen)
+{
+ int rc;
+ rc = osmo_nri_v_validate(range->first, nri_bitlen);
+ if (rc)
+ return rc;
+ rc = osmo_nri_v_validate(range->last, nri_bitlen);
+ if (rc)
+ return 2 * rc;
+ if (range->first > range->last)
+ return -3;
+ return 0;
+}
+
+/*! Return true when the given NRI range has at least one NRI value that appears in a list of other NRI ranges.
+ * \param[in] range NRI range to look for.
+ * \param[in] nri_ranges List NRI ranges.
+ * \returns true iff any NRI value from 'range' appears anywhere in nri_ranges.
+ */
+bool osmo_nri_range_overlaps_ranges(const struct osmo_nri_range *range, const struct osmo_nri_ranges *nri_ranges)
+{
+ struct osmo_nri_range *i;
+ if (!nri_ranges)
+ return false;
+ llist_for_each_entry(i, &nri_ranges->entries, entry) {
+ if (nri_range_overlaps_range(i, range))
+ return true;
+ }
+ return false;
+}
+
+/*! Allocate an empty struct osmo_nri_ranges (list of struct osmo_nri_range).
+ * \param ctx Talloc context to allocate from.
+ * \return allocated empty list.
+ */
+struct osmo_nri_ranges *osmo_nri_ranges_alloc(void *ctx)
+{
+ struct osmo_nri_ranges *nri_ranges;
+ nri_ranges = talloc_zero(ctx, struct osmo_nri_ranges);
+ OSMO_ASSERT(nri_ranges);
+ INIT_LLIST_HEAD(&nri_ranges->entries);
+ return nri_ranges;
+}
+
+/*! Free a struct osmo_nri_ranges.
+ * \param nri_ranges The list to discard.
+ */
+void osmo_nri_ranges_free(struct osmo_nri_ranges *nri_ranges)
+{
+ if (nri_ranges)
+ talloc_free(nri_ranges);
+}
+
+/*! Insert a new struct osmo_nri_range in an osmo_nri_ranges list, so that it remains sorted by 'first' values. */
+static void nri_ranges_add_entry_sorted(struct osmo_nri_ranges *nri_ranges, struct osmo_nri_range *add)
+{
+ struct osmo_nri_range *r;
+ struct llist_head *at_pos;
+ OSMO_ASSERT(nri_ranges);
+ at_pos = nri_ranges->entries.prev;
+ llist_for_each_entry(r, &nri_ranges->entries, entry) {
+ if (r->first <= add->first)
+ continue;
+ at_pos = r->entry.prev;
+ break;
+ }
+ llist_add(&add->entry, at_pos);
+}
+
+/*! Add a range of NRI values to a list of nri_range structs.
+ * Intelligently add and/or combine the entries in a list of NRI ranges to also include the NRI range given in 'add'.
+ * The list remains sorted by 'first' values.
+ * \param[inout] nri_ranges List of talloc allocated struct osmo_nri_range entries to add the new range to.
+ * \param[in] add NRI range to add to 'nri_ranges'.
+ * \returns 0 on success, negative on error (if the range in 'add' is invalid).
+ */
+int osmo_nri_ranges_add(struct osmo_nri_ranges *nri_ranges, const struct osmo_nri_range *add)
+{
+ struct osmo_nri_range *range;
+ struct osmo_nri_range *range_next;
+ struct osmo_nri_range *target = NULL;
+
+ if (osmo_nri_range_validate(add, 255))
+ return -1;
+ if (!nri_ranges)
+ return -1;
+
+ /* Is there an entry overlapping this range? */
+ llist_for_each_entry(range, &nri_ranges->entries, entry) {
+ if (!nri_range_touches(range, add))
+ continue;
+ target = range;
+ }
+
+ if (!target) {
+ /* No overlaps with existing ranges, create a new one. */
+ target = talloc_zero(nri_ranges, struct osmo_nri_range);
+ OSMO_ASSERT(target);
+ *target = *add;
+ nri_ranges_add_entry_sorted(nri_ranges, target);
+ return 0;
+ }
+
+ /* Overlap found, join into existing entry */
+ nri_range_extend(target, add);
+
+ /* Remove redundant entries */
+ llist_for_each_entry_safe(range, range_next, &nri_ranges->entries, entry) {
+ if (range == target)
+ continue;
+ if (!nri_range_touches(target, range))
+ continue;
+ nri_range_extend(target, range);
+ llist_del(&range->entry);
+ talloc_free(range);
+ }
+ return 0;
+}
+
+/*! Remove a range of NRI values from a list of nri_range structs.
+ * Intelligently drop and/or cut or split the entries in a list of NRI ranges to no longer include the NRI range given
+ * in 'del'. Note that after this, the list may have more entries than before, if a range was split into two smaller
+ * ranges.
+ * \param[inout] nri_ranges List of talloc allocated struct osmo_nri_range entries to remove values from.
+ * \param[in] del NRI range to remove from 'nri_ranges'.
+ * \returns 0 on success, negative on error (if the range in 'del' is invalid).
+ */
+int osmo_nri_ranges_del(struct osmo_nri_ranges *nri_ranges, const struct osmo_nri_range *del)
+{
+ struct osmo_nri_range *range;
+ struct osmo_nri_range *range_next;
+
+ if (osmo_nri_range_validate(del, 255))
+ return -1;
+ if (!nri_ranges)
+ return -1;
+
+ llist_for_each_entry_safe(range, range_next, &nri_ranges->entries, entry) {
+ bool head;
+ bool tail;
+ if (!nri_range_overlaps_range(range, del))
+ continue;
+
+ head = nri_v_matches_range(range, del->first) && (del->first > range->first);
+ tail = nri_v_matches_range(range, del->last) && (del->last < range->last);
+
+ if (head && tail) {
+ /* Range cut in two */
+ struct osmo_nri_range *new_tail;
+
+ /* Add a new entry for the tail section */
+ new_tail = talloc_zero(nri_ranges, struct osmo_nri_range);
+ OSMO_ASSERT(new_tail);
+ *new_tail = (struct osmo_nri_range){
+ .first = del->last + 1,
+ .last = range->last,
+ };
+ llist_add(&new_tail->entry, &range->entry);
+
+ /* Existing entry becomes the head section */
+ range->last = del->first - 1;
+ } else if (head) {
+ /* Range reduced, a head remains */
+ range->last = del->first - 1;
+ } else if (tail) {
+ /* Range reduced, a tail remains */
+ range->first = del->last + 1;
+ } else {
+ /* nothing remains */
+ llist_del(&range->entry);
+ talloc_free(range);
+ }
+ }
+ return 0;
+}
+
+/*! Compose a human readable representation of a list of NRI ranges in a buffer, like "23..42,123..142".
+ * \param[out] buf Target buffer.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] nri_ranges List NRI ranges.
+ * \returns strlen() of string that would be written if the buffer is large enough, like snprintf().
+ */
+int osmo_nri_ranges_to_str_buf(char *buf, size_t buflen, const struct osmo_nri_ranges *nri_ranges)
+{
+ struct osmo_nri_range *range;
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ bool first = true;
+ if (!nri_ranges || llist_empty(&nri_ranges->entries)) {
+ OSMO_STRBUF_PRINTF(sb, "empty");
+ return sb.chars_needed;
+ }
+ llist_for_each_entry(range, &nri_ranges->entries, entry) {
+ OSMO_STRBUF_PRINTF(sb, "%s%d..%d", first ? "" : ",", range->first, range->last);
+ }
+ return sb.chars_needed;
+}
+
+/*! Compose a human readable representation of a list of NRI ranges in a talloc buffer, like "23..42,123..142".
+ * \param[in] ctx Talloc context.
+ * \param[in] nri_ranges List of NRI ranges.
+ * \returns a talloc allocated string.
+ */
+char *osmo_nri_ranges_to_str_c(void *ctx, const struct osmo_nri_ranges *nri_ranges)
+{
+ OSMO_NAME_C_IMPL(ctx, 16, "ERROR", osmo_nri_ranges_to_str_buf, nri_ranges);
+}
+
+/*! Parse a string to an NRI value, allowing both decimal and hexadecimal formats; useful for VTY config
+ * implementations.
+ * \param[out] dst Write the resulting NRI value to this location.
+ * \param[in] str Decimal "511" or hex "0x1ff" string to parse.
+ * \returns 0 on success, negative on error.
+ */
+static int osmo_nri_parse(int16_t *dst, const char *str)
+{
+ int val;
+ int base = 10;
+ if (osmo_str_startswith(str, "0x"))
+ base = 16;
+ if (osmo_str_to_int(&val, str, base, 0, INT16_MAX))
+ return -1;
+ *dst = (int16_t)val;
+ return 0;
+}
+
+/*! Parse string arguments to a struct osmo_nri_range; useful for VTY config implementations.
+ * Validate and parse 'first' and optional 'last' string arguments into struct osmo_nri_range values.
+ * The strings may be in decimal format ("511") or hexadecimal with leading "0x" ("0x1ff").
+ * If only one of 'first'/'last' is provided, the resulting range will have only that value (first == last).
+ * \param[out] nri_range Target for parsed values.
+ * \param[in] first_str Decimal or hex string, representing the first value in the range, or NULL if omitted.
+ * \param[in] last_str Decimal or hex string, representing the last value in the range, or NULL if omitted.
+ * \returns 0 on success, negative on error.
+ */
+static int osmo_nri_parse_range(struct osmo_nri_range *nri_range, const char *first_str, const char *last_str)
+{
+ if (!nri_range)
+ return -1;
+ if (!first_str) {
+ first_str = last_str;
+ last_str = NULL;
+ if (!first_str)
+ return -1;
+ }
+ if (osmo_nri_parse(&nri_range->first, first_str))
+ return -1;
+ nri_range->last = nri_range->first;
+ if (last_str) {
+ if (osmo_nri_parse(&nri_range->last, last_str))
+ return -1;
+ }
+ return 0;
+}
+
+/*! VTY implementation for adding an NRI range to a list of ranges.
+ * Parse one or, if present, two argv arguments, which must be numbers representing the first and last value to add to
+ * the list of NRI ranges, in decimal format ("511") or hexadecimal with leading "0x" ("0x1ff"). If the range values
+ * surpass the nri_bitlen, return a warning in 'message', but still add the values to the list.
+ * \param[out] message Returned string constant to alert the user with, or NULL if all is well.
+ * \param[out] added_range If not NULL, write the range parsing result to this location.
+ * \param[in] nri_ranges List NRI ranges to add to.
+ * \param[in] argc Argument count.
+ * \param[in] argv Argument list.
+ * \param[in] nri_bitlen Valid NRI range in nr of bits used.
+ * \returns 0 on success, -1 on error, 1 for a warning (if adding was successful but the added range surpasses
+ * nri_bitlen).
+ */
+int osmo_nri_ranges_vty_add(const char **message, struct osmo_nri_range *added_range,
+ struct osmo_nri_ranges *nri_ranges, int argc, const char **argv, uint8_t nri_bitlen)
+{
+ struct osmo_nri_range add_range;
+ if (osmo_nri_parse_range(&add_range, argv[0], argc > 1 ? argv[1] : NULL)) {
+ *message = "Error: cannot parse NRI range";
+ return -1;
+ }
+
+ if (added_range)
+ *added_range = add_range;
+
+ if (osmo_nri_ranges_add(nri_ranges, &add_range)) {
+ *message = "Error: failed to add NRI range";
+ return -1;
+ }
+
+ if (nri_bitlen <= OSMO_NRI_BITLEN_MAX && osmo_nri_range_validate(&add_range, nri_bitlen)) {
+ *message = "Warning: NRI range surpasses current NRI bitlen";
+ return 1;
+ }
+
+ *message = NULL;
+ return 0;
+}
+
+/*! VTY implementation for removing an NRI range from a list of ranges.
+ * Parse one or, if present, two argv arguments, which must be numbers representing the first and last value to remove
+ * from the list of NRI ranges, in decimal format ("511") or hexadecimal with leading "0x" ("0x1ff").
+ * \param[out] message Returned string constant to alert the user with, or NULL if all is well.
+ * \param[out] removed_range If not NULL, write the range parsing result to this location.
+ * \param[in] nri_ranges List of NRI ranges to remove from.
+ * \param[in] argc Argument count.
+ * \param[in] argv Argument list.
+ * \returns 0 on success, -1 on error, 1 for a warning.
+ */
+int osmo_nri_ranges_vty_del(const char **message, struct osmo_nri_range *removed_range,
+ struct osmo_nri_ranges *nri_ranges, int argc, const char **argv)
+{
+ struct osmo_nri_range del_range;
+ if (osmo_nri_parse_range(&del_range, argv[0], argc > 1 ? argv[1] : NULL)) {
+ *message = "Error: cannot parse NRI range";
+ return -1;
+ }
+
+ if (removed_range)
+ *removed_range = del_range;
+
+ if (osmo_nri_ranges_del(nri_ranges, &del_range)) {
+ *message = "Error: failed to remove NRI range";
+ return -1;
+ }
+
+ *message = NULL;
+ return 0;
+}
diff --git a/src/gsm/gsm29118.c b/src/gsm/gsm29118.c
index 2c02b2fd..0ca5b52e 100644
--- a/src/gsm/gsm29118.c
+++ b/src/gsm/gsm29118.c
@@ -14,10 +14,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <osmocom/core/utils.h>
diff --git a/src/gsm/gsm29205.c b/src/gsm/gsm29205.c
index 6ceb8ee4..8fed0206 100644
--- a/src/gsm/gsm29205.c
+++ b/src/gsm/gsm29205.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include "config.h"
diff --git a/src/gsm/gsm44021.c b/src/gsm/gsm44021.c
new file mode 100644
index 00000000..5e9d5ab8
--- /dev/null
+++ b/src/gsm/gsm44021.c
@@ -0,0 +1,303 @@
+/*************************************************************************
+ * GSM CSD modified V.110 frame decoding/encoding (ubits <-> struct with D/S/X/E bits)
+ *************************************************************************/
+
+/* (C) 2022 by Harald Welte <laforge@osmocom.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <errno.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+
+/*! Decode a 60-bit GSM 12kbit/s CSD frame present as 60 ubits into a struct osmo_v110_decoded_frame.
+ * \param[out] caller-allocated output data structure, filled by this function
+ * \param[in] ra_bits One V.110 frame as 60 unpacked bits. */
+int osmo_csd_12k_6k_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits)
+{
+ /* 3GPP TS 44.021 Section 8.1.2 / 8.1.3
+ D1 D2 D3 D4 D5 D6 S1
+ D7 D8 D9 D10 D11 D12 X
+ D13 D14 D15 D16 D17 D18 S3
+ D19 D20 D21 D22 D23 D24 S4
+ E4 E5 E6 E7 D25 D26 D27
+ D28 D29 D30 S6 D31 D32 D33
+ D34 D35 D36 X D37 D38 D39
+ D40 D41 D42 S8 D43 D44 D45
+ D46 D47 D48 S9 */
+
+ if (n_bits < 60)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ fr->x_bits[0] = ra_bits[1 * 7 + 6];
+ fr->x_bits[1] = ra_bits[6 * 7 + 3];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ fr->s_bits[0] = ra_bits[0 * 7 + 6];
+ fr->s_bits[2] = ra_bits[2 * 7 + 6];
+ fr->s_bits[3] = ra_bits[3 * 7 + 6];
+ fr->s_bits[5] = ra_bits[5 * 7 + 3];
+ fr->s_bits[7] = ra_bits[7 * 7 + 3];
+ fr->s_bits[8] = ra_bits[8 * 7 + 3];
+
+ /* E1 .. E3 must be set by out-of-band knowledge! */
+
+ /* E4 .. E7 */
+ memcpy(fr->e_bits+3, ra_bits + 4 * 7 + 0, 4);
+
+ /* D-bits */
+ memcpy(fr->d_bits + 0 * 6 + 0, ra_bits + 0 * 7 + 0, 6);
+ memcpy(fr->d_bits + 1 * 6 + 0, ra_bits + 1 * 7 + 0, 6);
+ memcpy(fr->d_bits + 2 * 6 + 0, ra_bits + 2 * 7 + 0, 6);
+ memcpy(fr->d_bits + 3 * 6 + 0, ra_bits + 3 * 7 + 0, 6);
+ memcpy(fr->d_bits + 4 * 6 + 0, ra_bits + 4 * 7 + 4, 3);
+ memcpy(fr->d_bits + 4 * 6 + 3, ra_bits + 5 * 7 + 0, 3);
+ memcpy(fr->d_bits + 5 * 6 + 0, ra_bits + 5 * 7 + 4, 3);
+ memcpy(fr->d_bits + 5 * 6 + 3, ra_bits + 6 * 7 + 0, 3);
+ memcpy(fr->d_bits + 6 * 6 + 0, ra_bits + 6 * 7 + 4, 3);
+ memcpy(fr->d_bits + 6 * 6 + 3, ra_bits + 7 * 7 + 0, 3);
+ memcpy(fr->d_bits + 7 * 6 + 0, ra_bits + 7 * 7 + 4, 3);
+ memcpy(fr->d_bits + 7 * 6 + 3, ra_bits + 8 * 7 + 0, 3);
+
+ return 0;
+}
+
+int osmo_csd_12k_6k_encode_frame(ubit_t *ra_bits, size_t ra_bits_size, const struct osmo_v110_decoded_frame *fr)
+{
+ if (ra_bits_size < 60)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ ra_bits[1 * 7 + 6] = fr->x_bits[0];
+ ra_bits[6 * 7 + 3] = fr->x_bits[1];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ ra_bits[0 * 7 + 6] = fr->s_bits[0];
+ ra_bits[2 * 7 + 6] = fr->s_bits[2];
+ ra_bits[3 * 7 + 6] = fr->s_bits[3];
+ ra_bits[5 * 7 + 3] = fr->s_bits[5];
+ ra_bits[7 * 7 + 3] = fr->s_bits[7];
+ ra_bits[8 * 7 + 3] = fr->s_bits[8];
+
+ /* E1 .. E3 are dropped */
+
+ /* E4 .. E7 */
+ memcpy(ra_bits + 4 * 7 + 0, fr->e_bits+3, 4);
+
+ /* D-bits */
+ memcpy(ra_bits + 0 * 7 + 0, fr->d_bits + 0 * 6 + 0, 6);
+ memcpy(ra_bits + 1 * 7 + 0, fr->d_bits + 1 * 6 + 0, 6);
+ memcpy(ra_bits + 2 * 7 + 0, fr->d_bits + 2 * 6 + 0, 6);
+ memcpy(ra_bits + 3 * 7 + 0, fr->d_bits + 3 * 6 + 0, 6);
+ memcpy(ra_bits + 4 * 7 + 4, fr->d_bits + 4 * 6 + 0, 3);
+ memcpy(ra_bits + 5 * 7 + 0, fr->d_bits + 4 * 6 + 3, 3);
+ memcpy(ra_bits + 5 * 7 + 4, fr->d_bits + 5 * 6 + 0, 3);
+ memcpy(ra_bits + 6 * 7 + 0, fr->d_bits + 5 * 6 + 3, 3);
+ memcpy(ra_bits + 6 * 7 + 4, fr->d_bits + 6 * 6 + 0, 3);
+ memcpy(ra_bits + 7 * 7 + 0, fr->d_bits + 6 * 6 + 3, 3);
+ memcpy(ra_bits + 7 * 7 + 4, fr->d_bits + 7 * 6 + 0, 3);
+ memcpy(ra_bits + 8 * 7 + 0, fr->d_bits + 7 * 6 + 3, 3);
+
+ return 60;
+}
+
+/*! Decode a 36-bit GSM 3k6kbit/s CSD frame present as 36 ubits into a struct osmo_v110_decoded_frame.
+ * \param[out] caller-allocated output data structure, filled by this function
+ * \param[in] ra_bits One V.110 frame as 36 unpacked bits. */
+int osmo_csd_3k6_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits)
+{
+
+ /* 3GPP TS 44.021 Section 8.1.4
+ D1 D2 D3 S1 D4 D5 D6 X
+ D7 D8 D9 S3 D10 D11 D12 S4
+ E4 E5 E6 E7 D13 D14 D15 S6
+ D16 D17 D18 X D19 D20 D21 S8
+ D22 D23 D24 S9
+ */
+
+ if (n_bits < 36)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ fr->x_bits[0] = ra_bits[0 * 8 + 7];
+ fr->x_bits[1] = ra_bits[3 * 8 + 3];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ fr->s_bits[0] = ra_bits[0 * 8 + 3];
+ fr->s_bits[2] = ra_bits[1 * 8 + 3];
+ fr->s_bits[3] = ra_bits[1 * 8 + 7];
+ fr->s_bits[5] = ra_bits[2 * 8 + 7];
+ fr->s_bits[7] = ra_bits[3 * 8 + 7];
+ fr->s_bits[8] = ra_bits[4 * 8 + 3];
+
+ /* E1 .. E3 must be set by out-of-band knowledge! */
+
+ /* E4 .. E7 */
+ memcpy(fr->e_bits+3, ra_bits + 2 * 8 + 0, 4);
+
+ /* D-bits */
+ unsigned int d_idx = 0;
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 0]; /* D1 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 0]; /* D1 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 1]; /* D2 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 1]; /* D2 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 2]; /* D3 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 2]; /* D3 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 4]; /* D4 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 4]; /* D4 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 5]; /* D5 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 5]; /* D5 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 6]; /* D6 */
+ fr->d_bits[d_idx++] = ra_bits[0 * 8 + 6]; /* D6 */
+
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 0]; /* D7 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 0]; /* D7 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 1]; /* D8 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 1]; /* D8 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 2]; /* D9 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 2]; /* D9 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 4]; /* D10 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 4]; /* D10 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 5]; /* D11 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 5]; /* D11 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 6]; /* D12 */
+ fr->d_bits[d_idx++] = ra_bits[1 * 8 + 6]; /* D12 */
+
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 4]; /* D13 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 4]; /* D13 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 5]; /* D14 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 5]; /* D14 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 6]; /* D15 */
+ fr->d_bits[d_idx++] = ra_bits[2 * 8 + 6]; /* D15 */
+
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 0]; /* D16 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 0]; /* D16 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 1]; /* D17 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 1]; /* D17 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 2]; /* D18 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 2]; /* D18 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 4]; /* D19 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 4]; /* D19 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 5]; /* D20 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 5]; /* D20 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 6]; /* D21 */
+ fr->d_bits[d_idx++] = ra_bits[3 * 8 + 6]; /* D21 */
+
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 0]; /* D22 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 0]; /* D22 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 1]; /* D23 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 1]; /* D23 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 2]; /* D24 */
+ fr->d_bits[d_idx++] = ra_bits[4 * 8 + 2]; /* D24 */
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 0;
+}
+
+int osmo_csd_3k6_encode_frame(ubit_t *ra_bits, size_t ra_bits_size, const struct osmo_v110_decoded_frame *fr)
+{
+ if (ra_bits_size < 36)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ ra_bits[0 * 8 + 7] = fr->x_bits[0];
+ ra_bits[3 * 8 + 3] = fr->x_bits[1];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ ra_bits[0 * 8 + 3] = fr->s_bits[0];
+ ra_bits[1 * 8 + 3] = fr->s_bits[2];
+ ra_bits[1 * 8 + 7] = fr->s_bits[3];
+ ra_bits[2 * 8 + 7] = fr->s_bits[5];
+ ra_bits[3 * 8 + 7] = fr->s_bits[7];
+ ra_bits[4 * 8 + 3] = fr->s_bits[8];
+
+ /* E1 .. E3 are ignored */
+
+ /* E4 .. E7 */
+ memcpy(ra_bits + 2 * 8 + 0, fr->e_bits+3, 4);
+
+ /* D-bits */
+ unsigned int d_idx = 0;
+ ra_bits[0 * 8 + 0] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 1] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 2] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 4] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 5] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[0 * 8 + 6] = fr->d_bits[d_idx]; d_idx += 2;
+
+ ra_bits[1 * 8 + 0] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 1] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 2] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 4] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 5] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[1 * 8 + 6] = fr->d_bits[d_idx]; d_idx += 2;
+
+ ra_bits[2 * 8 + 4] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[2 * 8 + 5] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[2 * 8 + 6] = fr->d_bits[d_idx]; d_idx += 2;
+
+ ra_bits[3 * 8 + 0] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 1] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 2] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 4] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 5] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[3 * 8 + 6] = fr->d_bits[d_idx]; d_idx += 2;
+
+ ra_bits[4 * 8 + 0] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[4 * 8 + 1] = fr->d_bits[d_idx]; d_idx += 2;
+ ra_bits[4 * 8 + 2] = fr->d_bits[d_idx]; d_idx += 2;
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 36;
+}
+
+/*! Print a encoded "CSD modififed V.110" frame in the same table-like structure as the spec.
+ * \param outf output FILE stream to which to dump
+ * \param[in] fr unpacked bits to dump
+ * \param[in] in_len length of unpacked bits available at fr. */
+void osmo_csd_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len)
+{
+ switch (in_len) {
+ case 60:
+ for (unsigned int septet = 0; septet < 9; septet++) {
+ if (septet < 8) {
+ fprintf(outf, "%d\t%d\t%d\t%d\t%d\t%d\t%d\n", fr[septet * 7 + 0],
+ fr[septet * 7 + 1], fr[septet * 7 + 2], fr[septet * 7 + 3],
+ fr[septet * 7 + 4], fr[septet * 7 + 5], fr[septet*7 + 6]);
+ } else {
+ fprintf(outf, "%d\t%d\t%d\t%d\n", fr[septet * 7 + 0],
+ fr[septet * 7 + 1], fr[septet * 7 + 2], fr[septet * 7 + 3]);
+ }
+ }
+ break;
+ case 36:
+ for (unsigned int octet = 0; octet < 5; octet++) {
+ if (octet < 4) {
+ fprintf(outf, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ fr[octet * 8 + 0], fr[octet * 8 + 1], fr[octet * 8 + 2],
+ fr[octet * 8 + 3], fr[octet * 8 + 4], fr[octet * 8 + 5],
+ fr[octet * 8 + 6], fr[octet * 8 + 7]);
+ } else {
+ fprintf(outf, "%d\t%d\t%d\t%d\n", fr[octet * 8 + 0],
+ fr[octet * 8 + 1], fr[octet * 8 + 2], fr[octet * 8 + 3]);
+ }
+ }
+ break;
+ default:
+ fprintf(outf, "invalid input data length: %zu\n", in_len);
+ }
+}
diff --git a/src/gsm/gsm44068.c b/src/gsm/gsm44068.c
new file mode 100644
index 00000000..2556b128
--- /dev/null
+++ b/src/gsm/gsm44068.c
@@ -0,0 +1,121 @@
+/* Group Call Control (GCC) is an ETSI/3GPP standard protocol used between
+ * MS (Mobile Station) and MSC (Mobile Switchting Center) in 2G/GSM-R network.
+ * It is specified in 3GPP TS 44.068.
+ *
+ * (C) 2023 by Sysmocom s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Andreas Eversberg
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <stddef.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/gsm/protocol/gsm_44_068.h>
+
+/***********************************************************************
+ * Protocol Definitions
+ ***********************************************************************/
+
+const struct value_string osmo_gsm44068_msg_type_names[] = {
+ { OSMO_GSM44068_MSGT_IMMEDIATE_SETUP, "IMMEDIATE SETUP" },
+ { OSMO_GSM44068_MSGT_SETUP, "SETUP" },
+ { OSMO_GSM44068_MSGT_CONNECT, "CONNECT" },
+ { OSMO_GSM44068_MSGT_TERMINATION, "TERMINATION" },
+ { OSMO_GSM44068_MSGT_TERMINATION_REQUEST, "TERMINATION REQUEST" },
+ { OSMO_GSM44068_MSGT_TERMINATION_REJECT, "TERMINATION REJECT" },
+ { OSMO_GSM44068_MSGT_STATUS, "STATUS" },
+ { OSMO_GSM44068_MSGT_GET_STATUS, "GET STATUS" },
+ { OSMO_GSM44068_MSGT_SET_PARAMETER, "SET PARAMETER" },
+ { OSMO_GSM44068_MSGT_IMMEDIATE_SETUP_2, "IMMEDIATE SETUP 2" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_gsm44068_priority_level_names[] = {
+ { OSMO_GSM44068_PRIO_LEVEL_4, "priority level 4" },
+ { OSMO_GSM44068_PRIO_LEVEL_3, "priority level 3" },
+ { OSMO_GSM44068_PRIO_LEVEL_2, "priority level 2" },
+ { OSMO_GSM44068_PRIO_LEVEL_1, "priority level 1" },
+ { OSMO_GSM44068_PRIO_LEVEL_0, "priority level 0" },
+ { OSMO_GSM44068_PRIO_LEVEL_B, "priority level B" },
+ { OSMO_GSM44068_PRIO_LEVEL_A, "priority level A" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_gsm44068_cause_names[] = {
+ { OSMO_GSM44068_CAUSE_ILLEGAL_MS, "Illegal MS" },
+ { OSMO_GSM44068_CAUSE_IMEI_NOT_ACCEPTED, "IMEI not accepted" },
+ { OSMO_GSM44068_CAUSE_ILLEGAL_ME, "Illegal ME" },
+ { OSMO_GSM44068_CAUSE_SERVICE_NOT_AUTHORIZED, "Service not authorized" },
+ { OSMO_GSM44068_CAUSE_APP_NOT_SUPPORTED_ON_PROTO, "Application not supported on the protocol" },
+ { OSMO_GSM44068_CAUSE_RR_CONNECTION_ABORTED, "RR connection aborted" },
+ { OSMO_GSM44068_CAUSE_NORMAL_CALL_CLEARING, "Normal call clearing" },
+ { OSMO_GSM44068_CAUSE_NETWORK_FAILURE, "Network failure" },
+ { OSMO_GSM44068_CAUSE_BUSY, "Busy" },
+ { OSMO_GSM44068_CAUSE_CONGESTION, "Congestion" },
+ { OSMO_GSM44068_CAUSE_USER_NOT_ORIGINATOR, "User not originator of call" },
+ { OSMO_GSM44068_CAUSE_NET_WANTS_TO_MAINTAIN_CALL, "Network wants to maintain call" },
+ { OSMO_GSM44068_CAUSE_RESPONSE_TO_GET_STATUS, "Response to GET STATUS" },
+ { OSMO_GSM44068_CAUSE_SERVICE_OPTION_NOT_SUBSCR, "Service option not supported" },
+ { OSMO_GSM44068_CAUSE_REQUESTED_SERVICE_NOT_SUB, "Requested service option not subscribed" },
+ { OSMO_GSM44068_CAUSE_SERVICE_OPTION_OOO, "Service option temporarily out of order" },
+ { OSMO_GSM44068_CAUSE_CALL_CANNOT_BE_IDENTIFIED, "Call cannot be identified" },
+ { OSMO_GSM44068_CAUSE_RETRY_UPON_ENTRY_NEW_CALL, "retry upon entry into a new cell" },
+ { OSMO_GSM44068_CAUSE_INVALID_TRANSACTION_ID, "Invalid transaction identifier value" },
+ { OSMO_GSM44068_CAUSE_SEMANTICALLY_INCORRECT_MSG, "Semantically incorrect message" },
+ { OSMO_GSM44068_CAUSE_INVALID_MANDATORY_INFO, "Invalid mandatory information" },
+ { OSMO_GSM44068_CAUSE_MESSAGE_TYPE_NON_EXISTENT, "Message type non-existent or not implemented" },
+ { OSMO_GSM44068_CAUSE_MESSAGE_TYPE_NOT_COMPAT, "Message type not compatible with the protocol state" },
+ { OSMO_GSM44068_CAUSE_IE_NON_EXISTENT, "Information element non-existent or not implemented" },
+ { OSMO_GSM44068_CAUSE_IE_NOT_COMPAT, "Message type not compatible with the protocol state" },
+ { OSMO_GSM44068_CAUSE_PROTOCOL_ERROR, "Protocol error, unspecified" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_gsm44068_call_state_names[] = {
+ { OSMO_GSM44068_CSTATE_U0, "U0" },
+ { OSMO_GSM44068_CSTATE_U1, "U1" },
+ { OSMO_GSM44068_CSTATE_U2sl_U2, "U2sl/U2" },
+ { OSMO_GSM44068_CSTATE_U3, "U3" },
+ { OSMO_GSM44068_CSTATE_U4, "U4" },
+ { OSMO_GSM44068_CSTATE_U5, "U5" },
+ { OSMO_GSM44068_CSTATE_U0p, "U0.p" },
+ { OSMO_GSM44068_CSTATE_U2wr_U6, "U2wr/U6" },
+ { OSMO_GSM44068_CSTATE_U2r, "U2r" },
+ { OSMO_GSM44068_CSTATE_U2ws, "U2ws" },
+ { OSMO_GSM44068_CSTATE_U2sr, "U2sr" },
+ { OSMO_GSM44068_CSTATE_U2nc, "U2nc" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_gsm44068_talker_priority_names[] = {
+ { OSMO_GSM44068_PRIO_NORMAL, "Normal" },
+ { OSMO_GSM44068_PRIO_PRIVILEGED, "Privileged" },
+ { OSMO_GSM44068_PRIO_EMERGENCY, "Emergency" },
+ { 0, NULL }
+};
+
+const struct tlv_definition osmo_gsm44068_att_tlvdef = {
+ .def = {
+ [OSMO_GSM44068_IEI_MOBILE_IDENTITY] = { TLV_TYPE_TLV },
+ [OSMO_GSM44068_IEI_USER_USER] = { TLV_TYPE_TLV },
+ [OSMO_GSM44068_IEI_CALL_STATE] = { TLV_TYPE_SINGLE_TV },
+ [OSMO_GSM44068_IEI_STATE_ATTRIBUTES] = { TLV_TYPE_SINGLE_TV },
+ [OSMO_GSM44068_IEI_TALKER_PRIORITY] = { TLV_TYPE_SINGLE_TV },
+ [OSMO_GSM44068_IEI_SMS_INDICATIONS] = { TLV_TYPE_SINGLE_TV },
+ },
+};
diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c
index 17b0829d..64b17658 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -45,6 +41,8 @@
#include <osmocom/gsm/protocol/gsm_04_80.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
+#include <osmocom/gsm/protocol/gsm_44_068.h>
/*! \addtogroup gsm0408
* @{
@@ -123,6 +121,7 @@ const struct tlv_definition gsm48_rr_att_tlvdef = {
[GSM48_IE_CHDES_2_AFTER] = { TLV_TYPE_FIXED, 3 },
[GSM48_IE_MODE_SEC_CH] = { TLV_TYPE_TV },
[GSM48_IE_F_CH_SEQ_AFTER] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_EXTENDED_TSC_SET] = { TLV_TYPE_TV },
[GSM48_IE_MA_AFTER] = { TLV_TYPE_TLV },
[GSM48_IE_BA_RANGE] = { TLV_TYPE_TLV },
[GSM48_IE_GROUP_CHDES] = { TLV_TYPE_TLV },
@@ -131,10 +130,10 @@ const struct tlv_definition gsm48_rr_att_tlvdef = {
[GSM48_IE_REALTIME_DIFF] = { TLV_TYPE_TLV },
[GSM48_IE_START_TIME] = { TLV_TYPE_FIXED, 2 },
[GSM48_IE_TIMING_ADVANCE] = { TLV_TYPE_TV },
- [GSM48_IE_GROUP_CIP_SEQ] = { TLV_TYPE_SINGLE_TV },
- [GSM48_IE_CIP_MODE_SET] = { TLV_TYPE_SINGLE_TV },
- [GSM48_IE_GPRS_RESUMPT] = { TLV_TYPE_SINGLE_TV },
- [GSM48_IE_SYNC_IND] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_GROUP_CIP_SEQ_HO] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_CIP_MODE_SET_HO] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_GPRS_RESUMPT_HO] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_SYNC_IND_HO] = { TLV_TYPE_SINGLE_TV },
},
};
@@ -150,7 +149,7 @@ const struct tlv_definition gsm48_mm_att_tlvdef = {
[GSM48_IE_NET_DST] = { TLV_TYPE_TLV },
[GSM48_IE_LOCATION_AREA] = { TLV_TYPE_FIXED, 5 },
- [GSM48_IE_PRIORITY_LEV] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_PRIORITY_LEV_HO] = { TLV_TYPE_SINGLE_TV },
[GSM48_IE_FOLLOW_ON_PROC] = { TLV_TYPE_T },
[GSM48_IE_CTS_PERMISSION] = { TLV_TYPE_T },
},
@@ -163,13 +162,16 @@ static const struct value_string rr_cause_names[] = {
{ GSM48_RR_CAUSE_ABNORMAL_TIMER, "Abnormal release, timer expired" },
{ GSM48_RR_CAUSE_ABNORMAL_NOACT, "Abnormal release, no activity on radio path" },
{ GSM48_RR_CAUSE_PREMPTIVE_REL, "Preemptive release" },
+ { GSM48_RR_CAUSE_UTRAN_CFG_UNK, "UTRAN configuration unknown" },
{ GSM48_RR_CAUSE_HNDOVER_IMP, "Handover impossible, timing advance out of range" },
{ GSM48_RR_CAUSE_CHAN_MODE_UNACCT, "Channel mode unacceptable" },
{ GSM48_RR_CAUSE_FREQ_NOT_IMPL, "Frequency not implemented" },
+ { GSM48_RR_CAUSE_LEAVE_GROUP_CA, "Originator or talker leaving group call area" },
+ { GSM48_RR_CAUSE_LOW_LEVEL_FAIL, "Lower layer failure" },
{ GSM48_RR_CAUSE_CALL_CLEARED, "Call already cleared" },
{ GSM48_RR_CAUSE_SEMANT_INCORR, "Semantically incorrect message" },
{ GSM48_RR_CAUSE_INVALID_MAND_INF, "Invalid mandatory information" },
- { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existant or not implemented" },
+ { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existent or not implemented" },
{ GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT, "Message type not compatible with protocol state" },
{ GSM48_RR_CAUSE_COND_IE_ERROR, "Conditional IE error" },
{ GSM48_RR_CAUSE_NO_CELL_ALLOC_A, "No cell allocation available" },
@@ -418,19 +420,102 @@ const char *gsm48_rr_msg_name(uint8_t msgtype)
return get_value_string(rr_msg_names, msgtype);
}
+/* 3GPP TS 44.018 Table 10.4.2 */
+static const struct value_string rr_msg_type_short_names[] = {
+ { GSM48_MT_RR_SH_SI10, "System Information Type 10" },
+ { GSM48_MT_RR_SH_FACCH, "Notification/FACCH" },
+ { GSM48_MT_RR_SH_UL_FREE, "Uplink Free" },
+ { GSM48_MT_RR_SH_MEAS_REP, "Enhanced Measurement Report (uplink)" },
+ { GSM48_MT_RR_SH_MEAS_INFO, "Measurement Information (downlink)" },
+ { GSM48_MT_RR_SH_VGCS_RECON, "VBS/VGCS Reconfigure" },
+ { GSM48_MT_RR_SH_VGCS_RECON2, "VBS/VGCS Reconfigure2" },
+ { GSM48_MT_RR_SH_VGCS_INFO, "VGCS Additional Information" },
+ { GSM48_MT_RR_SH_VGCS_SMS, "VGCS SMS Information" },
+ { GSM48_MT_RR_SH_SI10bis, "System Information Type 10bis" },
+ { GSM48_MT_RR_SH_SI10ter, "System Information Type 10ter" },
+ { GSM48_MT_RR_SH_VGCS_NEIGH, "VGCS Neighbour Cell Information" },
+ { GSM48_MT_RR_SH_APP_DATA, "Notify Application Data" },
+ { 0, NULL }
+};
+
+/*! return string representation of RR Message Type using the RR short protocol discriminator */
+const char *gsm48_rr_short_pd_msg_name(uint8_t msgtype)
+{
+ return get_value_string(rr_msg_type_short_names, msgtype);
+}
const struct value_string gsm48_chan_mode_names[] = {
{ GSM48_CMODE_SIGN, "SIGNALLING" },
{ GSM48_CMODE_SPEECH_V1, "SPEECH_V1" },
{ GSM48_CMODE_SPEECH_EFR, "SPEECH_EFR" },
{ GSM48_CMODE_SPEECH_AMR, "SPEECH_AMR" },
+ { GSM48_CMODE_SPEECH_V4, "SPEECH_V4" },
+ { GSM48_CMODE_SPEECH_V5, "SPEECH_V5" },
+ { GSM48_CMODE_SPEECH_V6, "SPEECH_V6" },
+
+ { GSM48_CMODE_DATA_43k5_14k5, "DATA_43k5_14k5" },
+ { GSM48_CMODE_DATA_29k0_14k5, "DATA_29k0_14k5" },
+ { GSM48_CMODE_DATA_43k5_29k0, "DATA_43k5_29k0" },
+ { GSM48_CMODE_DATA_14k5_43k5, "DATA_14k5_43k5" },
+ { GSM48_CMODE_DATA_14k5_29k0, "DATA_14k5_29k0" },
+ { GSM48_CMODE_DATA_29k0_43k5, "DATA_29k0_43k5" },
+
+ { GSM48_CMODE_DATA_43k5, "DATA_43k5" },
+ { GSM48_CMODE_DATA_32k0, "DATA_32k0" },
+ { GSM48_CMODE_DATA_29k0, "DATA_29k0" },
{ GSM48_CMODE_DATA_14k5, "DATA_14k5" },
{ GSM48_CMODE_DATA_12k0, "DATA_12k0" },
{ GSM48_CMODE_DATA_6k0, "DATA_6k0" },
{ GSM48_CMODE_DATA_3k6, "DATA_3k6" },
+
+ { GSM48_CMODE_SPEECH_V1_VAMOS, "SPEECH_V1_VAMOS" },
+ { GSM48_CMODE_SPEECH_V2_VAMOS, "SPEECH_V2_VAMOS" },
+ { GSM48_CMODE_SPEECH_V3_VAMOS, "SPEECH_V3_VAMOS" },
+ { GSM48_CMODE_SPEECH_V5_VAMOS, "SPEECH_V5_VAMOS" },
{ 0, NULL },
};
+/*! Translate GSM48_CMODE_SPEECH_* to its corresponding GSM48_CMODE_SPEECH_*_VAMOS mode.
+ * If the mode has no equivalent VAMOS mode, return a negative value.
+ */
+enum gsm48_chan_mode gsm48_chan_mode_to_vamos(enum gsm48_chan_mode mode)
+{
+ switch (mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_V1_VAMOS:
+ return GSM48_CMODE_SPEECH_V1_VAMOS;
+ case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_SPEECH_V2_VAMOS:
+ return GSM48_CMODE_SPEECH_V2_VAMOS;
+ case GSM48_CMODE_SPEECH_AMR:
+ case GSM48_CMODE_SPEECH_V3_VAMOS:
+ return GSM48_CMODE_SPEECH_V3_VAMOS;
+ case GSM48_CMODE_SPEECH_V5_VAMOS:
+ return GSM48_CMODE_SPEECH_V5_VAMOS;
+ default:
+ return -1;
+ }
+}
+
+/*! Translate GSM48_CMODE_SPEECH_*_VAMOS to its corresponding GSM48_CMODE_SPEECH_* non-vamos mode.
+ * If the mode is not a VAMOS mode, return the unchanged mode.
+ */
+enum gsm48_chan_mode gsm48_chan_mode_to_non_vamos(enum gsm48_chan_mode mode)
+{
+ switch (mode) {
+ case GSM48_CMODE_SPEECH_V1_VAMOS:
+ return GSM48_CMODE_SPEECH_V1;
+ case GSM48_CMODE_SPEECH_V2_VAMOS:
+ return GSM48_CMODE_SPEECH_EFR;
+ case GSM48_CMODE_SPEECH_V3_VAMOS:
+ return GSM48_CMODE_SPEECH_AMR;
+ case GSM48_CMODE_SPEECH_V5_VAMOS:
+ return GSM48_CMODE_SPEECH_V5;
+ default:
+ return mode;
+ }
+}
+
const struct value_string gsm_chan_t_names[] = {
{ GSM_LCHAN_NONE, "NONE" },
{ GSM_LCHAN_SDCCH, "SDCCH" },
@@ -458,7 +543,8 @@ const char *gsm48_mi_type_name(uint8_t mi)
return get_value_string(mi_type_names, mi);
}
-/*! Return a human readable representation of a Mobile Identity in caller-provided buffer.
+/*! Deprecated, see osmo_mobile_identity instead.
+ * Return a human readable representation of a Mobile Identity in caller-provided buffer.
* \param[out] buf caller-provided output buffer
* \param[in] buf_len size of buf in bytes
* \param[in] mi Mobile Identity buffer containing 3GPP TS 04.08 style MI type and data.
@@ -479,9 +565,10 @@ char *osmo_mi_name_buf(char *buf, size_t buf_len, const uint8_t *mi, uint8_t mi_
if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) {
tmsi = osmo_load32be(&mi[1]);
snprintf(buf, buf_len, "TMSI-0x%08" PRIX32, tmsi);
- return buf;
+ } else {
+ snprintf(buf, buf_len, "TMSI-invalid");
}
- return "TMSI-invalid";
+ return buf;
case GSM_MI_TYPE_IMSI:
case GSM_MI_TYPE_IMEI:
@@ -491,11 +578,13 @@ char *osmo_mi_name_buf(char *buf, size_t buf_len, const uint8_t *mi, uint8_t mi_
return buf;
default:
- return "unknown";
+ snprintf(buf, buf_len, "unknown");
+ return buf;
}
}
-/*! Return a human readable representation of a Mobile Identity in static buffer.
+/*! Deprecated, see osmo_mobile_identity instead.
+ * Return a human readable representation of a Mobile Identity in static buffer.
* \param[in] mi Mobile Identity buffer containing 3GPP TS 04.08 style MI type and data.
* \param[in] mi_len Length of mi.
* \return A string like "IMSI-1234567", "TMSI-0x1234ABCD" or "unknown", "TMSI-invalid"...
@@ -506,7 +595,8 @@ const char *osmo_mi_name(const uint8_t *mi, uint8_t mi_len)
return osmo_mi_name_buf(mi_name, sizeof(mi_name), mi, mi_len);
}
-/*! Return a human readable representation of a Mobile Identity in dynamically-allocated buffer.
+/*! Deprecated, see osmo_mobile_identity instead.
+ * Return a human readable representation of a Mobile Identity in dynamically-allocated buffer.
* \param[in] ctx talloc context from which to allocate output buffer
* \param[in] mi Mobile Identity buffer containing 3GPP TS 04.08 style MI type and data.
* \param[in] mi_len Length of mi.
@@ -522,6 +612,478 @@ char *osmo_mi_name_c(const void *ctx, const uint8_t *mi, uint8_t mi_len)
return osmo_mi_name_buf(mi_name, buf_len, mi, mi_len);
}
+/*! Extract Mobile Identity from encoded bytes (3GPP TS 24.008 10.5.1.4).
+ *
+ * On failure (negative return value), mi->type == GSM_MI_TYPE_NONE, mi->string[] is all-zero and mi->tmsi ==
+ * GSM_RESERVED_TMSI.
+ *
+ * On success, mi->type reflects the decoded Mobile Identity type (GSM_MI_TYPE_IMSI, GSM_MI_TYPE_TMSI, GSM_MI_TYPE_IMEI
+ * or GSM_MI_TYPE_IMEISV).
+ *
+ * On success, mi->string always contains a human readable representation of the Mobile Identity digits: IMSI, IMEI and
+ * IMEISV as digits like "12345678", and TMSI as "0x" and 8 hexadecimal digits like "0x1234abcd".
+ *
+ * mi->tmsi contains the uint32_t TMSI value iff the extracted Mobile Identity was a TMSI, or GSM_RESERVED_TMSI
+ * otherwise.
+ *
+ * \param[out] mi Return buffer for decoded Mobile Identity.
+ * \param[in] mi_data The encoded Mobile Identity octets.
+ * \param[in] mi_len Number of octets in mi_data.
+ * \param[in] allow_hex If false, hexadecimal digits (>9) result in an error return value.
+ * \returns 0 on success, negative on error: -EBADMSG = invalid length indication or invalid data,
+ * -EINVAL = unknown Mobile Identity type.
+ */
+int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *mi_data, uint8_t mi_len,
+ bool allow_hex)
+{
+ int rc;
+ int nibbles_len;
+ char *str = NULL; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */
+ size_t str_size = 0; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */
+
+ if (!mi_data || mi_len < 1) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
+
+ nibbles_len = (mi_len - 1) * 2 + ((mi_data[0] & GSM_MI_ODD) ? 1 : 0);
+
+ *mi = (struct osmo_mobile_identity){
+ .type = mi_data[0] & GSM_MI_TYPE_MASK,
+ };
+
+ /* First do length checks */
+ switch (mi->type) {
+ case GSM_MI_TYPE_TMSI:
+ mi->tmsi = GSM_RESERVED_TMSI;
+ if (nibbles_len != (GSM23003_TMSI_NUM_BYTES * 2)) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
+ break;
+
+ case GSM_MI_TYPE_IMSI:
+ if (nibbles_len < GSM23003_IMSI_MIN_DIGITS || nibbles_len > GSM23003_IMSI_MAX_DIGITS) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
+ str = mi->imsi;
+ str_size = sizeof(mi->imsi);
+ break;
+
+ case GSM_MI_TYPE_IMEI:
+ if (nibbles_len != GSM23003_IMEI_NUM_DIGITS && nibbles_len != GSM23003_IMEI_NUM_DIGITS_NO_CHK) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
+ str = mi->imei;
+ str_size = sizeof(mi->imei);
+ break;
+
+ case GSM_MI_TYPE_IMEISV:
+ if (nibbles_len != GSM23003_IMEISV_NUM_DIGITS) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
+ str = mi->imeisv;
+ str_size = sizeof(mi->imeisv);
+ break;
+
+ default:
+ rc = -EINVAL;
+ goto return_error;
+ }
+
+ /* Decode BCD digits */
+ switch (mi->type) {
+ case GSM_MI_TYPE_TMSI:
+ /* MI is a 32bit integer TMSI. Length has been checked above. */
+ if ((mi_data[0] & 0xf0) != 0xf0) {
+ /* A TMSI always has the first nibble == 0xf */
+ rc = -EBADMSG;
+ goto return_error;
+ }
+ mi->tmsi = osmo_load32be(&mi_data[1]);
+ return 0;
+
+ case GSM_MI_TYPE_IMSI:
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ /* If the length is even, the last nibble (higher nibble of last octet) must be 0xf */
+ if (!(mi_data[0] & GSM_MI_ODD)
+ && ((mi_data[mi_len - 1] & 0xf0) != 0xf0)) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
+ rc = osmo_bcd2str(str, str_size, mi_data, 1, 1 + nibbles_len, allow_hex);
+ /* check mi->str printing rc */
+ if (rc < 1 || rc >= str_size) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
+ return 0;
+
+ default:
+ /* Already handled above, but as future bug paranoia: */
+ rc = -EINVAL;
+ goto return_error;
+ }
+
+return_error:
+ *mi = (struct osmo_mobile_identity){
+ .type = GSM_MI_TYPE_NONE,
+ };
+ return rc;
+}
+
+/*! Return the number of encoded Mobile Identity octets, without actually encoding.
+ * Useful to write tag-length header before encoding the MI.
+ * \param[in] mi Mobile Identity.
+ * \param[out] mi_digits If not NULL, store the number of nibbles of used MI data (i.e. strlen(mi->string) or 8 for a TMSI).
+ * \return octets that osmo_mobile_identity_encode_msgb() will write for this mi.
+ */
+int osmo_mobile_identity_encoded_len(const struct osmo_mobile_identity *mi, int *mi_digits)
+{
+ int mi_nibbles;
+ if (!mi)
+ return -EINVAL;
+ switch (mi->type) {
+ case GSM_MI_TYPE_TMSI:
+ mi_nibbles = GSM23003_TMSI_NUM_BYTES * 2;
+ break;
+ case GSM_MI_TYPE_IMSI:
+ mi_nibbles = strlen(mi->imsi);
+ if (mi_nibbles < GSM23003_IMSI_MIN_DIGITS
+ || mi_nibbles > GSM23003_IMSI_MAX_DIGITS)
+ return -EINVAL;
+ break;
+ case GSM_MI_TYPE_IMEI:
+ mi_nibbles = strlen(mi->imei);
+ if (mi_nibbles < GSM23003_IMEI_NUM_DIGITS_NO_CHK
+ || mi_nibbles > GSM23003_IMEI_NUM_DIGITS)
+ return -EINVAL;
+ break;
+ case GSM_MI_TYPE_IMEISV:
+ mi_nibbles = strlen(mi->imeisv);
+ if (mi_nibbles != GSM23003_IMEISV_NUM_DIGITS)
+ return -EINVAL;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+
+ if (mi_digits)
+ *mi_digits = mi_nibbles;
+
+ /* one type nibble, plus the MI nibbles, plus a filler nibble to complete the last octet:
+ * mi_octets = ceil((float)(mi_nibbles + 1) / 2)
+ */
+ return (mi_nibbles + 2) / 2;
+}
+
+/*! Encode Mobile Identity from uint32_t (TMSI) or digits string (all others) (3GPP TS 24.008 10.5.1.4).
+ *
+ * \param[out] buf Return buffer for encoded Mobile Identity.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] mi Mobile identity to encode.
+ * \param[in] allow_hex If false, hexadecimal digits (>9) result in an error return value.
+ * \returns Amount of bytes written to buf, or negative on error.
+ */
+int osmo_mobile_identity_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_mobile_identity *mi, bool allow_hex)
+{
+ int rc;
+ int nibbles_len;
+ int mi_octets;
+ const char *mi_str;
+
+ if (!buf || !buflen)
+ return -EIO;
+
+ mi_octets = osmo_mobile_identity_encoded_len(mi, &nibbles_len);
+ if (mi_octets < 0)
+ return mi_octets;
+ if (mi_octets > buflen)
+ return -ENOSPC;
+
+ buf[0] = (mi->type & GSM_MI_TYPE_MASK) | ((nibbles_len & 1) ? GSM_MI_ODD : 0);
+
+ switch (mi->type) {
+ case GSM_MI_TYPE_TMSI:
+ buf[0] |= 0xf0;
+ osmo_store32be(mi->tmsi, &buf[1]);
+ return mi_octets;
+
+ case GSM_MI_TYPE_IMSI:
+ mi_str = mi->imsi;
+ break;
+ case GSM_MI_TYPE_IMEI:
+ mi_str = mi->imei;
+ break;
+ case GSM_MI_TYPE_IMEISV:
+ mi_str = mi->imeisv;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ rc = osmo_str2bcd(buf, buflen, mi_str, 1, -1, allow_hex);
+ if (rc != mi_octets)
+ return -EINVAL;
+ return mi_octets;
+}
+
+/*! Encode Mobile Identity type and BCD digits, appended to a msgb.
+ * Example to add a GSM48_IE_MOBILE_ID IEI with tag and length to a msgb:
+ *
+ * struct osmo_mobile_identity mi = { .type = GSM_MI_TYPE_IMSI };
+ * OSMO_STRLCPY_ARRAY(mi.imsi, "1234567890123456");
+ * uint8_t *l = msgb_tl_put(msg, GSM48_IE_MOBILE_ID);
+ * int rc = osmo_mobile_identity_encode_msgb(msg, &mi, false);
+ * if (rc < 0)
+ * goto error;
+ * *l = rc;
+ *
+ * Example to add a BSSGP_IE_IMSI with tag and variable-size length, where the
+ * length needs to be known at the time of writing the IE tag-length header:
+ *
+ * struct osmo_mobile_identity mi = { .type = GSM_MI_TYPE_IMSI, };
+ * OSMO_STRLCPY_ARRAY(mi.imsi, pinfo->imsi);
+ * msgb_tvl_put(msg, BSSGP_IE_IMSI, osmo_mobile_identity_encoded_len(&mi, NULL));
+ * if (osmo_mobile_identity_encode_msgb(msg, &mi, false) < 0)
+ * goto error;
+ */
+int osmo_mobile_identity_encode_msgb(struct msgb *msg, const struct osmo_mobile_identity *mi, bool allow_hex)
+{
+ int rc = osmo_mobile_identity_encode_buf(msg->tail, msgb_tailroom(msg), mi, allow_hex);
+ if (rc < 0)
+ return rc;
+ msgb_put(msg, rc);
+ return rc;
+}
+
+/*! Extract Mobile Identity from a Complete Layer 3 message.
+ *
+ * Determine the Mobile Identity data and call osmo_mobile_identity_decode() to return a decoded struct
+ * osmo_mobile_identity.
+ *
+ * \param[out] mi Return buffer for decoded Mobile Identity.
+ * \param[in] l3_data The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp).
+ * \param[in] l3_len Length of l3_data in bytes.
+ * \returns 0 on success, negative on error: return codes as defined in osmo_mobile_identity_decode(), or
+ * -ENOTSUP = not a Complete Layer 3 message,
+ */
+int osmo_mobile_identity_decode_from_l3_buf(struct osmo_mobile_identity *mi, const uint8_t *l3_data, size_t l3_len,
+ bool allow_hex)
+{
+ const struct gsm48_hdr *gh;
+ int8_t pdisc = 0;
+ uint8_t mtype = 0;
+ const struct gsm48_loc_upd_req *lu;
+ const uint8_t *cm2_buf;
+ uint8_t cm2_len;
+ const uint8_t *mi_start;
+ const struct gsm48_pag_resp *paging_response;
+ const uint8_t *mi_data;
+ uint8_t mi_len;
+ const struct gsm48_imsi_detach_ind *idi;
+
+ *mi = (struct osmo_mobile_identity){
+ .type = GSM_MI_TYPE_NONE,
+ .tmsi = GSM_RESERVED_TMSI,
+ };
+
+ if (l3_len < sizeof(*gh))
+ return -EBADMSG;
+
+ gh = (void *)l3_data;
+ pdisc = gsm48_hdr_pdisc(gh);
+ mtype = gsm48_hdr_msg_type(gh);
+
+ switch (pdisc) {
+ case GSM48_PDISC_MM:
+
+ switch (mtype) {
+ case GSM48_MT_MM_LOC_UPD_REQUEST:
+ /* First make sure that lu-> can be dereferenced */
+ if (l3_len < sizeof(*gh) + sizeof(*lu))
+ return -EBADMSG;
+
+ /* Now we know there is enough msgb data to read a lu->mi_len, so also check that */
+ lu = (struct gsm48_loc_upd_req*)gh->data;
+ if (l3_len < sizeof(*gh) + sizeof(*lu) + lu->mi_len)
+ return -EBADMSG;
+ mi_data = lu->mi;
+ mi_len = lu->mi_len;
+ goto got_mi;
+
+ case GSM48_MT_MM_CM_SERV_REQ:
+ case GSM48_MT_MM_CM_REEST_REQ:
+ /* Unfortunately in Phase1 the Classmark2 length is variable, so we cannot
+ * just use gsm48_service_request struct, and need to parse it manually. */
+ if (l3_len < sizeof(*gh) + 2)
+ return -EBADMSG;
+
+ cm2_len = gh->data[1];
+ cm2_buf = gh->data + 2;
+ goto got_cm2;
+
+ case GSM48_MT_MM_IMSI_DETACH_IND:
+ if (l3_len < sizeof(*gh) + sizeof(*idi))
+ return -EBADMSG;
+ idi = (struct gsm48_imsi_detach_ind*) gh->data;
+ mi_data = idi->mi;
+ mi_len = idi->mi_len;
+ goto got_mi;
+
+ case GSM48_MT_MM_ID_RESP:
+ if (l3_len < sizeof(*gh) + 2)
+ return -EBADMSG;
+ mi_data = gh->data+1;
+ mi_len = gh->data[0];
+ goto got_mi;
+
+ default:
+ break;
+ }
+ break;
+
+ case GSM48_PDISC_RR:
+
+ switch (mtype) {
+ case GSM48_MT_RR_PAG_RESP:
+ if (l3_len < sizeof(*gh) + sizeof(*paging_response))
+ return -EBADMSG;
+ paging_response = (struct gsm48_pag_resp*)gh->data;
+ cm2_len = paging_response->cm2_len;
+ cm2_buf = (uint8_t*)&paging_response->cm2;
+ goto got_cm2;
+
+ case GSM48_MT_RR_TALKER_IND:
+ /* Check minimum size: Header + CM2 LV + minimum MI LV */
+ if (l3_len < sizeof(*gh) + 4 + 2)
+ return -EBADMSG;
+ /* CM2 shall be always 3 bytes in length */
+ if (gh->data[0] != 3)
+ return -EBADMSG;
+ cm2_len = gh->data[0];
+ cm2_buf = gh->data + 1;
+ goto got_cm2;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ return -ENOTSUP;
+
+got_cm2:
+ /* MI (Mobile Identity) LV follows the Classmark2 */
+
+ /* There must be at least a mi_len byte after the CM2 */
+ if (cm2_buf + cm2_len + 1 > l3_data + l3_len)
+ return -EBADMSG;
+
+ mi_start = cm2_buf + cm2_len;
+ mi_len = mi_start[0];
+ mi_data = mi_start + 1;
+
+got_mi:
+ /* mi_data points at the start of the Mobile Identity coding of mi_len bytes */
+ if (mi_data + mi_len > l3_data + l3_len)
+ return -EBADMSG;
+
+ return osmo_mobile_identity_decode(mi, mi_data, mi_len, allow_hex);
+}
+
+/*! Extract Mobile Identity from a Complete Layer 3 message.
+ *
+ * Determine the Mobile Identity data and call osmo_mobile_identity_decode() to return a decoded struct
+ * osmo_mobile_identity.
+ *
+ * \param[out] mi Return buffer for decoded Mobile Identity.
+ * \param[in] msg The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp).
+ * \returns 0 on success, negative on error: return codes as defined in osmo_mobile_identity_decode(), or
+ * -ENOTSUP = not a Complete Layer 3 message,
+ */
+int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex)
+{
+ return osmo_mobile_identity_decode_from_l3_buf(mi, msgb_l3(msg), msgb_l3len(msg), allow_hex);
+}
+
+/*! Return a human readable representation of a struct osmo_mobile_identity.
+ * Write a string like "IMSI-1234567", "TMSI-0x1234ABCD" or "NONE", "NULL".
+ * \param[out] buf String buffer to write to.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] mi Decoded Mobile Identity data.
+ * \return the strlen() of the string written when buflen is sufficiently large, like snprintf().
+ */
+int osmo_mobile_identity_to_str_buf(char *buf, size_t buflen, const struct osmo_mobile_identity *mi)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ if (!mi)
+ return snprintf(buf, buflen, "NULL");
+ OSMO_STRBUF_PRINTF(sb, "%s", gsm48_mi_type_name(mi->type));
+ switch (mi->type) {
+ case GSM_MI_TYPE_TMSI:
+ OSMO_STRBUF_PRINTF(sb, "-0x%08" PRIX32, mi->tmsi);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ OSMO_STRBUF_PRINTF(sb, "-%s", mi->imsi);
+ break;
+ case GSM_MI_TYPE_IMEI:
+ OSMO_STRBUF_PRINTF(sb, "-%s", mi->imei);
+ break;
+ case GSM_MI_TYPE_IMEISV:
+ OSMO_STRBUF_PRINTF(sb, "-%s", mi->imeisv);
+ break;
+ default:
+ break;
+ }
+ return sb.chars_needed;
+}
+
+/*! Like osmo_mobile_identity_to_str_buf(), but return the string in a talloc buffer.
+ * \param[in] ctx Talloc context to allocate from.
+ * \param[in] mi Decoded Mobile Identity data.
+ * \return a string like "IMSI-1234567", "TMSI-0x1234ABCD" or "NONE", "NULL".
+ */
+char *osmo_mobile_identity_to_str_c(void *ctx, const struct osmo_mobile_identity *mi)
+{
+ OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_mobile_identity_to_str_buf, mi)
+}
+
+/*! Compare two osmo_mobile_identity structs, returning typical cmp() result.
+ * \param[in] a Left side osmo_mobile_identity.
+ * \param[in] b Right side osmo_mobile_identity.
+ * \returns 0 if both are equal, -1 if a < b, 1 if a > b.
+ */
+int osmo_mobile_identity_cmp(const struct osmo_mobile_identity *a, const struct osmo_mobile_identity *b)
+{
+ int cmp;
+ if (a == b)
+ return 0;
+ if (!a)
+ return -1;
+ if (!b)
+ return 1;
+ cmp = OSMO_CMP(a->type, b->type);
+ if (cmp)
+ return cmp;
+ switch (a->type) {
+ case GSM_MI_TYPE_TMSI:
+ return OSMO_CMP(a->tmsi, b->tmsi);
+ case GSM_MI_TYPE_IMSI:
+ return strncmp(a->imsi, b->imsi, sizeof(a->imsi));
+ case GSM_MI_TYPE_IMEI:
+ return strncmp(a->imei, b->imei, sizeof(a->imei));
+ case GSM_MI_TYPE_IMEISV:
+ return strncmp(a->imeisv, b->imeisv, sizeof(a->imeisv));
+ default:
+ /* No known type, but both have the same type. */
+ return 0;
+ }
+}
+
/*! Checks is particular message is cipherable in A/Gb mode according to
* 3GPP TS 24.008 § 4.7.1.2
* \param[in] hdr Message header
@@ -674,7 +1236,8 @@ void gsm48_set_dtx(struct gsm48_cell_options *op, enum gsm48_dtx_mode full,
}
}
-/*! Generate TS 04.08 Mobile ID from TMSI
+/*! Deprecated, see osmo_mobile_identity instead.
+ * Generate TS 04.08 Mobile ID from TMSI
* \param[out] buf Caller-provided output buffer (7 bytes)
* \param[in] tmsi TMSI to be encoded
* \returns number of byes encoded (always 7) */
@@ -690,7 +1253,8 @@ int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi)
return 7;
}
-/*! Generate TS 24.008 §10.5.1.4 Mobile ID of BCD type from ASCII string
+/*! Deprecated, see osmo_mobile_identity instead.
+ * Generate TS 24.008 §10.5.1.4 Mobile ID of BCD type from ASCII string
* \param[out] buf Caller-provided output buffer of at least GSM48_MID_MAX_SIZE bytes
* \param[in] id Identity to be encoded
* \param[in] mi_type Type of identity (e.g. GSM_MI_TYPE_IMSI, IMEI, IMEISV)
@@ -722,7 +1286,8 @@ uint8_t gsm48_generate_mid(uint8_t *buf, const char *id, uint8_t mi_type)
return 2 + buf[1];
}
-/*! Generate TS 04.08 Mobile ID from IMSI
+/*! Deprecated, see osmo_mobile_identity instead.
+ * Generate TS 04.08 Mobile ID from IMSI
* \param[out] buf Caller-provided output buffer
* \param[in] imsi IMSI to be encoded
* \returns number of bytes used in \a buf */
@@ -731,16 +1296,19 @@ int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi)
return gsm48_generate_mid(buf, imsi, GSM_MI_TYPE_IMSI);
}
-/*! Convert TS 04.08 Mobile Identity (10.5.1.4) to string.
+/*! Deprecated, see osmo_mobile_identity instead.
+ * Convert TS 04.08 Mobile Identity (10.5.1.4) to string.
* This function does not validate the Mobile Identity digits, i.e. digits > 9 are returned as 'A'-'F'.
* \param[out] string Caller-provided buffer for output
* \param[in] str_len Length of \a string in bytes
* \param[in] mi Mobile Identity to be stringified
* \param[in] mi_len Length of \a mi in bytes
- * \returns WARNING: the return value of this function is not well implemented.
+ * \returns Return <= 0 on error, > 0 on success.
+ * WARNING: the return value of this function is not well implemented.
* Depending on the MI type and amount of output buffer, this may return
* the nr of written bytes, or the written strlen(), or the snprintf()
- * style strlen()-if-the-buffer-were-large-enough. */
+ * style strlen()-if-the-buffer-were-large-enough.
+ */
int gsm48_mi_to_string(char *string, int str_len, const uint8_t *mi, int mi_len)
{
int rc;
@@ -780,7 +1348,64 @@ int gsm48_mi_to_string(char *string, int str_len, const uint8_t *mi, int mi_len)
return 1;
}
-/*! Parse TS 04.08 Routing Area Identifier
+/*! Decode to struct osmo_routing_area_id from a 3GPP TS 24.008 § 10.5.5.15 Routing area identification.
+ * \param[out] dst Store the decoded result here.
+ * \param[in] ra_data The start of a Routing Area ID in encoded form, to be decoded.
+ * \param[in] ra_data_len Buffer size available to read from at *ra_data.
+ * \return the number of decoded bytes on success, or negative on error (if the input buffer is too small).
+ */
+int osmo_routing_area_id_decode(struct osmo_routing_area_id *dst, const uint8_t *ra_data, size_t ra_data_len)
+{
+ const struct gsm48_ra_id *ra_id;
+ if (ra_data_len < sizeof(*ra_id))
+ return -ENOSPC;
+
+ gsm48_decode_lai2((void *)ra_data, &dst->lac);
+
+ ra_id = (void *)ra_data;
+ dst->rac = ra_id->rac;
+
+ return sizeof(*ra_id);
+}
+
+/*! Encode struct osmo_routing_area_id to a 3GPP TS 24.008 § 10.5.5.15 Routing area identification: write to a buffer.
+ * \param[out] buf Return buffer for encoded Mobile Identity.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] src RA to encode.
+ * \return Amount of bytes written to buf, or negative on error.
+ */
+int osmo_routing_area_id_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_routing_area_id *src)
+{
+ struct gsm48_ra_id *ra_id;
+ if (buflen < sizeof(*ra_id))
+ return -ENOSPC;
+
+ gsm48_generate_lai2((void *)buf, &src->lac);
+
+ ra_id = (void *)buf;
+ ra_id->rac = src->rac;
+
+ return sizeof(*ra_id);
+}
+
+/*! Encode struct osmo_routing_area_id to a 3GPP TS 24.008 § 10.5.5.15 Routing area identification: append to msgb.
+ * To succeed, the msgb must have tailroom >= sizeof(struct gsm48_ra_id).
+ * \param[out] msg Append to this msgb.
+ * \param[in] src Encode this Routing Area ID.
+ * \return Number of bytes appended to msgb, or negative on error.
+ */
+int osmo_routing_area_id_encode_msgb(struct msgb *msg, const struct osmo_routing_area_id *src)
+{
+ int rc = osmo_routing_area_id_encode_buf(msg->tail, msgb_tailroom(msg), src);
+ if (rc <= 0)
+ return rc;
+ msgb_put(msg, rc);
+ return rc;
+}
+
+/*! Parse TS 04.08 Routing Area Identifier.
+ * Preferably use osmo_routing_area_id_decode() instead: struct osmo_routing_area_id is better integrated with other API
+ * like osmo_plmn_cmp().
* \param[out] Caller-provided memory for decoded RA ID
* \param[in] buf Input buffer pointing to RAI IE value */
void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf)
@@ -838,6 +1463,25 @@ int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid)
return 6;
}
+/*! Compare a TS 04.08 Routing Area Identifier
+ * \param[in] raid1 first Routing Area ID to compare.
+ * \param[in] raid2 second Routing Area ID to compare.
+ * \returns true if raid1 and raid2 match, false otherwise. */
+bool gsm48_ra_equal(const struct gprs_ra_id *raid1, const struct gprs_ra_id *raid2)
+{
+ if (raid1->mcc != raid2->mcc)
+ return false;
+ if (raid1->mnc != raid2->mnc)
+ return false;
+ if (raid1->mnc_3_digits != raid2->mnc_3_digits)
+ return false;
+ if (raid1->lac != raid2->lac)
+ return false;
+ if (raid1->rac != raid2->rac)
+ return false;
+ return true;
+}
+
/*! Determine number of paging sub-channels
* \param[in] chan_desc Control Channel Description
* \returns number of paging sub-channels
@@ -845,7 +1489,7 @@ int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid)
* Uses From Table 10.5.33 of GSM 04.08 to determine the number of
* paging sub-channels in the given control channel configuration
*/
-int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc)
+int gsm48_number_of_paging_subchannels(const struct gsm48_control_channel_descr *chan_desc)
{
unsigned int n_pag_blocks = gsm0502_get_n_pag_blocks(chan_desc);
@@ -1125,6 +1769,10 @@ char *gsm48_pdisc_msgtype_name_buf(char *buf, size_t buf_len, uint8_t pdisc, uin
case GSM48_PDISC_CC:
msgt_names = gsm48_cc_msgtype_names;
break;
+ case GSM48_PDISC_GROUP_CC:
+ case GSM48_PDISC_BCAST_CC:
+ msgt_names = osmo_gsm44068_msg_type_names;
+ break;
case GSM48_PDISC_NC_SS:
msgt_names = gsm48_nc_ss_msgtype_names;
break;
diff --git a/src/gsm/gsm48049.c b/src/gsm/gsm48049.c
index 5e743563..3ab907c9 100644
--- a/src/gsm/gsm48049.c
+++ b/src/gsm/gsm48049.c
@@ -95,7 +95,7 @@ const struct tlv_definition cbsp_att_tlvdef = {
[CBSP_IEI_RR_LOADING_LIST] = { TLV_TYPE_TL16V },
[CBSP_IEI_CAUSE] = { TLV_TYPE_TV },
[CBSP_IEI_DCS] = { TLV_TYPE_TV },
- [CBSP_IEI_RECOVERY_IND] { TLV_TYPE_TV },
+ [CBSP_IEI_RECOVERY_IND] = { TLV_TYPE_TV },
[CBSP_IEI_MSG_ID] = { TLV_TYPE_FIXED, 2 },
[CBSP_IEI_EMERG_IND] = { TLV_TYPE_TV },
[CBSP_IEI_WARN_TYPE] = { TLV_TYPE_FIXED, 2 },
diff --git a/src/gsm/gsm48_arfcn_range_encode.c b/src/gsm/gsm48_arfcn_range_encode.c
index 6423a9a8..afe552d8 100644
--- a/src/gsm/gsm48_arfcn_range_encode.c
+++ b/src/gsm/gsm48_arfcn_range_encode.c
@@ -166,6 +166,10 @@ int osmo_gsm48_range_enc_determine_range(const int *arfcns, const int size, int
{
int max = 0;
+ /* don't dereference arfcns[] array if size is 0 */
+ if (size == 0)
+ return OSMO_GSM48_ARFCN_RANGE_128;
+
/*
* Go for the easiest. And pick arfcns[0] == f0.
*/
diff --git a/src/gsm/gsm48_ie.c b/src/gsm/gsm48_ie.c
index 31028ba4..b95609fe 100644
--- a/src/gsm/gsm48_ie.c
+++ b/src/gsm/gsm48_ie.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -34,6 +30,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/mncc.h>
+#include <osmocom/core/bitvec.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48_ie.h>
@@ -47,10 +44,11 @@ static const char bcd_num_digits[] = {
};
/*! Like gsm48_decode_bcd_number2() but with less airtight bounds checking.
- * \param[out] Caller-provided output buffer
+ * \param[out] output Caller-provided output buffer
+ * \param[in] output_len sizeof(output)
* \param[in] bcd_lv Length-Value portion of to-be-decoded IE
* \param[in] h_len Length of an optional heder between L and V portion
- * \returns - in case of success; negative on error */
+ * \returns 0 in case of success; negative on error */
int gsm48_decode_bcd_number(char *output, int output_len,
const uint8_t *bcd_lv, int h_len)
{
@@ -142,7 +140,7 @@ static int asc_to_bcd(const char asc)
* \param[in] max_len Maximum Length of \a bcd_lv
* \param[in] h_len Length of an optional heder between L and V portion
* \param[in] input phone number as 0-terminated ASCII
- * \returns number of bytes used in \a bcd_lv
+ * \returns number of bytes used in \a bcd_lv; negative on error
*
* Depending on a context (e.g. called or calling party BCD number), the
* optional header between L and V parts can contain TON (Type Of Number),
@@ -182,8 +180,8 @@ int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
}
/*! Decode TS 04.08 Bearer Capability IE (10.5.4.5)
- * \param[out] Caller-provided memory for decoded output
- * \[aram[in] LV portion of TS 04.08 Bearer Capability
+ * \param[out] bcap Caller-provided memory for decoded output
+ * \param[in] lv LV portion of TS 04.08 Bearer Capability
* \returns 0 on success; negative on error */
int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
const uint8_t *lv)
@@ -206,7 +204,24 @@ int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
case GSM_MNCC_BCAP_SPEECH:
i = 1;
s = 0;
- while(!(lv[i] & 0x80)) {
+ if ((lv[1] & 0x80) != 0) { /* octet 3a is absent */
+ switch (bcap->radio) {
+ case GSM48_BCAP_RRQ_FR_ONLY:
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_FR;
+ break;
+ case GSM48_BCAP_RRQ_DUAL_HR:
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_HR;
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_FR;
+ break;
+ case GSM48_BCAP_RRQ_DUAL_FR:
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_FR;
+ bcap->speech_ver[s++] = GSM48_BCAP_SV_HR;
+ break;
+ }
+ bcap->speech_ver[s] = -1; /* end of list */
+ return 0;
+ }
+ while (!(lv[i] & 0x80)) {
i++; /* octet 3a etc */
if (in_len < i)
return 0;
@@ -219,9 +234,10 @@ int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
}
break;
case GSM_MNCC_BCAP_UNR_DIG:
+ case GSM_MNCC_BCAP_AUDIO:
case GSM_MNCC_BCAP_FAX_G3:
i = 1;
- while(!(lv[i] & 0x80)) {
+ while (!(lv[i] & 0x80)) {
i++; /* octet 3a etc */
if (in_len < i)
return 0;
@@ -235,7 +251,7 @@ int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
return 0;
bcap->data.rate_adaption = (lv[i] >> 3) & 3;
bcap->data.sig_access = lv[i] & 7;
- while(!(lv[i] & 0x80)) {
+ while (!(lv[i] & 0x80)) {
i++; /* octet 5a etc */
if (in_len < i)
return 0;
@@ -320,10 +336,11 @@ int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
lv[i] |= 0x80; /* last IE of octet 3 etc */
break;
case GSM48_BCAP_ITCAP_UNR_DIG_INF:
+ case GSM48_BCAP_ITCAP_3k1_AUDIO:
case GSM48_BCAP_ITCAP_FAX_G3:
lv[i++] |= 0x80; /* last IE of octet 3 etc */
/* octet 4 */
- lv[i++] = 0xb8;
+ lv[i++] = 0x88;
/* octet 5 */
lv[i++] = 0x80 | ((bcap->data.rate_adaption & 3) << 3)
| (bcap->data.sig_access & 7);
@@ -337,7 +354,9 @@ int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
lv[i++] = (bcap->data.parity & 7) |
((bcap->data.interm_rate & 3) << 5);
/* octet 6c */
- lv[i] = 0x80 | (bcap->data.modem_type & 0x1f);
+ lv[i] = 0x80 |
+ ((bcap->data.transp & 3) << 5) |
+ (bcap->data.modem_type & 0x1f);
break;
default:
return -EINVAL;
@@ -353,9 +372,9 @@ int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
}
/*! Decode TS 04.08 Call Control Capabilities IE (10.5.4.5a)
- * \param[out] Caller-provided memory for decoded CC capabilities
+ * \param[out] ccap Caller-provided memory for decoded CC capabilities
* \param[in] lv Length-Value of IE
- * \retursns 0 on success; negative on error */
+ * \returns 0 on success; negative on error */
int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv)
{
uint8_t in_len = lv[0];
@@ -439,7 +458,7 @@ int gsm48_encode_called(struct msgb *msg,
}
/*! Decode TS 04.08 Caller ID
- * \param[out] called Caller-provided memory for decoded number
+ * \param[out] callerid Caller-provided memory for decoded number
* \param[in] lv Length-Value portion of IE
* \returns 0 on success; negative on error */
int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
@@ -811,7 +830,7 @@ int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
{
uint8_t in_len = lv[0];
- if (in_len < 1 || in_len < sizeof(ssv->info))
+ if (in_len < 1 || in_len > sizeof(ssv->info))
return -EINVAL;
memcpy(ssv->info, lv + 1, in_len);
@@ -866,8 +885,9 @@ static int32_t smod(int32_t n, int32_t m)
* \param[in] cd Cell Channel Description IE
* \param[in] len Length of \a cd in bytes
* \returns 0 on success; negative on error */
-int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
- uint8_t len, uint8_t mask, uint8_t frqt)
+int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f,
+ const uint8_t *cd, uint8_t len,
+ uint8_t mask, uint8_t frqt)
{
int i;
@@ -1299,4 +1319,249 @@ int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
return 0;
}
+
+/*! Decode 3GPP TS 24.008 Mobile Station Classmark 3 (10.5.1.7).
+ * \param[out] classmark3_out user provided memory to store decoded classmark3.
+ * \param[in] classmark3 pointer to memory that contains the raw classmark bits.
+ * \param[in] classmark3_len length in bytes of the memory where classmark3 points to.
+ * \returns 0 on success; negative on error. */
+int gsm48_decode_classmark3(struct gsm48_classmark3 *classmark3_out,
+ const uint8_t *classmark3, size_t classmark3_len)
+{
+ struct bitvec bv;
+ uint8_t data[255];
+ struct gsm48_classmark3 *cm3 = classmark3_out;
+
+ /* if cm3 gets extended by spec, it will be truncated, but 255 bytes
+ * should be more than enough. */
+ if (classmark3_len > sizeof(data))
+ classmark3_len = sizeof(data);
+
+ memset(&bv, 0, sizeof(bv));
+ memset(data, 0, sizeof(data));
+ memset(classmark3_out, 0, sizeof(*classmark3_out));
+
+ memcpy(data, classmark3, classmark3_len);
+ bv.data = (uint8_t*) data;
+ bv.data_len = sizeof(data);
+
+ /* Parse bit vector, see also: 3GPP TS 24.008, section 10.5.1.7 */
+ bitvec_get_uint(&bv, 1);
+ cm3->mult_band_supp = bitvec_get_uint(&bv, 3);
+ switch (cm3->mult_band_supp) {
+ case 0x00:
+ cm3->a5_bits = bitvec_get_uint(&bv, 4);
+ break;
+ case 0x05:
+ case 0x06:
+ cm3->a5_bits = bitvec_get_uint(&bv, 4);
+ cm3->assoc_radio_cap_2 = bitvec_get_uint(&bv, 4);
+ cm3->assoc_radio_cap_1 = bitvec_get_uint(&bv, 4);
+ break;
+ case 0x01:
+ case 0x02:
+ case 0x04:
+ cm3->a5_bits = bitvec_get_uint(&bv, 4);
+ bitvec_get_uint(&bv, 4);
+ cm3->assoc_radio_cap_1 = bitvec_get_uint(&bv, 4);
+ break;
+ default:
+ return -1;
+ }
+
+ cm3->r_support.present = bitvec_get_uint(&bv, 1);
+ if (cm3->r_support.present)
+ cm3->r_support.r_gsm_assoc_radio_cap = bitvec_get_uint(&bv, 3);
+
+ cm3->hscsd_mult_slot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->hscsd_mult_slot_cap.present)
+ cm3->hscsd_mult_slot_cap.mslot_class = bitvec_get_uint(&bv, 5);
+
+ cm3->ucs2_treatment = bitvec_get_uint(&bv, 1);
+ cm3->extended_meas_cap = bitvec_get_uint(&bv, 1);
+
+ cm3->ms_meas_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->ms_meas_cap.present) {
+ cm3->ms_meas_cap.sms_value = bitvec_get_uint(&bv, 4);
+ cm3->ms_meas_cap.sm_value = bitvec_get_uint(&bv, 4);
+ }
+
+ cm3->ms_pos_method_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->ms_pos_method_cap.present)
+ cm3->ms_pos_method_cap.method = bitvec_get_uint(&bv, 5);
+
+ cm3->ecsd_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->ecsd_multislot_cap.present)
+ cm3->ecsd_multislot_cap.mslot_class = bitvec_get_uint(&bv, 5);
+
+ cm3->psk8_struct.present = bitvec_get_uint(&bv, 1);
+ if (cm3->psk8_struct.present) {
+ cm3->psk8_struct.mod_cap = bitvec_get_uint(&bv, 1);
+
+ cm3->psk8_struct.rf_pwr_cap_1.present = bitvec_get_uint(&bv, 1);
+ if (cm3->psk8_struct.rf_pwr_cap_1.present) {
+ cm3->psk8_struct.rf_pwr_cap_1.value =
+ bitvec_get_uint(&bv, 2);
+ }
+
+ cm3->psk8_struct.rf_pwr_cap_2.present = bitvec_get_uint(&bv, 1);
+ if (cm3->psk8_struct.rf_pwr_cap_2.present) {
+ cm3->psk8_struct.rf_pwr_cap_2.value =
+ bitvec_get_uint(&bv, 2);
+ }
+ }
+
+ cm3->gsm_400_bands_supp.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_400_bands_supp.present) {
+ cm3->gsm_400_bands_supp.value = bitvec_get_uint(&bv, 2);
+ if (cm3->gsm_400_bands_supp.value == 0x00)
+ return -1;
+ cm3->gsm_400_bands_supp.assoc_radio_cap =
+ bitvec_get_uint(&bv, 4);
+ }
+
+ cm3->gsm_850_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_850_assoc_radio_cap.present)
+ cm3->gsm_850_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->gsm_1900_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_1900_assoc_radio_cap.present)
+ cm3->gsm_1900_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->umts_fdd_rat_cap = bitvec_get_uint(&bv, 1);
+ cm3->umts_tdd_rat_cap = bitvec_get_uint(&bv, 1);
+ cm3->cdma200_rat_cap = bitvec_get_uint(&bv, 1);
+
+ cm3->dtm_gprs_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->dtm_gprs_multislot_cap.present) {
+ cm3->dtm_gprs_multislot_cap.mslot_class = bitvec_get_uint(&bv, 2);
+ cm3->dtm_gprs_multislot_cap.single_slot_dtm =
+ bitvec_get_uint(&bv, 1);
+ cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present =
+ bitvec_get_uint(&bv, 1);
+ if (cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present)
+ cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.
+ mslot_class = bitvec_get_uint(&bv, 2);
+ }
+
+ /* Release 4 starts here. */
+ cm3->single_band_supp.present = bitvec_get_uint(&bv, 1);
+ if (cm3->single_band_supp.present)
+ cm3->single_band_supp.value = bitvec_get_uint(&bv, 4);
+
+ cm3->gsm_750_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_750_assoc_radio_cap.present)
+ cm3->gsm_750_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->umts_1_28_mcps_tdd_rat_cap = bitvec_get_uint(&bv, 1);
+ cm3->geran_feature_package = bitvec_get_uint(&bv, 1);
+
+ cm3->extended_dtm_gprs_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->extended_dtm_gprs_multislot_cap.present) {
+ cm3->extended_dtm_gprs_multislot_cap.mslot_class =
+ bitvec_get_uint(&bv, 2);
+ cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.present =
+ bitvec_get_uint(&bv, 1);
+ if (cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.present)
+ cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.mslot_class =
+ bitvec_get_uint(&bv, 2);
+ }
+
+ /* Release 5 starts here */
+ cm3->high_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->high_multislot_cap.present)
+ cm3->high_multislot_cap.value = bitvec_get_uint(&bv, 2);
+
+ /* This used to be the GERAN Iu mode support bit, but the newer spec
+ * releases say that it should not be used (always zero), however
+ * we will just ignore tha state of this bit. */
+ bitvec_get_uint(&bv, 1);
+
+ cm3->geran_feature_package_2 = bitvec_get_uint(&bv, 1);
+ cm3->gmsk_multislot_power_prof = bitvec_get_uint(&bv, 2);
+ cm3->psk8_multislot_power_prof = bitvec_get_uint(&bv, 2);
+
+ /* Release 6 starts here */
+ cm3->t_gsm_400_bands_supp.present = bitvec_get_uint(&bv, 1);
+ if (cm3->t_gsm_400_bands_supp.present) {
+ cm3->t_gsm_400_bands_supp.value = bitvec_get_uint(&bv, 2);
+ cm3->t_gsm_400_bands_supp.assoc_radio_cap =
+ bitvec_get_uint(&bv, 4);
+ }
+
+ /* This used to be T-GSM 900 associated radio capability, but the
+ * newer spec releases say that this bit should not be used, but if
+ * it is used by some MS anyway we must assume that there is data
+ * we have to override. */
+ if (bitvec_get_uint(&bv, 1))
+ bitvec_get_uint(&bv, 4);
+
+ cm3->dl_advanced_rx_perf = bitvec_get_uint(&bv, 2);
+ cm3->dtm_enhancements_cap = bitvec_get_uint(&bv, 1);
+
+ cm3->dtm_gprs_high_multislot_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->dtm_gprs_high_multislot_cap.present) {
+ cm3->dtm_gprs_high_multislot_cap.mslot_class =
+ bitvec_get_uint(&bv, 3);
+ cm3->dtm_gprs_high_multislot_cap.offset_required =
+ bitvec_get_uint(&bv, 1);
+ cm3->dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.
+ present = bitvec_get_uint(&bv, 1);
+ if (cm3->dtm_gprs_high_multislot_cap.
+ dtm_egprs_high_multislot_cap.present)
+ cm3->dtm_gprs_high_multislot_cap.
+ dtm_egprs_high_multislot_cap.mslot_class =
+ bitvec_get_uint(&bv, 3);
+ }
+
+ cm3->repeated_acch_capability = bitvec_get_uint(&bv, 1);
+
+ /* Release 7 starts here */
+ cm3->gsm_710_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->gsm_710_assoc_radio_cap.present)
+ cm3->gsm_710_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->t_gsm_810_assoc_radio_cap.present = bitvec_get_uint(&bv, 1);
+ if (cm3->t_gsm_810_assoc_radio_cap.present)
+ cm3->t_gsm_810_assoc_radio_cap.value = bitvec_get_uint(&bv, 4);
+
+ cm3->ciphering_mode_setting_cap = bitvec_get_uint(&bv, 1);
+ cm3->add_pos_cap = bitvec_get_uint(&bv, 1);
+
+ /* Release 8 starts here */
+ cm3->e_utra_fdd_supp = bitvec_get_uint(&bv, 1);
+ cm3->e_utra_tdd_supp = bitvec_get_uint(&bv, 1);
+ cm3->e_utra_meas_rep_supp = bitvec_get_uint(&bv, 1);
+ cm3->prio_resel_supp = bitvec_get_uint(&bv, 1);
+
+ /* Release 9 starts here */
+ cm3->utra_csg_cells_rep = bitvec_get_uint(&bv, 1);
+
+ cm3->vamos_level = bitvec_get_uint(&bv, 2);
+
+ /* Release 10 starts here */
+ cm3->tighter_capability = bitvec_get_uint(&bv, 2);
+ cm3->sel_ciph_dl_sacch = bitvec_get_uint(&bv, 1);
+
+ /* Release 11 starts here */
+ cm3->cs_ps_srvcc_geran_utra = bitvec_get_uint(&bv, 2);
+ cm3->cs_ps_srvcc_geran_eutra = bitvec_get_uint(&bv, 2);
+
+ cm3->geran_net_sharing = bitvec_get_uint(&bv, 1);
+ cm3->e_utra_wb_rsrq_meas_supp = bitvec_get_uint(&bv, 1);
+
+ /* Release 12 starts here */
+ cm3->er_band_support = bitvec_get_uint(&bv, 1);
+ cm3->utra_mult_band_ind_supp = bitvec_get_uint(&bv, 1);
+ cm3->e_utra_mult_band_ind_supp = bitvec_get_uint(&bv, 1);
+ cm3->extended_tsc_set_cap_supp = bitvec_get_uint(&bv, 1);
+
+ /* Late addition of a release 11 feature */
+ cm3->extended_earfcn_val_range = bitvec_get_uint(&bv, 1);
+
+ return 0;
+}
/*! @} */
diff --git a/src/gsm/gsm48_rest_octets.c b/src/gsm/gsm48_rest_octets.c
index 518572ed..57c2c99a 100644
--- a/src/gsm/gsm48_rest_octets.c
+++ b/src/gsm/gsm48_rest_octets.c
@@ -46,8 +46,9 @@ int osmo_gsm48_rest_octets_si1_encode(uint8_t *data, uint8_t *nch_pos, int is180
if (nch_pos) {
bitvec_set_bit(&bv, H);
bitvec_set_uint(&bv, *nch_pos, 5);
- } else
+ } else {
bitvec_set_bit(&bv, L);
+ }
if (is1800_net)
bitvec_set_bit(&bv, L);
@@ -58,11 +59,88 @@ int osmo_gsm48_rest_octets_si1_encode(uint8_t *data, uint8_t *nch_pos, int is180
return bv.data_len;
}
+struct nch_pos {
+ uint8_t num_blocks;
+ uint8_t first_block;
+};
+
+/* 3GPP TS 44.010 Table 10.5.2.32.1b */
+static const struct nch_pos si1ro_nch_positions[] = {
+ [0x00] = {1, 0},
+ [0x01] = {1, 1},
+ [0x02] = {1, 2},
+ [0x03] = {1, 3},
+ [0x04] = {1, 4},
+ [0x05] = {1, 5},
+ [0x06] = {1, 6},
+
+ [0x07] = {2, 0},
+ [0x08] = {2, 1},
+ [0x09] = {2, 2},
+ [0x0a] = {2, 3},
+ [0x0b] = {2, 4},
+ [0x0c] = {2, 5},
+
+ [0x0d] = {3, 0},
+ [0x0e] = {3, 1},
+ [0x0f] = {3, 2},
+ [0x10] = {3, 3},
+ [0x11] = {3, 4},
+
+ [0x12] = {4, 0},
+ [0x13] = {4, 1},
+ [0x14] = {4, 2},
+ [0x15] = {4, 3},
+
+ [0x16] = {5, 0},
+ [0x17] = {5, 1},
+ [0x18] = {5, 2},
+
+ [0x19] = {6, 0},
+ [0x1a] = {6, 1},
+
+ [0x1b] = {7, 0},
+};
+
+/*! Decode the 5-bit 'NCH position' field within SI1 Rest Octets.
+ * \param[in] value 5-bit value from SI1 rest octets
+ * \param[out] num_blocks Number of CCCH used for NCH
+ * \param[out] first_block First CCCH block used for NCH
+ * \returns 0 on success; negative in case of error */
+int osmo_gsm48_si1ro_nch_pos_decode(uint8_t value, uint8_t *num_blocks, uint8_t *first_block)
+{
+ if (value >= ARRAY_SIZE(si1ro_nch_positions))
+ return -EINVAL;
+
+ *num_blocks = si1ro_nch_positions[value].num_blocks;
+ *first_block = si1ro_nch_positions[value].first_block;
+
+ return 0;
+}
+
+/*! Encode the 5-bit 'NCH position' field within SI1 Rest Octets.
+ * \param[in] num_blocks Number of CCCH used for NCH
+ * \param[in] first_block First CCCH block used for NCH
+ * \returns 5-bit value for SI1 rest octets on success; negative in case of error */
+int osmo_gsm48_si1ro_nch_pos_encode(uint8_t num_blocks, uint8_t first_block)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(si1ro_nch_positions); i++) {
+ if (si1ro_nch_positions[i].num_blocks == num_blocks &&
+ si1ro_nch_positions[i].first_block == first_block) {
+ return i;
+ }
+ }
+ return -EINVAL;
+}
+
/* Append Repeated E-UTRAN Neighbour Cell to bitvec: see 3GPP TS 44.018 Table 10.5.2.33b.1 */
static inline bool append_eutran_neib_cell(struct bitvec *bv, const struct osmo_earfcn_si2q *e, size_t *e_offset,
uint8_t budget)
{
unsigned i, skip = 0;
+ size_t offset = *e_offset;
int16_t rem = budget - 6; /* account for mandatory stop bit and THRESH_E-UTRAN_high */
uint8_t earfcn_budget;
@@ -93,7 +171,7 @@ static inline bool append_eutran_neib_cell(struct bitvec *bv, const struct osmo_
/* now we can proceed with actually adding EARFCNs within adjusted budget limit */
for (i = 0; i < e->length; i++) {
if (e->arfcn[i] != OSMO_EARFCN_INVALID) {
- if (skip < *e_offset) {
+ if (skip < offset) {
skip++; /* ignore EARFCNs added on previous calls */
} else {
earfcn_budget = 17; /* compute budget per-EARFCN */
@@ -134,8 +212,9 @@ static inline bool append_eutran_neib_cell(struct bitvec *bv, const struct osmo_
/* E-UTRAN_PRIORITY: 3GPP TS 45.008*/
bitvec_set_bit(bv, 1);
bitvec_set_uint(bv, e->prio, 3);
- } else
+ } else {
bitvec_set_bit(bv, 0);
+ }
/* THRESH_E-UTRAN_high */
bitvec_set_uint(bv, e->thresh_hi, 5);
@@ -144,15 +223,17 @@ static inline bool append_eutran_neib_cell(struct bitvec *bv, const struct osmo_
/* THRESH_E-UTRAN_low: */
bitvec_set_bit(bv, 1);
bitvec_set_uint(bv, e->thresh_lo, 5);
- } else
+ } else {
bitvec_set_bit(bv, 0);
+ }
if (e->qrxlm_valid) {
/* E-UTRAN_QRXLEVMIN: */
bitvec_set_bit(bv, 1);
bitvec_set_uint(bv, e->qrxlm, 5);
- } else
+ } else {
bitvec_set_bit(bv, 0);
+ }
return true;
}
@@ -161,7 +242,7 @@ static inline void append_earfcn(struct bitvec *bv, const struct osmo_earfcn_si2
{
bool appended;
unsigned int old = bv->cur_bit; /* save current position to make rollback possible */
- int rem = budget - 25;
+ int rem = ((int)budget) - 40;
if (rem <= 0)
return;
@@ -189,8 +270,29 @@ static inline void append_earfcn(struct bitvec *bv, const struct osmo_earfcn_si2
/* Priority and E-UTRAN Parameters Description */
bitvec_set_bit(bv, 1);
- /* No Serving Cell Priority Parameters Descr. */
- bitvec_set_bit(bv, 0);
+ /* budget: 10 bits used above */
+
+ /* Serving Cell Priority Parameters Descr. is Present,
+ * see also: 3GPP TS 44.018, Table 10.5.2.33b.1 */
+ bitvec_set_bit(bv, 1);
+
+ /* GERAN_PRIORITY */
+ bitvec_set_uint(bv, 0, 3);
+
+ /* THRESH_Priority_Search */
+ bitvec_set_uint(bv, 0, 4);
+
+ /* THRESH_GSM_low */
+ bitvec_set_uint(bv, 0, 4);
+
+ /* H_PRIO */
+ bitvec_set_uint(bv, 0, 2);
+
+ /* T_Reselection */
+ bitvec_set_uint(bv, 0, 2);
+
+ /* budget: 26 bits used above */
+
/* No 3G Priority Parameters Description */
bitvec_set_bit(bv, 0);
/* E-UTRAN Parameters Description */
@@ -214,12 +316,16 @@ static inline void append_earfcn(struct bitvec *bv, const struct osmo_earfcn_si2
/* Repeated E-UTRAN Neighbour Cells */
bitvec_set_bit(bv, 1);
+ /* budget: 34 bits used above */
+
appended = append_eutran_neib_cell(bv, e, e_offset, rem);
if (!appended) { /* appending is impossible within current budget: rollback */
bv->cur_bit = old;
return;
}
+ /* budget: further 6 bits used below, totalling 40 bits */
+
/* stop bit - end of Repeated E-UTRAN Neighbour Cells sequence: */
bitvec_set_bit(bv, 0);
@@ -404,8 +510,9 @@ static inline void append_uarfcns(struct bitvec *bv, const uint16_t *uarfcn_list
if (i < uarfcn_length) {
cu = uarfcn_list[i];
st = i;
- } else
+ } else {
break;
+ }
}
/* stop bit - end of Repeated UTRAN FDD Neighbour Cells */
@@ -508,8 +615,9 @@ static void append_selection_params(struct bitvec *bv,
bitvec_set_uint(bv, sp->cell_resel_off, 6);
bitvec_set_uint(bv, sp->temp_offs, 3);
bitvec_set_uint(bv, sp->penalty_time, 5);
- } else
+ } else {
bitvec_set_bit(bv, L);
+ }
}
/* Append power offset to bitvec */
@@ -519,8 +627,9 @@ static void append_power_offset(struct bitvec *bv,
if (po->present) {
bitvec_set_bit(bv, H);
bitvec_set_uint(bv, po->power_offset, 2);
- } else
+ } else {
bitvec_set_bit(bv, L);
+ }
}
/* Append GPRS indicator to bitvec */
@@ -532,8 +641,9 @@ static void append_gprs_ind(struct bitvec *bv,
bitvec_set_uint(bv, gi->ra_colour, 3);
/* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */
bitvec_set_bit(bv, gi->si13_position);
- } else
+ } else {
bitvec_set_bit(bv, L);
+ }
}
/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */
@@ -567,8 +677,9 @@ int osmo_gsm48_rest_octets_si3_encode(uint8_t *data, const struct osmo_gsm48_si_
if (si3->scheduling.present) {
bitvec_set_bit(&bv, H);
bitvec_set_uint(&bv, si3->scheduling.where, 3);
- } else
+ } else {
bitvec_set_bit(&bv, L);
+ }
/* GPRS Indicator */
append_gprs_ind(&bv, &si3->gprs_ind);
@@ -618,22 +729,25 @@ int osmo_gsm48_rest_octets_si4_encode(uint8_t *data, const struct osmo_gsm48_si_
if (si4->lsa_params.present) {
bitvec_set_bit(&bv, H);
append_lsa_params(&bv, &si4->lsa_params);
- } else
+ } else {
bitvec_set_bit(&bv, L);
+ }
/* Cell Identity */
if (1) {
bitvec_set_bit(&bv, H);
bitvec_set_uint(&bv, si4->cell_id, 16);
- } else
+ } else {
bitvec_set_bit(&bv, L);
+ }
/* LSA ID Information */
if (0) {
bitvec_set_bit(&bv, H);
/* FIXME */
- } else
+ } else {
bitvec_set_bit(&bv, L);
+ }
} else {
/* L and break indicator */
bitvec_set_bit(&bv, L);
@@ -671,25 +785,29 @@ int osmo_gsm48_rest_octets_si6_encode(uint8_t *data, const struct osmo_gsm48_si6
if (in->pch_nch_info.call_priority_present) {
bitvec_set_bit(&bv, 1);
bitvec_set_uint(&bv, in->pch_nch_info.call_priority, 3);
- } else
+ } else {
bitvec_set_bit(&bv, 0);
+ }
bitvec_set_bit(&bv, !!in->pch_nch_info.nln_status_sacch);
- } else
+ } else {
bitvec_set_bit(&bv, L);
+ }
if (in->vbs_vgcs_options.present) {
bitvec_set_bit(&bv, H);
bitvec_set_bit(&bv, !!in->vbs_vgcs_options.inband_notifications);
bitvec_set_bit(&bv, !!in->vbs_vgcs_options.inband_pagings);
- } else
+ } else {
bitvec_set_bit(&bv, L);
+ }
if (in->dtm_support.present) {
bitvec_set_bit(&bv, H);
bitvec_set_uint(&bv, in->dtm_support.rac, 8);
bitvec_set_uint(&bv, in->dtm_support.max_lapdm, 3);
- } else
+ } else {
bitvec_set_bit(&bv, L);
+ }
if (in->band_indicator_1900)
bitvec_set_bit(&bv, H);
@@ -699,13 +817,127 @@ int osmo_gsm48_rest_octets_si6_encode(uint8_t *data, const struct osmo_gsm48_si6
if (in->gprs_ms_txpwr_max_ccch.present) {
bitvec_set_bit(&bv, H);
bitvec_set_uint(&bv, in->gprs_ms_txpwr_max_ccch.max_txpwr, 5);
- } else
+ } else {
bitvec_set_bit(&bv, L);
+ }
bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
return bv.data_len;
}
+
+static unsigned int decode_t3192(unsigned int t3192)
+{
+ /* See also 3GPP TS 44.060
+ Table 12.24.2: GPRS Cell Options information element details */
+ static const unsigned int decode_t3192_tbl[8] = {500, 1000, 1500, 0, 80, 120, 160, 200};
+ OSMO_ASSERT(t3192 <= 7);
+ return decode_t3192_tbl[t3192];
+}
+
+static unsigned int decode_drx_timer(unsigned int drx)
+{
+ static const unsigned int decode_drx_timer_tbl[8] = {0, 1, 2, 4, 8, 16, 32, 64};
+ OSMO_ASSERT(drx <= 7);
+ return decode_drx_timer_tbl[drx];
+}
+
+static int decode_gprs_cell_opt(struct osmo_gprs_cell_options *gco, struct bitvec *bv)
+{
+ gco->nmo = bitvec_get_uint(bv, 2);
+ gco->t3168 = (bitvec_get_uint(bv, 3) + 1) * 500;
+ gco->t3192 = decode_t3192(bitvec_get_uint(bv, 3));
+ gco->drx_timer_max = decode_drx_timer(bitvec_get_uint(bv, 3));
+
+ /* ACCESS_BURST_TYPE: */
+ bitvec_get_uint(bv, 1);
+ /* CONTROL_ACK_TYPE: */
+ gco->ctrl_ack_type_use_block = bitvec_get_uint(bv, 1);
+ gco->bs_cv_max = bitvec_get_uint(bv, 4);
+
+ if (bitvec_get_uint(bv, 1)) {
+ bitvec_get_uint(bv, 3); /* DEC */
+ bitvec_get_uint(bv, 3); /* INC */
+ bitvec_get_uint(bv, 3); /* MAX */
+ }
+
+ if (bitvec_get_uint(bv, 1)) {
+ int ext_len = bitvec_get_uint(bv, 6);
+ if (ext_len < 0)
+ return ext_len;
+ unsigned int cur_bit = bv->cur_bit;
+ /* Extension Information */
+ /* R99 extension: */
+ gco->ext_info.egprs_supported = bitvec_get_uint(bv, 1);
+ if (gco->ext_info.egprs_supported) {
+ gco->ext_info.use_egprs_p_ch_req = !bitvec_get_uint(bv, 1);
+ gco->ext_info.bep_period = bitvec_get_uint(bv, 4);
+ }
+ gco->ext_info.pfc_supported = bitvec_get_uint(bv, 1);
+ gco->ext_info.dtm_supported = bitvec_get_uint(bv, 1);
+ gco->ext_info.bss_paging_coordination = bitvec_get_uint(bv, 1);
+ /* REL-4 extension: */
+ gco->ext_info.ccn_active = bitvec_get_uint(bv, 1);
+ bitvec_get_uint(bv, 1); /* NW_EXT_UTBF */
+ bv->cur_bit = cur_bit + ext_len + 1;
+ }
+ return 0;
+}
+
+static void decode_gprs_pwr_ctrl_pars(struct osmo_gprs_power_ctrl_pars *pcp, struct bitvec *bv)
+{
+ pcp->alpha = bitvec_get_uint(bv, 4);
+ pcp->t_avg_w = bitvec_get_uint(bv,5);
+ pcp->t_avg_t = bitvec_get_uint(bv, 5);
+ pcp->pc_meas_chan = bitvec_get_uint(bv, 1);
+ pcp->n_avg_i = bitvec_get_uint(bv, 4);
+}
+
+/*! Decode SI13 Rest Octests (04.08 Chapter 10.5.2.37b).
+ * \param[out] si13 decoded SI13 rest octets
+ * \param[in] encoded SI13 rest octets
+ * \returns parsed bits on success, negative on error */
+int osmo_gsm48_rest_octets_si13_decode(struct osmo_gsm48_si13_info *si13, const uint8_t *data)
+{
+ struct osmo_gprs_cell_options *co = &si13->cell_opts;
+ struct osmo_gprs_power_ctrl_pars *pcp = &si13->pwr_ctrl_pars;
+ struct bitvec bv;
+ int rc;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = (uint8_t *) data;
+ bv.data_len = 20;
+
+ memset(si13, 0, sizeof(*si13));
+
+
+ if (bitvec_get_bit_high(&bv) == H) {
+ si13->bcch_change_mark = bitvec_get_uint(&bv, 3);
+ si13->si_change_field = bitvec_get_uint(&bv, 4);
+ if (bitvec_get_uint(&bv, 1)) {
+ si13->bcch_change_mark = bitvec_get_uint(&bv, 2);
+ /* FIXME: implement parsing GPRS Mobile Allocation IE */
+ return -ENOTSUP;
+ }
+ if (bitvec_get_uint(&bv, 1)) {
+ /* PBCCH present in cell */
+ /* FIXME: parse not implemented */
+ return -ENOTSUP;
+ } else {
+ /* PBCCH not present in cell */
+ si13->rac = bitvec_get_uint(&bv, 8);
+ si13->spgc_ccch_sup = bitvec_get_uint(&bv, 1);
+ si13->prio_acc_thr = bitvec_get_uint(&bv, 3);
+ si13->net_ctrl_ord = bitvec_get_uint(&bv, 2);
+ if ((rc = decode_gprs_cell_opt(co, &bv)) < 0)
+ return rc;
+
+ decode_gprs_pwr_ctrl_pars(pcp, &bv);
+ }
+ }
+ return bv.cur_bit;
+}
+
/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a:
< GPRS Mobile Allocation IE > ::=
< HSN : bit (6) >
@@ -730,8 +962,9 @@ static int append_gprs_mobile_alloc(struct bitvec *bv)
/* We want to use a RFL number list */
bitvec_set_bit(bv, 1);
/* FIXME: RFL number list */
- } else
+ } else {
bitvec_set_bit(bv, 0);
+ }
if (0) {
/* We want to use a MA_BITMAP */
@@ -743,8 +976,9 @@ static int append_gprs_mobile_alloc(struct bitvec *bv)
/* We want to provide an ARFCN index list */
bitvec_set_bit(bv, 1);
/* FIXME */
- } else
+ } else {
bitvec_set_bit(bv, 0);
+ }
}
return 0;
}
@@ -862,24 +1096,20 @@ static int append_gprs_cell_opt(struct bitvec *bv,
} else {
/* extension information */
bitvec_set_bit(bv, 1);
+ /* R99 extension: */
if (!gco->ext_info.egprs_supported) {
/* 6bit length of extension */
- bitvec_set_uint(bv, (1 + 3)-1, 6);
+ bitvec_set_uint(bv, (1 + 5)-1, 6);
/* EGPRS supported in the cell */
bitvec_set_bit(bv, 0);
} else {
/* 6bit length of extension */
- bitvec_set_uint(bv, (1 + 5 + 3)-1, 6);
+ bitvec_set_uint(bv, (1 + 5 + 5)-1, 6);
/* EGPRS supported in the cell */
bitvec_set_bit(bv, 1);
- /* 1bit EGPRS PACKET CHANNEL REQUEST */
- if (gco->supports_egprs_11bit_rach == 0) {
- bitvec_set_bit(bv,
- gco->ext_info.use_egprs_p_ch_req);
- } else {
- bitvec_set_bit(bv, 0);
- }
+ /* 1bit EGPRS PACKET CHANNEL REQUEST (inverted logic) */
+ bitvec_set_bit(bv, !gco->ext_info.use_egprs_p_ch_req);
/* 4bit BEP PERIOD */
bitvec_set_uint(bv, gco->ext_info.bep_period, 4);
@@ -887,6 +1117,10 @@ static int append_gprs_cell_opt(struct bitvec *bv,
bitvec_set_bit(bv, gco->ext_info.pfc_supported);
bitvec_set_bit(bv, gco->ext_info.dtm_supported);
bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination);
+
+ /* REL-4 extension: */
+ bitvec_set_bit(bv, gco->ext_info.ccn_active);
+ bitvec_set_bit(bv, 0); /* NW_EXT_UTBF disabled */
}
return 0;
@@ -973,15 +1207,17 @@ void osmo_gsm48_rest_octets_si3_decode(struct osmo_gsm48_si_ro_info *si3, const
sp->cell_resel_off = bitvec_get_uint(&bv, 6);
sp->temp_offs = bitvec_get_uint(&bv, 3);
sp->penalty_time = bitvec_get_uint(&bv, 5);
- } else
+ } else {
sp->present = 0;
+ }
/* Optional Power Offset */
if (bitvec_get_bit_high(&bv) == H) {
po->present = 1;
po->power_offset = bitvec_get_uint(&bv, 2);
- } else
+ } else {
po->present = 0;
+ }
/* System Information 2ter Indicator */
if (bitvec_get_bit_high(&bv) == H)
@@ -999,26 +1235,71 @@ void osmo_gsm48_rest_octets_si3_decode(struct osmo_gsm48_si_ro_info *si3, const
if (bitvec_get_bit_high(&bv) == H) {
si3->scheduling.present = 1;
si3->scheduling.where = bitvec_get_uint(&bv, 3);
- } else
+ } else {
si3->scheduling.present = 0;
+ }
/* GPRS Indicator */
if (bitvec_get_bit_high(&bv) == H) {
gi->present = 1;
gi->ra_colour = bitvec_get_uint(&bv, 3);
gi->si13_position = bitvec_get_uint(&bv, 1);
- } else
+ } else {
gi->present = 0;
+ }
/* 3G Early Classmark Sending Restriction. If H, then controlled by
* early_cm_ctrl above */
if (bitvec_get_bit_high(&bv) == H)
- si3->early_cm_restrict_3g = 1;
- else
si3->early_cm_restrict_3g = 0;
+ else
+ si3->early_cm_restrict_3g = 1;
if (bitvec_get_bit_high(&bv) == H)
si3->si2quater_indicator = 1;
else
si3->si2quater_indicator = 0;
}
+
+
+void osmo_gsm48_rest_octets_si4_decode(struct osmo_gsm48_si_ro_info *si4, const uint8_t *data, int len)
+{
+ struct osmo_gsm48_si_selection_params *sp = &si4->selection_params;
+ struct osmo_gsm48_si_power_offset *po = &si4->power_offset;
+ struct osmo_gsm48_si3_gprs_ind *gi = &si4->gprs_ind;
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = (uint8_t *) data;
+ bv.data_len = len;
+
+ memset(si4, 0, sizeof(*si4));
+
+ /* Optional Selection Parameters */
+ if (bitvec_get_bit_high(&bv) == H) {
+ sp->present = 1;
+ sp->cbq = bitvec_get_uint(&bv, 1);
+ sp->cell_resel_off = bitvec_get_uint(&bv, 6);
+ sp->temp_offs = bitvec_get_uint(&bv, 3);
+ sp->penalty_time = bitvec_get_uint(&bv, 5);
+ } else {
+ sp->present = 0;
+ }
+
+ /* Optional Power Offset */
+ if (bitvec_get_bit_high(&bv) == H) {
+ po->present = 1;
+ po->power_offset = bitvec_get_uint(&bv, 2);
+ } else {
+ po->present = 0;
+ }
+
+ /* GPRS Indicator */
+ if (bitvec_get_bit_high(&bv) == H) {
+ gi->present = 1;
+ gi->ra_colour = bitvec_get_uint(&bv, 3);
+ gi->si13_position = bitvec_get_uint(&bv, 1);
+ } else {
+ gi->present = 0;
+ }
+}
diff --git a/src/gsm/gsm_04_08_gprs.c b/src/gsm/gsm_04_08_gprs.c
index 608fa8c1..80325939 100644
--- a/src/gsm/gsm_04_08_gprs.c
+++ b/src/gsm/gsm_04_08_gprs.c
@@ -65,7 +65,7 @@ const struct value_string gsm48_gmm_cause_names_[] = {
{ GMM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" },
{ GMM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" },
{ GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL,
- "Message type non-existant or not implemented" },
+ "Message type non-existent or not implemented" },
{ GMM_CAUSE_MSGT_INCOMP_P_STATE,
"Message type not compatible with protocol state" },
{ GMM_CAUSE_IE_NOTEXIST_NOTIMPL,
@@ -105,7 +105,7 @@ const struct value_string gsm48_gsm_cause_names_[] = {
{ GSM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" },
{ GSM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" },
{ GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL,
- "Message type non-existant or not implemented" },
+ "Message type non-existent or not implemented" },
{ GSM_CAUSE_MSGT_INCOMP_P_STATE,
"Message type not compatible with protocol state" },
{ GSM_CAUSE_IE_NOTEXIST_NOTIMPL,
diff --git a/src/gsm/gsm_utils.c b/src/gsm/gsm_utils.c
index ae77a9dc..bb403392 100644
--- a/src/gsm/gsm_utils.c
+++ b/src/gsm/gsm_utils.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \mainpage libosmogsm Documentation
@@ -84,6 +80,7 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/meas_rep.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm0502.h>
#include <stdlib.h>
#include <stdint.h>
@@ -96,7 +93,7 @@
#include <time.h>
#include <unistd.h>
-#include "../../config.h"
+#include "config.h"
#if (!EMBEDDED)
/* FIXME: this can be removed once we bump glibc requirements to 2.25: */
@@ -324,17 +321,18 @@ int gsm_septet_encode(uint8_t *result, const char *data)
* \param[in] septet_len Length of \a rdata
* \param[in] padding padding bits at start
* \returns number of bytes used in \a result */
-int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding)
+int gsm_septet_pack(uint8_t *result, const uint8_t *rdata, size_t septet_len, uint8_t padding)
{
int i = 0, z = 0;
uint8_t cb, nb;
int shift = 0;
- uint8_t *data = calloc(septet_len + 1, sizeof(uint8_t));
+ uint8_t *data = malloc(septet_len + 1);
if (padding) {
shift = 7 - padding;
/* the first zero is needed for padding */
memcpy(data + 1, rdata, septet_len);
+ data[0] = 0x00;
septet_len++;
} else
memcpy(data, rdata, septet_len);
@@ -369,6 +367,12 @@ int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len
return z;
}
+/*! Backwards compatibility wrapper for gsm_septets_pack(), deprecated. */
+int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding)
+{
+ return gsm_septet_pack(result, rdata, septet_len, padding);
+}
+
/*! GSM 7-bit alphabet TS 03.38 6.2.1 Character packing
* \param[out] result Caller-provided output buffer
* \param[in] n Maximum length of \a result in bytes
@@ -382,7 +386,7 @@ int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets)
size_t max_septets = n * 8 / 7;
/* prepare for the worst case, every character expanding to two bytes */
- uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t));
+ uint8_t *rdata = malloc(strlen(data) * 2);
y = gsm_septet_encode(rdata, data);
if (y > max_septets) {
@@ -393,7 +397,7 @@ int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets)
y = max_septets;
}
- o = gsm_septets2octets(result, rdata, y, 0);
+ o = gsm_septet_pack(result, rdata, y, 0);
if (octets)
*octets = o;
@@ -487,7 +491,7 @@ int osmo_get_rand_id(uint8_t *out, size_t len)
* \param[out] buf Pre-allocated bufer for storing IE
* \returns Number of bytes filled in buf
*/
-size_t gsm0858_rsl_ul_meas_enc(struct gsm_meas_rep_unidir *mru, bool dtxd_used,
+size_t gsm0858_rsl_ul_meas_enc(const struct gsm_meas_rep_unidir *mru, bool dtxd_used,
uint8_t *buf)
{
buf[0] = dtxd_used ? (1 << 6) : 0;
@@ -882,10 +886,21 @@ char *gsm_fn_as_gsmtime_str(uint32_t fn)
/*! Encode decoded \ref gsm_time to Frame Number
* \param[in] time GSM Time in decoded structure
* \returns GSM Frame Number */
-uint32_t gsm_gsmtime2fn(struct gsm_time *time)
+uint32_t gsm_gsmtime2fn(const struct gsm_time *time)
{
- /* TS 05.02 Chapter 4.3.3 TDMA frame number */
- return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1));
+ uint32_t fn;
+
+ /* See also:
+ * 3GPP TS 44.018, section 10.5.2.38, 3GPP TS 45.002 section 4.3.3, and
+ * 3GPP TS 48.058, section 9.3.8 */
+ fn = 51 * OSMO_MOD_FLR((time->t3-time->t2), 26) + time->t3 + 51 * 26 * time->t1;
+
+ /* Note: Corrupted input values may cause a resulting frame number
+ * larger then the maximum permitted value of GSM_MAX_FN. Even though
+ * the caller is expected to check the input values beforehand we must
+ * make sure that the result cannot exceed the value range of a valid
+ * GSM frame number. */
+ return fn % GSM_MAX_FN;
}
char *osmo_dump_gsmtime_buf(char *buf, size_t buf_len, const struct gsm_time *tm)
@@ -910,6 +925,45 @@ char *osmo_dump_gsmtime_c(const void *ctx, const struct gsm_time *tm)
return osmo_dump_gsmtime_buf(buf, 64, tm);
}
+#define GSM_RFN_THRESHOLD (GSM_RFN_MODULUS / 2)
+uint32_t gsm_rfn2fn(uint16_t rfn, uint32_t curr_fn)
+{
+ uint32_t curr_rfn;
+ uint32_t fn_rounded;
+ const uint32_t rfn32 = rfn; /* used as 32bit for calculations */
+
+ /* Ensure that all following calculations are performed with the
+ * relative frame number */
+ OSMO_ASSERT(rfn32 < GSM_RFN_MODULUS);
+
+ /* Compute an internal relative frame number from the full internal
+ frame number */
+ curr_rfn = gsm_fn2rfn(curr_fn);
+
+ /* Compute a "rounded" version of the internal frame number, which
+ * exactly fits in the RFN_MODULUS raster */
+ fn_rounded = GSM_TDMA_FN_SUB(curr_fn, curr_rfn);
+
+ /* If the delta between the internal and the external relative frame
+ * number exceeds a certain limit, we need to assume that the incoming
+ * request belongs to a the previous rfn period. To correct this,
+ * we roll back the rounded frame number by one RFN_MODULUS */
+ if (GSM_TDMA_FN_DIFF(rfn32, curr_rfn) > GSM_RFN_THRESHOLD) {
+ /* Race condition between rfn and curr_fn detected: rfn belongs
+ to the previous RFN_MODULUS cycle, wrapping... */
+ if (fn_rounded < GSM_RFN_MODULUS) {
+ /* Corner case detected: wrapping crosses GSM_MAX_FN border */
+ fn_rounded = GSM_TDMA_FN_SUB(GSM_MAX_FN, (GSM_TDMA_FN_SUB(GSM_RFN_MODULUS, fn_rounded)));
+ } else {
+ fn_rounded = GSM_TDMA_FN_SUB(fn_rounded, GSM_RFN_MODULUS);
+ }
+ }
+
+ /* The real frame number is the sum of the rounded frame number and the
+ * relative framenumber computed via RACH */
+ return GSM_TDMA_FN_SUM(fn_rounded, rfn32);
+}
+
/*! append range1024 encoded data to bit vector
* \param[out] bv Caller-provided output bit-vector
* \param[in] r Input Range1024 sructure */
diff --git a/src/gsm/gsup.c b/src/gsm/gsup.c
index 2f9d85d8..4f0a1b5f 100644
--- a/src/gsm/gsup.c
+++ b/src/gsm/gsup.c
@@ -101,7 +101,11 @@ const struct value_string osmo_gsup_message_type_names[] = {
OSMO_VALUE_STRING(OSMO_GSUP_MSGT_E_CLOSE),
OSMO_VALUE_STRING(OSMO_GSUP_MSGT_E_ABORT),
- OSMO_VALUE_STRING(OSMO_GSUP_MSGT_E_ROUTING_ERROR),
+ OSMO_VALUE_STRING(OSMO_GSUP_MSGT_ROUTING_ERROR),
+
+ OSMO_VALUE_STRING(OSMO_GSUP_MSGT_EPDG_TUNNEL_REQUEST),
+ OSMO_VALUE_STRING(OSMO_GSUP_MSGT_EPDG_TUNNEL_RESULT),
+ OSMO_VALUE_STRING(OSMO_GSUP_MSGT_EPDG_TUNNEL_ERROR),
{ 0, NULL }
};
@@ -122,6 +126,62 @@ int osmo_gsup_get_err_msg_type(enum osmo_gsup_message_type type_in)
return OSMO_GSUP_TO_MSGT_ERROR(type_in);
}
+static int decode_pdp_address(const uint8_t *data, size_t data_len, struct osmo_gsup_pdp_info *pdp_info)
+{
+ const struct gsm48_pdp_address *pdp_addr = (const struct gsm48_pdp_address *)data;
+ /* Explicitly pre-nitialize them to AF_UNSPEC to signal they are empty: */
+ pdp_info->pdp_address[0].u.sa.sa_family = AF_UNSPEC;
+ pdp_info->pdp_address[1].u.sa.sa_family = AF_UNSPEC;
+
+ if (data_len < 2)
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+
+ pdp_info->pdp_type_org = pdp_addr->organization;
+ pdp_info->pdp_type_nr = pdp_addr->type;
+
+ if (pdp_info->pdp_type_org != PDP_TYPE_ORG_IETF)
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+
+ /* Skip type-org + type-nr for easy calculations below: */
+ data_len -= 2;
+
+ switch (pdp_info->pdp_type_nr) {
+ case PDP_TYPE_N_IETF_IPv4:
+ if (data_len == 0)
+ return 0; /* empty, marked as AF_UNSET. */
+ if (data_len != sizeof(pdp_addr->ietf.v4))
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ pdp_info->pdp_address[0].u.sa.sa_family = AF_INET;
+ pdp_info->pdp_address[0].u.sin.sin_addr.s_addr = pdp_addr->ietf.v4;
+ return 0;
+ case PDP_TYPE_N_IETF_IPv6:
+ if (data_len == 0)
+ return 0; /* empty, marked as AF_UNSET. */
+ if (data_len != sizeof(pdp_addr->ietf.v6))
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ pdp_info->pdp_address[0].u.sa.sa_family = AF_INET6;
+ memcpy(&pdp_info->pdp_address[0].u.sin6.sin6_addr,
+ pdp_addr->ietf.v6,
+ sizeof(pdp_addr->ietf.v6));
+ return 0;
+ case PDP_TYPE_N_IETF_IPv4v6:
+ if (data_len == 0)
+ return 0; /* empty, marked as AF_UNSET. */
+ if (data_len != sizeof(pdp_addr->ietf.v4v6))
+ return -GMM_CAUSE_PROTO_ERR_UNSPEC;
+ pdp_info->pdp_address[0].u.sa.sa_family = AF_INET;
+ pdp_info->pdp_address[0].u.sin.sin_addr.s_addr = pdp_addr->ietf.v4v6.v4;
+ pdp_info->pdp_address[1].u.sa.sa_family = AF_INET6;
+ memcpy(&pdp_info->pdp_address[1].u.sin6.sin6_addr,
+ pdp_addr->ietf.v4v6.v6,
+ sizeof(pdp_addr->ietf.v4v6.v6));
+ return 0;
+ default:
+ /* reserved, both pdp_info->pdp_address are preinitialied to AF_UNSET. */
+ return 0;
+ }
+}
+
static int decode_pdp_info(uint8_t *data, size_t data_len,
struct osmo_gsup_pdp_info *pdp_info)
{
@@ -145,9 +205,9 @@ static int decode_pdp_info(uint8_t *data, size_t data_len,
pdp_info->context_id = osmo_decode_big_endian(value, value_len);
break;
- case OSMO_GSUP_PDP_TYPE_IE:
- pdp_info->pdp_type =
- osmo_decode_big_endian(value, value_len) & 0x0fff;
+ case OSMO_GSUP_PDP_ADDRESS_IE:
+ if ((rc = decode_pdp_address(value, value_len, pdp_info)) < 0)
+ return rc;
break;
case OSMO_GSUP_ACCESS_POINT_NAME_IE:
@@ -262,7 +322,7 @@ static int decode_auth_info(uint8_t *data, size_t data_len,
parse_error:
LOGP(DLGSUP, LOGL_ERROR,
- "GSUP IE type %d, length %zu invalid in PDP info\n", iei, value_len);
+ "GSUP IE type %d, length %zu invalid in auth info\n", iei, value_len);
return -1;
}
@@ -353,7 +413,7 @@ int osmo_gsup_decode(const uint8_t *const_data, size_t data_len,
switch (iei) {
case OSMO_GSUP_IMSI_IE:
- case OSMO_GSUP_PDP_TYPE_IE:
+ case OSMO_GSUP_PDP_ADDRESS_IE:
case OSMO_GSUP_ACCESS_POINT_NAME_IE:
case OSMO_GSUP_SRES_IE:
case OSMO_GSUP_KC_IE:
@@ -446,6 +506,11 @@ int osmo_gsup_decode(const uint8_t *const_data, size_t data_len,
gsup_msg->rand = value;
break;
+ case OSMO_GSUP_PCO_IE:
+ gsup_msg->pco = value;
+ gsup_msg->pco_len = value_len;
+ break;
+
case OSMO_GSUP_MSISDN_IE:
gsup_msg->msisdn_enc = value;
gsup_msg->msisdn_enc_len = value_len;
@@ -569,6 +634,11 @@ int osmo_gsup_decode(const uint8_t *const_data, size_t data_len,
gsup_msg->cause_sm = value[0];
break;
+ case OSMO_GSUP_NUM_VECTORS_REQ_IE:
+ if (gsup_msg->message_type == OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST)
+ gsup_msg->num_auth_vectors = value[0];
+ break;
+
default:
LOGP(DLGSUP, LOGL_NOTICE,
"GSUP IE type %d unknown\n", iei);
@@ -592,11 +662,45 @@ static void encode_pdp_info(struct msgb *msg, enum osmo_gsup_iei iei,
u8 = pdp_info->context_id;
msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE, sizeof(u8), &u8);
- if (pdp_info->pdp_type) {
- msgb_tlv_put(msg, OSMO_GSUP_PDP_TYPE_IE,
- OSMO_GSUP_PDP_TYPE_SIZE,
- osmo_encode_big_endian(pdp_info->pdp_type | 0xf000,
- OSMO_GSUP_PDP_TYPE_SIZE));
+ if (pdp_info->pdp_type_org == PDP_TYPE_ORG_IETF) {
+ struct gsm48_pdp_address pdp_addr;
+ uint8_t pdp_addr_len = 2;
+ pdp_addr.spare = 0x0f;
+ pdp_addr.organization = pdp_info->pdp_type_org;
+ pdp_addr.type = pdp_info->pdp_type_nr;
+
+ switch (pdp_info->pdp_type_nr) {
+ case PDP_TYPE_N_IETF_IPv4:
+ if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET) {
+ pdp_addr.ietf.v4 = pdp_info->pdp_address[0].u.sin.sin_addr.s_addr;
+ pdp_addr_len += sizeof(pdp_addr.ietf.v4);
+ }
+ break;
+ case PDP_TYPE_N_IETF_IPv6:
+ if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET6) {
+ memcpy(pdp_addr.ietf.v6,
+ &pdp_info->pdp_address[0].u.sin6.sin6_addr,
+ sizeof(pdp_addr.ietf.v6));
+ pdp_addr_len += sizeof(pdp_addr.ietf.v6);
+ }
+ break;
+ case PDP_TYPE_N_IETF_IPv4v6:
+ if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET) {
+ pdp_addr.ietf.v4v6.v4 = pdp_info->pdp_address[0].u.sin.sin_addr.s_addr;
+ pdp_addr_len += sizeof(pdp_addr.ietf.v4v6.v4);
+ }
+ if (pdp_info->pdp_address[0].u.sa.sa_family == AF_INET6) {
+ memcpy(pdp_addr.ietf.v4v6.v6,
+ &pdp_info->pdp_address[1].u.sin6.sin6_addr,
+ sizeof(pdp_addr.ietf.v4v6.v6));
+ pdp_addr_len += sizeof(pdp_addr.ietf.v4v6.v6);
+ }
+ break;
+ }
+
+ msgb_tlv_put(msg, OSMO_GSUP_PDP_ADDRESS_IE,
+ pdp_addr_len,
+ (const uint8_t *)&pdp_addr);
}
if (pdp_info->apn_enc) {
@@ -753,12 +857,18 @@ int osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg)
}
}
- for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) {
- const struct osmo_auth_vector *auth_vector;
+ if (gsup_msg->message_type == OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST) {
+ uint8_t num = gsup_msg->num_auth_vectors;
+ if (num != 0)
+ msgb_tlv_put(msg, OSMO_GSUP_NUM_VECTORS_REQ_IE, 1, &num);
+ } else {
+ for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) {
+ const struct osmo_auth_vector *auth_vector;
- auth_vector = &gsup_msg->auth_vectors[idx];
+ auth_vector = &gsup_msg->auth_vectors[idx];
- encode_auth_info(msg, OSMO_GSUP_AUTH_TUPLE_IE, auth_vector);
+ encode_auth_info(msg, OSMO_GSUP_AUTH_TUPLE_IE, auth_vector);
+ }
}
if (gsup_msg->auts)
@@ -767,6 +877,11 @@ int osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg)
if (gsup_msg->rand)
msgb_tlv_put(msg, OSMO_GSUP_RAND_IE, 16, gsup_msg->rand);
+ if (gsup_msg->pco && gsup_msg->pco_len > 0) {
+ if (gsup_msg->pco_len > OSMO_GSUP_MAX_PCO_LEN)
+ return -EINVAL;
+ msgb_tlv_put(msg, OSMO_GSUP_PCO_IE, gsup_msg->pco_len, gsup_msg->pco);
+ }
if (gsup_msg->cn_domain) {
uint8_t dn = gsup_msg->cn_domain;
msgb_tlv_put(msg, OSMO_GSUP_CN_DOMAIN_IE, 1, &dn);
@@ -900,6 +1015,7 @@ const struct value_string osmo_gsup_message_class_names[] = {
{ OSMO_GSUP_MESSAGE_CLASS_SMS, "SMS" },
{ OSMO_GSUP_MESSAGE_CLASS_USSD, "USSD" },
{ OSMO_GSUP_MESSAGE_CLASS_INTER_MSC, "Inter-MSC" },
+ { OSMO_GSUP_MESSAGE_CLASS_IPSEC_EPDG, "IPsec-ePDG" },
{}
};
diff --git a/src/gsm/ipa.c b/src/gsm/ipa.c
index 1563d0a3..6e41fd98 100644
--- a/src/gsm/ipa.c
+++ b/src/gsm/ipa.c
@@ -121,22 +121,25 @@ int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len,
memset(dec, 0, sizeof(*dec));
+ LOGP(DLMI, LOGL_DEBUG, "Rx IPA CCM ID_GET: ");
while (len >= 2) {
len -= 2;
t_len = *cur++;
t_tag = *cur++;
if (t_len < len_offset) {
+ LOGPC(DLMI, LOGL_DEBUG, "\n");
LOGP(DLMI, LOGL_ERROR, "minimal offset not included: %d < %d\n", t_len, len_offset);
return -EINVAL;
}
if (t_len > len + 1) {
+ LOGPC(DLMI, LOGL_DEBUG, "\n");
LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
return -EINVAL;
}
- DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
+ LOGPC(DLMI, LOGL_DEBUG, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
dec->lv[t_tag].len = t_len - len_offset;
dec->lv[t_tag].val = cur;
@@ -144,6 +147,7 @@ int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len,
cur += t_len - len_offset;
len -= t_len - len_offset;
}
+ LOGPC(DLMI, LOGL_DEBUG, "\n");
return 0;
}
@@ -164,17 +168,19 @@ int ipa_ccm_id_get_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned in
memset(dec, 0, sizeof(*dec));
+ LOGP(DLMI, LOGL_DEBUG, "Rx IPA CCM ID_GET: ");
while (len >= 2) {
len -= 2;
t_len = *cur++;
t_tag = *cur++;
if (t_len > len + 1) {
+ LOGPC(DLMI, LOGL_DEBUG, "\n");
LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
return -EINVAL;
}
- DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
+ LOGPC(DLMI, LOGL_DEBUG, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
dec->lv[t_tag].len = t_len-1;
dec->lv[t_tag].val = cur;
@@ -182,6 +188,7 @@ int ipa_ccm_id_get_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned in
cur += t_len-1;
len -= t_len-1;
}
+ LOGPC(DLMI, LOGL_DEBUG, "\n");
return 0;
}
@@ -202,6 +209,7 @@ int ipa_ccm_id_resp_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned i
memset(dec, 0, sizeof(*dec));
+ LOGP(DLMI, LOGL_DEBUG, "Rx IPA CCM ID_RESP: ");
while (len >= 3) {
len -= 3;
t_len = osmo_load16be(cur);
@@ -209,6 +217,7 @@ int ipa_ccm_id_resp_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned i
t_tag = *cur++;
if (t_len > len + 1) {
+ LOGPC(DLMI, LOGL_DEBUG, "\n");
LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d > %d\n", t_len, len + 1);
return -EINVAL;
}
@@ -221,6 +230,7 @@ int ipa_ccm_id_resp_parse(struct tlv_parsed *dec, const uint8_t *buf, unsigned i
cur += t_len-1;
len -= t_len-1;
}
+ LOGPC(DLMI, LOGL_DEBUG, "\n");
return 0;
}
@@ -257,34 +267,43 @@ int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
return 0;
}
+/*! Fill ud struct from tp structure.
+ * \param[in,out] ud ipaccess_unit to fill
+ * \param[in] tp the decoded TLV structure from eg. ID_RESP message
+ * \returns zero on success, negative on error
+ *
+ * This function expects parameter ud's fields to be initialized to zero if not yet set.
+ * Existing incoming string pointer fields are expected to be allocated using
+ * talloc and will be deallocated as such if replaced with the content of tp.
+ **/
int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud,
const struct tlv_parsed *tp)
{
int rc = 0;
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
- ud->serno = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_SERNR));
+ osmo_talloc_replace_string(ud, &ud->serno,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_SERNR));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
- ud->unit_name = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
+ osmo_talloc_replace_string(ud, &ud->unit_name,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
- ud->location1 = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
+ osmo_talloc_replace_string(ud, &ud->location1,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
- ud->location2 = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
+ osmo_talloc_replace_string(ud, &ud->location2,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
- ud->equipvers = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
+ osmo_talloc_replace_string(ud, &ud->equipvers,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
- ud->swversion = talloc_strdup(ud, (char *)
- TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
+ osmo_talloc_replace_string(ud, &ud->swversion,
+ (char *)TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
rc = osmo_macaddr_parse(ud->mac_addr, (char *)
@@ -378,7 +397,7 @@ struct msgb *ipa_ccm_make_id_resp(const struct ipaccess_unit *dev,
tag = msgb_put(msg, 3 + strlen(str) + 1);
tag[0] = 0x00;
tag[1] = 1 + strlen(str) + 1;
- tag[2] = ies_req[1];
+ tag[2] = ies_req[i];
memcpy(tag + 3, str, strlen(str) + 1);
}
ipa_prepend_header(msg, IPAC_PROTO_IPACCESS);
@@ -402,10 +421,14 @@ struct msgb *ipa_ccm_make_id_resp_from_req(const struct ipaccess_unit *dev,
/* build a array of the IEIs */
while (len >= 2) {
uint8_t t_len, t_tag;
- len -= 2;
+ len -= 2; /* subtract the length of the two bytes read below */
t_len = *cur++;
t_tag = *cur++;
+ /* as the 'tag' is included in the length of t_len, this cannot happen */
+ if (t_len == 0)
+ break;
+
if (t_len > len + 1) {
LOGP(DLINP, LOGL_ERROR, "IPA CCM tag 0x%02x does not fit\n", t_tag);
break;
@@ -413,13 +436,14 @@ struct msgb *ipa_ccm_make_id_resp_from_req(const struct ipaccess_unit *dev,
ies[num_ies++] = t_tag;
- cur += t_len;
+ /* we need to subtract one from t_len to account for the tag */
+ cur += t_len - 1;
/* prevent any unsigned integer underflow due to somebody sending us
* messages with wrong length values */
if (len <= t_len)
- len -= t_len;
- else
len = 0;
+ else
+ len -= t_len - 1;
}
return ipa_ccm_make_id_resp(dev, ies, num_ies);
}
@@ -463,7 +487,7 @@ int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
case IPAC_MSGT_PING:
ret = ipa_ccm_send_pong(bfd->fd);
if (ret < 0) {
- LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
+ LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
"message. Reason: %s\n", strerror(errno));
break;
}
diff --git a/src/gsm/iuup.c b/src/gsm/iuup.c
new file mode 100644
index 00000000..16a6f5e0
--- /dev/null
+++ b/src/gsm/iuup.c
@@ -0,0 +1,1064 @@
+/*! \file iu_up.c
+ * IuUP (Iu User Plane) according to 3GPP TS 25.415 */
+/*
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+
+#include <osmocom/core/crc8gen.h>
+#include <osmocom/core/crc16gen.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gsm/protocol/gsm_25_415.h>
+#include <osmocom/gsm/iuup.h>
+
+/***********************************************************************
+ * CRC Calculation
+ ***********************************************************************/
+
+/* Section 6.6.3.8 Header CRC */
+const struct osmo_crc8gen_code iuup_hdr_crc_code = {
+ .bits = 6,
+ .poly = 47,
+ .init = 0,
+ .remainder = 0,
+};
+
+/* Section 6.6.3.9 Payload CRC */
+const struct osmo_crc16gen_code iuup_data_crc_code = {
+ .bits = 10,
+ .poly = 563,
+ .init = 0,
+ .remainder = 0,
+};
+
+static int iuup_get_payload_offset(const uint8_t *iuup_pdu)
+{
+ uint8_t pdu_type = iuup_pdu[0] >> 4;
+ switch (pdu_type) {
+ case 0:
+ case 14:
+ return 4;
+ case 1:
+ return 3;
+ default:
+ return -1;
+ }
+}
+
+int osmo_iuup_compute_payload_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
+{
+ ubit_t buf[1024*8];
+ uint8_t pdu_type;
+ int offset, payload_len_bytes;
+
+ if (pdu_len < 1)
+ return -1;
+
+ pdu_type = iuup_pdu[0] >> 4;
+
+ /* Type 1 has no CRC */
+ if (pdu_type == 1)
+ return 0;
+
+ offset = iuup_get_payload_offset(iuup_pdu);
+ if (offset < 0)
+ return offset;
+
+ if (pdu_len < offset)
+ return -1;
+
+ payload_len_bytes = pdu_len - offset;
+ osmo_pbit2ubit(buf, iuup_pdu+offset, payload_len_bytes*8);
+ return osmo_crc16gen_compute_bits(&iuup_data_crc_code, buf, payload_len_bytes*8);
+}
+
+int osmo_iuup_compute_header_crc(const uint8_t *iuup_pdu, unsigned int pdu_len)
+{
+ ubit_t buf[2*8];
+
+ if (pdu_len < 2)
+ return -1;
+
+ osmo_pbit2ubit(buf, iuup_pdu, 2*8);
+ return osmo_crc8gen_compute_bits(&iuup_hdr_crc_code, buf, 2*8);
+}
+
+/***********************************************************************
+ * Internal State / FSM (Annex B)
+ ***********************************************************************/
+
+#define S(x) (1 << (x))
+
+#define IUUP_TIMER_INIT 1
+#define IUUP_TIMER_TA 2
+#define IUUP_TIMER_RC 3
+
+struct osmo_timer_nt {
+ uint32_t n; /* number of repetitions */
+ struct osmo_iuup_tnl_prim *retrans_itp;
+ struct osmo_timer_list timer;
+};
+
+struct osmo_iuup_instance {
+ struct osmo_iuup_rnl_config config;
+ struct osmo_fsm_inst *fi;
+
+ uint8_t mode_version;
+
+ struct {
+ struct osmo_timer_nt init;
+ struct osmo_timer_nt ta;
+ struct osmo_timer_nt rc;
+ } timer;
+ /* call-back function to pass primitives up to the user */
+ osmo_prim_cb user_prim_cb;
+ void *user_prim_priv;
+ osmo_prim_cb transport_prim_cb;
+ void *transport_prim_priv;
+ uint8_t type14_fn; /* 2 bits */
+};
+
+enum iuup_fsm_state {
+ IUUP_FSM_ST_NULL,
+ IUUP_FSM_ST_INIT,
+ IUUP_FSM_ST_TrM_DATA_XFER_READY,
+ IUUP_FSM_ST_SMpSDU_DATA_XFER_READY,
+};
+
+enum iuup_fsm_event {
+ IUUP_FSM_EVT_IUUP_CONFIG_REQ,
+ IUUP_FSM_EVT_IUUP_DATA_REQ,
+ IUUP_FSM_EVT_IUUP_DATA_IND,
+ IUUP_FSM_EVT_IUUP_STATUS_REQ,
+ IUUP_FSM_EVT_IUUP_STATUS_IND,
+ IUUP_FSM_EVT_SSASAR_UNITDATA_REQ,
+ IUUP_FSM_EVT_SSASAR_UNITDATA_IND,
+ IUUP_FSM_EVT_IUUP_UNITDATA_REQ,
+ IUUP_FSM_EVT_IUUP_UNITDATA_IND,
+ IUUP_FSM_EVT_INIT,
+ IUUP_FSM_EVT_LAST_INIT_ACK,
+ IUUP_FSM_EVT_INIT_NACK,
+};
+
+static const struct value_string iuup_fsm_event_names[] = {
+ { IUUP_FSM_EVT_IUUP_CONFIG_REQ, "IuUP-CONFIG-req" },
+ { IUUP_FSM_EVT_IUUP_DATA_REQ, "IuUP-DATA-req" },
+ { IUUP_FSM_EVT_IUUP_DATA_IND, "IuUP-DATA-ind" },
+ { IUUP_FSM_EVT_IUUP_STATUS_REQ, "IuUP-STATUS-req" },
+ { IUUP_FSM_EVT_IUUP_STATUS_IND, "IuUP-STATUS-ind" },
+ { IUUP_FSM_EVT_SSASAR_UNITDATA_REQ, "SSSAR-UNITDATA-req" },
+ { IUUP_FSM_EVT_SSASAR_UNITDATA_IND, "SSSAR-UNITDATA-ind" },
+ { IUUP_FSM_EVT_IUUP_UNITDATA_REQ, "IuUP-UNITDATA-req" },
+ { IUUP_FSM_EVT_IUUP_UNITDATA_IND, "IuUP-UNITDATA-ind" },
+ { IUUP_FSM_EVT_INIT, "INIT" },
+ { IUUP_FSM_EVT_LAST_INIT_ACK, "LAST_INIT_ACK" },
+ { IUUP_FSM_EVT_INIT_NACK, "INIT_NACK" },
+ { 0, NULL }
+};
+
+static inline uint8_t iuup_get_pdu_type(const uint8_t *data)
+{
+ return data[0] >> 4;
+}
+
+static inline uint8_t iuup_get_hdr_crc(const uint8_t *data)
+{
+ return data[2] >> 2;
+}
+
+/* Helper functions to store non-packed structs in msgb so that pointers are properly aligned: */
+#define IUUP_MSGB_SIZE 4096
+#define PTR_ALIGNMENT_BYTES 8
+#define IUUP_MSGB_HEADROOM_MIN_REQUIRED (OSMO_MAX(sizeof(struct osmo_iuup_tnl_prim), sizeof(struct osmo_iuup_rnl_prim)) + (PTR_ALIGNMENT_BYTES - 1))
+static inline struct msgb *osmo_iuup_msgb_alloc_c(void *ctx, size_t size)
+{
+ OSMO_ASSERT(size > IUUP_MSGB_HEADROOM_MIN_REQUIRED);
+ return msgb_alloc_headroom_c(ctx, size, IUUP_MSGB_HEADROOM_MIN_REQUIRED, "iuup-msgb");
+}
+
+/* push data so that the resulting pointer to write to is aligned to 8 byte */
+static inline __attribute__((assume_aligned(PTR_ALIGNMENT_BYTES)))
+unsigned char *aligned_msgb_push(struct msgb *msg, unsigned int len)
+{
+ uint8_t *ptr = (msgb_data(msg) - len);
+ size_t extra_size = ((uintptr_t)ptr & (PTR_ALIGNMENT_BYTES - 1));
+
+ return msgb_push(msg, len + extra_size);
+}
+
+struct osmo_iuup_rnl_prim *osmo_iuup_rnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size)
+{
+ struct msgb *msg;
+ struct osmo_iuup_rnl_prim *irp;
+
+ msg = osmo_iuup_msgb_alloc_c(ctx, size);
+ irp = (struct osmo_iuup_rnl_prim *)aligned_msgb_push(msg, sizeof(*irp));
+ osmo_prim_init(&irp->oph, SAP_IUUP_RNL, primitive, operation, msg);
+ return irp;
+}
+
+struct osmo_iuup_tnl_prim *osmo_iuup_tnl_prim_alloc(void *ctx, unsigned int primitive, unsigned int operation, unsigned int size)
+{
+ struct msgb *msg;
+ struct osmo_iuup_tnl_prim *itp;
+
+ msg = osmo_iuup_msgb_alloc_c(ctx, size);
+ itp = (struct osmo_iuup_tnl_prim *) aligned_msgb_push(msg, sizeof(*itp));
+ osmo_prim_init(&itp->oph, SAP_IUUP_TNL, primitive, operation, msg);
+ return itp;
+}
+
+/* 6.6.2.3.2 */
+static struct osmo_iuup_tnl_prim *itp_ctrl_ack_alloc(struct osmo_iuup_instance *iui, enum iuup_procedure proc_ind, uint8_t fn)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ struct iuup_ctrl_ack *ack;
+ itp = osmo_iuup_tnl_prim_alloc(iui, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ itp->oph.msg->l2h = msgb_put(itp->oph.msg, sizeof(struct iuup_ctrl_ack));
+ ack = (struct iuup_ctrl_ack *) msgb_l2(itp->oph.msg);
+ *ack = (struct iuup_ctrl_ack){
+ .hdr = {
+ .frame_nr = fn,
+ .ack_nack = IUUP_AN_ACK,
+ .pdu_type = IUUP_PDU_T_CONTROL,
+ .proc_ind = proc_ind,
+ .mode_version = iui->mode_version,
+ .payload_crc_hi = 0,
+ .header_crc = 0,
+ .payload_crc_lo = 0,
+ },
+ };
+ ack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(itp->oph.msg), msgb_l2len(itp->oph.msg));
+ return itp;
+}
+
+/* 6.6.2.3.3 */
+static struct osmo_iuup_tnl_prim *tnp_ctrl_nack_alloc(struct osmo_iuup_instance *iui, enum iuup_procedure proc_ind, enum iuup_error_cause error_cause, uint8_t fn)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ struct iuup_ctrl_nack *nack;
+ itp = osmo_iuup_tnl_prim_alloc(iui, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ itp->oph.msg->l2h = msgb_put(itp->oph.msg, sizeof(struct iuup_ctrl_nack));
+ nack = (struct iuup_ctrl_nack *) msgb_l2(itp->oph.msg);
+ *nack = (struct iuup_ctrl_nack){
+ .hdr = {
+ .frame_nr = fn,
+ .ack_nack = IUUP_AN_NACK,
+ .pdu_type = IUUP_PDU_T_CONTROL,
+ .proc_ind = proc_ind,
+ .mode_version = iui->mode_version,
+ .payload_crc_hi = 0,
+ .header_crc = 0,
+ .payload_crc_lo = 0,
+ },
+ .spare = 0,
+ .error_cause = error_cause,
+ };
+ nack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(itp->oph.msg), msgb_l2len(itp->oph.msg));
+ return itp;
+}
+
+/* 6.6.2.3.4.1 */
+static struct osmo_iuup_tnl_prim *tnp_ctrl_init_alloc(struct osmo_iuup_instance *iui)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ struct iuup_pdutype14_hdr *hdr;
+ struct iuup_ctrl_init_hdr *ihdr;
+ struct iuup_ctrl_init_rfci_hdr *ihdr_rfci;
+ struct iuup_ctrl_init_tail *itail;
+ unsigned int i, j;
+ uint16_t payload_crc;
+ uint8_t rfci_cnt;
+ struct msgb *msg;
+
+ itp = osmo_iuup_tnl_prim_alloc(iui, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ msg = itp->oph.msg;
+
+ msg->l2h = msgb_put(msg, sizeof(*hdr));
+ hdr = (struct iuup_pdutype14_hdr *)msgb_l2(msg);
+ hdr->frame_nr = iui->type14_fn++;
+ hdr->ack_nack = IUUP_AN_PROCEDURE;
+ hdr->pdu_type = IUUP_PDU_T_CONTROL;
+ hdr->proc_ind = IUUP_PROC_INIT;
+ hdr->mode_version = 0; /* Use here the minimum version required to negotiate */
+ hdr->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
+
+ ihdr = (struct iuup_ctrl_init_hdr *)msgb_put(msg, sizeof(*ihdr));
+ ihdr->chain_ind = 0; /* this frame is the last frame for the procedure. TODO: support several */
+ ihdr->num_subflows_per_rfci = iui->config.num_subflows;
+ ihdr->ti = iui->config.IPTIs_present ? 1 : 0;
+ ihdr->spare = 0;
+
+ /* RFCI + subflow size part: */
+ rfci_cnt = 0;
+ for (i = 0; i < ARRAY_SIZE(iui->config.rfci); i++) {
+ bool last;
+ uint8_t len_size;
+ struct osmo_iuup_rfci *rfci = &iui->config.rfci[i];
+ if (!rfci->used)
+ continue;
+ rfci_cnt++;
+ last = (rfci_cnt == iui->config.num_rfci);
+
+ len_size = 1;
+ for (j = 0; j < iui->config.num_subflows; j++) {
+ if (rfci->subflow_sizes[j] > UINT8_MAX)
+ len_size = 2;
+ }
+
+ ihdr_rfci = (struct iuup_ctrl_init_rfci_hdr *)msgb_put(msg, sizeof(*ihdr_rfci) + len_size * iui->config.num_subflows);
+ ihdr_rfci->rfci = rfci->id;
+ ihdr_rfci->li = len_size - 1;
+ ihdr_rfci->lri = last;
+ if (len_size == 2) {
+ uint16_t *buf = (uint16_t *)&ihdr_rfci->subflow_length[0];
+ for (j = 0; j < iui->config.num_subflows; j++)
+ osmo_store16be(rfci->subflow_sizes[j], buf++);
+ } else {
+ for (j = 0; j < iui->config.num_subflows; j++)
+ ihdr_rfci->subflow_length[j] = rfci->subflow_sizes[j];
+ }
+ /* early loop termination: */
+ if (last)
+ break;
+ }
+ /* Sanity check: */
+ if (rfci_cnt != iui->config.num_rfci) {
+ LOGP(DLIUUP, LOGL_ERROR, "rfci_cnt %u != num_rfci %u\n",
+ rfci_cnt, iui->config.num_rfci);
+ msgb_free(msg);
+ return NULL;
+ }
+
+ if (iui->config.IPTIs_present) {
+ uint8_t num_bytes = (iui->config.num_rfci + 1) / 2;
+ uint8_t *buf = msgb_put(msg, num_bytes);
+ rfci_cnt = 0;
+ for (i = 0; i < ARRAY_SIZE(iui->config.rfci); i++) {
+ struct osmo_iuup_rfci *rfci = &iui->config.rfci[i];
+ if (!rfci->used)
+ continue;
+ if (!(rfci_cnt & 0x01)) /* is even: */
+ buf[rfci_cnt / 2] = (((uint8_t)rfci->IPTI) << 4);
+ else
+ buf[rfci_cnt / 2] |= (rfci->IPTI & 0x0F);
+ rfci_cnt++;
+ /* early loop termination: */
+ if (rfci_cnt == iui->config.num_rfci)
+ break;
+ }
+ }
+
+ itail = (struct iuup_ctrl_init_tail *)msgb_put(msg, sizeof(*itail));
+ osmo_store16be(iui->config.supported_versions_mask, &itail->versions_supported);
+ itail->spare = 0;
+ itail->data_pdu_type = iui->config.data_pdu_type;
+
+ payload_crc = osmo_iuup_compute_payload_crc(msgb_l2(msg), msgb_l2len(msg));
+ hdr->payload_crc_hi = (payload_crc >> 8) & 0x03;
+ hdr->payload_crc_lo = payload_crc & 0xff;
+
+
+ return itp;
+}
+
+static struct osmo_iuup_rnl_prim *irp_init_ind_alloc(struct osmo_iuup_instance *iui)
+{
+ struct osmo_iuup_rnl_prim *irp;
+
+ irp = osmo_iuup_rnl_prim_alloc(iui, OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ irp->u.status.procedure = IUUP_PROC_INIT;
+ irp->u.status.u.initialization.mode_version = iui->mode_version;
+ irp->u.status.u.initialization.data_pdu_type = iui->config.data_pdu_type;
+ irp->u.status.u.initialization.num_subflows = iui->config.num_subflows;
+ irp->u.status.u.initialization.num_rfci = iui->config.num_rfci;
+ irp->u.status.u.initialization.IPTIs_present = iui->config.IPTIs_present;
+ memcpy(irp->u.status.u.initialization.rfci, iui->config.rfci, sizeof(iui->config.rfci));
+ return irp;
+}
+
+/* transform a RNL data primitive into a TNL data primitive (down the stack) */
+static struct osmo_iuup_tnl_prim *rnl_to_tnl_data(struct osmo_iuup_instance *iui,
+ struct osmo_iuup_rnl_prim *irp)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ struct osmo_iuup_rnl_data dt;
+ struct msgb *msg;
+ uint16_t payload_crc;
+ struct iuup_pdutype0_hdr *h0;
+ struct iuup_pdutype1_hdr *h1;
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST));
+
+ msg = irp->oph.msg;
+ dt = irp->u.data;
+
+ /* pull up to the IuUP payload and push a new primitive header in front */
+ msgb_pull_to_l3(msg);
+
+ /* push the PDU TYPE 0 / 1 header in front of the payload */
+ switch (iui->config.data_pdu_type) {
+ case 0:
+ msg->l2h = msgb_push(msg, sizeof(*h0));
+ h0 = (struct iuup_pdutype0_hdr *)msg->l2h;
+ h0->frame_nr = dt.frame_nr;
+ h0->pdu_type = IUUP_PDU_T_DATA_CRC;
+ h0->rfci = dt.rfci;
+ h0->fqc = dt.fqc;
+ h0->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
+ payload_crc = osmo_iuup_compute_payload_crc(msgb_l2(msg), msgb_l2len(msg));
+ h0->payload_crc_hi = (payload_crc >> 8) & 0x03;
+ h0->payload_crc_lo = payload_crc & 0xff;
+ break;
+ case 1:
+ msg->l2h = msgb_push(msg, sizeof(*h1));
+ h1 = (struct iuup_pdutype1_hdr *)msg->l2h;
+ h1->frame_nr = dt.frame_nr;
+ h1->pdu_type = IUUP_PDU_T_DATA_NOCRC;
+ h1->rfci = dt.rfci;
+ h1->fqc = dt.fqc;
+ h1->header_crc = osmo_iuup_compute_header_crc(msgb_l2(msg), msgb_l2len(msg));
+ h1->spare = 0;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ /* Avoid allocating irp out of 8byte-aligned address, Asan is not happy with it */
+ itp = (struct osmo_iuup_tnl_prim *) aligned_msgb_push(msg, sizeof(*itp));
+ osmo_prim_init(&itp->oph, SAP_IUUP_TNL, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST, msg);
+
+ return itp;
+}
+
+/* transform a TNL primitive into a RNL primitive (up the stack) */
+static struct osmo_iuup_rnl_prim *tnl_to_rnl_data(struct osmo_iuup_tnl_prim *itp)
+{
+ struct msgb *msg;
+ struct iuup_pdutype0_hdr *h0;
+ struct iuup_pdutype1_hdr *h1;
+ struct osmo_iuup_rnl_data dt;
+ struct osmo_iuup_rnl_prim *irp;
+
+ msg = itp->oph.msg;
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION));
+
+ switch (iuup_get_pdu_type(msgb_l2(msg))) {
+ case IUUP_PDU_T_DATA_CRC:
+ h0 = (struct iuup_pdutype0_hdr *) msgb_l2(msg);
+ dt.rfci = h0->rfci;
+ dt.frame_nr = h0->frame_nr;
+ dt.fqc = h0->fqc;
+ break;
+ case IUUP_PDU_T_DATA_NOCRC:
+ h1 = (struct iuup_pdutype1_hdr *) msgb_l2(msg);
+ dt.rfci = h1->rfci;
+ dt.frame_nr = h1->frame_nr;
+ dt.fqc = h1->fqc;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ /* pull up to the IuUP payload and push a new primitive header in front */
+ msgb_pull_to_l3(msg);
+
+ /* Avoid allocating irp out of 8byte-aligned address, Asan is not happy with it */
+ irp = (struct osmo_iuup_rnl_prim *) aligned_msgb_push(msg, sizeof(*irp));
+ osmo_prim_init(&irp->oph, SAP_IUUP_RNL, OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION, msg);
+ irp->u.data = dt;
+
+ return irp;
+}
+
+static struct osmo_iuup_rnl_prim *irp_error_event_alloc_c(void *ctx, enum iuup_error_cause cause, enum iuup_error_distance distance)
+{
+ struct osmo_iuup_rnl_prim *irp;
+ struct msgb *msg;
+ msg = msgb_alloc_c(ctx, sizeof(*irp), "iuup-tx");
+ irp = (struct osmo_iuup_rnl_prim *) msgb_put(msg, sizeof(*irp));
+ osmo_prim_init(&irp->oph, SAP_IUUP_RNL, OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION, msg);
+ irp->u.status.procedure = IUUP_PROC_ERR_EVENT;
+ irp->u.status.u.error_event.cause = cause;
+ irp->u.status.u.error_event.distance = distance;
+ return irp;
+}
+
+static struct osmo_iuup_tnl_prim *itp_copy_c(void *ctx, const struct osmo_iuup_tnl_prim *src_itp)
+{
+ struct msgb *msg;
+ struct osmo_iuup_tnl_prim *dst_itp;
+
+ msg = msgb_copy_c(ctx, src_itp->oph.msg, "iuup-tx-retrans");
+ dst_itp = (struct osmo_iuup_tnl_prim *)msgb_data(msg);
+ dst_itp->oph.msg = msg;
+ return dst_itp;
+}
+
+static void retransmit_initialization(struct osmo_iuup_instance *iui)
+{
+ struct osmo_iuup_tnl_prim *itp;
+ iui->fi->T = IUUP_TIMER_INIT;
+ osmo_timer_schedule(&iui->fi->timer, iui->config.t_init.t_ms / 1000, (iui->config.t_init.t_ms % 1000) * 1000);
+ itp = itp_copy_c(iui, iui->timer.init.retrans_itp);
+ iui->transport_prim_cb(&itp->oph, iui->transport_prim_priv);
+}
+
+/* return: whether the last Init was Acked correctly and hence can transition to next state */
+static bool iuup_rx_initialization(struct osmo_iuup_instance *iui, struct osmo_iuup_tnl_prim *itp)
+{
+ struct iuup_pdutype14_hdr *hdr;
+ struct iuup_ctrl_init_hdr *ihdr;
+ struct iuup_ctrl_init_rfci_hdr *ihdr_rfci;
+ struct iuup_ctrl_init_tail *itail;
+ enum iuup_error_cause err_cause;
+ uint8_t num_rfci = 0;
+ int i;
+ bool is_last;
+ uint16_t remote_mask, match_mask;
+ struct osmo_iuup_rnl_prim *irp;
+ struct osmo_iuup_tnl_prim *resp;
+
+ /* TODO: whenever we check message boundaries, length, etc. and we fail, send NACK */
+
+ hdr = (struct iuup_pdutype14_hdr *)msgb_l2(itp->oph.msg);
+ ihdr = (struct iuup_ctrl_init_hdr *)hdr->payload;
+ if (ihdr->num_subflows_per_rfci == 0) {
+ LOGPFSML(iui->fi, LOGL_NOTICE, "Initialization: Unexpected num_subflows=0 received\n");
+ err_cause = IUUP_ERR_CAUSE_UNEXPECTED_VALUE;
+ goto send_nack;
+ }
+ ihdr_rfci = (struct iuup_ctrl_init_rfci_hdr *)ihdr->rfci_data;
+
+ do {
+ struct osmo_iuup_rfci *rfci = &iui->config.rfci[num_rfci];
+ uint8_t l_size_bytes = ihdr_rfci->li + 1;
+ is_last = ihdr_rfci->lri;
+ if (num_rfci >= IUUP_MAX_RFCIS) {
+ LOGPFSML(iui->fi, LOGL_NOTICE, "Initialization: Too many RFCIs received (%u)\n",
+ num_rfci);
+ err_cause = IUUP_ERR_CAUSE_UNEXPECTED_RFCI;
+ goto send_nack;
+ }
+ rfci->used = 1;
+ rfci->id = ihdr_rfci->rfci;
+ if (l_size_bytes == 2) {
+ uint16_t *subflow_size = (uint16_t *)ihdr_rfci->subflow_length;
+ for (i = 0; i < ihdr->num_subflows_per_rfci; i++) {
+ rfci->subflow_sizes[i] = osmo_load16be(subflow_size);
+ subflow_size++;
+ }
+ } else {
+ uint8_t *subflow_size = ihdr_rfci->subflow_length;
+ for (i = 0; i < ihdr->num_subflows_per_rfci; i++) {
+ rfci->subflow_sizes[i] = *subflow_size;
+ subflow_size++;
+ }
+ }
+ num_rfci++;
+ ihdr_rfci++;
+ ihdr_rfci = (struct iuup_ctrl_init_rfci_hdr *)(((uint8_t *)ihdr_rfci) + ihdr->num_subflows_per_rfci * l_size_bytes);
+ } while (!is_last);
+
+ if (ihdr->ti) { /* Timing information present */
+ uint8_t *buf = (uint8_t *)ihdr_rfci;
+ uint8_t num_bytes = (num_rfci + 1) / 2;
+ iui->config.IPTIs_present = true;
+ for (i = 0; i < num_bytes - 1; i++) {
+ iui->config.rfci[i*2].IPTI = *buf >> 4;
+ iui->config.rfci[i*2 + 1].IPTI = *buf & 0x0f;
+ buf++;
+ }
+ iui->config.rfci[i*2].IPTI = *buf >> 4;
+ if (!(num_rfci & 0x01)) /* is even: */
+ iui->config.rfci[i*2 + 1].IPTI = *buf & 0x0f;
+ buf++;
+ itail = (struct iuup_ctrl_init_tail *)buf;
+ } else {
+ iui->config.IPTIs_present = false;
+ itail = (struct iuup_ctrl_init_tail *)ihdr_rfci;
+ }
+ if (itail->data_pdu_type > 1) {
+ LOGPFSML(iui->fi, LOGL_NOTICE, "Initialization: Unexpected Data PDU Type %u received\n", itail->data_pdu_type);
+ err_cause = IUUP_ERR_CAUSE_UNEXPECTED_VALUE;
+ goto send_nack;
+ }
+
+ remote_mask = osmo_load16be(&itail->versions_supported);
+ match_mask = (remote_mask & iui->config.supported_versions_mask);
+ if (match_mask == 0x0000) {
+ LOGPFSML(iui->fi, LOGL_NOTICE,
+ "Initialization: No match in supported versions local=0x%04x vs remote=0x%04x\n",
+ iui->config.supported_versions_mask, remote_mask);
+ err_cause = IUUP_ERR_CAUSE_UNEXPECTED_VALUE;
+ goto send_nack;
+ }
+ for (i = 15; i >= 0; i--) {
+ if (match_mask & (1<<i)) {
+ iui->mode_version = i;
+ break;
+ }
+ }
+
+ iui->config.num_rfci = num_rfci;
+ iui->config.num_subflows = ihdr->num_subflows_per_rfci;
+ iui->config.data_pdu_type = itail->data_pdu_type;
+
+ irp = irp_init_ind_alloc(iui);
+ iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
+
+ LOGPFSML(iui->fi, LOGL_DEBUG, "Tx Initialization ACK\n");
+ resp = itp_ctrl_ack_alloc(iui, IUUP_PROC_INIT, hdr->frame_nr);
+ iui->transport_prim_cb(&resp->oph, iui->transport_prim_priv);
+ return ihdr->chain_ind == 0;
+send_nack:
+ LOGPFSML(iui->fi, LOGL_NOTICE, "Tx Initialization NACK cause=%u orig_message=%s\n",
+ err_cause, osmo_hexdump((const unsigned char *) msgb_l2(itp->oph.msg), msgb_l2len(itp->oph.msg)));
+ resp = tnp_ctrl_nack_alloc(iui, IUUP_PROC_INIT, err_cause, hdr->frame_nr);
+ iui->transport_prim_cb(&resp->oph, iui->transport_prim_priv);
+ return false;
+}
+
+/**********************
+ * FSM STATE FUNCTIONS
+ **********************/
+static void iuup_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+ struct osmo_iuup_rnl_prim *user_prim = NULL;
+
+ switch (event) {
+ case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+ user_prim = data;
+ iui->config = user_prim->u.config;
+ iui->config.supported_versions_mask &= 0x0003; /* We only support versions 1 and 2 ourselves */
+ //TODO: if supported_versions_mask == 0x0000,no supported versions, send error to upper layers
+
+ if (iui->config.transparent)
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_TrM_DATA_XFER_READY, 0, 0);
+ else {
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_INIT, 0, 0);
+ }
+ break;
+ }
+}
+
+/* transparent mode data transfer */
+static void iuup_fsm_trm_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ //struct osmo_iuup_instance *iui = fi->priv;
+
+ switch (event) {
+ case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
+ break;
+ case IUUP_FSM_EVT_IUUP_DATA_REQ:
+ /* Data coming down from RNL (user) towards TNL (transport) */
+ break;
+ case IUUP_FSM_EVT_IUUP_DATA_IND:
+ /* Data coming up from TNL (transport) towards RNL (user) */
+ break;
+ case IUUP_FSM_EVT_IUUP_UNITDATA_REQ:
+ case IUUP_FSM_EVT_IUUP_UNITDATA_IND:
+ case IUUP_FSM_EVT_SSASAR_UNITDATA_REQ:
+ case IUUP_FSM_EVT_SSASAR_UNITDATA_IND:
+ /* no state change */
+ break;
+ }
+}
+
+static void iuup_fsm_init_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+
+ iui->type14_fn = 0;
+ if (iui->config.active) {
+ iui->timer.init.n = 0;
+ iui->timer.init.retrans_itp = tnp_ctrl_init_alloc(iui);
+ retransmit_initialization(iui);
+ }
+}
+
+static void iuup_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+ struct osmo_iuup_rnl_prim *irp;
+ struct osmo_iuup_tnl_prim *itp;
+
+ switch (event) {
+ case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+ /* the only permitted 'config req' type is the request to release the instance */
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
+ break;
+ case IUUP_FSM_EVT_INIT:
+ itp = data;
+ if (iuup_rx_initialization(iui, itp))
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_SMpSDU_DATA_XFER_READY, 0, 0);
+ break;
+ case IUUP_FSM_EVT_LAST_INIT_ACK:
+ /* last INIT ACK was received, transition to DATA_XFER_READY state */
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_SMpSDU_DATA_XFER_READY, 0, 0);
+ break;
+ case IUUP_FSM_EVT_INIT_NACK:
+ LOGPFSML(fi, LOGL_NOTICE, "Rx Initialization NACK N=%" PRIu32 "/%" PRIu32 "\n",
+ iui->timer.init.n, iui->config.t_init.n_max);
+ osmo_timer_del(&fi->timer);
+ if (iui->timer.init.n == iui->config.t_init.n_max) {
+ irp = irp_error_event_alloc_c(iui, IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK, IUUP_ERR_DIST_SECOND_FWD);
+ iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
+ return;
+ }
+ iui->timer.init.n++;
+ retransmit_initialization(iui);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+/* 3GPP TS 25.415 B.2.3 "Support Mode Data Transfer Ready State" */
+static void iuup_fsm_smpsdu_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+ struct osmo_iuup_rnl_prim *irp = NULL;
+ struct osmo_iuup_tnl_prim *itp = NULL;
+
+ switch (event) {
+ case IUUP_FSM_EVT_IUUP_CONFIG_REQ:
+ irp = data;
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_NULL, 0, 0);
+ break;
+ case IUUP_FSM_EVT_INIT:
+ /* "In case of handover or relocation, Initialisation procedures
+ * may have to be performed and Iu UP instance may have to enter
+ * the initialisation state." */
+ itp = data;
+ if (!iuup_rx_initialization(iui, itp))
+ osmo_fsm_inst_state_chg(fi, IUUP_FSM_ST_INIT, 0, 0);
+ break;
+ case IUUP_FSM_EVT_IUUP_DATA_REQ:
+ /* Data coming down from RNL (user) towards TNL (transport) */
+ irp = data;
+ itp = rnl_to_tnl_data(iui, irp);
+ iui->transport_prim_cb(&itp->oph, iui->transport_prim_priv);
+ break;
+ case IUUP_FSM_EVT_IUUP_DATA_IND:
+ /* Data coming up from TNL (transport) towards RNL (user) */
+ itp = data;
+ irp = tnl_to_rnl_data(itp);
+ iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
+ break;
+ }
+}
+
+static int iuup_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct osmo_iuup_instance *iui = fi->priv;
+ struct osmo_iuup_rnl_prim *irp;
+
+ switch (fi->T) {
+ case IUUP_TIMER_INIT:
+ OSMO_ASSERT(fi->state == IUUP_FSM_ST_INIT);
+ if (iui->timer.init.n == iui->config.t_init.n_max) {
+ irp = irp_error_event_alloc_c(iui, IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR, IUUP_ERR_DIST_LOCAL);
+ iui->user_prim_cb(&irp->oph, iui->user_prim_priv);
+ return 0;
+ }
+ iui->timer.init.n++;
+ retransmit_initialization(iui);
+ break;
+ case IUUP_TIMER_TA:
+ break;
+ case IUUP_TIMER_RC:
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ return 0;
+}
+
+
+static const struct osmo_fsm_state iuup_fsm_states[] = {
+ [IUUP_FSM_ST_NULL] = {
+ .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ),
+ .out_state_mask = S(IUUP_FSM_ST_INIT) |
+ S(IUUP_FSM_ST_TrM_DATA_XFER_READY),
+ .name = "NULL",
+ .action = iuup_fsm_null,
+ },
+ [IUUP_FSM_ST_TrM_DATA_XFER_READY] = {
+ .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
+ S(IUUP_FSM_EVT_IUUP_STATUS_REQ) |
+ S(IUUP_FSM_EVT_IUUP_DATA_REQ) |
+ S(IUUP_FSM_EVT_IUUP_DATA_IND) |
+ S(IUUP_FSM_EVT_IUUP_UNITDATA_REQ) |
+ S(IUUP_FSM_EVT_IUUP_UNITDATA_IND) |
+ S(IUUP_FSM_EVT_SSASAR_UNITDATA_REQ) |
+ S(IUUP_FSM_EVT_SSASAR_UNITDATA_IND),
+ .out_state_mask = S(IUUP_FSM_ST_NULL),
+ .name = "TrM_Data_Transfer_Ready",
+ .action = iuup_fsm_trm_data,
+ },
+ [IUUP_FSM_ST_INIT] = {
+ .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
+ S(IUUP_FSM_EVT_INIT) |
+ S(IUUP_FSM_EVT_LAST_INIT_ACK) |
+ S(IUUP_FSM_EVT_INIT_NACK),
+ .out_state_mask = S(IUUP_FSM_ST_NULL) |
+ S(IUUP_FSM_ST_SMpSDU_DATA_XFER_READY),
+ .name = "Initialisation",
+ .onenter = iuup_fsm_init_on_enter,
+ .action = iuup_fsm_init,
+ },
+ [IUUP_FSM_ST_SMpSDU_DATA_XFER_READY] = {
+ .in_event_mask = S(IUUP_FSM_EVT_IUUP_CONFIG_REQ) |
+ S(IUUP_FSM_EVT_INIT) |
+ S(IUUP_FSM_EVT_IUUP_DATA_REQ) |
+ S(IUUP_FSM_EVT_IUUP_DATA_IND),
+ .out_state_mask = S(IUUP_FSM_ST_NULL) |
+ S(IUUP_FSM_ST_INIT),
+ .name = "SMpSDU_Data_Transfer_Ready",
+ .action = iuup_fsm_smpsdu_data,
+ },
+};
+
+static struct osmo_fsm iuup_fsm = {
+ .name = "IuUP",
+ .states = iuup_fsm_states,
+ .num_states = ARRAY_SIZE(iuup_fsm_states),
+ .timer_cb = iuup_fsm_timer_cb,
+ .log_subsys = DLIUUP,
+ .event_names = iuup_fsm_event_names,
+};
+
+static int iuup_verify_pdu(const uint8_t *data, unsigned int len)
+{
+ int header_crc_computed, payload_crc_computed;
+ uint16_t payload_crc;
+ uint8_t pdu_type = iuup_get_pdu_type(data);
+ struct iuup_pdutype0_hdr *t0h;
+ struct iuup_pdutype14_hdr *t14h;
+
+ if (len < 3)
+ return -EINVAL;
+
+ header_crc_computed = osmo_iuup_compute_header_crc(data, len);
+ if (iuup_get_hdr_crc(data) != header_crc_computed) {
+ LOGP(DLIUUP, LOGL_NOTICE, "Header Checksum error: rx 0x%02x vs exp 0x%02x\n",
+ iuup_get_hdr_crc(data), header_crc_computed);
+ return -EIO;
+ }
+ switch (pdu_type) {
+ case IUUP_PDU_T_DATA_NOCRC:
+ if (len < 4)
+ return -EINVAL;
+ break;
+ case IUUP_PDU_T_DATA_CRC:
+ t0h = (struct iuup_pdutype0_hdr *) data;
+ payload_crc = ((uint16_t)t0h->payload_crc_hi << 8) | t0h->payload_crc_lo;
+ payload_crc_computed = osmo_iuup_compute_payload_crc(data, len);
+ if (payload_crc != payload_crc_computed)
+ goto payload_crc_err;
+ break;
+ case IUUP_PDU_T_CONTROL:
+ t14h = (struct iuup_pdutype14_hdr *) data;
+ if (t14h->ack_nack == IUUP_AN_PROCEDURE) {
+ payload_crc = ((uint16_t)t14h->payload_crc_hi << 8) | t14h->payload_crc_lo;
+ payload_crc_computed = osmo_iuup_compute_payload_crc(data, len);
+ if (payload_crc != payload_crc_computed)
+ goto payload_crc_err;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+
+payload_crc_err:
+ LOGP(DLIUUP, LOGL_NOTICE, "Payload Checksum error (pdu type %u): rx 0x%02x vs exp 0x%02x\n",
+ pdu_type, payload_crc, payload_crc_computed);
+ return -EIO;
+}
+
+/* A IuUP TNL SAP primitive from transport (lower layer) */
+int osmo_iuup_tnl_prim_up(struct osmo_iuup_instance *inst, struct osmo_iuup_tnl_prim *itp)
+{
+ struct osmo_prim_hdr *oph = &itp->oph;
+ struct iuup_pdutype14_hdr *t14h;
+ int rc = 0;
+
+ OSMO_ASSERT(oph->sap == SAP_IUUP_TNL);
+
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION):
+ if (iuup_verify_pdu(msgb_l2(oph->msg), msgb_l2len(oph->msg)) < 0) {
+ LOGPFSML(inst->fi, LOGL_NOTICE, "Discarding invalid IuUP PDU: %s\n",
+ osmo_hexdump((const unsigned char *) msgb_l2(oph->msg), msgb_l2len(oph->msg)));
+ /* don't return error as the caller is not responsible for the PDU which
+ * was transmitted from some remote peer */
+ return 0;
+ }
+ switch (iuup_get_pdu_type(msgb_l2(oph->msg))) {
+ case IUUP_PDU_T_DATA_CRC:
+ oph->msg->l3h = msgb_l2(oph->msg) + sizeof(struct iuup_pdutype0_hdr);
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_IND, itp);
+ break;
+ case IUUP_PDU_T_DATA_NOCRC:
+ oph->msg->l3h = msgb_l2(oph->msg) + sizeof(struct iuup_pdutype1_hdr);
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_IND, itp);
+ break;
+ case IUUP_PDU_T_CONTROL:
+ t14h = (struct iuup_pdutype14_hdr *) msgb_l2(oph->msg);
+ switch (t14h->ack_nack) {
+ case IUUP_AN_PROCEDURE:
+ switch (t14h->proc_ind) {
+ case IUUP_PROC_INIT:
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_INIT, itp);
+ break;
+ case IUUP_PROC_RATE_CTRL:
+ case IUUP_PROC_TIME_ALIGN:
+ case IUUP_PROC_ERR_EVENT:
+ LOGPFSML(inst->fi, LOGL_NOTICE, "Received Request for "
+ "unsupported IuUP procedure %u\n", t14h->proc_ind);
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_NOTICE, "Received Request for "
+ "unknown IuUP procedure %u\n", t14h->proc_ind);
+ break;
+ }
+ break;
+ case IUUP_AN_ACK:
+ switch (t14h->proc_ind) {
+ case IUUP_PROC_INIT:
+ rc = osmo_fsm_inst_dispatch(inst->fi,
+ IUUP_FSM_EVT_LAST_INIT_ACK, itp);
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_ERROR, "Received ACK for "
+ "unknown IuUP procedure %u\n", t14h->proc_ind);
+ break;
+ }
+ break;
+ case IUUP_AN_NACK:
+ switch (t14h->proc_ind) {
+ case IUUP_PROC_INIT:
+ rc = osmo_fsm_inst_dispatch(inst->fi,
+ IUUP_FSM_EVT_INIT_NACK, itp);
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_ERROR, "Received NACK for "
+ "unknown IuUP procedure %u\n", t14h->proc_ind);
+ break;
+ }
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_ERROR, "Received unknown IuUP ACK/NACK\n");
+ break;
+ }
+ break;
+ default:
+ LOGPFSML(inst->fi, LOGL_NOTICE, "Received unknown IuUP PDU type %u\n",
+ iuup_get_pdu_type(msgb_l2(oph->msg)));
+ break;
+ }
+ break;
+ default:
+ /* exception: return an error code due to a wrong primitive */
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+/* A IuUP RNL SAP primitive from user (higher layer) */
+int osmo_iuup_rnl_prim_down(struct osmo_iuup_instance *inst, struct osmo_iuup_rnl_prim *irp)
+{
+ struct osmo_prim_hdr *oph = &irp->oph;
+ int rc;
+
+ OSMO_ASSERT(oph->sap == SAP_IUUP_RNL);
+
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST):
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_CONFIG_REQ, irp);
+ msgb_free(irp->oph.msg);
+ break;
+ case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST):
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_DATA_REQ, irp);
+ if (rc != 0)
+ msgb_free(irp->oph.msg);
+ break;
+ case OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_REQUEST):
+ rc = osmo_fsm_inst_dispatch(inst->fi, IUUP_FSM_EVT_IUUP_STATUS_REQ, irp);
+ msgb_free(irp->oph.msg);
+ break;
+ default:
+ rc = -EINVAL;
+ msgb_free(irp->oph.msg);
+ }
+ return rc;
+}
+
+struct osmo_iuup_instance *osmo_iuup_instance_alloc(void *ctx, const char *id)
+{
+ struct osmo_iuup_instance *iui;
+ iui = talloc_zero(ctx, struct osmo_iuup_instance);
+ if (!iui)
+ return NULL;
+
+ iui->fi = osmo_fsm_inst_alloc(&iuup_fsm, NULL, iui, LOGL_DEBUG, id);
+ if (!iui->fi)
+ goto free_ret;
+
+ return iui;
+free_ret:
+ talloc_free(iui);
+ return NULL;
+}
+
+void osmo_iuup_instance_free(struct osmo_iuup_instance *iui)
+{
+ if (!iui)
+ return;
+
+ if (iui->fi)
+ osmo_fsm_inst_free(iui->fi);
+ iui->fi = NULL;
+ talloc_free(iui);
+}
+
+void osmo_iuup_instance_set_user_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv)
+{
+ iui->user_prim_cb = func;
+ iui->user_prim_priv = priv;
+}
+void osmo_iuup_instance_set_transport_prim_cb(struct osmo_iuup_instance *iui, osmo_prim_cb func, void *priv)
+{
+ iui->transport_prim_cb = func;
+ iui->transport_prim_priv = priv;
+}
+
+static __attribute__((constructor)) void on_dso_load_iuup_fsm(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&iuup_fsm) == 0);
+}
diff --git a/src/gsm/kasumi.c b/src/gsm/kasumi.c
index f93c002b..78885bdd 100644
--- a/src/gsm/kasumi.c
+++ b/src/gsm/kasumi.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
diff --git a/src/gsm/kdf.c b/src/gsm/kdf.c
new file mode 100644
index 00000000..4113aada
--- /dev/null
+++ b/src/gsm/kdf.c
@@ -0,0 +1,163 @@
+/*
+ * (C) 2021 by sysmocom s.f.m.c. GmbH
+ *
+ * Author: Eric Wild <ewild@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#if (USE_GNUTLS)
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#define HMAC_FUNC(k,lk,s,sl,out) gnutls_hmac_fast(GNUTLS_MAC_SHA256,k,lk,s,sl,out)
+#else
+#include <osmocom/crypt/kdf.h>
+#define HMAC_FUNC(k,lk,s,sl,out) hmac_sha256(k,lk,s,sl,out)
+#endif
+
+#include <osmocom/core/bit32gen.h>
+#include <osmocom/crypt/kdf.h>
+
+#include "kdf/common.h"
+#include "kdf/sha256.h"
+
+
+#if (USE_GNUTLS)
+/* gnutls < 3.3.0 requires global init.
+ * gnutls >= 3.3.0 does it automatic.
+ * It doesn't hurt calling it twice,
+ * as long it's not done at the same time (threads).
+ */
+__attribute__((constructor))
+static void on_dso_load_gnutls(void)
+{
+ if (!gnutls_check_version("3.3.0"))
+ gnutls_global_init();
+}
+
+__attribute__((destructor))
+static void on_dso_unload_gnutls(void)
+{
+ if (!gnutls_check_version("3.3.0"))
+ gnutls_global_deinit();
+}
+#endif
+
+/*
+ * This file uses the generic key derivation function defined in 3GPP TS 33.220 Annex B
+ *
+ * The S parameter always consists of concatenated values FC | P0 | L0 | Pi | Li | ...
+ * with Pi = Parameter number i and Li = Length of Pi (two octets)
+ *
+ * FC is either a single octet or two octets 0xff | FC
+ * FC values ranges depend on the specification parts that use the KDF,
+ * they are defined in 3GPP TS 33.220 Annex B.2.2
+ *
+ */
+
+/*! \addtogroup kdf
+ * @{
+ * key derivation functions
+ *
+ * \file kdf.c */
+
+/* 3GPP TS 33.102 B.5 */
+void osmo_kdf_kc128(const uint8_t* ck, const uint8_t* ik, uint8_t* kc128) {
+ uint8_t k[16*2];
+ uint8_t s[1];
+ uint8_t out_tmp256[32];
+ memcpy (&k[0], ck, 16);
+ memcpy (&k[16], ik, 16);
+
+ s[0] = 0x32; // yeah, really just one FC byte..
+
+ HMAC_FUNC(k, 32, s, 1, out_tmp256);
+ memcpy(kc128, out_tmp256, 16);
+}
+
+/* 3GPP TS 33.401 A.2 */
+void osmo_kdf_kasme(const uint8_t *ck, const uint8_t *ik, const uint8_t* plmn_id,
+ const uint8_t *sqn, const uint8_t *ak, uint8_t *kasme)
+{
+ uint8_t s[14];
+ uint8_t k[16*2];
+ int i;
+
+ memcpy(&k[0], ck, 16);
+ memcpy(&k[16], ik, 16);
+
+ s[0] = 0x10;
+ memcpy(&s[1], plmn_id, 3);
+ s[4] = 0x00;
+ s[5] = 0x03;
+
+ for (i = 0; i < 6; i++)
+ s[6+i] = sqn[i] ^ ak[i];
+ s[12] = 0x00;
+ s[13] = 0x06;
+
+ HMAC_FUNC(k, 32, s, 14, kasme);
+}
+
+/* 3GPP TS 33.401 A.3 */
+void osmo_kdf_enb(const uint8_t *kasme, uint32_t ul_count, uint8_t *kenb)
+{
+ uint8_t s[7];
+
+ s[0] = 0x11;
+ osmo_store32be(ul_count, &s[1]);
+ s[5] = 0x00;
+ s[6] = 0x04;
+
+ HMAC_FUNC(kasme, 32, s, 7, kenb);
+}
+
+/* 3GPP TS 33.401 A.4 */
+void osmo_kdf_nh(const uint8_t *kasme, const uint8_t *sync_input, uint8_t *nh)
+{
+ uint8_t s[35];
+
+ s[0] = 0x12;
+ memcpy(s+1, sync_input, 32);
+ s[33] = 0x00;
+ s[34] = 0x20;
+
+ HMAC_FUNC(kasme, 32, s, 35, nh);
+}
+
+/* 3GPP TS 33.401 A.7 */
+void osmo_kdf_nas(uint8_t algo_type, uint8_t algo_id, const uint8_t *kasme, uint8_t *knas)
+{
+ uint8_t s[7];
+ uint8_t out[32];
+
+ s[0] = 0x15;
+ s[1] = algo_type;
+ s[2] = 0x00;
+ s[3] = 0x01;
+ s[4] = algo_id;
+ s[5] = 0x00;
+ s[6] = 0x01;
+
+ HMAC_FUNC(kasme, 32, s, 7, out);
+ memcpy(knas, out+16, 16);
+}
+
+/*! @} */
diff --git a/src/gsm/kdf/common.h b/src/gsm/kdf/common.h
new file mode 100644
index 00000000..c1ef17ef
--- /dev/null
+++ b/src/gsm/kdf/common.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define CONFIG_CRYPTO_INTERNAL
+#define TEST_FAIL() 0
+
+#define MSG_DEBUG
+#define wpa_hexdump(x, args...)
+#define wpa_hexdump_key(x, args...)
+#define wpa_printf(x, args...)
+
+#define os_memcpy(x, y, z) memcpy(x, y, z)
+#define os_memcmp(x, y, z) memcmp(x, y, z)
+#define os_memset(x, y, z) memset(x, y, z)
+#define os_malloc(x) malloc(x)
+#define os_free(x) free(x)
+#define os_strlen(x) strlen(x)
+
+#define forced_memzero(ptr, len) memset(ptr, 0, len);
+
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+
+
+/* Macros for handling unaligned memory accesses */
+
+#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1]))
+#define WPA_PUT_BE16(a, val) \
+ do { \
+ (a)[0] = ((u16) (val)) >> 8; \
+ (a)[1] = ((u16) (val)) & 0xff; \
+ } while (0)
+
+#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0]))
+#define WPA_PUT_LE16(a, val) \
+ do { \
+ (a)[1] = ((u16) (val)) >> 8; \
+ (a)[0] = ((u16) (val)) & 0xff; \
+ } while (0)
+
+#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
+ ((u32) (a)[2]))
+#define WPA_PUT_BE24(a, val) \
+ do { \
+ (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[2] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \
+ (((u32) (a)[2]) << 8) | ((u32) (a)[3]))
+#define WPA_PUT_BE32(a, val) \
+ do { \
+ (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[3] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \
+ (((u32) (a)[1]) << 8) | ((u32) (a)[0]))
+#define WPA_PUT_LE32(a, val) \
+ do { \
+ (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \
+ (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[0] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \
+ (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \
+ (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \
+ (((u64) (a)[6]) << 8) | ((u64) (a)[7]))
+#define WPA_PUT_BE64(a, val) \
+ do { \
+ (a)[0] = (u8) (((u64) (val)) >> 56); \
+ (a)[1] = (u8) (((u64) (val)) >> 48); \
+ (a)[2] = (u8) (((u64) (val)) >> 40); \
+ (a)[3] = (u8) (((u64) (val)) >> 32); \
+ (a)[4] = (u8) (((u64) (val)) >> 24); \
+ (a)[5] = (u8) (((u64) (val)) >> 16); \
+ (a)[6] = (u8) (((u64) (val)) >> 8); \
+ (a)[7] = (u8) (((u64) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \
+ (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \
+ (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \
+ (((u64) (a)[1]) << 8) | ((u64) (a)[0]))
+
+
+#define __must_check
diff --git a/src/gsm/kdf/crypto.h b/src/gsm/kdf/crypto.h
new file mode 100644
index 00000000..6dca191c
--- /dev/null
+++ b/src/gsm/kdf/crypto.h
@@ -0,0 +1,470 @@
+/*
+ * WPA Supplicant / wrapper functions for crypto libraries
+ * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * This file defines the cryptographic functions that need to be implemented
+ * for wpa_supplicant and hostapd. When TLS is not used, internal
+ * implementation of MD5, SHA1, and AES is used and no external libraries are
+ * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
+ * crypto library used by the TLS implementation is expected to be used for
+ * non-TLS needs, too, in order to save space by not implementing these
+ * functions twice.
+ *
+ * Wrapper code for using each crypto library is in its own file (crypto*.c)
+ * and one of these files is build and linked in to provide the functions
+ * defined here.
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+/**
+ * md4_vector - MD4 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+#ifdef CONFIG_FIPS
+/**
+ * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed)
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
+ const size_t *len, u8 *mac);
+#else /* CONFIG_FIPS */
+#define md5_vector_non_fips_allow md5_vector
+#endif /* CONFIG_FIPS */
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
+ * @seed: Seed/key for the PRF
+ * @seed_len: Seed length in bytes
+ * @x: Buffer for PRF output
+ * @xlen: Output length in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function implements random number generation specified in NIST FIPS
+ * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
+ * SHA-1, but has different message padding.
+ */
+int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
+ size_t xlen);
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * des_encrypt - Encrypt one block with DES
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
+ */
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+/**
+ * aes_encrypt_init - Initialize AES for encryption
+ * @key: Encryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_encrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_encrypt - Encrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @plain: Plaintext data to be encrypted (16 bytes)
+ * @crypt: Buffer for the encrypted data (16 bytes)
+ */
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+
+/**
+ * aes_encrypt_deinit - Deinitialize AES encryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_encrypt_deinit(void *ctx);
+
+/**
+ * aes_decrypt_init - Initialize AES for decryption
+ * @key: Decryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_decrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_decrypt - Decrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @crypt: Encrypted data (16 bytes)
+ * @plain: Buffer for the decrypted data (16 bytes)
+ */
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+
+/**
+ * aes_decrypt_deinit - Deinitialize AES decryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_decrypt_deinit(void *ctx);
+
+
+enum crypto_hash_alg {
+ CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
+ CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
+ CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
+};
+
+struct crypto_hash;
+
+/**
+ * crypto_hash_init - Initialize hash/HMAC function
+ * @alg: Hash algorithm
+ * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
+ * @key_len: Length of the key in bytes
+ * Returns: Pointer to hash context to use with other hash functions or %NULL
+ * on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_hash_update - Add data to hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @data: Data buffer to add
+ * @len: Length of the buffer
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
+
+/**
+ * crypto_hash_finish - Complete hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @hash: Buffer for hash value or %NULL if caller is just freeing the hash
+ * context
+ * @len: Pointer to length of the buffer or %NULL if caller is just freeing the
+ * hash context; on return, this is set to the actual length of the hash value
+ * Returns: 0 on success, -1 if buffer is too small (len set to needed length),
+ * or -2 on other failures (including failed crypto_hash_update() operations)
+ *
+ * This function calculates the hash value and frees the context buffer that
+ * was used for hash calculation.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
+
+
+enum crypto_cipher_alg {
+ CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
+ CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
+};
+
+struct crypto_cipher;
+
+/**
+ * crypto_cipher_init - Initialize block/stream cipher function
+ * @alg: Cipher algorithm
+ * @iv: Initialization vector for block ciphers or %NULL for stream ciphers
+ * @key: Cipher key
+ * @key_len: Length of key in bytes
+ * Returns: Pointer to cipher context to use with other cipher functions or
+ * %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+ const u8 *iv, const u8 *key,
+ size_t key_len);
+
+/**
+ * crypto_cipher_encrypt - Cipher encrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @plain: Plaintext to cipher
+ * @crypt: Resulting ciphertext
+ * @len: Length of the plaintext
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
+ const u8 *plain, u8 *crypt, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Cipher decrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @crypt: Ciphertext to decrypt
+ * @plain: Resulting plaintext
+ * @len: Length of the cipher text
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
+ const u8 *crypt, u8 *plain, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Free cipher context
+ * @ctx: Context pointer from crypto_cipher_init()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_cipher_deinit(struct crypto_cipher *ctx);
+
+
+struct crypto_public_key;
+struct crypto_private_key;
+
+/**
+ * crypto_public_key_import - Import an RSA public key
+ * @key: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library supports X.509
+ * parsing. In that case, crypto_public_key_from_cert() is used to import the
+ * public key from a certificate.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
+
+/**
+ * crypto_private_key_import - Import an RSA private key
+ * @key: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * @passwd: Key encryption password or %NULL if key is not encrypted
+ * Returns: Pointer to the private key or %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+ size_t len,
+ const char *passwd);
+
+/**
+ * crypto_public_key_from_cert - Import an RSA public key from a certificate
+ * @buf: DER encoded X.509 certificate
+ * @len: Certificate buffer length in bytes
+ * Returns: Pointer to public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library does not support
+ * X.509 parsing. In that case, internal code will be used to parse the
+ * certificate and public key is imported using crypto_public_key_import().
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+ size_t len);
+
+/**
+ * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
+ * @key: Public key
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_public_key_encrypt_pkcs1_v15(
+ struct crypto_public_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
+ * @key: Private key
+ * @in: Encrypted buffer
+ * @inlen: Length of encrypted buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_decrypt_pkcs1_v15(
+ struct crypto_private_key *key, const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
+ * @key: Private key from crypto_private_key_import()
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted (signed) data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+ const u8 *in, size_t inlen,
+ u8 *out, size_t *outlen);
+
+/**
+ * crypto_public_key_free - Free public key
+ * @key: Public key
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_public_key_free(struct crypto_public_key *key);
+
+/**
+ * crypto_private_key_free - Free private key
+ * @key: Private key from crypto_private_key_import()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_private_key_free(struct crypto_private_key *key);
+
+/**
+ * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
+ * @key: Public key
+ * @crypt: Encrypted signature data (using the private key)
+ * @crypt_len: Encrypted signature data length
+ * @plain: Buffer for plaintext (at least crypt_len bytes)
+ * @plain_len: Plaintext length (max buffer size on input, real len on output);
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check crypto_public_key_decrypt_pkcs1(
+ struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
+ u8 *plain, size_t *plain_len);
+
+/**
+ * crypto_global_init - Initialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_global_init(void);
+
+/**
+ * crypto_global_deinit - Deinitialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_global_deinit(void);
+
+/**
+ * crypto_mod_exp - Modular exponentiation of large integers
+ * @base: Base integer (big endian byte array)
+ * @base_len: Length of base integer in bytes
+ * @power: Power integer (big endian byte array)
+ * @power_len: Length of power integer in bytes
+ * @modulus: Modulus integer (big endian byte array)
+ * @modulus_len: Length of modulus integer in bytes
+ * @result: Buffer for the result
+ * @result_len: Result length (max buffer size on input, real len on output)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function calculates result = base ^ power mod modulus. modules_len is
+ * used as the maximum size of modulus buffer. It is set to the used size on
+ * success.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
+ const u8 *power, size_t power_len,
+ const u8 *modulus, size_t modulus_len,
+ u8 *result, size_t *result_len);
+
+/**
+ * rc4_skip - XOR RC4 stream to given data with skip-stream-start
+ * @key: RC4 key
+ * @keylen: RC4 key length
+ * @skip: number of bytes to skip from the beginning of the RC4 stream
+ * @data: data to be XOR'ed with RC4 stream
+ * @data_len: buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Generate RC4 pseudo random stream for the given key, skip beginning of the
+ * stream, and XOR the end result with the data buffer to perform RC4
+ * encryption/decryption.
+ */
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+ u8 *data, size_t data_len);
+
+#endif /* CRYPTO_H */
diff --git a/src/gsm/kdf/sha1-internal.c b/src/gsm/kdf/sha1-internal.c
new file mode 100644
index 00000000..2216b439
--- /dev/null
+++ b/src/gsm/kdf/sha1-internal.c
@@ -0,0 +1,307 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+
+#include "common.h"
+#include "sha1.h"
+#include "sha1_i.h"
+//#include "md5.h"
+#include "crypto.h"
+
+typedef struct SHA1Context SHA1_CTX;
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+ SHA1_CTX ctx;
+ size_t i;
+
+ SHA1Init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ SHA1Update(&ctx, addr[i], len[i]);
+ SHA1Final(mac, &ctx);
+ return 0;
+}
+
+
+/* ===== start - public domain SHA1 implementation ===== */
+
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+ void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it. This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.
+
+-----------------
+Modified 4/01
+By Jouni Malinen <j@w1.fi>
+Minor changes to match the coding style used in Dynamics.
+
+Modified September 24, 2004
+By Jouni Malinen <j@w1.fi>
+Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined.
+
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef WORDS_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
+ (rol(block->l[i], 8) & 0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
+ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) \
+ z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R1(v,w,x,y,z,i) \
+ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R2(v,w,x,y,z,i) \
+ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);
+#define R3(v,w,x,y,z,i) \
+ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30);
+#define R4(v,w,x,y,z,i) \
+ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+ w=rol(w, 30);
+
+
+#ifdef VERBOSE /* SAK */
+void SHAPrintContext(SHA1_CTX *context, char *msg)
+{
+ printf("%s (%d,%d) %x %x %x %x %x\n",
+ msg,
+ context->count[0], context->count[1],
+ context->state[0],
+ context->state[1],
+ context->state[2],
+ context->state[3],
+ context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64])
+{
+ u32 a, b, c, d, e;
+ typedef union {
+ unsigned char c[64];
+ u32 l[16];
+ } CHAR64LONG16;
+ CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+ CHAR64LONG16 workspace;
+ block = &workspace;
+ os_memcpy(block, buffer, 64);
+#else
+ block = (CHAR64LONG16 *) buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+ os_memset(block, 0, 64);
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const void *_data, u32 len)
+{
+ u32 i, j;
+ const unsigned char *data = _data;
+
+#ifdef VERBOSE
+ SHAPrintContext(context, "before");
+#endif
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ os_memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ os_memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+ SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+ u32 i;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)
+ ((context->count[(i >= 4 ? 0 : 1)] >>
+ ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ SHA1Update(context, (unsigned char *) "\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1Update(context, (unsigned char *) "\0", 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform()
+ */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) &
+ 255);
+ }
+ /* Wipe variables */
+ i = 0;
+ os_memset(context->buffer, 0, 64);
+ os_memset(context->state, 0, 20);
+ os_memset(context->count, 0, 8);
+ os_memset(finalcount, 0, 8);
+}
+
+/* ===== end - public domain SHA1 implementation ===== */
diff --git a/src/gsm/kdf/sha1.c b/src/gsm/kdf/sha1.c
new file mode 100644
index 00000000..0d39f77b
--- /dev/null
+++ b/src/gsm/kdf/sha1.c
@@ -0,0 +1,162 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+ unsigned char tk[20];
+ const u8 *_addr[6];
+ size_t _len[6], i;
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return -1;
+ }
+
+ /* if key is longer than 64 bytes reset it to key = SHA1(key) */
+ if (key_len > 64) {
+ if (sha1_vector(1, &key, &key_len, tk))
+ return -1;
+ key = tk;
+ key_len = 20;
+ }
+
+ /* the HMAC_SHA1 transform looks like:
+ *
+ * SHA1(K XOR opad, SHA1(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with ipad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner SHA1 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ if (sha1_vector(1 + num_elem, _addr, _len, mac))
+ return -1;
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer SHA1 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ _addr[1] = mac;
+ _len[1] = SHA1_MAC_LEN;
+ return sha1_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 of failure
+ */
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., PMK in IEEE 802.11i).
+ */
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ u8 counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label) + 1;
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = (u8 *) label;
+ len[0] = label_len;
+ addr[1] = data;
+ len[1] = data_len;
+ addr[2] = &counter;
+ len[2] = 1;
+
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ if (plen >= SHA1_MAC_LEN) {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ &buf[pos]))
+ return -1;
+ pos += SHA1_MAC_LEN;
+ } else {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ hash))
+ return -1;
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+
+ return 0;
+}
diff --git a/src/gsm/kdf/sha1.h b/src/gsm/kdf/sha1.h
new file mode 100644
index 00000000..f0c1a5f9
--- /dev/null
+++ b/src/gsm/kdf/sha1.h
@@ -0,0 +1,33 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA1_H
+#define SHA1_H
+
+#define SHA1_MAC_LEN 20
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac);
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
+int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed,
+ size_t seed_len, u8 *out, size_t outlen);
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen);
+#endif /* SHA1_H */
diff --git a/src/gsm/kdf/sha1_i.h b/src/gsm/kdf/sha1_i.h
new file mode 100644
index 00000000..ec2f82f7
--- /dev/null
+++ b/src/gsm/kdf/sha1_i.h
@@ -0,0 +1,29 @@
+/*
+ * SHA1 internal definitions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA1_I_H
+#define SHA1_I_H
+
+struct SHA1Context {
+ u32 state[5];
+ u32 count[2];
+ unsigned char buffer[64];
+};
+
+void SHA1Init(struct SHA1Context *context);
+void SHA1Update(struct SHA1Context *context, const void *data, u32 len);
+void SHA1Final(unsigned char digest[20], struct SHA1Context *context);
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+#endif /* SHA1_I_H */
diff --git a/src/gsm/kdf/sha256-internal.c b/src/gsm/kdf/sha256-internal.c
new file mode 100644
index 00000000..18cc8f80
--- /dev/null
+++ b/src/gsm/kdf/sha256-internal.c
@@ -0,0 +1,231 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+
+#include "common.h"
+#include "sha256.h"
+#include "sha256_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ struct sha256_state ctx;
+ size_t i;
+
+ sha256_init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ if (sha256_process(&ctx, addr[i], len[i]))
+ return -1;
+ if (sha256_done(&ctx, mac))
+ return -1;
+ return 0;
+}
+
+
+/* ===== start - public domain SHA256 implementation ===== */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+/* the K array */
+static const unsigned long K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+
+/* Various logical functions */
+#define RORc(x, y) \
+( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
+ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) RORc((x), (n))
+#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+/* compress 512-bits */
+static int sha256_compress(struct sha256_state *md, unsigned char *buf)
+{
+ u32 S[8], W[64], t0, t1;
+ u32 t;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->state[i];
+ }
+
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++)
+ W[i] = WPA_GET_BE32(buf + (4 * i));
+
+ /* fill W[16..63] */
+ for (i = 16; i < 64; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+ W[i - 16];
+ }
+
+ /* Compress */
+#define RND(a,b,c,d,e,f,g,h,i) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+ for (i = 0; i < 64; ++i) {
+ RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
+ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+ }
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->state[i] = md->state[i] + S[i];
+ }
+ return 0;
+}
+
+
+/* Initialize the hash state */
+void sha256_init(struct sha256_state *md)
+{
+ md->curlen = 0;
+ md->length = 0;
+ md->state[0] = 0x6A09E667UL;
+ md->state[1] = 0xBB67AE85UL;
+ md->state[2] = 0x3C6EF372UL;
+ md->state[3] = 0xA54FF53AUL;
+ md->state[4] = 0x510E527FUL;
+ md->state[5] = 0x9B05688CUL;
+ md->state[6] = 0x1F83D9ABUL;
+ md->state[7] = 0x5BE0CD19UL;
+}
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+*/
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen)
+{
+ unsigned long n;
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ while (inlen > 0) {
+ if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) {
+ if (sha256_compress(md, (unsigned char *) in) < 0)
+ return -1;
+ md->length += SHA256_BLOCK_SIZE * 8;
+ in += SHA256_BLOCK_SIZE;
+ inlen -= SHA256_BLOCK_SIZE;
+ } else {
+ n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen));
+ os_memcpy(md->buf + md->curlen, in, n);
+ md->curlen += n;
+ in += n;
+ inlen -= n;
+ if (md->curlen == SHA256_BLOCK_SIZE) {
+ if (sha256_compress(md, md->buf) < 0)
+ return -1;
+ md->length += 8 * SHA256_BLOCK_SIZE;
+ md->curlen = 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (32 bytes)
+ @return CRYPT_OK if successful
+*/
+int sha256_done(struct sha256_state *md, unsigned char *out)
+{
+ int i;
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ /* increase the length of the message */
+ md->length += md->curlen * 8;
+
+ /* append the '1' bit */
+ md->buf[md->curlen++] = (unsigned char) 0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->curlen > 56) {
+ while (md->curlen < SHA256_BLOCK_SIZE) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+ sha256_compress(md, md->buf);
+ md->curlen = 0;
+ }
+
+ /* pad up to 56 bytes of zeroes */
+ while (md->curlen < 56) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+
+ /* store length */
+ WPA_PUT_BE64(md->buf + 56, md->length);
+ sha256_compress(md, md->buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++)
+ WPA_PUT_BE32(out + (4 * i), md->state[i]);
+
+ return 0;
+}
+
+/* ===== end - public domain SHA256 implementation ===== */
diff --git a/src/gsm/kdf/sha256.c b/src/gsm/kdf/sha256.c
new file mode 100644
index 00000000..4f8b6cc5
--- /dev/null
+++ b/src/gsm/kdf/sha256.c
@@ -0,0 +1,156 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (32 bytes)
+ */
+void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+ unsigned char tk[32];
+ const u8 *_addr[6];
+ size_t _len[6], i;
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return;
+ }
+
+ /* if key is longer than 64 bytes reset it to key = SHA256(key) */
+ if (key_len > 64) {
+ sha256_vector(1, &key, &key_len, tk);
+ key = tk;
+ key_len = 32;
+ }
+
+ /* the HMAC_SHA256 transform looks like:
+ *
+ * SHA256(K XOR opad, SHA256(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with ipad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner SHA256 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ sha256_vector(1 + num_elem, _addr, _len, mac);
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 64; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer SHA256 */
+ _addr[0] = k_pad;
+ _len[0] = 64;
+ _addr[1] = mac;
+ _len[1] = SHA256_MAC_LEN;
+ sha256_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ */
+void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ u16 counter = 1;
+ size_t pos, plen;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 counter_le[2], length_le[2];
+
+ addr[0] = counter_le;
+ len[0] = 2;
+ addr[1] = (u8 *) label;
+ len[1] = os_strlen(label);
+ addr[2] = data;
+ len[2] = data_len;
+ addr[3] = length_le;
+ len[3] = sizeof(length_le);
+
+ WPA_PUT_LE16(length_le, buf_len * 8);
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ WPA_PUT_LE16(counter_le, counter);
+ if (plen >= SHA256_MAC_LEN) {
+ hmac_sha256_vector(key, key_len, 4, addr, len,
+ &buf[pos]);
+ pos += SHA256_MAC_LEN;
+ } else {
+ hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+}
diff --git a/src/gsm/kdf/sha256.h b/src/gsm/kdf/sha256.h
new file mode 100644
index 00000000..b1ce6afe
--- /dev/null
+++ b/src/gsm/kdf/sha256.h
@@ -0,0 +1,30 @@
+/*
+ * SHA256 hash implementation and interface functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA256_H
+#define SHA256_H
+
+#define SHA256_MAC_LEN 32
+
+void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void tls_prf_sha256(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen);
+
+#endif /* SHA256_H */
diff --git a/src/gsm/kdf/sha256_i.h b/src/gsm/kdf/sha256_i.h
new file mode 100644
index 00000000..20ae4889
--- /dev/null
+++ b/src/gsm/kdf/sha256_i.h
@@ -0,0 +1,31 @@
+/*
+ * SHA-256 internal definitions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * 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.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef SHA256_I_H
+#define SHA256_I_H
+
+#define SHA256_BLOCK_SIZE 64
+
+struct sha256_state {
+ u64 length;
+ u32 state[8], curlen;
+ u8 buf[SHA256_BLOCK_SIZE];
+};
+
+void sha256_init(struct sha256_state *md);
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen);
+int sha256_done(struct sha256_state *md, unsigned char *out);
+
+#endif /* SHA256_I_H */
diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c
index 80840293..1fc986d6 100644
--- a/src/gsm/lapdm.c
+++ b/src/gsm/lapdm.c
@@ -19,10 +19,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup lapdm
@@ -31,6 +27,7 @@
#include <stdio.h>
#include <stdint.h>
+#include <inttypes.h>
#include <string.h>
#include <errno.h>
@@ -61,6 +58,7 @@
#define LAPDm_ADDR_SAPI(addr) (((addr) >> 2) & 0x7)
#define LAPDm_ADDR_CR(addr) (((addr) >> 1) & 0x1)
#define LAPDm_ADDR_EA(addr) ((addr) & 0x1)
+#define LAPDm_ADDR_SHORT_L2(addr) ((addr) & 0x3)
/* 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))
@@ -126,17 +124,20 @@ const struct value_string osmo_ph_prim_names[] = {
{ 0, NULL }
};
+extern void *tall_lapd_ctx;
+
static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg);
static int send_rslms_dlsap(struct osmo_dlsap_prim *dp,
struct lapd_msg_ctx *lctx);
static int update_pending_frames(struct lapd_msg_ctx *lctx);
static void lapdm_dl_init(struct lapdm_datalink *dl,
- struct lapdm_entity *entity, int t200_ms, uint32_t n200)
+ struct lapdm_entity *entity, int t200_ms, uint32_t n200,
+ const char *name)
{
memset(dl, 0, sizeof(*dl));
dl->entity = entity;
- lapd_dl_init(&dl->dl, 1, 8, 251); /* Section 5.8.5 of TS 04.06 */
+ lapd_dl_init2(&dl->dl, 1, 8, 251, name); /* Section 5.8.5 of TS 04.06 */
dl->dl.reestablish = 0; /* GSM uses no reestablish */
dl->dl.send_ph_data_req = lapdm_send_ph_data_req;
dl->dl.send_dlsap = send_rslms_dlsap;
@@ -165,7 +166,7 @@ void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200)
for (i = 0; i < ARRAY_SIZE(t200_ms_sapi_arr); i++)
t200_ms_sapi_arr[i] = t200 * 1000;
- return lapdm_entity_init2(le, mode, t200_ms_sapi_arr, N200);
+ return lapdm_entity_init3(le, mode, t200_ms_sapi_arr, N200, NULL);
}
/*! initialize a LAPDm entity and all datalinks inside
@@ -177,10 +178,30 @@ void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200)
void lapdm_entity_init2(struct lapdm_entity *le, enum lapdm_mode mode,
const int *t200_ms, int n200)
{
+ lapdm_entity_init3(le, mode, t200_ms, n200, NULL);
+}
+
+/*! initialize a LAPDm entity and all datalinks inside
+ * \param[in] le LAPDm entity
+ * \param[in] mode lapdm_mode (BTS/MS)
+ * \param[in] t200_ms per-SAPI array of T200 re-transmission timer in milli-seconds
+ * \param[in] n200 N200 re-transmisison count
+ * \param[in] name human-readable name (will be copied internally + extended with SAPI)
+ */
+void lapdm_entity_init3(struct lapdm_entity *le, enum lapdm_mode mode,
+ const int *t200_ms, int n200, const char *name_pfx)
+{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
- lapdm_dl_init(&le->datalink[i], le, t200_ms[i], n200);
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
+ char name[256];
+ if (name_pfx) {
+ snprintf(name, sizeof(name), "%s[%s]", name_pfx, i == 0 ? "0" : "3");
+ lapdm_dl_init(&le->datalink[i], le, (t200_ms) ? t200_ms[i] : 0, n200, name);
+ } else
+ lapdm_dl_init(&le->datalink[i], le, (t200_ms) ? t200_ms[i] : 0, n200, NULL);
+ INIT_LLIST_HEAD(&le->datalink[i].tx_ui_queue);
+ }
lapdm_entity_set_mode(le, mode);
}
@@ -213,7 +234,7 @@ void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode)
const int t200_ms_dcch[_NR_DL_SAPI] = { 1000, 1000 };
const int t200_ms_acch[_NR_DL_SAPI] = { 2000, 2000 };
- lapdm_channel_init2(lc, mode, t200_ms_dcch, t200_ms_acch, GSM_LCHAN_SDCCH);
+ lapdm_channel_init3(lc, mode, t200_ms_dcch, t200_ms_acch, GSM_LCHAN_SDCCH, NULL);
}
/*! initialize a LAPDm channel and all its channels
@@ -226,14 +247,42 @@ void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode)
int lapdm_channel_init2(struct lapdm_channel *lc, enum lapdm_mode mode,
const int *t200_ms_dcch, const int *t200_ms_acch, enum gsm_chan_t chan_t)
{
+ return lapdm_channel_init3(lc, mode, t200_ms_dcch, t200_ms_acch, chan_t, NULL);
+}
+
+/*! initialize a LAPDm channel and all its channels
+ * \param[in] lc \ref lapdm_channel to be initialized
+ * \param[in] mode \ref lapdm_mode (BTS/MS)
+ * \param[in] t200_ms_dcch per-SAPI array of T200 in milli-seconds for DCCH
+ * \param[in] t200_ms_acch per-SAPI array of T200 in milli-seconds for SACCH
+ * \param[in] chan_t GSM channel type (to correctly set N200)
+ * \param[in] name_pfx human-readable name (copied by function + extended with ACCH/DCCH)
+ */
+int lapdm_channel_init3(struct lapdm_channel *lc, enum lapdm_mode mode,
+ const int *t200_ms_dcch, const int *t200_ms_acch, enum gsm_chan_t chan_t,
+ const char *name_pfx)
+{
int n200_dcch = get_n200_dcch(chan_t);
+ char namebuf[256];
+ char *name = NULL;
+
if (n200_dcch < 0)
return -EINVAL;
- lapdm_entity_init2(&lc->lapdm_acch, mode, t200_ms_acch, N200_TR_SACCH);
+ osmo_talloc_replace_string(tall_lapd_ctx, &lc->name, name_pfx);
+
+ if (name_pfx) {
+ snprintf(namebuf, sizeof(namebuf), "%s[ACCH]", name_pfx);
+ name = namebuf;
+ }
+ lapdm_entity_init3(&lc->lapdm_acch, mode, t200_ms_acch, N200_TR_SACCH, name);
lc->lapdm_acch.lapdm_ch = lc;
- lapdm_entity_init2(&lc->lapdm_dcch, mode, t200_ms_dcch, n200_dcch);
+ if (name_pfx) {
+ snprintf(namebuf, sizeof(namebuf), "%s[DCCH]", name_pfx);
+ name = namebuf;
+ }
+ lapdm_entity_init3(&lc->lapdm_dcch, mode, t200_ms_dcch, n200_dcch, name);
lc->lapdm_dcch.lapdm_ch = lc;
return 0;
@@ -248,6 +297,7 @@ void lapdm_entity_exit(struct lapdm_entity *le)
for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
dl = &le->datalink[i];
lapd_dl_exit(&dl->dl);
+ msgb_queue_free(&dl->tx_ui_queue);
}
}
@@ -286,8 +336,8 @@ static void lapdm_pad_msgb(struct msgb *msg, uint8_t n201)
return;
}
- data = msgb_put(msg, pad_len);
- memset(data, 0x2B, pad_len);
+ data = msgb_put(msg, pad_len); /* TODO: random padding */
+ memset(data, GSM_MACBLOCK_PADDING, pad_len);
}
/* input function that L2 calls when sending messages up to L3 */
@@ -311,11 +361,23 @@ static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg,
/* if there is a pending message, queue it */
if (le->tx_pending || le->flags & LAPDM_ENT_F_POLLING_ONLY) {
+ struct msgb *old_msg;
+
+ /* In 'RTS' mode there can be only one message. */
+ if (le->flags & LAPDM_ENT_F_RTS) {
+ /* Overwrite existing message by removing it first. */
+ if ((old_msg = msgb_dequeue(&dl->dl.tx_queue))) {
+ msgb_free(old_msg);
+ /* Reset V(S) to V(A), because there is no outstanding message now. */
+ dl->dl.v_send = dl->dl.v_ack;
+ }
+ }
+
*msgb_push(msg, 1) = pad;
*msgb_push(msg, 1) = link_id;
*msgb_push(msg, 1) = chan_nr;
msgb_enqueue(&dl->dl.tx_queue, msg);
- return -EBUSY;
+ return 0;
}
osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
@@ -330,7 +392,87 @@ static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg,
return le->l1_prim_cb(&pp.oph, le->l1_ctx);
}
-static struct msgb *tx_dequeue_msgb(struct lapdm_entity *le)
+static int tx_ph_data_enqueue_ui(struct lapdm_datalink *dl, struct msgb *msg,
+ uint8_t chan_nr, uint8_t link_id, uint8_t pad)
+{
+ struct lapdm_entity *le = dl->entity;
+ struct osmo_phsap_prim pp;
+
+ /* if there is a pending message, queue it */
+ if (le->tx_pending || le->flags & LAPDM_ENT_F_POLLING_ONLY) {
+ *msgb_push(msg, 1) = pad;
+ *msgb_push(msg, 1) = link_id;
+ *msgb_push(msg, 1) = chan_nr;
+ msgb_enqueue(&dl->tx_ui_queue, msg);
+ return 0;
+ }
+
+ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_REQUEST, msg);
+ pp.u.data.chan_nr = chan_nr;
+ pp.u.data.link_id = link_id;
+
+ /* send the frame now */
+ le->tx_pending = 0; /* disabled flow control */
+ lapdm_pad_msgb(msg, pad);
+
+ return le->l1_prim_cb(&pp.oph, le->l1_ctx);
+}
+
+/* Get transmit frame from queue, if any. In polling mode, indicate RTS to LAPD and start T200, if pending. */
+static struct msgb *tx_dequeue_msgb(struct lapdm_datalink *dl, uint32_t fn)
+{
+ struct msgb *msg;
+
+ /* Call RTS function of LAPD, to queue next frame. */
+ if (dl->entity->flags & LAPDM_ENT_F_RTS) {
+ struct lapd_msg_ctx lctx;
+ int rc;
+
+ /* Poll next frame. */
+ lctx.dl = &dl->dl;
+ rc = lapd_ph_rts_ind(&lctx);
+
+ /* If T200 has been started, calculate timeout FN. */
+ if (rc == 1) {
+ /* Set T200 in advance. */
+ dl->t200_timeout = fn;
+ ADD_MODULO(dl->t200_timeout, dl->t200_fn, GSM_MAX_FN);
+
+ LOGDL(&dl->dl, LOGL_INFO,
+ "T200 running from FN %"PRIu32" to FN %"PRIu32" (%"PRIu32" frames).\n",
+ fn, dl->t200_timeout, dl->t200_fn);
+ }
+ }
+
+ /* If there is no frame from LAPD, send UI frame, if any. */
+ msg = msgb_dequeue(&dl->dl.tx_queue);
+ if (msg)
+ LOGDL(&dl->dl, LOGL_INFO, "Sending frame from TX queue. (FN %"PRIu32")\n", fn);
+ else {
+ msg = msgb_dequeue(&dl->tx_ui_queue);
+ if (msg)
+ LOGDL(&dl->dl, LOGL_INFO, "Sending UI frame from TX queue. (FN %"PRIu32")\n", fn);
+ }
+ return msg;
+}
+
+/* Dequeue a Downlink message for DCCH (dedicated channel) */
+static struct msgb *tx_dequeue_dcch_msgb(struct lapdm_entity *le, uint32_t fn)
+{
+ struct msgb *msg;
+
+ /* SAPI=0 always has higher priority than SAPI=3 */
+ msg = tx_dequeue_msgb(&le->datalink[DL_SAPI0], fn);
+ if (msg == NULL) { /* no SAPI=0 messages, dequeue SAPI=3 (if any) */
+ msg = tx_dequeue_msgb(&le->datalink[DL_SAPI3], fn);
+ }
+
+ return msg;
+}
+
+/* Dequeue a Downlink message for ACCH (associated channel) */
+static struct msgb *tx_dequeue_acch_msgb(struct lapdm_entity *le, uint32_t fn)
{
struct lapdm_datalink *dl;
int last = le->last_tx_dequeue;
@@ -342,7 +484,7 @@ static struct msgb *tx_dequeue_msgb(struct lapdm_entity *le)
/* next */
i = (i + 1) % n;
dl = &le->datalink[i];
- if ((msg = msgb_dequeue(&dl->dl.tx_queue)))
+ if ((msg = tx_dequeue_msgb(dl, fn)))
break;
} while (i != last);
@@ -356,12 +498,17 @@ static struct msgb *tx_dequeue_msgb(struct lapdm_entity *le)
/*! dequeue a msg that's pending transmission via L1 and wrap it into
* a osmo_phsap_prim */
-int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp)
+int lapdm_phsap_dequeue_prim_fn(struct lapdm_entity *le, struct osmo_phsap_prim *pp, uint32_t fn)
{
struct msgb *msg;
uint8_t pad;
- msg = tx_dequeue_msgb(le);
+ /* Dequeue depending on channel type: DCCH or ACCH.
+ * See 3GPP TS 44.005, section 4.2.2 "Priority". */
+ if (le == &le->lapdm_ch->lapdm_dcch)
+ msg = tx_dequeue_dcch_msgb(le, fn);
+ else
+ msg = tx_dequeue_acch_msgb(le, fn);
if (!msg)
return -ENODEV;
@@ -383,6 +530,57 @@ int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp
return 0;
}
+static void lapdm_t200_fn_dl(struct lapdm_datalink *dl, uint32_t fn)
+{
+ uint32_t diff;
+
+ OSMO_ASSERT((dl->dl.lapd_flags & LAPD_F_RTS));
+
+ /* If T200 is running, check if it has fired. */
+ if (dl->dl.t200_rts != LAPD_T200_RTS_RUNNING)
+ return;
+
+ /* Calculate how many frames fn is behind t200_timeout.
+ * If it is negative (>= GSM_MAX_FN / 2), we have not reached t200_timeout yet.
+ * If it is 0 or positive, we reached it or we are a bit too late, which is not a problem.
+ */
+ diff = fn;
+ ADD_MODULO(diff, GSM_MAX_FN - dl->t200_timeout, GSM_MAX_FN);
+ if (diff >= GSM_MAX_FN / 2)
+ return;
+
+ LOGDL(&dl->dl, LOGL_INFO, "T200 timeout at FN %"PRIu32", detected at FN %"PRIu32".\n", dl->t200_timeout, fn);
+
+ lapd_t200_timeout(&dl->dl);
+}
+
+/*! Get receive frame number from L1. It is used to check the T200 timeout.
+ * This function is used if LAPD is in RTS mode only. (Applies if the LAPDM_ENT_F_POLLING_ONLY flag is set.)
+ * This function must be called for every valid or invalid data frame received.
+ * The frame number fn must be the frame number of the first burst of a data frame.
+ * This function must be called after the frame is delivered to layer 2.
+ * In case of TCH, this this function must be called for every speech frame received, meaning that there was no valid
+ * data frame. */
+void lapdm_t200_fn(struct lapdm_entity *le, uint32_t fn)
+{
+ unsigned int i;
+
+ if (!(le->flags & LAPDM_ENT_F_POLLING_ONLY)) {
+ LOGP(DLLAPD, LOGL_ERROR, "Function call not allowed on timer based T200.\n");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
+ lapdm_t200_fn_dl(&le->datalink[i], fn);
+}
+
+/*! dequeue a msg that's pending transmission via L1 and wrap it into
+ * a osmo_phsap_prim */
+int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp)
+{
+ return lapdm_phsap_dequeue_prim_fn(le, pp, 0);
+}
+
/* get next frame from the tx queue. because the ms has multiple datalinks,
* each datalink's queue is read round-robin.
*/
@@ -478,7 +676,7 @@ static int rsl_rll_error(uint8_t cause, struct lapdm_msg_ctx *mctx)
{
struct msgb *msg;
- LOGP(DLLAPD, LOGL_NOTICE, "sending MDL-ERROR-IND %d\n", cause);
+ LOGDL(&mctx->dl->dl, LOGL_NOTICE, "sending MDL-ERROR-IND %d\n", cause);
msg = rsl_rll_simple(RSL_MT_ERROR_IND, mctx->chan_nr, mctx->link_id, 0);
msgb_tlv_put(msg, RSL_IE_RLM_CAUSE, 1, &cause);
return rslms_sendmsg(msg, mctx->dl->entity);
@@ -523,7 +721,7 @@ static int send_rslms_dlsap(struct osmo_dlsap_prim *dp,
}
if (!rll_msg) {
- LOGP(DLLAPD, LOGL_ERROR, "Unsupported op %d, prim %d. Please "
+ LOGDL(dl, LOGL_ERROR, "Unsupported op %d, prim %d. Please "
"fix!\n", dp->oph.primitive, dp->oph.operation);
return -EINVAL;
}
@@ -592,7 +790,8 @@ static int update_pending_frames(struct lapd_msg_ctx *lctx)
LAPDm_CTRL_PF_BIT(msg->l2h[1]));
rc = 0;
} else if (LAPDm_CTRL_is_S(msg->l2h[1])) {
- LOGP(DLLAPD, LOGL_ERROR, "Supervisory frame in queue, this shouldn't happen\n");
+ msg->l2h[1] = LAPDm_CTRL_S(dl->v_recv, LAPDm_CTRL_S_BITS(msg->l2h[1]),
+ LAPDm_CTRL_PF_BIT(msg->l2h[1]));
}
}
@@ -634,7 +833,7 @@ static int lapdm_rx_not_permitted(const struct lapdm_entity *le,
/* input into layer2 (from layer 1) */
static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
- uint8_t chan_nr, uint8_t link_id)
+ uint8_t chan_nr, uint8_t link_id, uint32_t fn)
{
uint8_t cbits = chan_nr >> 3;
uint8_t sapi; /* we cannot take SAPI from link_id, as L1 has no clue */
@@ -649,6 +848,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
memset(&mctx, 0, sizeof(mctx));
mctx.chan_nr = chan_nr;
mctx.link_id = link_id;
+ mctx.fn = fn;
/* check for L1 chan_nr/link_id and determine LAPDm hdr format */
if (cbits == 0x10 || cbits == 0x12) {
@@ -660,17 +860,24 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
if (mctx.link_id & 0x40) {
/* It was received from network on SACCH */
- /* If UI on SACCH sent by BTS, lapdm_fmt must be B4 */
- if (le->mode == LAPDM_MODE_MS
- && LAPDm_CTRL_is_U(msg->l2h[3])
- && LAPDm_CTRL_U_BITS(msg->l2h[3]) == 0) {
+ /* A Short L3 header has both bits == 0. */
+ if (LAPDm_ADDR_SHORT_L2(msg->l2h[2]) == 0) {
+ mctx.lapdm_fmt = LAPDm_FMT_Bter;
+ n201 = N201_Bter_SACCH;
+ sapi = 0;
+ } else if (le->mode == LAPDM_MODE_MS
+ && LAPDm_CTRL_is_U(msg->l2h[3])
+ && LAPDm_CTRL_U_BITS(msg->l2h[3]) == 0) {
+ /* If UI on SACCH sent by BTS, lapdm_fmt must be B4 */
mctx.lapdm_fmt = LAPDm_FMT_B4;
n201 = N201_B4;
- LOGP(DLLAPD, LOGL_INFO, "fmt=B4\n");
+ /* sapi is found after two-btyte L1 header */
+ sapi = (msg->l2h[2] >> 2) & 7;
} else {
mctx.lapdm_fmt = LAPDm_FMT_B;
n201 = N201_AB_SACCH;
- LOGP(DLLAPD, LOGL_INFO, "fmt=B\n");
+ /* sapi is found after two-btyte L1 header */
+ sapi = (msg->l2h[2] >> 2) & 7;
}
/* SACCH frames have a two-byte L1 header that
* OsmocomBB L1 doesn't strip */
@@ -678,20 +885,24 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
mctx.ta_ind = msg->l2h[1];
msgb_pull(msg, 2);
msg->l2h += 2;
- sapi = (msg->l2h[0] >> 2) & 7;
} else {
- mctx.lapdm_fmt = LAPDm_FMT_B;
- LOGP(DLLAPD, LOGL_INFO, "fmt=B\n");
- n201 = N201_AB_SDCCH;
- sapi = (msg->l2h[0] >> 2) & 7;
+ /* A Short L3 header has both bits == 0. */
+ if (LAPDm_ADDR_SHORT_L2(msg->l2h[0]) == 0) {
+ mctx.lapdm_fmt = LAPDm_FMT_Bter;
+ n201 = N201_Bter_SDCCH;
+ sapi = 0;
+ } else {
+ mctx.lapdm_fmt = LAPDm_FMT_B;
+ n201 = N201_AB_SDCCH;
+ sapi = (msg->l2h[0] >> 2) & 7;
+ }
}
}
mctx.dl = lapdm_datalink_for_sapi(le, sapi);
/* G.2.1 No action on frames containing an unallocated SAPI. */
if (!mctx.dl) {
- LOGP(DLLAPD, LOGL_NOTICE, "Received frame for unsupported "
- "SAPI %d!\n", sapi);
+ LOGP(DLLAPD, LOGL_NOTICE, "Received frame for unsupported SAPI %d!\n", sapi);
msgb_free(msg);
return -EIO;
}
@@ -705,8 +916,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
mctx.link_id |= LAPDm_ADDR_SAPI(msg->l2h[0]);
/* G.2.3 EA bit set to "0" is not allowed in GSM */
if (!LAPDm_ADDR_EA(msg->l2h[0])) {
- LOGP(DLLAPD, LOGL_NOTICE, "EA bit 0 is not allowed in "
- "GSM\n");
+ LOGDL(lctx.dl, LOGL_NOTICE, "EA bit 0 is not allowed in GSM\n");
msgb_free(msg);
rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, &mctx);
return -EINVAL;
@@ -737,8 +947,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
/* 5.3.3 UI frames with invalid SAPI values shall be
* discarded
*/
- LOGP(DLLAPD, LOGL_INFO, "sapi=%u (discarding)\n",
- lctx.sapi);
+ LOGDL(lctx.dl, LOGL_INFO, "sapi=%u (discarding)\n", lctx.sapi);
msgb_free(msg);
return 0;
}
@@ -755,8 +964,7 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
* MDL-ERROR-INDICATION primitive with cause
* "frame not implemented" is sent to the
* mobile management entity. */
- LOGP(DLLAPD, LOGL_NOTICE, "we don't support "
- "multi-octet length\n");
+ LOGDL(lctx.dl, LOGL_NOTICE, "we don't support multi-octet length\n");
msgb_free(msg);
rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, &mctx);
return -EINVAL;
@@ -771,21 +979,21 @@ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le,
memcpy(&mctx.dl->mctx, &mctx, sizeof(mctx.dl->mctx));
rc =lapdm_rx_not_permitted(le, &lctx);
if (rc > 0) {
- LOGP(DLLAPD, LOGL_NOTICE, "received message not permitted\n");
+ LOGDL(lctx.dl, LOGL_NOTICE, "received message not permitted\n");
msgb_free(msg);
rsl_rll_error(rc, &mctx);
return -EINVAL;
}
/* send to LAPD */
+ LOGDL(lctx.dl, LOGL_DEBUG, "Frame received at FN %"PRIu32".\n", fn);
rc = lapd_ph_data_ind(msg, &lctx);
break;
case LAPDm_FMT_Bter:
- /* FIXME */
- msgb_free(msg);
- break;
+ /* fall-through */
case LAPDm_FMT_Bbis:
+ /* Update context so that users can read fields like fn: */
+ memcpy(&mctx.dl->mctx, &mctx, sizeof(mctx.dl->mctx));
/* directly pass up to layer3 */
- LOGP(DLLAPD, LOGL_INFO, "fmt=Bbis UI\n");
msg->l3h = msg->l2h;
msgb_pull_to_l3(msg);
rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, &mctx, msg);
@@ -838,56 +1046,33 @@ int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le)
if (oph->sap != SAP_GSM_PH) {
LOGP(DLLAPD, LOGL_ERROR, "primitive for unknown SAP %u\n",
oph->sap);
- rc = -ENODEV;
- goto free;
+ msgb_free(oph->msg);
+ return -ENODEV;
}
- switch (oph->primitive) {
- case PRIM_PH_DATA:
- if (oph->operation != PRIM_OP_INDICATION) {
- LOGP(DLLAPD, LOGL_ERROR, "PH_DATA is not INDICATION %u\n",
- oph->operation);
- rc = -ENODEV;
- goto free;
- }
+ switch (OSMO_PRIM_HDR(oph)) {
+ case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_INDICATION):
rc = l2_ph_data_ind(oph->msg, le, pp->u.data.chan_nr,
- pp->u.data.link_id);
+ pp->u.data.link_id, pp->u.data.fn);
break;
- case PRIM_PH_RTS:
- if (oph->operation != PRIM_OP_INDICATION) {
- LOGP(DLLAPD, LOGL_ERROR, "PH_RTS is not INDICATION %u\n",
- oph->operation);
- rc = -ENODEV;
- goto free;
- }
+ case OSMO_PRIM(PRIM_PH_RTS, PRIM_OP_INDICATION):
rc = l2_ph_data_conf(oph->msg, le);
break;
- case PRIM_PH_RACH:
- switch (oph->operation) {
- case PRIM_OP_INDICATION:
- rc = l2_ph_rach_ind(le, pp->u.rach_ind.ra, pp->u.rach_ind.fn,
- pp->u.rach_ind.acc_delay);
- break;
- case PRIM_OP_CONFIRM:
- rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn);
- break;
- default:
- rc = -EIO;
- goto free;
- }
+ case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION):
+ rc = l2_ph_rach_ind(le, pp->u.rach_ind.ra, pp->u.rach_ind.fn,
+ pp->u.rach_ind.acc_delay);
+ break;
+ case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_CONFIRM):
+ rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn);
break;
default:
LOGP(DLLAPD, LOGL_ERROR, "Unknown primitive %u\n",
oph->primitive);
- rc = -EINVAL;
- goto free;
+ msgb_free(oph->msg);
+ return -EINVAL;
}
return rc;
-
-free:
- msgb_free(oph->msg);
- return rc;
}
@@ -930,7 +1115,7 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl)
if (sapi != 0) {
/* According to clause 6, the contention resolution
* procedure is only permitted with SAPI value 0 */
- LOGP(DLLAPD, LOGL_ERROR, "SAPI != 0 but contention"
+ LOGDL(&dl->dl, LOGL_ERROR, "SAPI != 0 but contention"
"resolution (discarding)\n");
msgb_free(msg);
return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
@@ -946,7 +1131,7 @@ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl)
/* check if the layer3 message length exceeds N201 */
if (length > n201) {
- LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) "
+ LOGDL(&dl->dl, LOGL_ERROR, "frame too large: %d > N201(%d) "
"(discarding)\n", length, n201);
msgb_free(msg);
return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
@@ -973,9 +1158,10 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
uint8_t sapi = link_id & 7;
struct tlv_parsed tv;
int length, ui_bts;
+ bool use_b_ter;
if (!le) {
- LOGP(DLLAPD, LOGL_ERROR, "lapdm_datalink without entity error\n");
+ LOGDL(&dl->dl, LOGL_ERROR, "lapdm_datalink without entity error\n");
msgb_free(msg);
return -EMLINK;
}
@@ -992,35 +1178,38 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
le->tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER);
}
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
- LOGP(DLLAPD, LOGL_ERROR, "unit data request without message "
- "error\n");
+ LOGDL(&dl->dl, LOGL_ERROR, "unit data request without message error\n");
msgb_free(msg);
return -EINVAL;
}
msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO);
length = TLVP_LEN(&tv, RSL_IE_L3_INFO);
+ /* check for Bter frame */
+ use_b_ter = (length == ((link_id & 0x40) ? 21 : 23) && sapi == 0);
/* check if the layer3 message length exceeds N201 */
- if (length + ((link_id & 0x40) ? 4 : 2) + !ui_bts > 23) {
- LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) "
+ if (length + ((link_id & 0x40) ? 4 : 2) + !ui_bts > 23 && !use_b_ter) {
+ LOGDL(&dl->dl, LOGL_ERROR, "frame too large: %d > N201(%d) "
"(discarding)\n", length,
((link_id & 0x40) ? 18 : 20) + ui_bts);
msgb_free(msg);
return -EIO;
}
- LOGP(DLLAPD, LOGL_INFO, "sending unit data (tx_power=%d, ta=%d)\n",
- le->tx_power, le->ta);
+ LOGDL(&dl->dl, LOGL_INFO, "sending unit data (tx_power=%d, ta=%d)\n", le->tx_power, le->ta);
/* Remove RLL header from msgb and set length to L3-info */
msgb_pull_to_l3(msg);
msgb_trim(msg, length);
/* Push L1 + LAPDm header on msgb */
- msg->l2h = msgb_push(msg, 2 + !ui_bts);
- msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd);
- msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
- if (!ui_bts)
- msg->l2h[2] = LAPDm_LEN(length);
+ if (!use_b_ter) {
+ msg->l2h = msgb_push(msg, 2 + !ui_bts);
+ msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd);
+ msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UI, 0);
+ if (!ui_bts)
+ msg->l2h[2] = LAPDm_LEN(length);
+ } else
+ msg->l2h = msg->data;
if (link_id & 0x40) {
msg->l2h = msgb_push(msg, 2);
msg->l2h[0] = le->tx_power;
@@ -1028,7 +1217,7 @@ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl)
}
/* Tramsmit */
- return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, 23);
+ return tx_ph_data_enqueue_ui(dl, msg, chan_nr, link_id, 23);
}
/* L3 requests transfer of acknowledged information */
@@ -1041,8 +1230,7 @@ static int rslms_rx_rll_data_req(struct msgb *msg, struct lapdm_datalink *dl)
rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
- LOGP(DLLAPD, LOGL_ERROR, "data request without message "
- "error\n");
+ LOGDL(&dl->dl, LOGL_ERROR, "data request without message error\n");
msgb_free(msg);
return -EINVAL;
}
@@ -1068,7 +1256,7 @@ static int rslms_rx_rll_susp_req(struct msgb *msg, struct lapdm_datalink *dl)
struct osmo_dlsap_prim dp;
if (sapi != 0) {
- LOGP(DLLAPD, LOGL_ERROR, "SAPI != 0 while suspending\n");
+ LOGDL(&dl->dl, LOGL_ERROR, "SAPI != 0 while suspending\n");
msgb_free(msg);
return -EINVAL;
}
@@ -1098,7 +1286,7 @@ static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl)
rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh));
if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) {
- LOGP(DLLAPD, LOGL_ERROR, "resume without message error\n");
+ LOGDL(&dl->dl, LOGL_ERROR, "resume without message error\n");
msgb_free(msg);
return send_rll_simple(RSL_MT_REL_IND, &dl->mctx);
}
@@ -1235,7 +1423,7 @@ static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc)
case RSL_MT_SUSP_REQ:
case RSL_MT_RES_REQ:
case RSL_MT_RECON_REQ:
- LOGP(DLLAPD, LOGL_NOTICE, "(%p) RLL Message '%s' unsupported in BTS side LAPDm\n",
+ LOGP(DLLAPD, LOGL_NOTICE, "(%s) RLL Message '%s' unsupported in BTS side LAPDm\n",
lc->name, rsl_msg_name(msg_type));
msgb_free(msg);
return -EINVAL;
@@ -1262,14 +1450,14 @@ static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc)
/* This is triggered in abnormal error conditions where
* set_lapdm_context() was not called for the channel earlier. */
if (!dl->dl.lctx.dl) {
- LOGP(DLLAPD, LOGL_NOTICE, "(%p) RLL Message '%s' received without LAPDm context. (sapi %d)\n",
+ LOGP(DLLAPD, LOGL_NOTICE, "(%s) RLL Message '%s' received without LAPDm context. (sapi %d)\n",
lc->name, rsl_msg_name(msg_type), sapi);
msgb_free(msg);
return -EINVAL;
}
break;
default:
- LOGP(DLLAPD, LOGL_INFO, "(%p) RLL Message '%s' received. (sapi %d)\n",
+ LOGP(DLLAPD, LOGL_INFO, "(%s) RLL Message '%s' received. (sapi %d)\n",
lc->name, rsl_msg_name(msg_type), sapi);
}
@@ -1426,6 +1614,7 @@ void lapdm_entity_reset(struct lapdm_entity *le)
for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
dl = &le->datalink[i];
lapd_dl_reset(&dl->dl);
+ msgb_queue_free(&dl->tx_ui_queue);
}
}
@@ -1439,7 +1628,22 @@ void lapdm_channel_reset(struct lapdm_channel *lc)
/*! Set the flags of a LAPDm entity */
void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags)
{
+ unsigned int dl_flags = 0;
+ struct lapdm_datalink *dl;
+ int i;
+
le->flags = flags;
+
+ /* Set flags at LAPD. */
+ if (le->flags & LAPDM_ENT_F_RTS)
+ dl_flags |= LAPD_F_RTS;
+ if (le->flags & LAPDM_ENT_F_DROP_2ND_REJ)
+ dl_flags |= LAPD_F_DROP_2ND_REJ;
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++) {
+ dl = &le->datalink[i];
+ lapd_dl_set_flags(&dl->dl, dl_flags);
+ }
}
/*! Set the flags of all LAPDm entities in a LAPDm channel */
@@ -1449,4 +1653,25 @@ void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags)
lapdm_entity_set_flags(&lc->lapdm_acch, flags);
}
+/*! Set the T200 FN timer of a LAPDm entity
+ * \param[in] \ref lapdm_entity
+ * \param[in] t200_fn Array of T200 timeout in frame numbers for all SAPIs (0, 3) */
+void lapdm_entity_set_t200_fn(struct lapdm_entity *le, const uint32_t *t200_fn)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(le->datalink); i++)
+ le->datalink[i].t200_fn = t200_fn[i];
+}
+
+/*! Set the T200 FN timer of all LAPDm entities in a LAPDm channel
+ * \param[in] \ref lapdm_channel
+ * \param[in] t200_fn_dcch Array of T200 timeout in frame numbers for all SAPIs (0, 3) on SDCCH/FACCH
+ * \param[in] t200_fn_acch Array of T200 timeout in frame numbers for all SAPIs (0, 3) on SACCH */
+void lapdm_channel_set_t200_fn(struct lapdm_channel *lc, const uint32_t *t200_fn_dcch, const uint32_t *t200_fn_acch)
+{
+ lapdm_entity_set_t200_fn(&lc->lapdm_dcch, t200_fn_dcch);
+ lapdm_entity_set_t200_fn(&lc->lapdm_acch, t200_fn_acch);
+}
+
/*! @} */
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index a0e3b324..2c4c621c 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -40,6 +40,14 @@ abis_nm_put_sw_file;
abis_nm_get_sw_conf;
abis_nm_get_sw_desc_len;
+abis_nm_ipacc_freq_band_desc;
+abis_nm_ipacc_ciph_algo_desc;
+abis_nm_ipacc_chant_desc;
+abis_nm_ipacc_chanm_desc;
+abis_nm_ipacc_gprs_coding_desc;
+abis_nm_ipacc_rtp_feat_desc;
+abis_nm_ipacc_rsl_feat_desc;
+
osmo_sitype_strs;
osmo_c4;
osmo_get_rand_id;
@@ -86,6 +94,8 @@ gprs_service_t_strs;
gsm0341_build_msg;
+gsm0406_dlci_sapi_names;
+
gsm0480_create_notifySS;
gsm0480_create_unstructuredSS_Notify;
gsm0480_create_ussd_resp;
@@ -108,6 +118,8 @@ gsm0480_gen_reject;
gsm0502_calc_paging_group;
gsm0502_fn_remap;
+gsm0502_hop_seq_gen;
+gsm0502_fn2ccch_block;
gsm0503_xcch;
gsm0503_rach;
@@ -118,6 +130,11 @@ gsm0503_cs3;
gsm0503_cs2_np;
gsm0503_cs3_np;
gsm0503_tch_fr;
+gsm0503_tch_f24;
+gsm0503_tch_h24;
+gsm0503_tch_f48;
+gsm0503_tch_f96;
+gsm0503_tch_f144;
gsm0503_tch_hr;
gsm0503_tch_afs_12_2;
gsm0503_tch_afs_10_2;
@@ -133,6 +150,7 @@ gsm0503_tch_ahs_6_7;
gsm0503_tch_ahs_5_9;
gsm0503_tch_ahs_5_15;
gsm0503_tch_ahs_4_75;
+gsm0503_tch_axs_sid_update;
gsm0503_mcs1_dl_hdr;
gsm0503_mcs1_ul_hdr;
gsm0503_mcs1;
@@ -150,10 +168,14 @@ gsm0503_mcs8;
gsm0503_mcs9;
gsm0808_att_tlvdef;
+gsm0808_old_bss_to_new_bss_info_att_tlvdef;
gsm0808_bssap_name;
gsm0808_bssmap_name;
gsm0808_cause_name;
gsm0808_cause_class_name;
+gsm0808_get_cause;
+gsm0808_diagnostics_octet_location_str;
+gsm0808_diagnostics_bit_location_str;
gsm0808_create_ass;
gsm0808_create_ass2;
gsm0808_create_assignment_completed;
@@ -162,6 +184,7 @@ gsm0808_create_ass_compl2;
gsm0808_create_assignment_failure;
gsm0808_create_ass_fail;
gsm0808_create_cipher;
+gsm0808_create_cipher2;
gsm0808_create_cipher_complete;
gsm0808_create_cipher_reject;
gsm0808_create_cipher_reject_ext;
@@ -183,6 +206,7 @@ gsm0808_create_lcls_conn_ctrl_ack;
gsm0808_create_lcls_notification;
gsm0808_create_reset;
gsm0808_create_reset_ack;
+gsm0808_create_sapi_reject_cause;
gsm0808_create_sapi_reject;
gsm0808_create_handover_required;
gsm0808_create_handover_required_reject;
@@ -195,23 +219,64 @@ gsm0808_create_handover_succeeded;
gsm0808_create_handover_complete;
gsm0808_create_handover_failure;
gsm0808_create_handover_performed;
+gsm0808_create_vgcs_vbs_setup;
+gsm0808_create_vgcs_vbs_setup_ack;
+gsm0808_create_vgcs_vbs_setup_refuse;
+gsm0808_create_vgcs_vbs_assign_req;
+gsm0808_create_vgcs_vbs_assign_res;
+gsm0808_create_vgcs_vbs_assign_fail;
+gsm0808_create_uplink_request;
+gsm0808_create_uplink_request_ack;
+gsm0808_create_uplink_request_cnf;
+gsm0808_create_uplink_app_data;
+gsm0808_create_uplink_release_ind;
+gsm0808_create_uplink_reject_cmd;
+gsm0808_create_uplink_release_cmd;
+gsm0808_create_uplink_seized_cmd;
+gsm0808_create_vgcs_additional_info;
+gsm0808_create_vgcs_vbs_area_cell_info;
+gsm0808_create_vgcs_vbs_assign_stat;
+gsm0808_create_vgcs_sms;
+gsm0808_create_notification_data;
+gsm0808_create_common_id;
gsm0808_prepend_dtap_header;
gsm0808_enc_cause;
gsm0808_enc_aoip_trasp_addr;
gsm0808_dec_aoip_trasp_addr;
gsm0808_dec_osmux_cid;
gsm0808_enc_speech_codec;
+gsm0808_enc_speech_codec2;
gsm0808_dec_speech_codec;
gsm0808_enc_speech_codec_list;
+gsm0808_enc_speech_codec_list2;
gsm0808_dec_speech_codec_list;
gsm0808_enc_channel_type;
gsm0808_dec_channel_type;
gsm0808_enc_encrypt_info;
gsm0808_dec_encrypt_info;
+gsm0808_enc_kc128;
+gsm0808_dec_kc128;
gsm0808_enc_cell_id_list;
gsm0808_enc_cell_id_list2;
gsm0808_dec_cell_id_list;
gsm0808_dec_cell_id_list2;
+gsm0808_enc_group_callref;
+gsm0808_dec_group_callref;
+gsm0808_enc_priority;
+gsm0808_dec_priority;
+gsm0808_enc_vgcs_feature_flags;
+gsm0808_dec_vgcs_feature_flags;
+gsm0808_enc_data_identity;
+gsm0808_dec_data_identity;
+gsm0808_enc_msisdn;
+gsm0808_dec_msisdn;
+gsm0808_enc_talker_identity;
+gsm0808_dec_talker_identity;
+gsm0808_enc_assign_req;
+gsm0808_dec_assign_req;
+gsm0808_enc_cell_id_list_segment;
+gsm0808_dec_cell_id_list_segment;
+gsm0808_dec_call_id;
gsm0808_cell_id_list_add;
gsm0808_cell_id_to_list;
gsm0808_cell_id_to_cgi;
@@ -233,6 +298,8 @@ gsm0808_chan_type_to_speech_codec;
gsm0808_speech_codec_from_chan_type;
gsm0808_sc_cfg_from_gsm48_mr_cfg;
gsm48_mr_cfg_from_gsm0808_sc_cfg;
+gsm0808_amr_modes_from_cfg;
+gsm0808_amr_mode_names;
gsm0808_speech_codec_type_names;
gsm0808_permitted_speech_names;
gsm0808_chosen_enc_alg_names;
@@ -246,6 +313,9 @@ gsm0808_enc_lcls;
gsm0808_dec_lcls;
gsm0808_msgb_put_cell_id_u;
gsm0808_decode_cell_id_u;
+gsm0808_create_perform_location_request;
+gsm0808_create_perform_location_response;
+gsm0808_create_perform_location_abort;
gsm29118_msgb_alloc;
gsm29118_create_alert_req;
@@ -308,11 +378,17 @@ osmo_gsm48_rest_octets_si2quater_encode;
osmo_gsm48_rest_octets_si6_encode;
osmo_gsm48_rest_octets_si3_encode;
osmo_gsm48_rest_octets_si4_encode;
+osmo_gsm48_rest_octets_si13_decode;
osmo_gsm48_rest_octets_si13_encode;
osmo_gsm48_rest_octets_si3_decode;
+osmo_gsm48_rest_octets_si4_decode;
+osmo_gsm48_si1ro_nch_pos_encode;
+osmo_gsm48_si1ro_nch_pos_decode;
+gsm48_rr_short_pd_msg_name;
gsm48_rr_msg_name;
gsm48_cc_state_name;
gsm48_construct_ra;
+gsm48_ra_equal;
gsm48_encode_ra;
gsm48_hdr_gmm_cipherable;
gsm48_decode_bcd_number;
@@ -326,6 +402,7 @@ gsm48_decode_cccap;
gsm48_decode_connected;
gsm48_decode_facility;
gsm48_decode_freq_list;
+gsm48_decode_classmark3;
gsm48_decode_keypad;
gsm48_decode_lai;
gsm48_decode_notify;
@@ -356,6 +433,20 @@ gsm48_generate_mid;
gsm48_generate_mid_from_imsi;
gsm48_generate_mid_from_tmsi;
gsm48_mi_to_string;
+gsm48_chan_mode_to_vamos;
+gsm48_chan_mode_to_non_vamos;
+osmo_mobile_identity_to_str_buf;
+osmo_mobile_identity_to_str_c;
+osmo_mobile_identity_cmp;
+osmo_mobile_identity_decode;
+osmo_mobile_identity_decode_from_l3_buf;
+osmo_mobile_identity_decode_from_l3;
+osmo_mobile_identity_encoded_len;
+osmo_mobile_identity_encode_buf;
+osmo_mobile_identity_encode_msgb;
+osmo_routing_area_id_decode;
+osmo_routing_area_id_encode_buf;
+osmo_routing_area_id_encode_msgb;
gsm48_mm_att_tlvdef;
gsm48_number_of_paging_subchannels;
gsm48_parse_ra;
@@ -372,6 +463,7 @@ gsm48_generate_lai2;
gsm48_decode_lai2;
osmo_bts_features_descs;
osmo_bts_feature_name;
+osmo_bts_features_names;
osmo_plmn_to_bcd;
osmo_plmn_from_bcd;
osmo_mcc_name;
@@ -384,16 +476,27 @@ osmo_plmn_name;
osmo_plmn_name_buf;
osmo_plmn_name_c;
osmo_plmn_name2;
+osmo_lai_cmp;
osmo_lai_name;
osmo_lai_name_buf;
osmo_lai_name_c;
+osmo_rai_cmp;
osmo_rai_name;
osmo_rai_name_buf;
osmo_rai_name_c;
+osmo_rai_name2;
+osmo_rai_name2_buf;
+osmo_rai_name2_c;
+osmo_cgi_cmp;
osmo_cgi_name;
osmo_cgi_name_buf;
osmo_cgi_name_c;
osmo_cgi_name2;
+osmo_cgi_ps_cmp;
+osmo_cgi_ps_name;
+osmo_cgi_ps_name_buf;
+osmo_cgi_ps_name_c;
+osmo_cgi_ps_name2;
osmo_gummei_name;
osmo_gummei_name_buf;
osmo_gummei_name_c;
@@ -417,6 +520,13 @@ gsm48_pdisc_msgtype_name_buf;
gsm48_pdisc_msgtype_name_c;
gsm48_reject_value_names;
+osmo_gsm44068_msg_type_names;
+osmo_gsm44068_priority_level_names;
+osmo_gsm44068_cause_names;
+osmo_gsm44068_call_state_names;
+osmo_gsm44068_talker_priority_names;
+osmo_gsm44068_att_tlvdef;
+
gsm_7bit_decode;
gsm_7bit_decode_ussd;
gsm_7bit_encode;
@@ -442,13 +552,17 @@ gsm_gsmtime2fn;
osmo_dump_gsmtime;
osmo_dump_gsmtime_buf;
osmo_dump_gsmtime_c;
+gsm_rfn2fn;
gsm_milenage;
gsm_septet_encode;
+gsm_septet_pack;
gsm_septets2octets;
lapd_dl_exit;
lapd_dl_init;
+lapd_dl_init2;
+lapd_dl_set_name;
lapd_dl_reset;
lapd_msgb_alloc;
lapd_ph_data_ind;
@@ -459,6 +573,7 @@ lapd_state_names;
lapdm_channel_exit;
lapdm_channel_init;
lapdm_channel_init2;
+lapdm_channel_init3;
lapdm_channel_reset;
lapdm_channel_set_flags;
lapdm_channel_set_l1;
@@ -468,10 +583,15 @@ lapdm_datalink_for_sapi;
lapdm_entity_exit;
lapdm_entity_init;
lapdm_entity_init2;
+lapdm_entity_init3;
lapdm_entity_reset;
lapdm_entity_set_flags;
lapdm_entity_set_mode;
+lapdm_entity_set_t200_fn;
+lapdm_channel_set_t200_fn;
lapdm_phsap_dequeue_prim;
+lapdm_phsap_dequeue_prim_fn;
+lapdm_t200_fn;
lapdm_phsap_up;
lapdm_rslms_recvmsg;
@@ -495,14 +615,23 @@ osmo_a5_2;
osmo_auth_alg_name;
osmo_auth_alg_parse;
osmo_auth_gen_vec;
+osmo_auth_gen_vec2;
osmo_auth_gen_vec_auts;
+osmo_auth_gen_vec_auts2;
osmo_auth_3g_from_2g;
osmo_auth_load;
osmo_auth_register;
osmo_auth_supported;
+osmo_auth_c2;
osmo_auth_c3;
osmo_sub_auth_type_names;
+osmo_kdf_kc128;
+osmo_kdf_kasme;
+osmo_kdf_enb;
+osmo_kdf_nh;
+osmo_kdf_nas;
+
osmo_rsl2sitype;
osmo_sitype2rsl;
@@ -554,6 +683,11 @@ osmo_shift_tlv;
osmo_match_shift_tlv;
osmo_shift_lv;
+osmo_tlv_prot_msg_name;
+osmo_tlv_prot_ie_name;
+osmo_tlv_prot_validate_tp;
+osmo_tlv_prot_parse;
+
gan_msgt_vals;
gan_pdisc_vals;
@@ -659,6 +793,84 @@ osmo_cbsp_encode;
osmo_cbsp_decode;
osmo_cbsp_recv_buffered;
osmo_cbsp_errstr;
+osmo_cbsp_segmentation_cb;
+
+osmo_i460_demux_in;
+osmo_i460_mux_enqueue;
+osmo_i460_mux_out;
+osmo_i460_subchan_add;
+osmo_i460_subchan_del;
+osmo_i460_ts_init;
+
+osmo_nri_v_validate;
+osmo_nri_v_matches_ranges;
+osmo_nri_v_limit_by_ranges;
+osmo_tmsi_nri_v_get;
+osmo_tmsi_nri_v_set;
+osmo_tmsi_nri_v_limit_by_ranges;
+osmo_nri_range_validate;
+osmo_nri_range_overlaps_ranges;
+osmo_nri_ranges_alloc;
+osmo_nri_ranges_free;
+osmo_nri_ranges_add;
+osmo_nri_ranges_del;
+osmo_nri_ranges_vty_add;
+osmo_nri_ranges_vty_del;
+osmo_nri_ranges_to_str_buf;
+osmo_nri_ranges_to_str_c;
+
+osmo_bssmap_le_msgt_names;
+osmo_bssap_le_enc;
+osmo_bssap_le_dec;
+osmo_lcs_cause_enc;
+osmo_lcs_cause_dec;
+osmo_bssmap_le_ie_enc_location_type;
+osmo_bssmap_le_ie_dec_location_type;
+osmo_bssmap_le_msgt;
+osmo_bssap_le_pdu_to_str_buf;
+osmo_bssap_le_pdu_to_str_c;
+
+osmo_bsslap_enc;
+osmo_bsslap_dec;
+osmo_bsslap_msgt_names;
+
+osmo_gad_enc;
+osmo_gad_dec;
+osmo_gad_to_str_buf;
+osmo_gad_to_str_c;
+osmo_gad_enc_lat;
+osmo_gad_dec_lat;
+osmo_gad_enc_lon;
+osmo_gad_dec_lon;
+osmo_gad_enc_unc;
+osmo_gad_dec_unc;
+osmo_gad_raw_read;
+osmo_gad_raw_write;
+osmo_gad_type_names;
+
+osmo_iuup_compute_header_crc;
+osmo_iuup_compute_payload_crc;
+osmo_iuup_instance_alloc;
+osmo_iuup_instance_free;
+osmo_iuup_instance_set_user_prim_cb;
+osmo_iuup_instance_set_transport_prim_cb;
+osmo_iuup_tnl_prim_up;
+osmo_iuup_rnl_prim_down;
+osmo_iuup_rnl_prim_alloc;
+osmo_iuup_tnl_prim_alloc;
+
+osmo_csd_12k_6k_decode_frame;
+osmo_csd_12k_6k_encode_frame;
+osmo_csd_3k6_decode_frame;
+osmo_csd_3k6_encode_frame;
+osmo_csd_ubit_dump;
+
+osmo_rlp_decode;
+osmo_rlp_encode;
+osmo_rlp_fcs_compute;
+osmo_rlp_ftype_s_vals;
+osmo_rlp_ftype_u_vals;
+osmo_rlp_ftype_vals;
local: *;
};
diff --git a/src/gsm/milenage/milenage.c b/src/gsm/milenage/milenage.c
index 3c14ab96..ba9ab33b 100644
--- a/src/gsm/milenage/milenage.c
+++ b/src/gsm/milenage/milenage.c
@@ -244,19 +244,13 @@ int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc)
{
u8 res[8], ck[16], ik[16];
- int i;
if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL))
return -1;
osmo_auth_c3(kc, ck, ik);
+ osmo_auth_c2(sres, res, sizeof(res), 1);
-#ifdef GSM_MILENAGE_ALT_SRES
- os_memcpy(sres, res, 4);
-#else /* GSM_MILENAGE_ALT_SRES */
- for (i = 0; i < 4; i++)
- sres[i] = res[i] ^ res[i + 4];
-#endif /* GSM_MILENAGE_ALT_SRES */
return 0;
}
diff --git a/src/gsm/mncc.c b/src/gsm/mncc.c
index 938cf9a6..8a7dc4d6 100644
--- a/src/gsm/mncc.c
+++ b/src/gsm/mncc.c
@@ -20,7 +20,7 @@
*
*/
-#include "../config.h"
+#include "config.h"
#ifdef HAVE_SYS_SOCKET_H
diff --git a/src/gsm/rlp.c b/src/gsm/rlp.c
new file mode 100644
index 00000000..1e90a689
--- /dev/null
+++ b/src/gsm/rlp.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2022-2023 Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*! \addtogroup rlp
+ * @{
+ * RLP (Radio Link Protocol) as per 3GPP TS 24.022
+ *
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/gsm/rlp.h>
+
+const struct value_string osmo_rlp_ftype_vals[] = {
+ { OSMO_RLP_FT_U, "U" },
+ { OSMO_RLP_FT_S, "S" },
+ { OSMO_RLP_FT_IS, "IS" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_rlp_ftype_u_vals[] = {
+ { OSMO_RLP_U_FT_SABM, "SABM" },
+ { OSMO_RLP_U_FT_UA, "UA" },
+ { OSMO_RLP_U_FT_DISC, "DISC" },
+ { OSMO_RLP_U_FT_DM, "DM" },
+ { OSMO_RLP_U_FT_NULL, "NULL" },
+ { OSMO_RLP_U_FT_UI, "UI" },
+ { OSMO_RLP_U_FT_XID, "XID" },
+ { OSMO_RLP_U_FT_TEST, "TEST" },
+ { OSMO_RLP_U_FT_REMAP, "REMAP" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_rlp_ftype_s_vals[] = {
+ { OSMO_RLP_S_FT_RR, "RR" },
+ { OSMO_RLP_S_FT_REJ, "REJ" },
+ { OSMO_RLP_S_FT_RNR, "RNR" },
+ { OSMO_RLP_S_FT_SREJ, "SREJ" },
+ { 0, NULL }
+};
+
+/* number of bytes used up by FCS */
+#define FCS_SIZE_BYTES 3
+
+/*! decode a RLP frame into its abstract representation. Doesn't check FCS correctness.
+ * \param[out] out caller-allocated memory for output of decoded frame
+ * \param[in] version RLP version number to use when decoding
+ * \param[in] data raw RLP frame input data
+ * \param[in] data_len length of data (in octets); must be 30 (240bit) or 72 (576bit)
+ * \returns 0 in case of success; negative on error */
+int osmo_rlp_decode(struct osmo_rlp_frame_decoded *out, uint8_t version, const uint8_t *data, size_t data_len)
+{
+ const uint8_t hdr_len = 2; /* will become a variable when we introduce v2 support */
+ uint8_t n_s, n_r;
+
+ if (data_len != 240/8 && data_len != 576/8)
+ return -EINVAL;
+
+ /* we only support version 0+1 so far */
+ if (version >= 2)
+ return -ENOTSUP;
+
+ memset(out, 0, sizeof(*out));
+ out->version = version;
+
+ out->c_r = data[0] & 1;
+ n_s = (data[0] >> 3) | (data[1] & 1) << 5;
+ n_r = (data[1] >> 2);
+ out->fcs = (data[data_len-1] << 16) | (data[data_len-2]) << 8 | (data[data_len-3] << 0);
+ out->p_f = (data[1] >> 1) & 1;
+
+ switch (n_s) {
+ case 0x3f:
+ out->ftype = OSMO_RLP_FT_U;
+ out->u_ftype = n_r & 0x1f;
+ if (out->u_ftype == OSMO_RLP_U_FT_XID) {
+ memcpy(out->info, data + hdr_len, data_len - (hdr_len + FCS_SIZE_BYTES));
+ out->info_len = data_len - (hdr_len + FCS_SIZE_BYTES);
+ }
+ break;
+ case 0x3e:
+ out->ftype = OSMO_RLP_FT_S;
+ out->s_ftype = (data[0] >> 1) & 3;
+ out->n_r = n_r;
+ break;
+ default:
+ out->ftype = OSMO_RLP_FT_IS;
+ out->s_ftype = (data[0] >> 1) & 3;
+ out->n_s = n_s;
+ out->n_r = n_r;
+ memcpy(out->info, data + hdr_len, data_len - (hdr_len + FCS_SIZE_BYTES));
+ out->info_len = data_len - (2 + 3);
+ break;
+ }
+
+ return 0;
+}
+
+/*! encode a RLP frame from its abstract representation. Generates FCS.
+ * \param[out] out caller-allocated output buffer
+ * \param[in] out_size size of output buffer (in octets); must be 30 (240bit) or 72 (576bit)
+ * \param[in] in decoded RLP frame which is to be encoded
+ * \returns number of output bytes used; negative on error */
+int osmo_rlp_encode(uint8_t *out, size_t out_size, const struct osmo_rlp_frame_decoded *in)
+{
+ const uint8_t hdr_len = 2; /* will become a variable when we introduce v2 support */
+ uint8_t n_s, n_r, s_bits;
+ uint32_t fcs;
+
+ /* we only support version 0+1 so far */
+ if (in->version >= 2)
+ return -ENOTSUP;
+
+ if (out_size != 240/8 && out_size != 576/8)
+ return -EINVAL;
+
+ memset(out, 0, out_size);
+
+ if (in->c_r)
+ out[0] |= 0x01;
+ if (in->p_f)
+ out[1] |= 0x02;
+
+ switch (in->ftype) {
+ case OSMO_RLP_FT_U:
+ n_s = 0x3f;
+ n_r = in->u_ftype;
+ s_bits = 0;
+ if (in->u_ftype == OSMO_RLP_U_FT_XID) {
+ if (in->info_len > out_size - (hdr_len + FCS_SIZE_BYTES))
+ return -EINVAL;
+ memcpy(out+hdr_len, in->info, in->info_len);
+ }
+ break;
+ case OSMO_RLP_FT_S:
+ n_s = 0x3e;
+ n_r = in->n_r;
+ s_bits = in->s_ftype;
+ break;
+ case OSMO_RLP_FT_IS:
+ /* we only support 240 bit so far */
+ if (in->info_len > out_size - (hdr_len + FCS_SIZE_BYTES))
+ return -EINVAL;
+ n_s = in->n_s;
+ n_r = in->n_r;
+ s_bits = in->s_ftype;
+ memcpy(out+hdr_len, in->info, in->info_len);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* patch N(S) into output data */
+ out[0] |= (n_s & 0x1F) << 3;
+ out[1] |= (n_s & 0x20) >> 5;
+
+ /* patch N(R) / M-bits into output data */
+ out[1] |= (n_r & 0x3f) << 2;
+
+ /* patch S-bits into output data */
+ out[0] |= (s_bits & 3) << 1;
+
+ /* compute FCS + add it to end of frame */
+ fcs = osmo_rlp_fcs_compute(out, out_size - FCS_SIZE_BYTES);
+ out[out_size - 3] = (fcs >> 0) & 0xff;
+ out[out_size - 2] = (fcs >> 8) & 0xff;
+ out[out_size - 1] = (fcs >> 16) & 0xff;
+
+ return out_size;
+}
+
+
+static const uint32_t rlp_fcs_table[256] = {
+ 0x00B29D2D, 0x00643A5B, 0x0044D87A, 0x00927F0C, 0x00051C38, 0x00D3BB4E, 0x00F3596F, 0x0025FE19,
+ 0x008694BC, 0x005033CA, 0x0070D1EB, 0x00A6769D, 0x003115A9, 0x00E7B2DF, 0x00C750FE, 0x0011F788,
+ 0x00DA8E0F, 0x000C2979, 0x002CCB58, 0x00FA6C2E, 0x006D0F1A, 0x00BBA86C, 0x009B4A4D, 0x004DED3B,
+ 0x00EE879E, 0x003820E8, 0x0018C2C9, 0x00CE65BF, 0x0059068B, 0x008FA1FD, 0x00AF43DC, 0x0079E4AA,
+ 0x0062BB69, 0x00B41C1F, 0x0094FE3E, 0x00425948, 0x00D53A7C, 0x00039D0A, 0x00237F2B, 0x00F5D85D,
+ 0x0056B2F8, 0x0080158E, 0x00A0F7AF, 0x007650D9, 0x00E133ED, 0x0037949B, 0x001776BA, 0x00C1D1CC,
+ 0x000AA84B, 0x00DC0F3D, 0x00FCED1C, 0x002A4A6A, 0x00BD295E, 0x006B8E28, 0x004B6C09, 0x009DCB7F,
+ 0x003EA1DA, 0x00E806AC, 0x00C8E48D, 0x001E43FB, 0x008920CF, 0x005F87B9, 0x007F6598, 0x00A9C2EE,
+ 0x0049DA1E, 0x009F7D68, 0x00BF9F49, 0x0069383F, 0x00FE5B0B, 0x0028FC7D, 0x00081E5C, 0x00DEB92A,
+ 0x007DD38F, 0x00AB74F9, 0x008B96D8, 0x005D31AE, 0x00CA529A, 0x001CF5EC, 0x003C17CD, 0x00EAB0BB,
+ 0x0021C93C, 0x00F76E4A, 0x00D78C6B, 0x00012B1D, 0x00964829, 0x0040EF5F, 0x00600D7E, 0x00B6AA08,
+ 0x0015C0AD, 0x00C367DB, 0x00E385FA, 0x0035228C, 0x00A241B8, 0x0074E6CE, 0x005404EF, 0x0082A399,
+ 0x0099FC5A, 0x004F5B2C, 0x006FB90D, 0x00B91E7B, 0x002E7D4F, 0x00F8DA39, 0x00D83818, 0x000E9F6E,
+ 0x00ADF5CB, 0x007B52BD, 0x005BB09C, 0x008D17EA, 0x001A74DE, 0x00CCD3A8, 0x00EC3189, 0x003A96FF,
+ 0x00F1EF78, 0x0027480E, 0x0007AA2F, 0x00D10D59, 0x00466E6D, 0x0090C91B, 0x00B02B3A, 0x00668C4C,
+ 0x00C5E6E9, 0x0013419F, 0x0033A3BE, 0x00E504C8, 0x007267FC, 0x00A4C08A, 0x008422AB, 0x005285DD,
+ 0x001F18F0, 0x00C9BF86, 0x00E95DA7, 0x003FFAD1, 0x00A899E5, 0x007E3E93, 0x005EDCB2, 0x00887BC4,
+ 0x002B1161, 0x00FDB617, 0x00DD5436, 0x000BF340, 0x009C9074, 0x004A3702, 0x006AD523, 0x00BC7255,
+ 0x00770BD2, 0x00A1ACA4, 0x00814E85, 0x0057E9F3, 0x00C08AC7, 0x00162DB1, 0x0036CF90, 0x00E068E6,
+ 0x00430243, 0x0095A535, 0x00B54714, 0x0063E062, 0x00F48356, 0x00222420, 0x0002C601, 0x00D46177,
+ 0x00CF3EB4, 0x001999C2, 0x00397BE3, 0x00EFDC95, 0x0078BFA1, 0x00AE18D7, 0x008EFAF6, 0x00585D80,
+ 0x00FB3725, 0x002D9053, 0x000D7272, 0x00DBD504, 0x004CB630, 0x009A1146, 0x00BAF367, 0x006C5411,
+ 0x00A72D96, 0x00718AE0, 0x005168C1, 0x0087CFB7, 0x0010AC83, 0x00C60BF5, 0x00E6E9D4, 0x00304EA2,
+ 0x00932407, 0x00458371, 0x00656150, 0x00B3C626, 0x0024A512, 0x00F20264, 0x00D2E045, 0x00044733,
+ 0x00E45FC3, 0x0032F8B5, 0x00121A94, 0x00C4BDE2, 0x0053DED6, 0x008579A0, 0x00A59B81, 0x00733CF7,
+ 0x00D05652, 0x0006F124, 0x00261305, 0x00F0B473, 0x0067D747, 0x00B17031, 0x00919210, 0x00473566,
+ 0x008C4CE1, 0x005AEB97, 0x007A09B6, 0x00ACAEC0, 0x003BCDF4, 0x00ED6A82, 0x00CD88A3, 0x001B2FD5,
+ 0x00B84570, 0x006EE206, 0x004E0027, 0x0098A751, 0x000FC465, 0x00D96313, 0x00F98132, 0x002F2644,
+ 0x00347987, 0x00E2DEF1, 0x00C23CD0, 0x00149BA6, 0x0083F892, 0x00555FE4, 0x0075BDC5, 0x00A31AB3,
+ 0x00007016, 0x00D6D760, 0x00F63541, 0x00209237, 0x00B7F103, 0x00615675, 0x0041B454, 0x00971322,
+ 0x005C6AA5, 0x008ACDD3, 0x00AA2FF2, 0x007C8884, 0x00EBEBB0, 0x003D4CC6, 0x001DAEE7, 0x00CB0991,
+ 0x00686334, 0x00BEC442, 0x009E2663, 0x00488115, 0x00DFE221, 0x00094557, 0x0029A776, 0x00FF0000
+};
+
+/*! compute RLP FCS according to 3GPP TS 24.022 Section 4.4.
+ * \param[in] in input data over which to compute FCS
+ * \param[in] in_len length of input data (in octets)
+ * \returns computed frame check sequence (FCS). */
+uint32_t osmo_rlp_fcs_compute(const uint8_t *in, size_t in_len)
+{
+ uint32_t divider = 0;
+ size_t i;
+
+ for (i = 0; i < in_len; i++) {
+ uint8_t input = in[i] ^ (divider & 0xff);
+ divider = (divider >> 8) ^ rlp_fcs_table[input];
+ }
+
+ return divider;
+}
+
+/*! @} */
diff --git a/src/gsm/rsl.c b/src/gsm/rsl.c
index 5534aa2a..fbba982a 100644
--- a/src/gsm/rsl.c
+++ b/src/gsm/rsl.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdint.h>
@@ -126,6 +122,11 @@ const struct tlv_definition rsl_att_tlvdef = {
[RSL_IE_TFO_STATUS] = { TLV_TYPE_TV },
[RSL_IE_LLP_APDU] = { TLV_TYPE_TLV },
[RSL_IE_SIEMENS_MRPCI] = { TLV_TYPE_TV },
+ [RSL_IE_OSMO_REP_ACCH_CAP] = { TLV_TYPE_TLV },
+ [RSL_IE_OSMO_TRAINING_SEQUENCE] = { TLV_TYPE_TLV },
+ [RSL_IE_OSMO_TEMP_OVP_ACCH_CAP] = { TLV_TYPE_TLV },
+ [RSL_IE_OSMO_OSMUX_CID] = { TLV_TYPE_TLV },
+ [RSL_IE_IPAC_SRTP_CONFIG] = { TLV_TYPE_TLV },
[RSL_IE_IPAC_PROXY_UDP] = { TLV_TYPE_FIXED, 2 },
[RSL_IE_IPAC_BSCMPL_TOUT] = { TLV_TYPE_TV },
[RSL_IE_IPAC_REMOTE_IP] = { TLV_TYPE_FIXED, 4 },
@@ -134,6 +135,8 @@ const struct tlv_definition rsl_att_tlvdef = {
[RSL_IE_IPAC_LOCAL_PORT] = { TLV_TYPE_FIXED, 2 },
[RSL_IE_IPAC_SPEECH_MODE] = { TLV_TYPE_TV },
[RSL_IE_IPAC_LOCAL_IP] = { TLV_TYPE_FIXED, 4 },
+ [RSL_IE_IPAC_CONN_STAT] = { TLV_TYPE_TLV, 28 },
+ [RSL_IE_IPAC_HO_C_PARMS] = { TLV_TYPE_TLV },
[RSL_IE_IPAC_CONN_ID] = { TLV_TYPE_FIXED, 2 },
[RSL_IE_IPAC_RTP_CSD_FMT] = { TLV_TYPE_TV },
[RSL_IE_IPAC_RTP_JIT_BUF] = { TLV_TYPE_FIXED, 2 },
@@ -157,6 +160,7 @@ uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot)
switch (type) {
case RSL_CHAN_Lm_ACCHs:
+ case RSL_CHAN_OSMO_VAMOS_Lm_ACCHs:
subch &= 0x01;
break;
case RSL_CHAN_SDCCH4_ACCH:
@@ -185,38 +189,34 @@ int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *tim
{
*timeslot = chan_nr & 0x7;
- if ((chan_nr & 0xf8) == RSL_CHAN_Bm_ACCHs) {
- *type = RSL_CHAN_Bm_ACCHs;
- *subch = 0;
- } else if ((chan_nr & 0xf0) == RSL_CHAN_Lm_ACCHs) {
- *type = RSL_CHAN_Lm_ACCHs;
- *subch = (chan_nr >> 3) & 0x1;
- } else if ((chan_nr & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
- *type = RSL_CHAN_SDCCH4_ACCH;
- *subch = (chan_nr >> 3) & 0x3;
- } else if ((chan_nr & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
- *type = RSL_CHAN_SDCCH8_ACCH;
- *subch = (chan_nr >> 3) & 0x7;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_BCCH) {
- *type = RSL_CHAN_BCCH;
- *subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_RACH) {
- *type = RSL_CHAN_RACH;
- *subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_PCH_AGCH) {
- *type = RSL_CHAN_PCH_AGCH;
- *subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_OSMO_PDCH) {
- *type = RSL_CHAN_OSMO_PDCH;
+ switch (chan_nr & RSL_CHAN_NR_MASK) {
+ case RSL_CHAN_Bm_ACCHs:
+ case RSL_CHAN_BCCH:
+ case RSL_CHAN_RACH:
+ case RSL_CHAN_PCH_AGCH:
+ case RSL_CHAN_OSMO_PDCH:
+ case RSL_CHAN_OSMO_CBCH4:
+ case RSL_CHAN_OSMO_CBCH8:
+ case RSL_CHAN_OSMO_VAMOS_Bm_ACCHs:
+ *type = chan_nr & RSL_CHAN_NR_MASK;
*subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_OSMO_CBCH4) {
- *type = RSL_CHAN_OSMO_CBCH4;
- *subch = 0;
- } else if ((chan_nr & 0xf8) == RSL_CHAN_OSMO_CBCH8) {
- *type = RSL_CHAN_OSMO_CBCH8;
- *subch = 0;
- } else
- return -EINVAL;
+ break;
+ default:
+ if ((chan_nr & 0xf0) == RSL_CHAN_Lm_ACCHs) {
+ *type = RSL_CHAN_Lm_ACCHs;
+ *subch = (chan_nr >> 3) & 0x1;
+ } else if ((chan_nr & 0xe0) == RSL_CHAN_SDCCH4_ACCH) {
+ *type = RSL_CHAN_SDCCH4_ACCH;
+ *subch = (chan_nr >> 3) & 0x3;
+ } else if ((chan_nr & 0xc0) == RSL_CHAN_SDCCH8_ACCH) {
+ *type = RSL_CHAN_SDCCH8_ACCH;
+ *subch = (chan_nr >> 3) & 0x7;
+ } else if ((chan_nr & 0xf0) == RSL_CHAN_OSMO_VAMOS_Lm_ACCHs) {
+ *type = RSL_CHAN_OSMO_VAMOS_Lm_ACCHs;
+ *subch = (chan_nr >> 3) & 0x1;
+ } else
+ return -EINVAL;
+ }
return 0;
}
@@ -232,26 +232,30 @@ char *rsl_chan_nr_str_buf(char *buf, size_t buf_len, uint8_t chan_nr)
int ts = chan_nr & 7;
uint8_t cbits = chan_nr >> 3;
- if (cbits == 0x01)
+ if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs)
snprintf(buf, buf_len, "TCH/F on TS%d", ts);
- else if ((cbits & 0x1e) == 0x02)
+ else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0))
snprintf(buf, buf_len, "TCH/H(%u) on TS%d", cbits & 0x01, ts);
- else if ((cbits & 0x1c) == 0x04)
+ else if ((cbits & 0x1c) == ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0))
snprintf(buf, buf_len, "SDCCH/4(%u) on TS%d", cbits & 0x03, ts);
- else if ((cbits & 0x18) == 0x08)
+ else if ((cbits & 0x18) == ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0))
snprintf(buf, buf_len, "SDCCH/8(%u) on TS%d", cbits & 0x07, ts);
- else if (cbits == 0x10)
+ else if (cbits == ABIS_RSL_CHAN_NR_CBITS_BCCH)
snprintf(buf, buf_len, "BCCH on TS%d", ts);
- else if (cbits == 0x11)
+ else if (cbits == ABIS_RSL_CHAN_NR_CBITS_RACH)
snprintf(buf, buf_len, "RACH on TS%d", ts);
- else if (cbits == 0x12)
+ else if (cbits == ABIS_RSL_CHAN_NR_CBITS_PCH_AGCH)
snprintf(buf, buf_len, "PCH/AGCH on TS%d", ts);
- else if (cbits == 0x18)
+ else if (cbits == ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH)
snprintf(buf, buf_len, "PDCH on TS%d", ts);
- else if (cbits == 0x19)
+ else if (cbits == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH4)
snprintf(buf, buf_len, "CBCH(SDCCH/4) on TS%d", ts);
- else if (cbits == 0x1a)
+ else if (cbits == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8)
snprintf(buf, buf_len, "CBCH(SDCCH/8) on TS%d", ts);
+ else if (cbits == ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Bm_ACCHs)
+ snprintf(buf, buf_len, "VAMOS TCH/F on TS%d", ts);
+ else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Lm_ACCHs(0))
+ snprintf(buf, buf_len, "VAMOS TCH/H(%u) on TS%d", cbits & 0x01, ts);
else
snprintf(buf, buf_len, "UNKNOWN on TS%d", ts);
@@ -264,7 +268,7 @@ char *rsl_chan_nr_str_buf(char *buf, size_t buf_len, uint8_t chan_nr)
*/
const char *rsl_chan_nr_str(uint8_t chan_nr)
{
- static __thread char str[20];
+ static __thread char str[32];
return rsl_chan_nr_str_buf(str, sizeof(str), chan_nr);
}
@@ -275,10 +279,10 @@ const char *rsl_chan_nr_str(uint8_t chan_nr)
*/
char *rsl_chan_nr_str_c(const void *ctx, uint8_t chan_nr)
{
- char *str = talloc_size(ctx, 20);
+ char *str = talloc_size(ctx, 32);
if (!str)
return NULL;
- return rsl_chan_nr_str_buf(str, 20, chan_nr);
+ return rsl_chan_nr_str_buf(str, 32, chan_nr);
}
static const struct value_string rsl_err_vals[] = {
@@ -295,6 +299,9 @@ static const struct value_string rsl_err_vals[] = {
{ RSL_ERR_CCCH_OVERLOAD, "CCCH Overload" },
{ RSL_ERR_ACCH_OVERLOAD, "ACCH Overload" },
{ RSL_ERR_PROCESSOR_OVERLOAD, "Processor Overload" },
+ { RSL_ERR_BTS_NOT_EQUIPPED, "BTS not equipped" },
+ { RSL_ERR_REMOTE_TRANSC_FAIL, "Remote Transcoder Failure" },
+ { RSL_ERR_NOTIFICATION_OVERFL, "Notification Overflow" },
{ RSL_ERR_RES_UNAVAIL, "Resource not available, unspecified" },
{ RSL_ERR_TRANSC_UNAVAIL, "Transcoding not available" },
{ RSL_ERR_SERV_OPT_UNAVAIL, "Service or Option not available" },
@@ -546,7 +553,7 @@ void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr,
/* construct a RSLms RLL message (DATA INDICATION, UNIT DATA
* INDICATION) and send it off via RSLms */
- /* Push the L3 IE tag and lengh */
+ /* Push the L3 IE tag and length */
msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
/* Then push the RSL header */
@@ -614,6 +621,10 @@ const struct tlv_definition rsl_ipac_eie_tlvdef = {
[RSL_IPAC_EIE_3G_NCELL_LIST] = { TLV_TYPE_TLV },
[RSL_IPAC_EIE_SDCCH_CTL_PARAM] = { TLV_TYPE_TV },
[RSL_IPAC_EIE_AMR_CONV_THRESH] = { TLV_TYPE_FIXED, 9 },
+ /* Osmocom extensions: */
+ [RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG]= { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_OSMO_MS_PWR_CTL] = { TLV_TYPE_TLV },
+ [RSL_IPAC_EIE_OSMO_PC_THRESH_COMP]= { TLV_TYPE_TLV },
},
};
diff --git a/src/gsm/rxlev_stat.c b/src/gsm/rxlev_stat.c
index 9c650cc1..f1b77f4c 100644
--- a/src/gsm/rxlev_stat.c
+++ b/src/gsm/rxlev_stat.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <unistd.h>
diff --git a/src/gsm/sysinfo.c b/src/gsm/sysinfo.c
index b615871f..40e26524 100644
--- a/src/gsm/sysinfo.c
+++ b/src/gsm/sysinfo.c
@@ -46,7 +46,7 @@ osmo_static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_si
/* bs11 forgot the l2 len, 0-6 rest octets */
osmo_static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size);
osmo_static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size);
-
+osmo_static_assert(sizeof(struct gsm48_system_information_type_10) == 1, _si10_size);
osmo_static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size);
static const uint8_t sitype2rsl[_MAX_SYSINFO_TYPE] = {
diff --git a/src/gsm/tlv_parser.c b/src/gsm/tlv_parser.c
index 159b42bd..8dd460db 100644
--- a/src/gsm/tlv_parser.c
+++ b/src/gsm/tlv_parser.c
@@ -1,4 +1,4 @@
-/* (C) 2008-2017 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2008-2020 by Harald Welte <laforge@gnumonks.org>
* (C) 2016-2017 by sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
@@ -24,6 +24,7 @@
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
#include <osmocom/gsm/tlv.h>
/*! \addtogroup tlv
@@ -125,7 +126,7 @@ int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src)
* \param[inout] msg Caller-allocated message buffer with sufficient tailroom
* \param[in] type TLV type/format to use during encode
* \param[in] tag Tag of TLV to be encoded
- * \parma[in] len Length of TLV to be encoded
+ * \param[in] len Length of TLV to be encoded
* \param[in] val Value part of TLV to be encoded
* \returns 0 on success; negative in case of error */
int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,
@@ -224,20 +225,33 @@ int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const
* \param[in] def structure defining the valid TLV tags / configurations
* \param[in] buf the input data buffer to be parsed
* \param[in] buf_len length of the input data buffer
- * \returns number of bytes consumed by the TLV entry / IE parsed; negative in case of error
+ * \returns number of bytes consumed by the TLV entry / IE parsed; negative in case of error.
+ *
+ * In IEs of type TLV_TYPE_SINGLE_TV, the data pointer \ref o_val will point to the
+ * byte shared by both the Tag and te Value, hence the tag is to be trimmed
+ * by the caller.
*/
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)
{
uint8_t tag;
- int len;
+ int len; /* number of bytes consumed by TLV entry */
+
+ if (buf_len < 1)
+ return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
tag = *buf;
*o_tag = tag;
/* single octet TV IE */
- if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) {
+ if (def->def[tag >> 4].type == TLV_TYPE_SINGLE_TV) {
+ *o_tag = tag >> 4;
+ *o_val = buf;
+ *o_len = 1;
+ return 1;
+ } else if ((tag > 0x0f) && (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV)) {
+ /* backward compat for old IEs with half-octet tag defined as 0xN0: */
*o_tag = tag & 0xf0;
*o_val = buf;
*o_len = 1;
@@ -264,56 +278,54 @@ int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,
break;
case TLV_TYPE_TLV:
tlv: /* GSM TS 04.07 11.2.4: Type 4 TLV */
- if (buf + 1 > buf + buf_len)
- return -1;
+ if (buf_len < 2)
+ return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
*o_val = buf+2;
*o_len = *(buf+1);
len = *o_len + 2;
- if (len > buf_len)
- return -2;
break;
case TLV_TYPE_vTvLV_GAN: /* 44.318 / 11.1.4 */
/* FIXME: variable-length TAG! */
+ if (buf_len < 2)
+ return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
if (*(buf+1) & 0x80) {
+ if (buf_len < 3)
+ return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
/* like TL16Vbut without highest bit of len */
- if (2 > buf_len)
- return -1;
*o_val = buf+3;
*o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2);
len = *o_len + 3;
- if (len > buf_len)
- return -2;
} else {
/* like TLV */
goto tlv;
}
break;
case TLV_TYPE_TvLV:
+ if (buf_len < 2)
+ return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
if (*(buf+1) & 0x80) {
/* like TLV, but without highest bit of len */
- if (buf + 1 > buf + buf_len)
- return -1;
*o_val = buf+2;
*o_len = *(buf+1) & 0x7f;
len = *o_len + 2;
- if (len > buf_len)
- return -2;
break;
}
/* like TL16V, fallthrough */
case TLV_TYPE_TL16V:
- if (2 > buf_len)
- return -1;
+ if (buf_len < 3)
+ return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
*o_val = buf+3;
*o_len = *(buf+1) << 8 | *(buf+2);
len = *o_len + 3;
- if (len > buf_len)
- return -2;
break;
default:
- return -3;
+ return OSMO_TLVP_ERR_UNKNOWN_TLV_TYPE;
}
+ if (buf_len < len) {
+ *o_val = NULL;
+ return OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER;
+ }
return len;
}
@@ -369,12 +381,12 @@ int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,
const uint8_t *val;
uint16_t parsed_len;
if (ofs > buf_len)
- return -1;
+ return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
val = &buf[ofs+1];
len = buf[ofs];
parsed_len = len + 1;
if (ofs + parsed_len > buf_len)
- return -2;
+ return OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER;
num_parsed++;
ofs += parsed_len;
/* store the resulting val and len */
@@ -390,12 +402,12 @@ int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,
const uint8_t *val;
uint16_t parsed_len;
if (ofs > buf_len)
- return -1;
+ return OSMO_TLVP_ERR_OFS_BEYOND_BUFFER;
val = &buf[ofs+1];
len = buf[ofs];
parsed_len = len + 1;
if (ofs + parsed_len > buf_len)
- return -2;
+ return OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER;
num_parsed++;
ofs += parsed_len;
/* store the resulting val and len */
@@ -431,7 +443,7 @@ int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,
return num_parsed;
}
-/*! take a master (src) tlvdev and fill up all empty slots in 'dst'
+/*! take a master (src) tlv_definition and fill up all empty slots in 'dst'
* \param dst TLV parser definition that is to be patched
* \param[in] src TLV parser definition whose content is patched into \a dst */
void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
@@ -627,4 +639,108 @@ fail:
return -1;
}
+static __thread char ienamebuf[32];
+static __thread char msgnamebuf[32];
+
+/*! get the message name for given msg_type in protocol pdef */
+const char *osmo_tlv_prot_msg_name(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type)
+{
+ if (pdef->msg_def[msg_type].name) {
+ return pdef->msg_def[msg_type].name;
+ } else if (pdef->msgt_names) {
+ return get_value_string(pdef->msgt_names, msg_type);
+ } else {
+ snprintf(msgnamebuf, sizeof(msgnamebuf), "Unknown msg_type 0x%02x", msg_type);
+ return msgnamebuf;
+ }
+}
+
+/*! get the IE name for given IEI in protocol pdef */
+const char *osmo_tlv_prot_ie_name(const struct osmo_tlv_prot_def *pdef, uint8_t iei)
+{
+ if (pdef->ie_def[iei].name) {
+ return pdef->ie_def[iei].name;
+ } else {
+ snprintf(ienamebuf, sizeof(ienamebuf), "Unknown IEI 0x%02x", iei);
+ return ienamebuf;
+ }
+}
+
+/*! Validate an already TLV-decoded message against the protocol definition.
+ * \param[in] pdef protocol definition of given protocol
+ * \param[in] msg_type message type of the parsed message
+ * \param[in] tp TLV parser result
+ * \param[in] log_subsys logging sub-system for log messages
+ * \param[in] log_pfx prefix for log messages
+ * \returns 0 in case of success; negative osmo_tlv_parser_error in case of error
+ */
+int osmo_tlv_prot_validate_tp(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type,
+ const struct tlv_parsed *tp, int log_subsys, const char *log_pfx)
+{
+ const struct osmo_tlv_prot_msg_def *msg_def= &pdef->msg_def[msg_type];
+ unsigned int err = 0;
+ unsigned int i;
+
+ if (msg_def->mand_ies) {
+ for (i = 0; i < msg_def->mand_count; i++) {
+ uint8_t iei = msg_def->mand_ies[i];
+ if (!TLVP_PRESENT(tp, iei)) {
+ LOGP(log_subsys, LOGL_ERROR, "%s %s %s: Missing Mandatory IE: %s\n",
+ log_pfx, pdef->name, osmo_tlv_prot_msg_name(pdef, msg_type),
+ osmo_tlv_prot_ie_name(pdef, iei));
+ if (!err)
+ err = OSMO_TLVP_ERR_MAND_IE_MISSING;
+ }
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tp->lv); i++) {
+ uint16_t min_len;
+
+ if (!TLVP_PRESENT(tp, i))
+ continue;
+
+ min_len = pdef->ie_def[i].min_len;
+ if (TLVP_LEN(tp, i) < min_len) {
+ LOGP(log_subsys, LOGL_ERROR, "%s %s %s: Short IE %s: %u < %u\n", log_pfx,
+ pdef->name, osmo_tlv_prot_msg_name(pdef, msg_type),
+ osmo_tlv_prot_ie_name(pdef, i), TLVP_LEN(tp, i), min_len);
+ if (!err)
+ err = OSMO_TLVP_ERR_IE_TOO_SHORT;
+ }
+ }
+
+ return err;
+}
+
+/*! Parse + Validate a TLV-encoded message against the protocol definition.
+ * \param[in] pdef protocol definition of given protocol
+ * \param[out] dec caller-allocated pointer to \ref tlv_parsed
+ * \param[in] dec_multiples length of the tlv_parsed[] in \a dec.
+ * \param[in] msg_type message type of the parsed message
+ * \param[in] buf the input data buffer to be parsed
+ * \param[in] buf_len length of the input data buffer
+ * \param[in] lv_tag an initial LV tag at the start of the buffer
+ * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag
+ * \param[in] log_subsys logging sub-system for log messages
+ * \param[in] log_pfx prefix for log messages
+ * \returns 0 in case of success; negative osmo_tlv_parser_error in case of error
+ */
+int osmo_tlv_prot_parse(const struct osmo_tlv_prot_def *pdef,
+ struct tlv_parsed *dec, unsigned int dec_multiples, uint8_t msg_type,
+ const uint8_t *buf, unsigned int buf_len, uint8_t lv_tag, uint8_t lv_tag2,
+ int log_subsys, const char *log_pfx)
+{
+ int rc;
+
+ rc = tlv_parse2(dec, dec_multiples, pdef->tlv_def, buf, buf_len, lv_tag, lv_tag2);
+ if (rc < 0) {
+ LOGP(log_subsys, LOGL_ERROR, "%s %s %s: TLV parser error %d\n", log_pfx,
+ pdef->name, osmo_tlv_prot_msg_name(pdef, msg_type), rc);
+ return rc;
+ }
+
+ return osmo_tlv_prot_validate_tp(pdef, msg_type, dec, log_subsys, log_pfx);
+}
+
/*! @} */
diff --git a/src/gsm/tuak/KeccakP-1600-3gpp.c b/src/gsm/tuak/KeccakP-1600-3gpp.c
new file mode 100644
index 00000000..3f5e2ad4
--- /dev/null
+++ b/src/gsm/tuak/KeccakP-1600-3gpp.c
@@ -0,0 +1,176 @@
+/* -----------------------------------------------------------------------
+ * code extracted from 3GPP TS 35.231, annex E for Keccak core functions
+ * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2402
+ *-----------------------------------------------------------------------*/
+
+/* This code may be freely used or adapted.
+*/
+
+#include "KeccakP-1600-3gpp.h"
+
+
+const uint8_t Rho[25] = {0,1,62,28,27,36,44,6,55,20,3,10,43,25,39,41,45,
+ 15,21,8,18,2,61,56,14};
+
+const uint8_t Pi[25] = {0,6,12,18,24,3,9,10,16,22,1,7,13,19,20,4,5,11,17,
+ 23,2,8,14,15,21};
+
+const uint8_t Iota[24] = {1,146,218,112,155,33,241,89,138,136,57,42,187,203,
+ 217,83,82,192,26,106,241,208,33,120};
+
+#define ROTATE64(value, n) \
+((((uint64_t)(value))<<(n)) | (((uint64_t)(value))>>(64-(n))))
+
+/* ---------------------------------------------------------------------
+ 64-bit version of Keccak_f(1600)
+ ---------------------------------------------------------------------
+*/
+void Keccak_f_64(uint64_t s[25])
+{ uint64_t t[5];
+ uint8_t i, j, round;
+
+ for(round=0; round<24; ++round)
+ { /* Theta function */
+ for(i=0; i<5; ++i)
+ t[i] = s[i] ^ s[5+i] ^ s[10+i] ^ s[15+i] ^ s[20+i];
+ for(i=0; i<5; ++i, s+=5)
+ { s[0] ^= t[4] ^ ROTATE64(t[1], 1);
+ s[1] ^= t[0] ^ ROTATE64(t[2], 1);
+ s[2] ^= t[1] ^ ROTATE64(t[3], 1);
+ s[3] ^= t[2] ^ ROTATE64(t[4], 1);
+ s[4] ^= t[3] ^ ROTATE64(t[0], 1);
+ }
+ s -= 25;
+
+ /* Rho function */
+ for(i=1; i<25; ++i)
+ s[i] = ROTATE64(s[i], Rho[i]);
+
+ /* Pi function */
+ for(t[1] = s[i=1]; (j=Pi[i]) > 1; s[i]=s[j], i=j);
+ s[i] = t[1];
+
+ /* Chi function */
+ for(i=0; i<5; ++i, s += 5)
+ { t[0] = (~s[1]) & s[2];
+ t[1] = (~s[2]) & s[3];
+ t[2] = (~s[3]) & s[4];
+ t[3] = (~s[4]) & s[0];
+ t[4] = (~s[0]) & s[1];
+ for(j=0; j<5; ++j) s[j] ^= t[j];
+ }
+ s -= 25;
+
+ /* Iota function */
+ t[0] = Iota[round];
+ *s ^= (t[0] | (t[0]<<11) | (t[0]<<26) | (t[0]<<57))
+ & 0x800000008000808BULL; /* set & mask bits 0,1,3,7,15,31,63 */
+ }
+}
+
+
+/* ---------------------------------------------------------------------
+ 8-bit version of Keccak_f(1600)
+ ---------------------------------------------------------------------
+*/
+void Keccak_f_8(uint8_t s[200])
+{ uint8_t t[40], i, j, k, round;
+
+ for(round=0; round<24; ++round)
+ { /* Theta function */
+ for(i=0; i<40; ++i)
+ t[i]=s[i]^s[40+i]^s[80+i]^s[120+i]^s[160+i];
+ for(i=0; i<200; i+=8)
+ for(j = (i+32)%40, k=0; k<8; ++k)
+ s[i+k] ^= t[j+k];
+ for(i=0; i<40; t[i] = (t[i]<<1)|j, i+=8)
+ for(j = t[i+7]>>7, k=7; k; --k)
+ t[i+k] = (t[i+k]<<1)|(t[i+k-1]>>7);
+ for(i=0; i<200; i+=8)
+ for(j = (i+8)%40, k=0; k<8; ++k)
+ s[i+k] ^= t[j+k];
+
+ /* Rho function */
+ for(i=8; i<200; i+=8)
+ { for(j = Rho[i>>3]>>3, k=0; k<8; ++k) /* j:=bytes to shift, s->t */
+ t[(k+j)&7] = s[i+k];
+ for(j = Rho[i>>3]&7, k=7; k; --k) /* j:=bits to shift, t->s */
+ s[i+k] = (t[k]<<j) | (t[k-1]>>(8-j));
+ s[i] = (t[0]<<j) | (t[7]>>(8-j));
+ }
+
+ /* Pi function */
+ for(k=8; k<16; ++k) t[k] = s[k]; /* =memcpy(t+8, s+8, 8) */
+ for(i=1; (j=Pi[i])>1; i=j)
+ for(k=0; k<8; ++k) /* =memcpy(s+(i<<3), s+(j<<3), 8) */
+ s[(i<<3)|k] = s[(j<<3)|k];
+ for(k=0; k<8; ++k) /* =memcpy(s+(i<<3), t+8, 8) */
+ s[(i<<3)|k] = t[k+8];
+
+ /* Chi function */
+ for(i=0; i<200; i+=40)
+ { for(j=0; j<40; ++j)
+ t[j]=(~s[i+(j+8)%40]) & s[i+(j+16)%40];
+ for(j=0; j<40; ++j) s[i+j]^=t[j];
+ }
+
+ /* Iota function */
+ k = Iota[round];
+ s[0] ^= k & 0x8B; /* bits 0, 1, 3, 7 */
+ s[1] ^= (k<<3)&0x80; /* bit 15 */
+ s[3] ^= (k<<2)&0x80; /* bit 31 */
+ s[7] ^= (k<<1)&0x80; /* bit 63 */
+
+ }
+}
+
+/* ---------------------------------------------------------------------
+ 32-bit version of Keccak_f(1600)
+ ---------------------------------------------------------------------
+*/
+void Keccak_f_32(uint32_t s[50])
+{ uint32_t t[10];
+ uint8_t i, j, round, k;
+
+ for(round=0; round<24; ++round)
+ { /* Theta function */
+ for(i=0; i<10; ++i)
+ t[i] = s[i] ^ s[10+i] ^ s[20+i] ^ s[30+i] ^ s[40+i];
+ for(i=0; i<5; ++i)
+ for(j=8, k=2; ; j%=10, k=(k+2)%10)
+ { *s++ ^= t[j++] ^ ((t[k]<<1)|(t[k+1]>>31));
+ *s++ ^= t[j++] ^ ((t[k+1]<<1)|(t[k]>>31));
+ if(j==8) break;
+ }
+ s -= 50;
+
+ /* Rho function */
+ for(i=2; i<50; i+=2)
+ { k = Rho[i>>1] & 0x1f;
+ t[0] = (s[i+1] << k) | (s[i] >> (32-k));
+ t[1] = (s[i] << k) | (s[i+1] >> (32-k));
+ k = Rho[i>>1] >> 5;
+ s[i] = t[1-k], s[i+1] = t[k];
+ }
+
+ /* Pi function */
+ for(i=2, t[0]=s[2], t[1]=s[3]; (j=(Pi[i>>1]<<1))>2; i=j)
+ s[i]=s[j], s[i+1]=s[j+1];
+ s[i]=t[0], s[i+1]=t[1];
+
+ /* Chi function */
+ for(i=0; i<5; ++i, s+=10)
+ { for(j=0; j<10; ++j)
+ t[j] = (~s[(j+2)%10]) & s[(j+4)%10];
+ for(j=0; j<10; ++j)
+ s[j] ^= t[j];
+ }
+ s -= 50;
+
+ /* Iota function */
+ t[0] = Iota[round];
+ s[0] ^= (t[0] | (t[0]<<11) | (t[0]<<26)) & 0x8000808B;
+ s[1] ^= (t[0]<<25) & 0x80000000;
+ }
+}
+
diff --git a/src/gsm/tuak/KeccakP-1600-3gpp.h b/src/gsm/tuak/KeccakP-1600-3gpp.h
new file mode 100644
index 00000000..a23cc460
--- /dev/null
+++ b/src/gsm/tuak/KeccakP-1600-3gpp.h
@@ -0,0 +1,25 @@
+/* -----------------------------------------------------------------------
+ * code extracted from 3GPP TS 35.231, annex E for Keccak core functions
+ * https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=2402
+ *-----------------------------------------------------------------------*/
+
+/* this is the trick to make the code cross-platform
+ * at least, Win32 / Linux */
+
+#if defined(_WIN32) || defined(__WIN32__)
+# include <windows.h>
+# define EXPORTIT __declspec(dllexport)
+#else
+# define EXPORTIT
+#endif
+
+#include <stdint.h>
+
+/*------------------------------------------------------------------------
+ * KeccakP-1600-3gpp.h
+ *------------------------------------------------------------------------*/
+
+EXPORTIT void Keccak_f_8 (uint8_t s[200]);
+EXPORTIT void Keccak_f_32(uint32_t s[50]);
+EXPORTIT void Keccak_f_64(uint64_t s[25]);
+
diff --git a/src/gsm/tuak/tuak.c b/src/gsm/tuak/tuak.c
new file mode 100644
index 00000000..c044a377
--- /dev/null
+++ b/src/gsm/tuak/tuak.c
@@ -0,0 +1,372 @@
+/* (C) 2023 by Harald Welte <laforge@osmocom.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+
+#include "KeccakP-1600-3gpp.h"
+
+/* TUAK authentication algorithm
+ * as proposed by 3GPP as an alternative to Milenage
+ * algorithm based on SHA-3 (more exactly its KeccakP-1600 permutation)
+ * see 3GPP TS 35.231, 232 and 233 */
+
+static unsigned int g_keccak_iterations = 1;
+static const char algoname[] = "TUAK1.0";
+const uint8_t zero16[16] = { 0, };
+
+void tuak_set_keccak_iterations(unsigned int i)
+{
+ g_keccak_iterations = i;
+}
+
+/* append data from 'input' to 'buf' at 'idx', reversing byte order */
+#define PUSH_DATA(buf, idx, input, nbytes) \
+ for (int i = nbytes-1; i >= 0; i--) { \
+ buf[idx++] = input[i]; \
+ }
+
+/* like memcpy(), but reversing they order of bytes */
+void memcpy_reverse(uint8_t *dst, const uint8_t *src, size_t len)
+{
+ for (size_t i = 0; i < len; i++)
+ dst[i] = src[len-i-1];
+}
+
+static void tuak_core(uint8_t buf[200], const uint8_t *opc, uint8_t instance, const uint8_t *_rand,
+ const uint8_t *amf, const uint8_t *sqn, const uint8_t *k, uint8_t k_len_bytes,
+ unsigned int keccac_iterations)
+{
+ unsigned int idx = 0;
+
+ PUSH_DATA(buf, idx, opc, 32);
+ buf[idx++] = instance;
+ PUSH_DATA(buf, idx, algoname, strlen(algoname)); /* without trailing NUL */
+ PUSH_DATA(buf, idx, _rand, 16);
+ PUSH_DATA(buf, idx, amf, 2);
+ PUSH_DATA(buf, idx, sqn, 6);
+ PUSH_DATA(buf, idx, k, k_len_bytes);
+ memset(buf+idx, 0, 32-k_len_bytes); idx += 32-k_len_bytes;
+ buf[idx++] = 0x1f;
+ memset(buf+idx, 0, 38); idx += 38;
+ buf[idx++] = 0x80;
+ memset(buf+idx, 0, 64); idx += 64;
+ OSMO_ASSERT(idx == 200);
+
+ for (unsigned int i = 0; i < keccac_iterations; i++)
+ Keccak_f_64((uint64_t *) buf);
+}
+
+/**
+ * tuak_f1 - TUAK f1 algorithm
+ * @opc: OPc = 256-bit value derived from OP and K
+ * @k: K = 128-bit or 256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sqn: SQN = 48-bit sequence number
+ * @amf: AMF = 16-bit authentication management field
+ * @mac_a: Buffer for MAC-A = 64/128/256-bit network authentication code
+ * Returns: 0 on success, -1 on failure
+ */
+int tuak_f1(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_a, uint8_t mac_a_len_bytes,
+ unsigned int keccac_iterations)
+{
+ uint8_t buf[200];
+ uint8_t instance = 0x00;
+
+ switch (mac_a_len_bytes) {
+ case 8:
+ instance |= 0x08;
+ break;
+ case 16:
+ instance |= 0x10;
+ break;
+ case 32:
+ instance |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (k_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, opc, instance, _rand, amf, sqn, k, k_len_bytes, keccac_iterations);
+
+ memcpy_reverse(mac_a, buf, mac_a_len_bytes);
+
+ return 0;
+}
+
+/**
+ * tuak_f1star - TUAK f1* algorithm
+ * @opc: OPc = 256-bit value derived from OP and K
+ * @k: K = 128-bit or 256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sqn: SQN = 48-bit sequence number
+ * @amf: AMF = 16-bit authentication management field
+ * @mac_s: Buffer for MAC-S = 64/128/256-bit resync authentication code
+ * Returns: 0 on success, -1 on failure
+ */
+int tuak_f1star(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_s, uint8_t mac_s_len_bytes,
+ unsigned int keccac_iterations)
+{
+ uint8_t buf[200];
+ uint8_t instance = 0x80;
+
+ switch (mac_s_len_bytes) {
+ case 8:
+ instance |= 0x08;
+ break;
+ case 16:
+ instance |= 0x10;
+ break;
+ case 32:
+ instance |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (k_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, opc, instance, _rand, amf, sqn, k, k_len_bytes, keccac_iterations);
+
+ memcpy_reverse(mac_s, buf, mac_s_len_bytes);
+
+ return 0;
+}
+
+/**
+ * tuak_f2345 - TUAK f2, f3, f4, f5, algorithms
+ * @opc: OPc = 256-bit value derived from OP and K
+ * @k: K = 128/256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @res: Buffer for RES = 32/64/128/256-bit signed response (f2), or %NULL
+ * @ck: Buffer for CK = 128/256-bit confidentiality key (f3), or %NULL
+ * @ik: Buffer for IK = 128/256-bit integrity key (f4), or %NULL
+ * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int tuak_f2345(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, uint8_t *res, uint8_t res_len_bytes,
+ uint8_t *ck, uint8_t ck_len_bytes,
+ uint8_t *ik, uint8_t ik_len_bytes, uint8_t *ak, unsigned int keccac_iterations)
+{
+ uint8_t buf[200];
+ uint8_t instance = 0x40;
+
+ switch (res_len_bytes) {
+ case 4:
+ break;
+ case 8:
+ instance |= 0x08;
+ break;
+ case 16:
+ instance |= 0x10;
+ break;
+ case 32:
+ instance |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (ck_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x04;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (ik_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (k_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance |= 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, opc, instance, _rand, zero16, zero16, k, k_len_bytes, keccac_iterations);
+
+ if (res)
+ memcpy_reverse(res, buf, res_len_bytes);
+
+ if (ck)
+ memcpy_reverse(ck, buf + 32, ck_len_bytes);
+
+ if (ik)
+ memcpy_reverse(ik, buf + 64, ik_len_bytes);
+
+ if (ak)
+ memcpy_reverse(ak, buf + 96, 6);
+
+ return 0;
+}
+
+/**
+ * tuak_f5star - TUAK f5* algorithm
+ * @opc: OPc = 256-bit value derived from OP and K
+ * @k: K = 128/256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @ak: Buffer for AK = 48-bit anonymity key (f5)
+ * Returns: 0 on success, -1 on failure
+ */
+int tuak_f5star(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, uint8_t *ak, unsigned int keccac_iterations)
+{
+ uint8_t buf[200];
+ uint8_t instance = 0xc0;
+
+ switch (k_len_bytes) {
+ case 16:
+ break;
+ case 32:
+ instance += 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, opc, instance, _rand, zero16, zero16, k, k_len_bytes, keccac_iterations);
+
+ memcpy_reverse(ak, buf + 96, 6);
+
+ return 0;
+}
+
+/**
+ * tuak_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 256-bit operator variant algorithm configuration field (encr.)
+ * @amf: AMF = 16-bit authentication management field
+ * @k: K = 128/256-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: Buffer for AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128/256-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128/256-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 32/64/128-bit signed response (f2), or %NULL
+ * @res_len: Max length for res; set to used length or 0 on failure
+ */
+void tuak_generate(const uint8_t *opc, const uint8_t *amf, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *sqn, const uint8_t *_rand, uint8_t *autn, uint8_t *ik,
+ uint8_t *ck, uint8_t *res, size_t *res_len)
+{
+ int i;
+ uint8_t mac_a[8], ak[6];
+
+ if (*res_len < 4) {
+ *res_len = 0;
+ return;
+ }
+ if (tuak_f1(opc, k, k_len_bytes, _rand, sqn, amf, mac_a, sizeof(mac_a), g_keccak_iterations) ||
+ tuak_f2345(opc, k, k_len_bytes, _rand, res, *res_len, ck, 16, ik, 16, ak, g_keccak_iterations)) {
+ *res_len = 0;
+ return;
+ }
+
+ /* AUTN = (SQN ^ AK) || AMF || MAC */
+ for (i = 0; i < 6; i++)
+ autn[i] = sqn[i] ^ ak[i];
+ memcpy(autn + 6, amf, 2);
+ memcpy(autn + 8, mac_a, 8);
+}
+
+
+/**
+ * tuak_auts - Milenage AUTS validation
+ * @opc: OPc = 256-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128/256-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @auts: AUTS = 112-bit authentication token from client
+ * @sqn: Buffer for SQN = 48-bit sequence number
+ * Returns: 0 = success (sqn filled), -1 on failure
+ */
+int tuak_auts(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, const uint8_t *auts, uint8_t *sqn)
+{
+ uint8_t amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+ uint8_t ak[6], mac_s[8];
+ int i;
+
+ if (tuak_f5star(opc, k, k_len_bytes, _rand, ak, g_keccak_iterations))
+ return -1;
+ for (i = 0; i < 6; i++)
+ sqn[i] = auts[i] ^ ak[i];
+ if (tuak_f1star(opc, k, k_len_bytes, _rand, sqn, amf, mac_s, 8, g_keccak_iterations) ||
+ memcmp(mac_s, auts + 6, 8) != 0)
+ return -1;
+ return 0;
+}
+
+int tuak_opc_gen(uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *op)
+{
+ uint8_t buf[200];
+ uint8_t instance;
+
+ switch (k_len_bytes) {
+ case 16:
+ instance = 0x00;
+ break;
+ case 32:
+ instance = 0x01;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tuak_core(buf, op, instance, zero16, zero16, zero16, k, k_len_bytes, g_keccak_iterations);
+
+ memcpy_reverse(opc, buf, 32);
+
+ return 0;
+}
diff --git a/src/gsm/tuak/tuak.h b/src/gsm/tuak/tuak.h
new file mode 100644
index 00000000..1a808224
--- /dev/null
+++ b/src/gsm/tuak/tuak.h
@@ -0,0 +1,33 @@
+#pragma once
+#include <stdint.h>
+
+/* low-level functions */
+
+int tuak_f1(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_a, uint8_t mac_a_len_bytes,
+ unsigned int keccac_iterations);
+
+int tuak_f1star(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_s, uint8_t mac_s_len_bytes,
+ unsigned int keccac_iterations);
+
+int tuak_f2345(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, uint8_t *res, uint8_t res_len_bytes,
+ uint8_t *ck, uint8_t ck_len_bytes,
+ uint8_t *ik, uint8_t ik_len_bytes, uint8_t *ak, unsigned int keccac_iterations);
+
+int tuak_f5star(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, uint8_t *ak, unsigned int keccac_iterations);
+
+/* high-level API */
+
+void tuak_set_keccak_iterations(unsigned int i);
+
+void tuak_generate(const uint8_t *opc, const uint8_t *amf, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *sqn, const uint8_t *_rand, uint8_t *autn, uint8_t *ik,
+ uint8_t *ck, uint8_t *res, size_t *res_len);
+
+int tuak_auts(const uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes,
+ const uint8_t *_rand, const uint8_t *auts, uint8_t *sqn);
+
+int tuak_opc_gen(uint8_t *opc, const uint8_t *k, uint8_t k_len_bytes, const uint8_t *op);
diff --git a/src/isdn/Makefile.am b/src/isdn/Makefile.am
new file mode 100644
index 00000000..3e7f86eb
--- /dev/null
+++ b/src/isdn/Makefile.am
@@ -0,0 +1,26 @@
+# This is _NOT_ the library release version, it's an API version.
+# Please read chapter "Library interface versions" of the libtool documentation
+# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
+LIBVERSION=1:0:1
+
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
+
+if ENABLE_PSEUDOTALLOC
+AM_CPPFLAGS += -I$(top_srcdir)/src/pseudotalloc
+endif
+
+noinst_LTLIBRARIES = libisdnint.la
+lib_LTLIBRARIES = libosmoisdn.la
+
+libisdnint_la_SOURCES = i460_mux.c lapd_core.c v110.c v110_ta.c
+
+libisdnint_la_LDFLAGS = -no-undefined
+libisdnint_la_LIBADD = $(top_builddir)/src/core/libosmocore.la
+
+libosmoisdn_la_SOURCES =
+libosmoisdn_la_LDFLAGS = $(LTLDFLAGS_OSMOISDN) -version-info $(LIBVERSION) -no-undefined
+libosmoisdn_la_LIBADD = libisdnint.la $(TALLOC_LIBS)
+
+EXTRA_DIST = libosmoisdn.map
+EXTRA_libosmoisdn_la_DEPENDENCIES = libosmoisdn.map
diff --git a/src/isdn/i460_mux.c b/src/isdn/i460_mux.c
new file mode 100644
index 00000000..b070bbdc
--- /dev/null
+++ b/src/isdn/i460_mux.c
@@ -0,0 +1,387 @@
+/*! \file i460_mux.c
+ * ITU-T I.460 sub-channel multiplexer + demultiplexer */
+/*
+ * (C) 2020 by Harald Welte <laforge@gnumonks.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/isdn/i460_mux.h>
+
+/*! count the number of sub-channels in this I.460 slot.
+ * \param[in] ts timeslot that holds the I.460 subchannels.
+ * \return number of subchannels. */
+int osmo_i460_subchan_count(struct osmo_i460_timeslot *ts)
+{
+ int i, num_used = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
+ if (ts->schan[i].rate != OSMO_I460_RATE_NONE)
+ num_used++;
+ }
+
+ return num_used;
+}
+
+/* does this channel have no sub-streams (single 64k subchannel)? */
+static bool osmo_i460_has_single_64k_schan(struct osmo_i460_timeslot *ts)
+{
+ if (osmo_i460_subchan_count(ts) != 1)
+ return false;
+
+ if (ts->schan[0].rate != OSMO_I460_RATE_64k)
+ return false;
+
+ return true;
+}
+
+/***********************************************************************
+ * Demultiplexer
+ ***********************************************************************/
+
+/* append a single bit to a sub-channel */
+static void demux_subchan_append_bit(struct osmo_i460_subchan *schan, uint8_t bit)
+{
+ struct osmo_i460_subchan_demux *demux = &schan->demux;
+
+ OSMO_ASSERT(demux->out_bitbuf);
+ OSMO_ASSERT(demux->out_idx < demux->out_bitbuf_size);
+
+ demux->out_bitbuf[demux->out_idx++] = bit ? 1 : 0;
+
+ if (demux->out_idx >= demux->out_bitbuf_size) {
+ if (demux->out_cb_bits)
+ demux->out_cb_bits(schan, demux->user_data, demux->out_bitbuf, demux->out_idx);
+ else {
+ /* pack bits into bytes */
+ OSMO_ASSERT((demux->out_idx % 8) == 0);
+ unsigned int num_bytes = demux->out_idx / 8;
+ uint8_t bytes[num_bytes];
+ osmo_ubit2pbit(bytes, demux->out_bitbuf, demux->out_idx);
+ demux->out_cb_bytes(schan, demux->user_data, bytes, num_bytes);
+ }
+ demux->out_idx = 0;
+ }
+}
+
+/* extract those bits relevant to this schan of each byte in 'data' */
+static void demux_subchan_extract_bits(struct osmo_i460_subchan *schan, const uint8_t *data, size_t data_len)
+{
+ int i;
+
+ for (i = 0; i < data_len; i++) {
+ uint8_t inbyte = data[i];
+ /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two
+ * most significant bits, hence we extract msb-first */
+ uint8_t inbits = inbyte << schan->bit_offset;
+
+ /* extract the bits relevant to the given schan */
+ switch (schan->rate) {
+ case OSMO_I460_RATE_8k:
+ demux_subchan_append_bit(schan, inbits & 0x80);
+ break;
+ case OSMO_I460_RATE_16k:
+ demux_subchan_append_bit(schan, inbits & 0x80);
+ demux_subchan_append_bit(schan, inbits & 0x40);
+ break;
+ case OSMO_I460_RATE_32k:
+ demux_subchan_append_bit(schan, inbits & 0x80);
+ demux_subchan_append_bit(schan, inbits & 0x40);
+ demux_subchan_append_bit(schan, inbits & 0x20);
+ demux_subchan_append_bit(schan, inbits & 0x10);
+ break;
+ case OSMO_I460_RATE_64k:
+ demux_subchan_append_bit(schan, inbits & 0x80);
+ demux_subchan_append_bit(schan, inbits & 0x40);
+ demux_subchan_append_bit(schan, inbits & 0x20);
+ demux_subchan_append_bit(schan, inbits & 0x10);
+ demux_subchan_append_bit(schan, inbits & 0x08);
+ demux_subchan_append_bit(schan, inbits & 0x04);
+ demux_subchan_append_bit(schan, inbits & 0x02);
+ demux_subchan_append_bit(schan, inbits & 0x01);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ }
+}
+
+/*! Feed multiplexed data (from an E1 timeslot) into de-multiplexer.
+ * \param[in] ts timeslot state.
+ * \param[in] data input data bytes as received from E1/T1.
+ * \param[in] data_len length of data in bytes. */
+void osmo_i460_demux_in(struct osmo_i460_timeslot *ts, const uint8_t *data, size_t data_len)
+{
+ struct osmo_i460_subchan *schan;
+ struct osmo_i460_subchan_demux *demux;
+ int i;
+
+ /* fast path if entire 64k slot is used */
+ if (osmo_i460_has_single_64k_schan(ts)) {
+ schan = &ts->schan[0];
+ demux = &schan->demux;
+ if (demux->out_cb_bytes)
+ demux->out_cb_bytes(schan, demux->user_data, data, data_len);
+ else {
+ ubit_t bits[data_len*8];
+ osmo_pbit2ubit(bits, data, data_len*8);
+ demux->out_cb_bits(schan, demux->user_data, bits, data_len*8);
+ }
+ return;
+ }
+
+ /* Slow path iterating over all lchans */
+ for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
+ schan = &ts->schan[i];
+ if (schan->rate == OSMO_I460_RATE_NONE)
+ continue;
+ demux_subchan_extract_bits(schan, data, data_len);
+ }
+}
+
+
+/***********************************************************************
+ * Multiplexer
+ ***********************************************************************/
+
+/*! enqueue a to-be-transmitted message buffer containing unpacked bits */
+void osmo_i460_mux_enqueue(struct osmo_i460_subchan *schan, struct msgb *msg)
+{
+ OSMO_ASSERT(msgb_length(msg) > 0);
+ msgb_enqueue(&schan->mux.tx_queue, msg);
+}
+
+/* mux: pull the next bit out of the given sub-channel */
+static ubit_t mux_schan_provide_bit(struct osmo_i460_subchan *schan)
+{
+ struct osmo_i460_subchan_mux *mux = &schan->mux;
+ struct msgb *msg;
+ ubit_t bit;
+
+ /* if we don't have anything to transmit, return '1' bits */
+ if (llist_empty(&mux->tx_queue)) {
+ /* User code now has a last chance to put something into the queue. */
+ if (mux->in_cb_queue_empty)
+ mux->in_cb_queue_empty(schan, mux->user_data);
+
+ /* If the queue is still empty, return idle bits */
+ if (llist_empty(&mux->tx_queue))
+ return 0x01;
+ }
+ msg = llist_entry(mux->tx_queue.next, struct msgb, list);
+ bit = msgb_pull_u8(msg);
+
+ /* free msgb if we have pulled the last bit */
+ if (msgb_length(msg) <= 0) {
+ llist_del(&msg->list);
+ talloc_free(msg);
+ }
+
+ return bit;
+}
+
+/*! provide one byte with the subchan-specific bits of given sub-channel.
+ * \param[in] schan sub-channel that is to provide bits
+ * \param[out] mask bitmask of those bits filled in
+ * \returns bits of given sub-channel */
+static uint8_t mux_subchan_provide_bits(struct osmo_i460_subchan *schan, uint8_t *mask)
+{
+ uint8_t outbits = 0;
+ uint8_t outmask;
+
+ /* I.460 defines sub-channel 0 is using bit positions 1+2 (the two
+ * most significant bits, hence we provide msb-first */
+
+ switch (schan->rate) {
+ case OSMO_I460_RATE_8k:
+ outbits = mux_schan_provide_bit(schan) << 7;
+ outmask = 0x80;
+ break;
+ case OSMO_I460_RATE_16k:
+ outbits |= mux_schan_provide_bit(schan) << 7;
+ outbits |= mux_schan_provide_bit(schan) << 6;
+ outmask = 0xC0;
+ break;
+ case OSMO_I460_RATE_32k:
+ outbits |= mux_schan_provide_bit(schan) << 7;
+ outbits |= mux_schan_provide_bit(schan) << 6;
+ outbits |= mux_schan_provide_bit(schan) << 5;
+ outbits |= mux_schan_provide_bit(schan) << 4;
+ outmask = 0xF0;
+ break;
+ case OSMO_I460_RATE_64k:
+ outbits |= mux_schan_provide_bit(schan) << 7;
+ outbits |= mux_schan_provide_bit(schan) << 6;
+ outbits |= mux_schan_provide_bit(schan) << 5;
+ outbits |= mux_schan_provide_bit(schan) << 4;
+ outbits |= mux_schan_provide_bit(schan) << 3;
+ outbits |= mux_schan_provide_bit(schan) << 2;
+ outbits |= mux_schan_provide_bit(schan) << 1;
+ outbits |= mux_schan_provide_bit(schan) << 0;
+ outmask = 0xFF;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ *mask = outmask >> schan->bit_offset;
+ return outbits >> schan->bit_offset;
+}
+
+/* provide one byte of multiplexed I.460 bits */
+static uint8_t mux_timeslot_provide_bits(struct osmo_i460_timeslot *ts)
+{
+ uint8_t ret = 0xff; /* unused bits must be '1' as per I.460 */
+
+ for (int i = 0; i < ARRAY_SIZE(ts->schan); i++) {
+ struct osmo_i460_subchan *schan = &ts->schan[i];
+ uint8_t bits, mask;
+
+ if (schan->rate == OSMO_I460_RATE_NONE)
+ continue;
+ bits = mux_subchan_provide_bits(schan, &mask);
+ ret &= ~mask;
+ ret |= bits;
+ }
+
+ return ret;
+}
+
+
+/*! Get multiplexed data from de-multiplexer (for feeding it into an E1 timeslot).
+ * \param[in] ts timeslot state.
+ * \param[out] out caller-provided buffer where to store generated output bytes.
+ * \param[in] out_len number of bytes to be stored at out. */
+int osmo_i460_mux_out(struct osmo_i460_timeslot *ts, uint8_t *out, size_t out_len)
+{
+ int i;
+
+ /* fast path if entire 64k slot is used */
+ //if (osmo_i460_has_single_64k_schan(ts)) { }
+
+ for (i = 0; i < out_len; i++)
+ out[i] = mux_timeslot_provide_bits(ts);
+
+ return out_len;
+}
+
+
+/***********************************************************************
+ * Initialization / Control
+ ***********************************************************************/
+
+
+static int alloc_bitbuf(void *ctx, struct osmo_i460_subchan *schan, size_t num_bits)
+{
+ struct osmo_i460_subchan_demux *demux = &schan->demux;
+
+ talloc_free(demux->out_bitbuf);
+ demux->out_bitbuf = talloc_zero_size(ctx, num_bits);
+ if (!demux->out_bitbuf)
+ return -ENOMEM;
+ demux->out_bitbuf_size = num_bits;
+
+ return 0;
+}
+
+
+static int find_unused_subchan_idx(const struct osmo_i460_timeslot *ts)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
+ const struct osmo_i460_subchan *schan = &ts->schan[i];
+ if (schan->rate == OSMO_I460_RATE_NONE)
+ return i;
+ }
+ return -1;
+}
+
+/* reset subchannel struct into a defined state */
+static void subchan_reset(struct osmo_i460_subchan *schan, bool first_time)
+{
+ /* Before we zero out the subchannel struct, we must be sure that the
+ * tx_queue is cleared and all dynamically allocated memory is freed.
+ * However, on an uninitalized subchannel struct we can not be sure
+ * that the pointers are valid. If the subchannel is reset the first
+ * time the caller must set first_time to true. */
+ if (!first_time) {
+ if (schan->demux.out_bitbuf)
+ talloc_free(schan->demux.out_bitbuf);
+ msgb_queue_free(&schan->mux.tx_queue);
+ }
+
+ /* Reset subchannel to a defined state */
+ memset(schan, 0, sizeof(*schan));
+ schan->rate = OSMO_I460_RATE_NONE;
+ INIT_LLIST_HEAD(&schan->mux.tx_queue);
+}
+
+/*! initialize an I.460 timeslot */
+void osmo_i460_ts_init(struct osmo_i460_timeslot *ts)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ts->schan); i++) {
+ struct osmo_i460_subchan *schan = &ts->schan[i];
+ schan->ts = ts;
+ subchan_reset(schan, true);
+ }
+}
+
+/*! add a new sub-channel to the given timeslot
+ * \param[in] ctx talloc context from where to allocate the internal buffer
+ * \param[in] ts timeslot to which to add a sub-channel
+ * \param[in] chd description of the sub-channel to be added
+ * \return pointer to sub-channel on success, NULL on error */
+struct osmo_i460_subchan *
+osmo_i460_subchan_add(void *ctx, struct osmo_i460_timeslot *ts, const struct osmo_i460_schan_desc *chd)
+{
+ struct osmo_i460_subchan *schan;
+ int idx, rc;
+
+ idx = find_unused_subchan_idx(ts);
+ if (idx < 0)
+ return NULL;
+
+ schan = &ts->schan[idx];
+
+ schan->rate = chd->rate;
+ schan->bit_offset = chd->bit_offset;
+
+ schan->demux.out_cb_bits = chd->demux.out_cb_bits;
+ schan->demux.out_cb_bytes = chd->demux.out_cb_bytes;
+ schan->demux.user_data = chd->demux.user_data;
+ schan->mux.in_cb_queue_empty = chd->mux.in_cb_queue_empty;
+ schan->mux.user_data = chd->mux.user_data;
+ rc = alloc_bitbuf(ctx, schan, chd->demux.num_bits);
+ if (rc < 0) {
+ subchan_reset(schan, false);
+ return NULL;
+ }
+
+ /* return number of schan in use */
+ return schan;
+}
+
+/* remove a su-channel from the multiplex */
+void osmo_i460_subchan_del(struct osmo_i460_subchan *schan)
+{
+ subchan_reset(schan, false);
+}
+
+/*! @} */
diff --git a/src/gsm/lapd_core.c b/src/isdn/lapd_core.c
index a0f3c2b4..b32ed263 100644
--- a/src/gsm/lapd_core.c
+++ b/src/isdn/lapd_core.c
@@ -1,7 +1,7 @@
/*! \file lapd_core.c
* LAPD core implementation */
/*
- * (C) 2010-2011 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010-2020 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2011 by Andreas Eversberg <jolly@eversberg.eu>
*
* All Rights Reserved
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*! \addtogroup lapd
@@ -73,6 +69,7 @@
//#define TEST_CONTENT_RESOLUTION_NETWORK
#include <stdio.h>
+#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
@@ -82,7 +79,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/lapd_core.h>
+#include <osmocom/isdn/lapd_core.h>
#include <osmocom/gsm/rsl.h>
/* TS 04.06 Table 4 / Section 3.8.1 */
@@ -104,13 +101,14 @@
#define CR_NET2USER_RESP 0
#define LAPD_HEADROOM 56
+#define LAPD_TAILROOM 16
#define SBIT(a) (1 << a)
#define ALL_STATES 0xffffffff
static void lapd_t200_cb(void *data);
static void lapd_t203_cb(void *data);
-static int lapd_send_i(struct lapd_msg_ctx *lctx, int line);
+static int lapd_send_i(struct lapd_datalink *dl, int line, bool rts);
static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
/* UTILITY FUNCTIONS */
@@ -120,7 +118,7 @@ struct msgb *lapd_msgb_alloc(int length, const char *name)
/* adding space for padding, FIXME: add as an option */
if (length < 21)
length = 21;
- return msgb_alloc_headroom(length + LAPD_HEADROOM, LAPD_HEADROOM, name);
+ return msgb_alloc_headroom(length + LAPD_HEADROOM + LAPD_TAILROOM, LAPD_HEADROOM, name);
}
static inline uint8_t do_mod(uint8_t x, uint8_t m)
@@ -171,12 +169,17 @@ static void lapd_dl_flush_hist(struct lapd_datalink *dl)
}
}
-static void lapd_dl_flush_tx(struct lapd_datalink *dl)
+static void lapd_dl_flush_tx_queue(struct lapd_datalink *dl)
{
struct msgb *msg;
while ((msg = msgb_dequeue(&dl->tx_queue)))
msgb_free(msg);
+}
+
+static void lapd_dl_flush_tx(struct lapd_datalink *dl)
+{
+ lapd_dl_flush_tx_queue(dl);
lapd_dl_flush_hist(dl);
}
@@ -201,41 +204,79 @@ static inline const char *lapd_state_name(enum lapd_state state)
static void lapd_start_t200(struct lapd_datalink *dl)
{
- if (osmo_timer_pending(&dl->t200))
- return;
- LOGP(DLLAPD, LOGL_INFO, "start T200 (dl=%p, timeout=%d.%06ds)\n",
- dl, dl->t200_sec, dl->t200_usec);
- osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec);
+ if ((dl->lapd_flags & LAPD_F_RTS)) {
+ if (dl->t200_rts != LAPD_T200_RTS_OFF)
+ return;
+ LOGDL(dl, LOGL_INFO, "Start T200. (pending until triggered by RTS)\n");
+ dl->t200_rts = LAPD_T200_RTS_PENDING;
+ } else {
+ if (osmo_timer_pending(&dl->t200))
+ return;
+ LOGDL(dl, LOGL_INFO, "Start T200 (timeout=%d.%06ds).\n", dl->t200_sec, dl->t200_usec);
+ osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec);
+ }
+}
+
+/*! Handle timeout condition of T200 in RTS mode.
+ * The caller (LAPDm code) implements the T200 timer and must detect timeout condition.
+ * The function gets called by LAPDm code when it detects a timeout of T200.
+ * \param[in] dl caller-allocated datalink structure */
+int lapd_t200_timeout(struct lapd_datalink *dl)
+{
+ OSMO_ASSERT((dl->lapd_flags & LAPD_F_RTS));
+
+ if (dl->t200_rts != LAPD_T200_RTS_RUNNING)
+ return -EINVAL;
+
+ dl->t200_rts = LAPD_T200_RTS_OFF;
+
+ lapd_t200_cb(dl);
+
+ return 0;
}
static void lapd_start_t203(struct lapd_datalink *dl)
{
if (osmo_timer_pending(&dl->t203))
return;
- LOGP(DLLAPD, LOGL_INFO, "start T203 (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "start T203\n");
osmo_timer_schedule(&dl->t203, dl->t203_sec, dl->t203_usec);
}
static void lapd_stop_t200(struct lapd_datalink *dl)
{
- if (!osmo_timer_pending(&dl->t200))
- return;
- LOGP(DLLAPD, LOGL_INFO, "stop T200 (dl=%p)\n", dl);
- osmo_timer_del(&dl->t200);
+ if ((dl->lapd_flags & LAPD_F_RTS)) {
+ if (dl->t200_rts == LAPD_T200_RTS_OFF)
+ return;
+ dl->t200_rts = LAPD_T200_RTS_OFF;
+ } else {
+ if (!osmo_timer_pending(&dl->t200))
+ return;
+ osmo_timer_del(&dl->t200);
+ }
+ LOGDL(dl, LOGL_INFO, "stop T200\n");
+}
+
+static bool lapd_is_t200_started(struct lapd_datalink *dl)
+{
+ if ((dl->lapd_flags & LAPD_F_RTS))
+ return (dl->t200_rts != LAPD_T200_RTS_OFF);
+ else
+ return osmo_timer_pending(&dl->t200);
}
static void lapd_stop_t203(struct lapd_datalink *dl)
{
if (!osmo_timer_pending(&dl->t203))
return;
- LOGP(DLLAPD, LOGL_INFO, "stop T203 (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "stop T203\n");
osmo_timer_del(&dl->t203);
}
static void lapd_dl_newstate(struct lapd_datalink *dl, uint32_t state)
{
- LOGP(DLLAPD, LOGL_INFO, "new state %s -> %s (dl=%p)\n",
- lapd_state_name(dl->state), lapd_state_name(state), dl);
+ LOGDL(dl, LOGL_INFO, "new state %s -> %s\n",
+ lapd_state_name(dl->state), lapd_state_name(state));
if (state != LAPD_STATE_MF_EST && dl->state == LAPD_STATE_MF_EST) {
/* stop T203 on leaving MF EST state, if running */
@@ -253,11 +294,16 @@ static void lapd_dl_newstate(struct lapd_datalink *dl, uint32_t state)
dl->state = state;
}
-static void *tall_lapd_ctx = NULL;
+void *tall_lapd_ctx = NULL;
-/* init datalink instance and allocate history */
-void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range,
- int maxf)
+/*! Initialize LAPD datalink instance and allocate history
+ * \param[in] dl caller-allocated datalink structure
+ * \param[in] k maximum number of unacknowledged frames
+ * \param[in] v_range range of sequence numbers
+ * \param[in] maxf maximum frame size (after defragmentation)
+ * \param[in] name human-readable name for this LAPD datalink */
+void lapd_dl_init2(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf,
+ const char *name)
{
int m;
@@ -293,22 +339,47 @@ void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range,
}
}
- LOGP(DLLAPD, LOGL_INFO, "Init DL layer: sequence range = %d, k = %d, "
- "history range = %d (dl=%p)\n", dl->v_range, dl->k,
- dl->range_hist, dl);
+ if (!tall_lapd_ctx) {
+ tall_lapd_ctx = talloc_named_const(NULL, 1, "lapd context");
+ OSMO_ASSERT(tall_lapd_ctx);
+ }
+
+ talloc_free(dl->name);
+ if (name)
+ dl->name = talloc_strdup(tall_lapd_ctx, name);
+ else
+ dl->name = talloc_asprintf(tall_lapd_ctx, "dl=%p", dl);
+
+ LOGDL(dl, LOGL_INFO, "Init DL layer: sequence range = %d, k = %d, "
+ "history range = %d\n", dl->v_range, dl->k, dl->range_hist);
lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- if (!tall_lapd_ctx)
- tall_lapd_ctx = talloc_named_const(NULL, 1, "lapd context");
dl->tx_hist = talloc_zero_array(tall_lapd_ctx,
struct lapd_history, dl->range_hist);
}
+/*! Initialize LAPD datalink instance and allocate history
+ * \param[in] dl caller-allocated datalink structure
+ * \param[in] k maximum number of unacknowledged frames
+ * \param[in] v_range range of sequence numbers
+ * \param[in] maxf maximum frame size (after defragmentation) */
+void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf)
+{
+ lapd_dl_init2(dl, k, v_range, maxf, NULL);
+}
+
+void lapd_dl_set_name(struct lapd_datalink *dl, const char *name)
+{
+ if (!name)
+ return;
+ osmo_talloc_replace_string(tall_lapd_ctx, &dl->name, name);
+}
+
/* reset to IDLE state */
void lapd_dl_reset(struct lapd_datalink *dl)
{
- LOGP(DLLAPD, LOGL_INFO, "Resetting LAPDm instance\n");
+ LOGDL(dl, LOGL_INFO, "Resetting LAPD instance\n");
/* enter idle state (and remove eventual cont_res) */
lapd_dl_newstate(dl, LAPD_STATE_IDLE);
/* flush buffer */
@@ -322,11 +393,25 @@ void lapd_dl_reset(struct lapd_datalink *dl)
lapd_stop_t203(dl);
if (dl->state == LAPD_STATE_IDLE)
return;
- LOGP(DLLAPD, LOGL_INFO, "Resetting LAPDm instance (dl=%p)\n", dl);
/* enter idle state (and remove eventual cont_res) */
lapd_dl_newstate(dl, LAPD_STATE_IDLE);
}
+/*! Set lapd_flags to change behaviour
+ * \param[in] dl \ref lapd_datalink instance
+ * \param[in] flags \ref lapd_flags */
+int lapd_dl_set_flags(struct lapd_datalink *dl, unsigned int flags)
+{
+ if (lapd_is_t200_started(dl) && (flags & LAPD_F_RTS) != (dl->lapd_flags & LAPD_F_RTS)) {
+ LOGDL(dl, LOGL_ERROR, "Changing RTS flag not allowed while T200 is running.\n");
+ return -EINVAL;
+ }
+
+ dl->lapd_flags = flags;
+
+ return 0;
+}
+
/* reset and de-allocate history buffer */
void lapd_dl_exit(struct lapd_datalink *dl)
{
@@ -339,6 +424,8 @@ void lapd_dl_exit(struct lapd_datalink *dl)
/* free history buffer list */
talloc_free(dl->tx_hist);
dl->tx_hist = NULL;
+ talloc_free(dl->name);
+ dl->name = NULL;
}
/*! Set the \ref lapdm_mode of a LAPDm entity */
@@ -389,9 +476,9 @@ static int mdl_error(uint8_t cause, struct lapd_msg_ctx *lctx)
struct lapd_datalink *dl = lctx->dl;
struct osmo_dlsap_prim dp;
- LOGP(DLLAPD, LOGL_NOTICE,
- "sending MDL-ERROR-IND cause %d from state %s (dl=%p)\n",
- cause, lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_NOTICE,
+ "sending MDL-ERROR-IND cause %d from state %s\n",
+ cause, lapd_state_name(dl->state));
osmo_prim_init(&dp.oph, 0, PRIM_MDL_ERROR, PRIM_OP_INDICATION, NULL);
dp.u.error_ind.cause = cause;
return dl->send_dlsap(&dp, lctx);
@@ -546,7 +633,7 @@ static int lapd_reestablish(struct lapd_datalink *dl)
struct osmo_dlsap_prim dp;
struct msgb *msg;
- LOGP(DLLAPD, LOGL_DEBUG, "lapd reestablish (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_DEBUG, "LAPD reestablish\n");
msg = lapd_msgb_alloc(0, "DUMMY");
osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
@@ -559,12 +646,13 @@ static void lapd_t200_cb(void *data)
{
struct lapd_datalink *dl = data;
- LOGP(DLLAPD, LOGL_INFO, "Timeout T200 state=%s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_INFO, "Timeout T200 state=%s\n", lapd_state_name(dl->state));
switch (dl->state) {
case LAPD_STATE_SABM_SENT:
/* 5.4.1.3 */
+ /* increment re-transmission counter */
+ dl->retrans_ctr++;
if (dl->retrans_ctr >= dl->n200_est_rel + 1) {
/* flush tx and send buffers */
lapd_dl_flush_tx(dl);
@@ -583,18 +671,16 @@ static void lapd_t200_cb(void *data)
}
/* retransmit SABM command */
lapd_send_resend(dl);
- /* increment re-transmission counter */
- dl->retrans_ctr++;
/* restart T200 (PH-READY-TO-SEND) */
lapd_start_t200(dl);
break;
case LAPD_STATE_DISC_SENT:
/* 5.4.4.3 */
+ /* increment re-transmission counter */
+ dl->retrans_ctr++;
if (dl->retrans_ctr >= dl->n200_est_rel + 1) {
/* send MDL ERROR INIDCATION to L3 */
mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx);
- /* send RELEASE INDICATION to L3 */
- send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx);
/* flush tx and send buffers */
lapd_dl_flush_tx(dl);
lapd_dl_flush_send(dl);
@@ -603,12 +689,12 @@ static void lapd_t200_cb(void *data)
/* 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 */
+ /* send RELEASE INDICATION to L3 */
+ send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx);
break;
}
/* retransmit DISC command */
lapd_send_resend(dl);
- /* increment re-transmission counter */
- dl->retrans_ctr++;
/* restart T200 (PH-READY-TO-SEND) */
lapd_start_t200(dl);
break;
@@ -628,8 +714,7 @@ static void lapd_t200_cb(void *data)
int length = dl->tx_hist[h].msg->len;
struct lapd_msg_ctx nctx;
- LOGP(DLLAPD, LOGL_INFO, "retransmit last frame"
- " V(S)=%d (dl=%p)\n", vs, dl);
+ LOGDL(dl, LOGL_INFO, "retransmit last frame V(S)=%d\n", vs);
/* Create I frame (segment) from tx_hist */
memcpy(&nctx, &dl->lctx, sizeof(nctx));
/* keep nctx.ldp */
@@ -660,8 +745,7 @@ static void lapd_t200_cb(void *data)
} else if (dl->own_busy) {
lapd_send_rnr(&dl->lctx, 1, 1);
} else {
- LOGP(DLLAPD, LOGL_INFO, "unhandled, "
- "pls. fix (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "unhandled, pls. fix\n");
}
}
/* restart T200 (PH-READY-TO-SEND) */
@@ -672,14 +756,13 @@ static void lapd_t200_cb(void *data)
/* reestablish */
if (!dl->reestablish)
break;
- LOGP(DLLAPD, LOGL_NOTICE, "N200+1 reached, performing "
- "reestablishment. (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_NOTICE, "N200+1 reached, performingreestablishment\n");
lapd_reestablish(dl);
}
break;
default:
- LOGP(DLLAPD, LOGL_INFO, "T200 expired in unexpected "
- "dl->state %s (dl=%p)\n", lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_INFO, "T200 expired in unexpected dl->state %s)\n",
+ lapd_state_name(dl->state));
}
}
@@ -688,12 +771,10 @@ static void lapd_t203_cb(void *data)
{
struct lapd_datalink *dl = data;
- LOGP(DLLAPD, LOGL_INFO, "Timeout T203 state=%s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_INFO, "Timeout T203 state=%s\n", lapd_state_name(dl->state));
if (dl->state != LAPD_STATE_MF_EST) {
- LOGP(DLLAPD, LOGL_ERROR, "T203 fired outside MF EST state, "
- "please fix! (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_ERROR, "T203 fired outside MF EST state, please fix!\n");
return;
}
@@ -703,13 +784,11 @@ static void lapd_t203_cb(void *data)
lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV);
/* transmit a supervisory command with P bit set to 1 as follows: */
if (!dl->own_busy) {
- LOGP(DLLAPD, LOGL_INFO,
- "transmit an RR poll command (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "transmit an RR poll command\n");
/* Send RR with P=1 */
lapd_send_rr(&dl->lctx, 1, 1);
} else {
- LOGP(DLLAPD, LOGL_INFO,
- "transmit an RNR poll command (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "transmit an RNR poll command\n");
/* Send RNR with P=1 */
lapd_send_rnr(&dl->lctx, 1, 1);
}
@@ -717,12 +796,14 @@ static void lapd_t203_cb(void *data)
lapd_start_t200(dl);
}
-/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */
-static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
+/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value
+ * In case of a sequence error, the cause is returned with negative sign. */
+static int lapd_acknowledge(struct lapd_msg_ctx *lctx)
{
struct lapd_datalink *dl = lctx->dl;
uint8_t nr = lctx->n_recv;
- int s = 0, rej = 0, t200_reset = 0;
+ int s = 0, rej = 0;
+ bool t200_reset = false;
int i, h;
/* supervisory frame ? */
@@ -738,7 +819,7 @@ static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
if (dl->tx_hist[h].msg) {
msgb_free(dl->tx_hist[h].msg);
dl->tx_hist[h].msg = NULL;
- LOGP(DLLAPD, LOGL_INFO, "ack frame %d\n", i);
+ LOGDL(dl, LOGL_INFO, "ack frame %d\n", i);
}
}
@@ -747,9 +828,8 @@ static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
* 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)) {
- t200_reset = 1;
+ if ((!rej && nr != dl->v_ack) || (rej && nr == dl->v_ack)) {
+ t200_reset = true;
lapd_stop_t200(dl);
/* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */
}
@@ -757,10 +837,9 @@ static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
* N(R) is called valid, if and only if
* (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8.
*/
- if (sub_mod(nr, dl->v_ack, dl->v_range)
- > sub_mod(dl->v_send, dl->v_ack, dl->v_range)) {
- LOGP(DLLAPD, LOGL_NOTICE, "N(R) sequence error (dl=%p)\n", dl);
- mdl_error(MDL_CAUSE_SEQ_ERR, lctx);
+ if (sub_mod(nr, dl->v_ack, dl->v_range) > sub_mod(dl->v_send, dl->v_ack, dl->v_range)) {
+ LOGDL(dl, LOGL_NOTICE, "N(R) sequence error\n");
+ return -MDL_CAUSE_SEQ_ERR;
}
}
@@ -771,8 +850,7 @@ static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
* and if there are outstanding I frames, restart T200 */
if (t200_reset && !rej) {
if (dl->tx_hist[sub_mod(dl->v_send, 1, dl->range_hist)].msg) {
- LOGP(DLLAPD, LOGL_INFO, "start T200, due to unacked I "
- "frame(s) (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "start T200, due to unacked I frame(s)\n");
lapd_start_t200(dl);
}
}
@@ -782,467 +860,493 @@ static void lapd_acknowledge(struct lapd_msg_ctx *lctx)
/* Stop T203, if running */
lapd_stop_t203(dl);
/* Start T203, if T200 is not running in MF EST state, if enabled */
- if (!osmo_timer_pending(&dl->t200)
- && (dl->t203_sec || dl->t203_usec)
- && (dl->state == LAPD_STATE_MF_EST)) {
+ if (!lapd_is_t200_started(dl) && (dl->t203_sec || dl->t203_usec) && (dl->state == LAPD_STATE_MF_EST))
lapd_start_t203(dl);
- }
+
+ return 0;
}
/* L1 -> L2 */
-/* Receive a LAPD U (Unnumbered) message from L1 */
-static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
+/* Receive a LAPD U SABM(E) message from L1 */
+static int lapd_rx_u_sabm(struct msgb *msg, struct lapd_msg_ctx *lctx)
{
struct lapd_datalink *dl = lctx->dl;
int length = lctx->length;
int rc = 0;
uint8_t prim, op;
- switch (lctx->s_u) {
- case LAPD_U_SABM:
- case LAPD_U_SABME:
- prim = PRIM_DL_EST;
- op = PRIM_OP_INDICATION;
-
- LOGP(DLLAPD, LOGL_INFO, "SABM(E) received in state %s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
- /* 5.7.1 */
- dl->seq_err_cond = 0;
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGP(DLLAPD, LOGL_ERROR,
- "SABM response error (dl=%p)\n", dl);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
+ prim = PRIM_DL_EST;
+ op = PRIM_OP_INDICATION;
- /* G.4.5 If SABM is received with L>N201 or with M bit
- * set, AN MDL-ERROR-INDICATION is sent to MM.
- */
- if (lctx->more || length > lctx->n201) {
- LOGP(DLLAPD, LOGL_ERROR,
- "SABM too large error (dl=%p)\n", dl);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
- return -EIO;
- }
+ LOGDL(dl, LOGL_INFO, "SABM(E) received in state %s\n", lapd_state_name(dl->state));
+ /* 5.7.1 */
+ dl->seq_err_cond = 0;
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.resp) {
+ LOGDL(dl, LOGL_ERROR, "SABM response error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
- switch (dl->state) {
- case LAPD_STATE_IDLE:
- break;
- case LAPD_STATE_MF_EST:
- LOGP(DLLAPD, LOGL_INFO, "SABM command, multiple "
- "frame established state (dl=%p)\n", dl);
- /* If link is lost on the remote side, we start over
- * and send DL-ESTABLISH indication again. */
- /* Additionally, continue in case of content resoltion
- * (GSM network). This happens, if the mobile has not
- * yet received UA or another mobile (collision) tries
- * to establish connection. The mobile must receive
- * UA again. */
- /* 5.4.2.1 */
- if (!length) {
- /* If no content resolution, this is a
- * re-establishment. */
- LOGP(DLLAPD, LOGL_INFO,
- "Remote reestablish (dl=%p)\n", dl);
- break;
- }
- if (!dl->cont_res) {
- LOGP(DLLAPD, LOGL_INFO, "SABM command not "
- "allowed in state %s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
- mdl_error(MDL_CAUSE_SABM_MF, lctx);
- msgb_free(msg);
- return 0;
- }
- /* Ignore SABM if content differs from first SABM. */
- if (dl->mode == LAPD_MODE_NETWORK && length) {
-#ifdef TEST_CONTENT_RESOLUTION_NETWORK
- dl->cont_res->data[0] ^= 0x01;
-#endif
- if (memcmp(dl->cont_res->data, msg->data,
- length)) {
- LOGP(DLLAPD, LOGL_INFO, "Another SABM "
- "with different content - "
- "ignoring! (dl=%p)\n", dl);
- msgb_free(msg);
- return 0;
- }
- }
- /* send UA again */
- lapd_send_ua(lctx, length, msg->l3h);
- msgb_free(msg);
- return 0;
- case LAPD_STATE_DISC_SENT:
- /* 5.4.6.2 send DM with F=P */
- lapd_send_dm(lctx);
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- msgb_free(msg);
- return send_dl_simple(prim, op, lctx);
- default:
- /* collision: Send UA, but still wait for rx UA, then
- * change to MF_EST state.
- */
+ /* G.4.5 If SABM is received with L>N201 or with M bit
+ * set, AN MDL-ERROR-INDICATION is sent to MM.
+ */
+ if (lctx->more || length > lctx->n201) {
+ LOGDL(dl, LOGL_ERROR, "SABM too large error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+ return -EIO;
+ }
+
+ switch (dl->state) {
+ case LAPD_STATE_IDLE:
+ break;
+ case LAPD_STATE_TIMER_RECOV:
+ LOGDL(dl, LOGL_INFO, "SABM command, timer recovery state\n");
+ /* If link is lost on the remote side, we start over
+ * and send DL-ESTABLISH indication again. */
+ /* 3GPP TS 44.006 8.6.3 "Procedures for re-establishment" */
+ if (length) {
/* check for contention resoultion */
- if (dl->tx_hist[0].msg && dl->tx_hist[0].msg->len) {
- LOGP(DLLAPD, LOGL_NOTICE, "SABM not allowed "
- "during contention resolution (state=%s, dl=%p)\n",
- lapd_state_name(dl->state), dl);
- mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx);
- }
- lapd_send_ua(lctx, length, msg->l3h);
+ LOGDL(dl, LOGL_ERROR, "SABM L>0 not expected in timer "
+ "recovery state\n");
+ mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx);
+ lapd_send_dm(lctx);
msgb_free(msg);
return 0;
}
- /* save message context for further use */
- memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
-#ifndef TEST_CONTENT_RESOLUTION_NETWORK
- /* send UA response */
- lapd_send_ua(lctx, length, msg->l3h);
-#endif
- /* set Vs, Vr and Va to 0 */
- dl->v_send = dl->v_recv = dl->v_ack = 0;
- /* clear tx_hist */
- lapd_dl_flush_hist(dl);
- /* enter multiple-frame-established state */
- lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
- /* store content resolution data on network side
- * Note: cont_res will be removed when changing state again,
- * so it must be allocated AFTER lapd_dl_newstate(). */
- if (dl->mode == LAPD_MODE_NETWORK && length) {
- dl->cont_res = lapd_msgb_alloc(length, "CONT RES");
- memcpy(msgb_put(dl->cont_res, length), msg->l3h,
- length);
- LOGP(DLLAPD, LOGL_NOTICE,
- "Store content res. (dl=%p)\n", dl);
- }
- /* send notification to L3 */
- if (length == 0) {
- /* 5.4.1.2 Normal establishment procedures */
- rc = send_dl_simple(prim, op, lctx);
- msgb_free(msg);
- } else {
- /* 5.4.1.4 Contention resolution establishment */
- msgb_trim(msg, length);
- rc = send_dl_l3(prim, op, lctx, msg);
- }
+ /* re-establishment, continue below */
+ lapd_stop_t200(dl);
break;
- case LAPD_U_DM:
- LOGP(DLLAPD, LOGL_INFO, "DM received in state %s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.cmd) {
- LOGP(DLLAPD, LOGL_ERROR,
- "DM command error (dl=%p)\n", dl);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
+ case LAPD_STATE_MF_EST:
+ LOGDL(dl, LOGL_INFO, "SABM command, multiple frame established state\n");
+ /* If link is lost on the remote side, we start over
+ * and send DL-ESTABLISH indication again. */
+ /* Additionally, continue in case of content resoltion
+ * (GSM network). This happens, if the mobile has not
+ * yet received UA or another mobile (collision) tries
+ * to establish connection. The mobile must receive
+ * UA again. */
+ /* 5.4.2.1 */
+ if (!length) {
+ /* If no content resolution, this is a
+ * re-establishment. */
+ LOGDL(dl, LOGL_INFO, "Remote reestablish\n");
+ break;
}
- if (!lctx->p_f) {
- /* 5.4.1.2 DM responses with the F bit set to "0"
- * shall be ignored.
- */
+ if (!dl->cont_res) {
+ LOGDL(dl, LOGL_INFO, "SABM command not allowed in state %s\n",
+ lapd_state_name(dl->state));
+ mdl_error(MDL_CAUSE_SABM_MF, lctx);
msgb_free(msg);
return 0;
}
- switch (dl->state) {
- case LAPD_STATE_SABM_SENT:
- break;
- case LAPD_STATE_MF_EST:
- if (lctx->p_f) {
- LOGP(DLLAPD, LOGL_INFO, "unsolicited DM "
- "response (dl=%p)\n", dl);
- mdl_error(MDL_CAUSE_UNSOL_DM_RESP, lctx);
- } else {
- LOGP(DLLAPD, LOGL_INFO, "unsolicited DM "
- "response, multiple frame established "
- "state (dl=%p)\n", dl);
- mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
- /* reestablish */
- if (!dl->reestablish) {
- msgb_free(msg);
- return 0;
- }
- LOGP(DLLAPD, LOGL_NOTICE, "Performing "
- "reestablishment. (dl=%p)\n", dl);
- lapd_reestablish(dl);
- }
- msgb_free(msg);
- return 0;
- case LAPD_STATE_TIMER_RECOV:
- /* FP = 0 (DM is normal in case PF = 1) */
- if (!lctx->p_f) {
- LOGP(DLLAPD, LOGL_INFO, "unsolicited DM "
- "response, multiple frame established "
- "state (dl=%p)\n", dl);
- mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
+ /* Ignore SABM if content differs from first SABM. */
+ if (dl->mode == LAPD_MODE_NETWORK && length) {
+#ifdef TEST_CONTENT_RESOLUTION_NETWORK
+ dl->cont_res->data[0] ^= 0x01;
+#endif
+ if (memcmp(dl->cont_res->data, msg->data,
+ length)) {
+ LOGDL(dl, LOGL_INFO, "Another SABM with different content - "
+ "ignoring!\n");
msgb_free(msg);
- /* reestablish */
- if (!dl->reestablish)
- return 0;
- LOGP(DLLAPD, LOGL_NOTICE, "Performing "
- "reestablishment. (dl=%p)\n", dl);
- return lapd_reestablish(dl);
+ return 0;
}
- break;
- case LAPD_STATE_DISC_SENT:
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* go to idle state */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
- msgb_free(msg);
- return 0;
- case LAPD_STATE_IDLE:
- /* 5.4.5 all other frame types shall be discarded */
- default:
- LOGP(DLLAPD, LOGL_INFO, "unsolicited DM response! "
- "(discarding) (dl=%p)\n", dl);
- msgb_free(msg);
- return 0;
}
- /* stop timer T200 */
+ /* send UA again */
+ lapd_send_ua(lctx, length, msg->l3h);
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_DISC_SENT:
+ /* 5.4.6.2 send DM with F=P */
+ lapd_send_dm(lctx);
+ /* stop Timer T200 */
lapd_stop_t200(dl);
- /* go to idle state */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx);
msgb_free(msg);
- break;
- case LAPD_U_UI:
- LOGP(DLLAPD, LOGL_INFO, "UI received (dl=%p)\n", dl);
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGP(DLLAPD, LOGL_ERROR, "UI indicates response "
- "error (dl=%p)\n", dl);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
+ return send_dl_simple(prim, op, lctx);
+ default:
+ /* collision: Send UA, but still wait for rx UA, then
+ * change to MF_EST state.
+ */
+ /* check for contention resoultion */
+ if (dl->tx_hist[0].msg && dl->tx_hist[0].msg->len) {
+ LOGDL(dl, LOGL_NOTICE, "SABM not allowed during contention "
+ "resolution (state=%s)\n", lapd_state_name(dl->state));
+ mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx);
}
+ lapd_send_ua(lctx, length, msg->l3h);
+ msgb_free(msg);
+ return 0;
+ }
+ /* save message context for further use */
+ memcpy(&dl->lctx, lctx, sizeof(dl->lctx));
+#ifndef TEST_CONTENT_RESOLUTION_NETWORK
+ /* send UA response */
+ lapd_send_ua(lctx, length, msg->l3h);
+#endif
+ /* set Vs, Vr and Va to 0 */
+ dl->v_send = dl->v_recv = dl->v_ack = 0;
+ /* clear tx_hist */
+ lapd_dl_flush_hist(dl);
+ /* enter multiple-frame-established state */
+ lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
+ /* store content resolution data on network side
+ * Note: cont_res will be removed when changing state again,
+ * so it must be allocated AFTER lapd_dl_newstate(). */
+ if (dl->mode == LAPD_MODE_NETWORK && length) {
+ dl->cont_res = lapd_msgb_alloc(length, "CONT RES");
+ memcpy(msgb_put(dl->cont_res, length), msg->l3h,
+ length);
+ LOGDL(dl, LOGL_INFO, "Store content res.\n");
+ }
+ /* send notification to L3 */
+ if (length == 0) {
+ /* 5.4.1.2 Normal establishment procedures */
+ rc = send_dl_simple(prim, op, lctx);
+ msgb_free(msg);
+ } else {
+ /* 5.4.1.4 Contention resolution establishment */
+ msgb_trim(msg, length);
+ rc = send_dl_l3(prim, op, lctx, msg);
+ }
+ return rc;
+}
+
+/* Receive a LAPD U DM message from L1 */
+static int lapd_rx_u_dm(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ int rc = 0;
- /* G.4.5 If UI is received with L>N201 or with M bit
- * set, AN MDL-ERROR-INDICATION is sent to MM.
+ LOGDL(dl, LOGL_INFO, "DM received in state %s\n", lapd_state_name(dl->state));
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.cmd) {
+ LOGDL(dl, LOGL_ERROR, "DM command error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+ if (!lctx->p_f) {
+ /* 5.4.1.2 DM responses with the F bit set to "0"
+ * shall be ignored.
*/
- if (length > lctx->n201 || lctx->more) {
- LOGP(DLLAPD, LOGL_ERROR, "UI too large error "
- "(%d > N201(%d) or M=%d) (dl=%p)\n", length,
- lctx->n201, lctx->more, dl);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
- return -EIO;
+ msgb_free(msg);
+ return 0;
+ }
+ switch (dl->state) {
+ case LAPD_STATE_SABM_SENT:
+ break;
+ case LAPD_STATE_MF_EST:
+ if (lctx->p_f) {
+ LOGDL(dl, LOGL_INFO, "unsolicited DM response\n");
+ mdl_error(MDL_CAUSE_UNSOL_DM_RESP, lctx);
+ } else {
+ LOGDL(dl, LOGL_INFO, "unsolicited DM response, "
+ "multiple frame established state\n");
+ mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
+ /* reestablish */
+ if (!dl->reestablish) {
+ msgb_free(msg);
+ return 0;
+ }
+ LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
+ lapd_reestablish(dl);
}
-
- /* do some length checks */
- if (length == 0) {
- /* 5.3.3 UI frames received with the length indicator
- * set to "0" shall be ignored
- */
- LOGP(DLLAPD, LOGL_INFO,
- "length=0 (discarding) (dl=%p)\n", dl);
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_TIMER_RECOV:
+ /* FP = 0 (DM is normal in case PF = 1) */
+ if (!lctx->p_f) {
+ LOGDL(dl, LOGL_INFO, "unsolicited DM response, multiple frame "
+ "established state\n");
+ mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx);
msgb_free(msg);
- return 0;
+ /* reestablish */
+ if (!dl->reestablish)
+ return 0;
+ LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
+ return lapd_reestablish(dl);
}
- msgb_trim(msg, length);
- rc = send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx,
- msg);
break;
- case LAPD_U_DISC:
- prim = PRIM_DL_REL;
- op = PRIM_OP_INDICATION;
-
- LOGP(DLLAPD, LOGL_INFO, "DISC received in state %s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
- /* flush tx and send buffers */
+ case LAPD_STATE_DISC_SENT:
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* go to idle state */
lapd_dl_flush_tx(dl);
lapd_dl_flush_send(dl);
- /* 5.7.1 */
- dl->seq_err_cond = 0;
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGP(DLLAPD, LOGL_ERROR,
- "DISC response error (dl=%p)\n", dl);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
- if (length > 0 || lctx->more) {
- /* 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(DLLAPD, LOGL_ERROR,
- "U frame iwth incorrect parameters (dl=%p)\n", dl);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
- return -EIO;
- }
- switch (dl->state) {
- case LAPD_STATE_IDLE:
- LOGP(DLLAPD, LOGL_INFO,
- "DISC in idle state (dl=%p)\n", dl);
- /* send DM with F=P */
- msgb_free(msg);
- return lapd_send_dm(lctx);
- case LAPD_STATE_SABM_SENT:
- LOGP(DLLAPD, LOGL_INFO,
- "DISC in SABM state (dl=%p)\n", dl);
- /* 5.4.6.2 send DM with F=P */
- lapd_send_dm(lctx);
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* go to idle state */
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- msgb_free(msg);
- return send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION,
- lctx);
- case LAPD_STATE_MF_EST:
- case LAPD_STATE_TIMER_RECOV:
- LOGP(DLLAPD, LOGL_INFO,
- "DISC in est state (dl=%p)\n", dl);
- break;
- case LAPD_STATE_DISC_SENT:
- LOGP(DLLAPD, LOGL_INFO,
- "DISC in disc state (dl=%p)\n", dl);
- prim = PRIM_DL_REL;
- op = PRIM_OP_CONFIRM;
- break;
- default:
- lapd_send_ua(lctx, length, msg->l3h);
- msgb_free(msg);
- return 0;
- }
- /* send UA response */
- lapd_send_ua(lctx, length, msg->l3h);
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_IDLE:
+ /* 5.4.5 all other frame types shall be discarded */
+ default:
+ LOGDL(dl, LOGL_INFO, "unsolicited DM response! (discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ /* stop timer T200 */
+ lapd_stop_t200(dl);
+ /* go to idle state */
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx);
+ msgb_free(msg);
+ return rc;
+}
+
+/* Receive a LAPD U UI message from L1 */
+static int lapd_rx_u_ui(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ int length = lctx->length;
+
+ LOGDL(dl, LOGL_INFO, "UI received\n");
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.resp) {
+ LOGDL(dl, LOGL_ERROR, "UI indicates response error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+
+ /* G.4.5 If UI is received with L>N201 or with M bit
+ * set, AN MDL-ERROR-INDICATION is sent to MM.
+ */
+ if (length > lctx->n201 || lctx->more) {
+ LOGDL(dl, LOGL_ERROR, "UI too large error (%d > N201(%d) or M=%d)\n",
+ length, lctx->n201, lctx->more);
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+ return -EIO;
+ }
+
+ /* do some length checks */
+ if (length == 0) {
+ /* 5.3.3 UI frames received with the length indicator
+ * set to "0" shall be ignored
+ */
+ LOGDL(dl, LOGL_INFO, "length=0 (discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ msgb_trim(msg, length);
+ return send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx, msg);
+}
+
+/* Receive a LAPD U DISC message from L1 */
+static int lapd_rx_u_disc(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ int length = lctx->length;
+ int rc = 0;
+ uint8_t prim, op;
+
+ prim = PRIM_DL_REL;
+ op = PRIM_OP_INDICATION;
+
+ LOGDL(dl, LOGL_INFO, "DISC received in state %s\n", lapd_state_name(dl->state));
+ /* flush tx and send buffers */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ /* 5.7.1 */
+ dl->seq_err_cond = 0;
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.resp) {
+ LOGDL(dl, LOGL_ERROR, "DISC response error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+ if (length > 0 || lctx->more) {
+ /* 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.
+ */
+ LOGDL(dl, LOGL_ERROR, "U frame iwth incorrect parameters\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+ return -EIO;
+ }
+ switch (dl->state) {
+ case LAPD_STATE_IDLE:
+ LOGDL(dl, LOGL_INFO, "DISC in idle state\n");
+ /* send DM with F=P */
+ msgb_free(msg);
+ return lapd_send_dm(lctx);
+ case LAPD_STATE_SABM_SENT:
+ LOGDL(dl, LOGL_INFO, "DISC in SABM state\n");
+ /* 5.4.6.2 send DM with F=P */
+ lapd_send_dm(lctx);
/* stop Timer T200 */
lapd_stop_t200(dl);
- /* enter idle state, keep tx-buffer with UA response */
+ /* go to idle state */
lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- /* send notification to L3 */
- rc = send_dl_simple(prim, op, lctx);
msgb_free(msg);
+ return send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION,
+ lctx);
+ case LAPD_STATE_MF_EST:
+ case LAPD_STATE_TIMER_RECOV:
+ LOGDL(dl, LOGL_INFO, "DISC in est state\n");
break;
- case LAPD_U_UA:
- LOGP(DLLAPD, LOGL_INFO, "UA received in state %s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
- /* G.2.2 Wrong value of the C/R bit */
- if (lctx->cr == dl->cr.rem2loc.cmd) {
- LOGP(DLLAPD, LOGL_ERROR, "UA indicates command "
- "error (dl=%p)\n", dl);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
- return -EINVAL;
- }
+ case LAPD_STATE_DISC_SENT:
+ LOGDL(dl, LOGL_INFO, "DISC in disc state\n");
+ prim = PRIM_DL_REL;
+ op = PRIM_OP_CONFIRM;
+ break;
+ default:
+ lapd_send_ua(lctx, length, msg->l3h);
+ msgb_free(msg);
+ return 0;
+ }
+ /* send UA response */
+ lapd_send_ua(lctx, length, msg->l3h);
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* enter idle state, keep tx-buffer with UA response */
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ /* send notification to L3 */
+ rc = send_dl_simple(prim, op, lctx);
+ msgb_free(msg);
+ return rc;
+}
- /* G.4.5 If UA is received with L>N201 or with M bit
- * set, AN MDL-ERROR-INDICATION is sent to MM.
- */
- if (lctx->more || length > lctx->n201) {
- LOGP(DLLAPD, LOGL_ERROR,
- "UA too large error (dl=%p)\n", dl);
- msgb_free(msg);
- mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
- return -EIO;
- }
+/* Receive a LAPD U UA message from L1 */
+static int lapd_rx_u_ua(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+ int length = lctx->length;
+ int rc = 0;
- if (!lctx->p_f) {
- /* 5.4.1.2 A UA response with the F bit set to "0"
- * shall be ignored.
- */
- LOGP(DLLAPD, LOGL_INFO,
- "F=0 (discarding) (dl=%p)\n", dl);
- msgb_free(msg);
- return 0;
- }
- switch (dl->state) {
- case LAPD_STATE_SABM_SENT:
- break;
- case LAPD_STATE_MF_EST:
- case LAPD_STATE_TIMER_RECOV:
- LOGP(DLLAPD, LOGL_INFO, "unsolicited UA response! "
- "(discarding) (dl=%p)\n", dl);
- mdl_error(MDL_CAUSE_UNSOL_UA_RESP, lctx);
- msgb_free(msg);
- return 0;
- case LAPD_STATE_DISC_SENT:
- LOGP(DLLAPD, LOGL_INFO,
- "UA in disconnect state (dl=%p)\n", dl);
- /* stop Timer T200 */
- lapd_stop_t200(dl);
+ LOGDL(dl, LOGL_INFO, "UA received in state %s\n", lapd_state_name(dl->state));
+ /* G.2.2 Wrong value of the C/R bit */
+ if (lctx->cr == dl->cr.rem2loc.cmd) {
+ LOGDL(dl, LOGL_ERROR, "UA indicates command error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
+ return -EINVAL;
+ }
+
+ /* G.4.5 If UA is received with L>N201 or with M bit
+ * set, AN MDL-ERROR-INDICATION is sent to MM.
+ */
+ if (lctx->more || length > lctx->n201) {
+ LOGDL(dl, LOGL_ERROR, "UA too large error\n");
+ msgb_free(msg);
+ mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx);
+ return -EIO;
+ }
+
+ if (!lctx->p_f) {
+ /* 5.4.1.2 A UA response with the F bit set to "0"
+ * shall be ignored.
+ */
+ LOGDL(dl, LOGL_INFO, "F=0 (discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ switch (dl->state) {
+ case LAPD_STATE_SABM_SENT:
+ break;
+ case LAPD_STATE_MF_EST:
+ case LAPD_STATE_TIMER_RECOV:
+ LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n");
+ mdl_error(MDL_CAUSE_UNSOL_UA_RESP, lctx);
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_DISC_SENT:
+ LOGDL(dl, LOGL_INFO, "UA in disconnect state\n");
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* go to idle state */
+ lapd_dl_flush_tx(dl);
+ lapd_dl_flush_send(dl);
+ lapd_dl_newstate(dl, LAPD_STATE_IDLE);
+ rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
+ msgb_free(msg);
+ return 0;
+ case LAPD_STATE_IDLE:
+ /* 5.4.5 all other frame types shall be discarded */
+ default:
+ LOGDL(dl, LOGL_INFO, "unsolicited UA response! (discarding)\n");
+ msgb_free(msg);
+ return 0;
+ }
+ LOGDL(dl, LOGL_INFO, "UA in SABM state\n");
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
+ /* compare UA with SABME if contention resolution is applied */
+ if (dl->tx_hist[0].msg->len) {
+ if (length != (dl->tx_hist[0].msg->len)
+ || !!memcmp(dl->tx_hist[0].msg->data, msg->l3h,
+ length)) {
+ LOGDL(dl, LOGL_INFO, "**** UA response mismatches ****\n");
/* go to idle state */
lapd_dl_flush_tx(dl);
lapd_dl_flush_send(dl);
lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx);
- msgb_free(msg);
- return 0;
- case LAPD_STATE_IDLE:
- /* 5.4.5 all other frame types shall be discarded */
- default:
- LOGP(DLLAPD, LOGL_INFO, "unsolicited UA response! "
- "(discarding) (dl=%p)\n", dl);
+ rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx);
msgb_free(msg);
return 0;
}
- LOGP(DLLAPD, LOGL_INFO, "UA in SABM state (dl=%p)\n", dl);
- /* stop Timer T200 */
- lapd_stop_t200(dl);
- /* compare UA with SABME if contention resolution is applied */
- if (dl->tx_hist[0].msg->len) {
- if (length != (dl->tx_hist[0].msg->len)
- || !!memcmp(dl->tx_hist[0].msg->data, msg->l3h,
- length)) {
- LOGP(DLLAPD, LOGL_INFO, "**** UA response "
- "mismatches **** (dl=%p)\n", dl);
- rc = send_dl_simple(PRIM_DL_REL,
- PRIM_OP_INDICATION, lctx);
- msgb_free(msg);
- /* go to idle state */
- lapd_dl_flush_tx(dl);
- lapd_dl_flush_send(dl);
- lapd_dl_newstate(dl, LAPD_STATE_IDLE);
- return 0;
- }
- }
- /* set Vs, Vr and Va to 0 */
- dl->v_send = dl->v_recv = dl->v_ack = 0;
- /* clear tx_hist */
- lapd_dl_flush_hist(dl);
- /* enter multiple-frame-established state */
- lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
- /* send outstanding frames, if any (resume / reconnect) */
- lapd_send_i(lctx, __LINE__);
- /* send notification to L3 */
- rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx);
- msgb_free(msg);
- break;
+ }
+ /* set Vs, Vr and Va to 0 */
+ dl->v_send = dl->v_recv = dl->v_ack = 0;
+ /* clear tx_hist */
+ lapd_dl_flush_hist(dl);
+ /* enter multiple-frame-established state */
+ lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
+ /* send outstanding frames, if any (resume / reconnect) */
+ lapd_send_i(dl, __LINE__, false);
+ /* send notification to L3 */
+ rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx);
+ msgb_free(msg);
+ return rc;
+}
+
+/* Receive a LAPD U FRMR message from L1 */
+static int lapd_rx_u_frmr(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+
+ LOGDL(dl, LOGL_NOTICE, "Frame reject received\n");
+ /* send MDL ERROR INIDCATION to L3 */
+ mdl_error(MDL_CAUSE_FRMR, lctx);
+ msgb_free(msg);
+ /* reestablish */
+ if (!dl->reestablish)
+ return 0;
+ LOGDL(dl, LOGL_NOTICE, "Performing reestablishment\n");
+ return lapd_reestablish(dl);
+}
+
+/* Receive a LAPD U (Unnumbered) message from L1 */
+static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx)
+{
+ switch (lctx->s_u) {
+ case LAPD_U_SABM:
+ case LAPD_U_SABME:
+ return lapd_rx_u_sabm(msg, lctx);
+ case LAPD_U_DM:
+ return lapd_rx_u_dm(msg, lctx);
+ case LAPD_U_UI:
+ return lapd_rx_u_ui(msg, lctx);
+ case LAPD_U_DISC:
+ return lapd_rx_u_disc(msg, lctx);
+ case LAPD_U_UA:
+ return lapd_rx_u_ua(msg, lctx);
case LAPD_U_FRMR:
- LOGP(DLLAPD, LOGL_NOTICE,
- "Frame reject received (dl=%p)\n", dl);
- /* send MDL ERROR INIDCATION to L3 */
- mdl_error(MDL_CAUSE_FRMR, lctx);
- msgb_free(msg);
- /* reestablish */
- if (!dl->reestablish)
- break;
- LOGP(DLLAPD, LOGL_NOTICE,
- "Performing reestablishment. (dl=%p)\n", dl);
- rc = lapd_reestablish(dl);
- break;
+ return lapd_rx_u_frmr(msg, lctx);
default:
/* G.3.1 */
- LOGP(DLLAPD, LOGL_NOTICE,
- "Unnumbered frame not allowed. (dl=%p)\n", dl);
+ LOGDL(lctx->dl, LOGL_NOTICE, "Unnumbered frame not allowed\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
return -EINVAL;
}
- return rc;
}
/* Receive a LAPD S (Supervisory) message from L1 */
@@ -1256,8 +1360,7 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
* 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(DLLAPD, LOGL_ERROR,
- "S frame with incorrect parameters (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_ERROR, "S frame with incorrect parameters\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_SFRM_INC_PARAM, lctx);
return -EIO;
@@ -1267,8 +1370,7 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
&& lctx->p_f
&& dl->state != LAPD_STATE_TIMER_RECOV) {
/* 5.4.2.2: Inidcate error on supervisory reponse F=1 */
- LOGP(DLLAPD, LOGL_NOTICE,
- "S frame response with F=1 error (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_NOTICE, "S frame response with F=1 error\n");
mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx);
}
@@ -1281,15 +1383,13 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
/* fall though */
case LAPD_STATE_SABM_SENT:
case LAPD_STATE_DISC_SENT:
- LOGP(DLLAPD, LOGL_NOTICE,
- "S frame ignored in this state (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_NOTICE, "S frame ignored in this state\n");
msgb_free(msg);
return 0;
}
switch (lctx->s_u) {
case LAPD_S_RR:
- LOGP(DLLAPD, LOGL_INFO, "RR received in state %s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_INFO, "RR received in state %s\n", lapd_state_name(dl->state));
/* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
lapd_acknowledge(lctx);
@@ -1297,10 +1397,8 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
if (lctx->cr == dl->cr.rem2loc.cmd
&& lctx->p_f) {
if (!dl->own_busy && !dl->seq_err_cond) {
- LOGP(DLLAPD, LOGL_INFO, "RR frame command "
- "with polling bit set and we are not "
- "busy, so we reply with RR frame "
- "response (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "RR frame command with polling bit set and "
+ "we are not busy, so we reply with RR frame response\n");
lapd_send_rr(lctx, 1, 0);
/* NOTE: In case of sequence error condition,
* the REJ frame has been transmitted when
@@ -1308,18 +1406,15 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
* done here
*/
} else if (dl->own_busy) {
- LOGP(DLLAPD, LOGL_INFO, "RR frame command "
- "with polling bit set and we are busy, "
- "so we reply with RR frame response (dl=%p)\n",
- dl);
+ LOGDL(dl, LOGL_INFO, "RR frame command with polling bit set and "
+ "we are busy, so we reply with RR frame response\n");
lapd_send_rnr(lctx, 1, 0);
}
} else if (lctx->cr == dl->cr.rem2loc.resp
&& lctx->p_f
&& dl->state == LAPD_STATE_TIMER_RECOV) {
- LOGP(DLLAPD, LOGL_INFO, "RR response with F==1, "
- "and we are in timer recovery state, so "
- "we leave that state (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "RR response with F==1, and we are in timer recovery "
+ "state, so we leave that state\n");
/* V(S) to the N(R) in the RR frame */
dl->v_send = lctx->n_recv;
/* stop Timer T200 */
@@ -1328,56 +1423,52 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
}
/* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
break;
case LAPD_S_RNR:
- LOGP(DLLAPD, LOGL_INFO, "RNR received in state %s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_INFO, "RNR received in state %s\n", lapd_state_name(dl->state));
/* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
lapd_acknowledge(lctx);
/* 5.5.5 */
/* Set peer receiver busy condition */
dl->peer_busy = 1;
+ /* Flush pending messages in TX queue. */
+ lapd_dl_flush_tx_queue(dl);
+ /* stop Timer T200 */
+ lapd_stop_t200(dl);
if (lctx->p_f) {
if (lctx->cr == dl->cr.rem2loc.cmd) {
if (!dl->own_busy) {
- LOGP(DLLAPD, LOGL_INFO, "RNR poll "
- "command and we are not busy, "
- "so we reply with RR final "
- "response (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "RNR poll command and we are not busy, "
+ "so we reply with RR final response\n");
/* Send RR with F=1 */
lapd_send_rr(lctx, 1, 0);
} else {
- LOGP(DLLAPD, LOGL_INFO, "RNR poll "
- "command and we are busy, so "
- "we reply with RNR final "
- "response (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "RNR poll command and we are busy, so "
+ "we reply with RNR final response\n");
/* Send RNR with F=1 */
lapd_send_rnr(lctx, 1, 0);
}
} else if (dl->state == LAPD_STATE_TIMER_RECOV) {
- LOGP(DLLAPD, LOGL_INFO, "RNR poll response "
- "and we in timer recovery state, so "
- "we leave that state (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "RNR poll response and we in timer recovery "
+ "state, so we leave that state\n");
/* 5.5.7 Clear timer recovery condition */
lapd_dl_newstate(dl, LAPD_STATE_MF_EST);
/* V(S) to the N(R) in the RNR frame */
dl->v_send = lctx->n_recv;
}
} else
- LOGP(DLLAPD, LOGL_INFO, "RNR not polling/final state "
- "received (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "RNR not polling/final state received\n");
/* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
break;
case LAPD_S_REJ:
- LOGP(DLLAPD, LOGL_INFO, "REJ received in state %s (dl=%p)\n",
- lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_INFO, "REJ received in state %s\n", lapd_state_name(dl->state));
/* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */
lapd_acknowledge(lctx);
@@ -1387,17 +1478,16 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
dl->peer_busy = 0;
/* V(S) and V(A) to the N(R) in the REJ frame */
dl->v_send = dl->v_ack = lctx->n_recv;
+ /* Flush pending messages in TX queue. */
+ lapd_dl_flush_tx_queue(dl);
/* stop Timer T200 */
lapd_stop_t200(dl);
/* 5.5.3.2 */
if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) {
if (!dl->own_busy && !dl->seq_err_cond) {
- LOGP(DLLAPD, LOGL_INFO, "REJ poll "
- "command not in timer recovery "
- "state and not in own busy "
- "condition received, so we "
- "respond with RR final "
- "response (dl=%p)\n", dl);
+ LOGDL(dl, 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");
lapd_send_rr(lctx, 1, 0);
/* NOTE: In case of sequence error
* condition, the REJ frame has been
@@ -1406,33 +1496,28 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
* here
*/
} else if (dl->own_busy) {
- LOGP(DLLAPD, LOGL_INFO, "REJ poll "
- "command not in timer recovery "
- "state and in own busy "
- "condition received, so we "
- "respond with RNR final "
- "response (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "REJ poll command not in timer recovery "
+ "state and in own busy condition received, so we "
+ "respond with RNR final response\n");
lapd_send_rnr(lctx, 1, 0);
}
} else
- LOGP(DLLAPD, LOGL_INFO, "REJ response or not "
- "polling command not in timer recovery "
- "state received (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "REJ response or not polling command not "
+ "in timer recovery state received\n");
/* send MDL ERROR INIDCATION to L3 */
if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) {
- LOGP(DLLAPD, LOGL_ERROR,
- "unsolicited supervisory response! (dl=%p)\n",
- dl);
+ LOGDL(dl, LOGL_ERROR, "unsolicited supervisory response!\n");
mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx);
}
} else if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) {
- LOGP(DLLAPD, LOGL_INFO, "REJ poll response in timer "
- "recovery state received (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "REJ poll response in timer recovery state received\n");
/* 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 = lctx->n_recv;
+ /* Flush pending messages in TX queue. */
+ lapd_dl_flush_tx_queue(dl);
/* stop Timer T200 */
lapd_stop_t200(dl);
/* 5.5.7 Clear timer recovery condition */
@@ -1442,15 +1527,14 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
dl->peer_busy = 0;
/* V(S) and V(A) to the N(R) in the REJ frame */
dl->v_send = dl->v_ack = lctx->n_recv;
+ /* Flush pending messages in TX queue. */
+ lapd_dl_flush_tx_queue(dl);
/* 5.5.3.2 */
if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) {
if (!dl->own_busy && !dl->seq_err_cond) {
- LOGP(DLLAPD, LOGL_INFO, "REJ poll "
- "command in timer recovery "
- "state and not in own busy "
- "condition received, so we "
- "respond with RR final "
- "response (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "REJ poll command in timer recovery "
+ "state and not in own busy condition received, so we "
+ "respond with RR final response\n");
lapd_send_rr(lctx, 1, 0);
/* NOTE: In case of sequence error
* condition, the REJ frame has been
@@ -1459,30 +1543,25 @@ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx)
* here
*/
} else if (dl->own_busy) {
- LOGP(DLLAPD, LOGL_INFO, "REJ poll "
- "command in timer recovery "
- "state and in own busy "
- "condition received, so we "
- "respond with RNR final "
- "response (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "REJ poll command in timer recovery "
+ "state and in own busy condition received, so we "
+ "respond with RNR final response\n");
lapd_send_rnr(lctx, 1, 0);
}
} else
- LOGP(DLLAPD, LOGL_INFO, "REJ response or not "
- "polling command in timer recovery "
- "state received (dl=%p)\n", dl);
+ LOGDL(dl, 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 */
- lapd_send_i(lctx, __LINE__);
+ /* Send message, if possible due to acknowledged data and new V(S) and V(A). */
+ lapd_send_i(dl, __LINE__, false);
break;
default:
/* G.3.1 */
- LOGP(DLLAPD, LOGL_ERROR,
- "Supervisory frame not allowed. (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_ERROR, "Supervisory frame not allowed\n");
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
return -EINVAL;
@@ -1499,14 +1578,16 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
uint8_t ns = lctx->n_send;
int length = lctx->length;
int rc;
+ bool i_frame_in_queue = false;
+ int mdl_cause = 0;
- LOGP(DLLAPD, LOGL_INFO, "I received in state %s on SAPI(%u) (dl=%p)\n",
- lapd_state_name(dl->state), lctx->sapi, dl);
+ LOGDL(dl, LOGL_INFO, "I received in state %s on SAPI(%u)\n",
+ lapd_state_name(dl->state), lctx->sapi);
/* G.2.2 Wrong value of the C/R bit */
if (lctx->cr == dl->cr.rem2loc.resp) {
- LOGP(DLLAPD, LOGL_ERROR,
- "I frame response not allowed (dl=%p state %s)\n", dl, lapd_state_name(dl->state));
+ LOGDL(dl, LOGL_ERROR, "I frame response not allowed (state %s)\n",
+ lapd_state_name(dl->state));
msgb_free(msg);
mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx);
return -EINVAL;
@@ -1517,8 +1598,8 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
* 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(DLLAPD, LOGL_ERROR,
- "I frame length not allowed (dl=%p state %s)\n", dl, lapd_state_name(dl->state));
+ LOGDL(dl, LOGL_ERROR, "I frame length not allowed (state %s)\n",
+ lapd_state_name(dl->state));
msgb_free(msg);
mdl_error(MDL_CAUSE_IFRM_INC_LEN, lctx);
return -EIO;
@@ -1529,8 +1610,8 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
* cause "I frame with incorrect use of M bit" is sent to the
* mobile management entity. */
if (lctx->more && length < lctx->n201) {
- LOGP(DLLAPD, LOGL_ERROR,
- "I frame with M bit too short (dl=%p state %s)\n", dl, lapd_state_name(dl->state));
+ LOGDL(dl, LOGL_ERROR, "I frame with M bit too short (state %s)\n",
+ lapd_state_name(dl->state));
msgb_free(msg);
mdl_error(MDL_CAUSE_IFRM_INC_MBITS, lctx);
return -EIO;
@@ -1545,21 +1626,19 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
/* fall though */
case LAPD_STATE_SABM_SENT:
case LAPD_STATE_DISC_SENT:
- LOGP(DLLAPD, LOGL_NOTICE,
- "I frame ignored in state %s (dl=%p)\n", lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_NOTICE, "I frame ignored in state %s\n", lapd_state_name(dl->state));
msgb_free(msg);
return 0;
}
/* 5.7.1: N(s) sequence error */
if (ns != dl->v_recv) {
- LOGP(DLLAPD, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, "
- "V(R)=%u (dl=%p state %s)\n", ns, dl->v_recv, dl, lapd_state_name(dl->state));
+ LOGDL(dl, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, V(R)=%u (state %s)\n",
+ ns, dl->v_recv, lapd_state_name(dl->state));
/* discard data */
msgb_free(msg);
- if (dl->seq_err_cond != 1) {
- /* FIXME: help me understand what exactly todo here
- */
+ /* Send reject, but suppress second reject if LAPD_F_DROP_2ND_REJ flag is set. */
+ if (dl->seq_err_cond != 1 || !(dl->lapd_flags & LAPD_F_DROP_2ND_REJ)) {
dl->seq_err_cond = 1;
lapd_send_rej(lctx, lctx->p_f);
} else {
@@ -1577,10 +1656,12 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
}
/* Even if N(s) sequence error, acknowledge to N(R)-1 */
/* 5.5.3.1: Acknowlege all transmitted frames up the N(R)-1 */
- lapd_acknowledge(lctx); /* V(A) is also set here */
+ mdl_cause = lapd_acknowledge(lctx); /* V(A) is also set here */
+ if (mdl_cause < 0)
+ mdl_error(-mdl_cause, lctx);
/* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
return 0;
}
@@ -1588,18 +1669,23 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
/* Increment receiver state */
dl->v_recv = inc_mod(dl->v_recv, dl->v_range);
- LOGP(DLLAPD, LOGL_INFO, "incrementing V(R) to %u (dl=%p)\n",
- dl->v_recv, dl);
+ LOGDL(dl, LOGL_INFO, "incrementing V(R) to %u\n", dl->v_recv);
+
+ /* Update all pending frames in the queue to the new V(R) state. */
+ if (dl->update_pending_frames) {
+ rc = dl->update_pending_frames(lctx);
+ if (!rc)
+ i_frame_in_queue = true;
+ }
/* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */
- lapd_acknowledge(lctx); /* V(A) is also set here */
+ mdl_cause = lapd_acknowledge(lctx); /* 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 (!lctx->more && !dl->rcv_buffer) {
- LOGP(DLLAPD, LOGL_INFO,
- "message in single I frame (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "message in single I frame\n");
/* send a DATA INDICATION to L3 */
msgb_trim(msg, length);
rc = send_dl_l3(PRIM_DL_DATA, PRIM_OP_INDICATION, lctx,
@@ -1607,50 +1693,51 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
} else {
/* create rcv_buffer */
if (!dl->rcv_buffer) {
- LOGP(DLLAPD, LOGL_INFO, "message in multiple "
- "I frames (first message) (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "message in multiple I frames (first message)\n");
dl->rcv_buffer = lapd_msgb_alloc(dl->maxf,
"LAPD RX");
dl->rcv_buffer->l3h = dl->rcv_buffer->data;
}
/* concat. rcv_buffer */
if (msgb_l3len(dl->rcv_buffer) + length > dl->maxf) {
- LOGP(DLLAPD, LOGL_NOTICE, "Received frame "
- "overflow! (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_NOTICE, "Received frame overflow!\n");
} else {
memcpy(msgb_put(dl->rcv_buffer, length),
msg->l3h, length);
}
/* if the last segment was received */
if (!lctx->more) {
- LOGP(DLLAPD, LOGL_INFO, "message in multiple "
- "I frames (last message) (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "message in multiple I frames (last message)\n");
rc = send_dl_l3(PRIM_DL_DATA,
PRIM_OP_INDICATION, lctx,
dl->rcv_buffer);
dl->rcv_buffer = NULL;
} else
- LOGP(DLLAPD, LOGL_INFO, "message in multiple "
- "I frames (next message) (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "message in multiple I frames (next message)\n");
msgb_free(msg);
}
+ /* the L3 or higher (called in-line above via send_dl_l3) might have destroyed the
+ * data link meanwhile. See OS#1761 */
+ if (dl->state == LAPD_STATE_NULL)
+ return 0;
} else
- LOGP(DLLAPD, LOGL_INFO, "I frame ignored during own receiver "
- "busy condition\n");
+ LOGDL(dl, LOGL_INFO, "I frame ignored during own receiver busy condition\n");
+
+ /* Indicate sequence error, if exists. */
+ if (mdl_cause < 0)
+ mdl_error(-mdl_cause, lctx);
/* Check for P bit */
if (lctx->p_f) {
/* 5.5.2.1 */
/* check if we are not in own receiver busy */
if (!dl->own_busy) {
- LOGP(DLLAPD, LOGL_INFO,
- "we are not busy, send RR (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "we are not busy, send RR\n");
/* Send RR with F=1 */
rc = lapd_send_rr(lctx, 1, 0);
} else {
- LOGP(DLLAPD, LOGL_INFO,
- "we are busy, send RNR (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "we are busy, send RNR\n");
/* Send RNR with F=1 */
rc = lapd_send_rnr(lctx, 1, 0);
}
@@ -1659,32 +1746,24 @@ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx)
/* check if we are not in own receiver busy */
if (!dl->own_busy) {
/* NOTE: V(R) is already set above */
- rc = lapd_send_i(lctx, __LINE__);
-
- /* if update_pending_iframe returns 0 it updated
- * the lapd header of an iframe in the tx queue */
- if (rc && dl->update_pending_frames)
- rc = dl->update_pending_frames(lctx);
-
- if (rc) {
- LOGP(DLLAPD, LOGL_INFO, "we are not busy and "
- "have no pending data, send RR (dl=%p)\n",
- dl);
+ rc = lapd_send_i(dl, __LINE__, false);
+ if (rc && !i_frame_in_queue) {
+ LOGDL(dl, LOGL_INFO, "we are not busy and have no pending data, "
+ "send RR\n");
/* Send RR with F=0 */
return lapd_send_rr(lctx, 0, 0);
}
/* all I or one RR is sent, we are done */
return 0;
} else {
- LOGP(DLLAPD, LOGL_INFO,
- "we are busy, send RNR (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "we are busy, send RNR\n");
/* Send RNR with F=0 */
rc = lapd_send_rnr(lctx, 0, 0);
}
}
/* Send message, if possible due to acknowledged data */
- lapd_send_i(lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
return rc;
}
@@ -1705,14 +1784,38 @@ int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx)
rc = lapd_rx_i(msg, lctx);
break;
default:
- LOGP(DLLAPD, LOGL_NOTICE,
- "unknown LAPD format (dl=%p)\n", lctx->dl);
+ LOGDL(lctx->dl, LOGL_NOTICE, "unknown LAPD format 0x%02x\n", lctx->format);
msgb_free(msg);
rc = -EINVAL;
}
return rc;
}
+/*! Enqueue next LAPD frame and run pending T200. (Must be called when frame is ready to send.)
+ * The caller (LAPDm code) calls this function before it sends the next frame.
+ * If there is no frame in the TX queue, LAPD will enqueue next I-frame, if possible.
+ * If the T200 is pending, it is changed to running state.
+ * \param[in] lctx LAPD context
+ * \param[out] rc set to 1, if timer T200 state changed to running, set to 0, if not. */
+int lapd_ph_rts_ind(struct lapd_msg_ctx *lctx)
+{
+ struct lapd_datalink *dl = lctx->dl;
+
+ /* If there is no pending frame, try to enqueue next I frame. */
+ if (llist_empty(&dl->tx_queue) && (dl->state == LAPD_STATE_MF_EST || dl->state == LAPD_STATE_TIMER_RECOV)) {
+ /* Send an I frame, if there are pending outgoing messages. */
+ lapd_send_i(dl, __LINE__, true);
+ }
+
+ /* Run T200 at RTS, if pending. Tell caller that is has been started. (rc = 1) */
+ if (dl->t200_rts == LAPD_T200_RTS_PENDING) {
+ dl->t200_rts = LAPD_T200_RTS_RUNNING;
+ return 1;
+ }
+
+ return 0;
+}
+
/* L3 -> L2 */
/* send unit data */
@@ -1736,6 +1839,20 @@ static int lapd_udata_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
return dl->send_ph_data_req(&nctx, msg);
}
+static void msg_to_tx_hist(struct lapd_history *tx_hist, const struct msgb *msg, int length, int more)
+{
+ tx_hist->msg = lapd_msgb_alloc(msg->len, "HIST");
+ tx_hist->more = more;
+ msgb_put(tx_hist->msg, msg->len);
+ if (length)
+ memcpy(tx_hist->msg->data, msg->l3h, msg->len);
+}
+
+static void msg_to_tx_hist0(struct lapd_datalink *dl, const struct msgb *msg)
+{
+ return msg_to_tx_hist(&dl->tx_hist[0], msg, msg->len, 0);
+}
+
/* request link establishment */
static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
{
@@ -1744,11 +1861,9 @@ static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
struct lapd_msg_ctx nctx;
if (msg->len)
- LOGP(DLLAPD, LOGL_INFO, "perform establishment with content "
- "(SABM) (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "perform establishment with content (SABM)\n");
else
- LOGP(DLLAPD, LOGL_INFO,
- "perform normal establishm. (SABM), (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "perform normal establishm. (SABM)\n");
/* Flush send-queue */
/* Clear send-buffer */
@@ -1776,11 +1891,8 @@ static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
nctx.more = 0;
/* Transmit-buffer carries exactly one segment */
- dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST");
- msgb_put(dl->tx_hist[0].msg, msg->len);
- if (msg->len)
- memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len);
- dl->tx_hist[0].more = 0;
+ msg_to_tx_hist0(dl, msg);
+
/* set Vs to 0, because it is used as index when resending SABM */
dl->v_send = 0;
@@ -1803,29 +1915,26 @@ static int lapd_data_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
struct msgb *msg = dp->oph.msg;
if (msgb_l3len(msg) == 0) {
- LOGP(DLLAPD, LOGL_ERROR,
- "writing an empty message is not possible. (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_ERROR, "writing an empty message is not possible\n");
msgb_free(msg);
return -1;
}
- LOGP(DLLAPD, LOGL_INFO,
- "writing message to send-queue: l3len: %d (dl=%p)\n",
- msgb_l3len(msg), dl);
+ LOGDL(dl, LOGL_INFO, "writing message to send-queue: l3len: %d\n", msgb_l3len(msg));
/* Write data into the send queue */
msgb_enqueue(&dl->send_queue, msg);
/* Send message, if possible */
- lapd_send_i(&dl->lctx, __LINE__);
+ lapd_send_i(dl, __LINE__, false);
return 0;
}
/* Send next I frame from queued/buffered data */
-static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
+static int lapd_send_i(struct lapd_datalink *dl, int line, bool rts)
{
- struct lapd_datalink *dl = lctx->dl;
+ struct lapd_msg_ctx *lctx = &dl->lctx;
uint8_t k = dl->k;
uint8_t h;
struct msgb *msg;
@@ -1833,20 +1942,26 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
int rc = - 1; /* we sent nothing */
struct lapd_msg_ctx nctx;
+ if (!rts)
+ LOGDL(dl, LOGL_INFO, "%s() called from line %d\n", __func__, line);
- LOGP(DLLAPD, LOGL_INFO,
- "%s() called from line %d (dl=%p)\n", __func__, line, dl);
+ if ((dl->lapd_flags & LAPD_F_RTS) && !llist_empty(&dl->tx_queue)) {
+ if (!rts)
+ LOGDL(dl, LOGL_INFO, "There is a frame in the TX queue, not checking for sending I frame.\n");
+ return rc;
+ }
next_frame:
if (dl->peer_busy) {
- LOGP(DLLAPD, LOGL_INFO, "peer busy, not sending (dl=%p)\n", dl);
+ if (!rts)
+ LOGDL(dl, LOGL_INFO, "Peer busy, not sending.\n");
return rc;
}
if (dl->state == LAPD_STATE_TIMER_RECOV) {
- LOGP(DLLAPD, LOGL_INFO,
- "timer recovery, not sending (dl=%p)\n", dl);
+ if (!rts)
+ LOGDL(dl, LOGL_INFO, "Timer recovery, not sending.\n");
return rc;
}
@@ -1857,9 +1972,9 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
* of the error recovery procedures as described in subclauses 5.5.4 and
* 5.5.7. */
if (dl->v_send == add_mod(dl->v_ack, k, dl->v_range)) {
- LOGP(DLLAPD, LOGL_INFO, "k frames outstanding, not sending "
- "more (k=%u V(S)=%u V(A)=%u) (dl=%p)\n", k, dl->v_send,
- dl->v_ack, dl);
+ if (!rts)
+ LOGDL(dl, 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;
}
@@ -1875,8 +1990,7 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
/* No more data to be sent */
if (!dl->send_buffer)
return rc;
- LOGP(DLLAPD, LOGL_INFO, "get message from "
- "send-queue (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "get message from send-queue\n");
}
/* How much is left in the send-buffer? */
@@ -1885,10 +1999,9 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
length = left;
if (length > lctx->n201)
length = lctx->n201;
- LOGP(DLLAPD, LOGL_INFO, "msg-len %d sent %d left %d N201 %d "
- "length %d first byte %02x (dl=%p)\n",
- msgb_l3len(dl->send_buffer), dl->send_out, left,
- lctx->n201, length, dl->send_buffer->l3h[0], dl);
+ LOGDL(dl, LOGL_INFO, "msg-len %d sent %d left %d N201 %d length %d "
+ "first byte %02x\n", msgb_l3len(dl->send_buffer), dl->send_out, left,
+ lctx->n201, length, dl->send_buffer->l3h[0]);
/* If message in send-buffer is completely sent */
if (left == 0) {
msgb_free(dl->send_buffer);
@@ -1896,14 +2009,14 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
goto next_message;
}
- LOGP(DLLAPD, LOGL_INFO, "send I frame %sV(S)=%d (dl=%p)\n",
- (left > length) ? "segment " : "", dl->v_send, dl);
+ LOGDL(dl, LOGL_INFO, "send I frame %sV(S)=%d\n",
+ (left > length) ? "segment " : "", dl->v_send);
/* Create I frame (segment) and transmit-buffer content */
msg = lapd_msgb_alloc(length, "LAPD I");
msg->l3h = msgb_put(msg, length);
/* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ memcpy(&nctx, lctx, sizeof(nctx));
/* keep nctx.ldp */
/* keep nctx.sapi */
/* keep nctx.tei */
@@ -1921,23 +2034,19 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
memcpy(msg->l3h, dl->send_buffer->l3h + dl->send_out,
length);
/* store in tx_hist */
- dl->tx_hist[h].msg = lapd_msgb_alloc(msg->len, "HIST");
- msgb_put(dl->tx_hist[h].msg, msg->len);
- if (length)
- memcpy(dl->tx_hist[h].msg->data, msg->l3h, msg->len);
- dl->tx_hist[h].more = nctx.more;
+ msg_to_tx_hist(&dl->tx_hist[h], msg, length, nctx.more);
+
/* Add length to track how much is already in the tx buffer */
dl->send_out += length;
} else {
- LOGP(DLLAPD, LOGL_INFO, "resend I frame from tx buffer "
- "V(S)=%d (dl=%p)\n", dl->v_send, dl);
+ LOGDL(dl, 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_hist[h].msg->len;
msg = lapd_msgb_alloc(length, "LAPD I resend");
msg->l3h = msgb_put(msg, length);
/* assemble message */
- memcpy(&nctx, &dl->lctx, sizeof(nctx));
+ memcpy(&nctx, lctx, sizeof(nctx));
/* keep nctx.ldp */
/* keep nctx.sapi */
/* keep nctx.tei */
@@ -1959,7 +2068,7 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
/* 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 (!osmo_timer_pending(&dl->t200)) {
+ if (!lapd_is_t200_started(dl)) {
/* stop Timer T203, if running */
lapd_stop_t203(dl);
/* start Timer T200 */
@@ -1968,7 +2077,11 @@ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line)
dl->send_ph_data_req(&nctx, msg);
- rc = 0; /* we sent something */
+ /* When using RTS, we send only one frame. */
+ if ((dl->lapd_flags & LAPD_F_RTS))
+ return 0;
+
+ rc = 0; /* We sent an I frame, so sending RR frame is not required. */
goto next_frame;
}
@@ -1978,16 +2091,15 @@ static int lapd_susp_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
struct lapd_datalink *dl = lctx->dl;
struct msgb *msg = dp->oph.msg;
- LOGP(DLLAPD, LOGL_INFO, "perform suspension (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "perform suspension\n");
/* put back the send-buffer to the send-queue (first position) */
if (dl->send_buffer) {
- LOGP(DLLAPD, LOGL_INFO, "put frame in sendbuffer back to "
- "queue (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "put frame in sendbuffer back to queue\n");
llist_add(&dl->send_buffer->list, &dl->send_queue);
dl->send_buffer = NULL;
} else
- LOGP(DLLAPD, LOGL_INFO, "no frame in sendbuffer (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "no frame in sendbuffer\n");
/* Clear transmit buffer, but keep send buffer */
lapd_dl_flush_tx(dl);
@@ -2007,9 +2119,7 @@ static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
struct msgb *msg = dp->oph.msg;
struct lapd_msg_ctx nctx;
- LOGP(DLLAPD, LOGL_INFO,
- "perform re-establishment (SABM) length=%d (dl=%p)\n",
- msg->len, dl);
+ LOGDL(dl, LOGL_INFO, "perform re-establishment (SABM) length=%d\n", msg->len);
/* be sure that history is empty */
lapd_dl_flush_hist(dl);
@@ -2050,11 +2160,8 @@ static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
nctx.length = 0;
nctx.more = 0;
- dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST");
- msgb_put(dl->tx_hist[0].msg, msg->len);
- if (msg->len)
- memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len);
- dl->tx_hist[0].more = 0;
+ msg_to_tx_hist0(dl, msg);
+
/* set Vs to 0, because it is used as index when resending SABM */
dl->v_send = 0;
@@ -2079,7 +2186,7 @@ static int lapd_rel_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
/* local release */
if (dp->u.rel_req.mode) {
- LOGP(DLLAPD, LOGL_INFO, "perform local release (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "perform local release\n");
msgb_free(msg);
/* stop Timer T200 */
lapd_stop_t200(dl);
@@ -2099,7 +2206,7 @@ static int lapd_rel_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
/* flush tx_hist */
lapd_dl_flush_hist(dl);
- LOGP(DLLAPD, LOGL_INFO, "perform normal release (DISC) (dl=%p)\n", dl);
+ LOGDL(dl, LOGL_INFO, "perform normal release (DISC)\n");
/* Push LAPD header on msgb */
/* assemble message */
@@ -2114,11 +2221,8 @@ static int lapd_rel_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
nctx.length = 0;
nctx.more = 0;
- dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST");
- msgb_put(dl->tx_hist[0].msg, msg->len);
- if (msg->len)
- memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len);
- dl->tx_hist[0].more = 0;
+ msg_to_tx_hist0(dl, msg);
+
/* set Vs to 0, because it is used as index when resending DISC */
dl->v_send = 0;
@@ -2224,22 +2328,20 @@ int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
}
}
if (!supported) {
- LOGP(DLLAPD, LOGL_NOTICE,
- "Message %u/%u unsupported. (dl=%p)\n", dp->oph.primitive,
- dp->oph.operation, dl);
+ LOGDL(dl, LOGL_NOTICE, "Message %u/%u unsupported\n",
+ dp->oph.primitive, dp->oph.operation);
msgb_free(msg);
return 0;
}
if (i == L2DOWNSLLEN) {
- LOGP(DLLAPD, LOGL_NOTICE, "Message %u/%u unhandled at this "
- "state %s. (dl=%p)\n", dp->oph.primitive,
- dp->oph.operation, lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_NOTICE, "Message %u/%u unhandled at this state %s\n",
+ dp->oph.primitive, dp->oph.operation, lapd_state_name(dl->state));
msgb_free(msg);
return 0;
}
- LOGP(DLLAPD, LOGL_INFO, "Message %s received in state %s (dl=%p)\n",
- l2downstatelist[i].name, lapd_state_name(dl->state), dl);
+ LOGDL(dl, LOGL_INFO, "Message %s received in state %s\n",
+ l2downstatelist[i].name, lapd_state_name(dl->state));
rc = l2downstatelist[i].rout(dp, lctx);
diff --git a/src/isdn/libosmoisdn.map b/src/isdn/libosmoisdn.map
new file mode 100644
index 00000000..8f34f0d8
--- /dev/null
+++ b/src/isdn/libosmoisdn.map
@@ -0,0 +1,52 @@
+LIBOSMOISDN_1.0 {
+global:
+
+tall_lapd_ctx;
+lapd_dl_exit;
+lapd_dl_init;
+lapd_dl_init2;
+lapd_dl_set_name;
+lapd_dl_reset;
+lapd_dl_set_flags;
+lapd_msgb_alloc;
+lapd_ph_data_ind;
+lapd_ph_rts_ind;
+lapd_recv_dlsap;
+lapd_set_mode;
+lapd_state_names;
+lapd_t200_timeout;
+
+osmo_i460_demux_in;
+osmo_i460_mux_enqueue;
+osmo_i460_mux_out;
+osmo_i460_subchan_add;
+osmo_i460_subchan_del;
+osmo_i460_subchan_count;
+osmo_i460_ts_init;
+
+osmo_v110_decode_frame;
+osmo_v110_encode_frame;
+osmo_v110_ubit_dump;
+osmo_v110_e1e2e3;
+osmo_v110_sync_ra1_get_user_data_chunk_bitlen;
+osmo_v110_sync_ra1_get_user_data_rate;
+osmo_v110_sync_ra1_get_intermediate_rate;
+osmo_v110_sync_ra1_user_to_ir;
+osmo_v110_sync_ra1_ir_to_user;
+
+osmo_v110_ta_alloc;
+osmo_v110_ta_free;
+osmo_v110_ta_set_timer_val_ms;
+osmo_v110_ta_frame_in;
+osmo_v110_ta_frame_out;
+osmo_v110_ta_sync_ind;
+osmo_v110_ta_desync_ind;
+osmo_v110_ta_get_status;
+osmo_v110_ta_get_circuit;
+osmo_v110_ta_set_circuit;
+
+osmo_v110_ta_circuit_names;
+osmo_v110_ta_circuit_descs;
+
+local: *;
+};
diff --git a/src/isdn/v110.c b/src/isdn/v110.c
new file mode 100644
index 00000000..b4bc4e30
--- /dev/null
+++ b/src/isdn/v110.c
@@ -0,0 +1,590 @@
+/* V.110 frames according to ITU-T V.110
+ *
+ * This code implements the following functionality:
+ * - parsing/encoding of osmo_v110_decoded_frame from/to actual 80-bit V.110 frame
+ * - synchronous rate adapting of user bit rate to V.110 D-bits as per Table 6
+ *
+ * It is (at least initially) a very "naive" implementation, as it first and foremost
+ * aims to be functional and correct, rather than efficient in any way. Hence it
+ * operates on unpacked bits (ubit_t, 1 bit per byte), and has various intermediate
+ * representations and indirect function calls. If needed, a more optimized variant
+ * can always be developed later on.
+ */
+
+/* (C) 2022 by Harald Welte <laforge@osmocom.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+
+#include <osmocom/isdn/v110.h>
+
+/*************************************************************************
+ * V.110 frame decoding/encoding (ubits <-> struct with D/S/X/E bits)
+ *************************************************************************/
+
+/*! Decode a 80-bit V.110 frame present as 80 ubits into a struct osmo_v110_decoded_frame.
+ * \param[out] fr caller-allocated output data structure, filled by this function
+ * \param[in] ra_bits One V.110 frame as 80 unpacked bits.
+ * \param[in] n_bits number of unpacked bits provided in ra_bits
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_decode_frame(struct osmo_v110_decoded_frame *fr, const ubit_t *ra_bits, size_t n_bits)
+{
+ if (n_bits < 80)
+ return -EINVAL;
+
+ /* X1 .. X2 */
+ fr->x_bits[0] = ra_bits[2 * 8 + 7];
+ fr->x_bits[1] = ra_bits[7 * 8 + 7];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ fr->s_bits[0] = ra_bits[1 * 8 + 7];
+ fr->s_bits[2] = ra_bits[3 * 8 + 7];
+ fr->s_bits[3] = ra_bits[4 * 8 + 7];
+ fr->s_bits[5] = ra_bits[6 * 8 + 7];
+ fr->s_bits[7] = ra_bits[8 * 8 + 7];
+ fr->s_bits[8] = ra_bits[9 * 8 + 7];
+
+ /* E1 .. E7 */
+ memcpy(fr->e_bits, ra_bits + 5 * 8 + 1, 7);
+
+ /* D-bits */
+ memcpy(fr->d_bits + 0 * 6, ra_bits + 1 * 8 + 1, 6);
+ memcpy(fr->d_bits + 1 * 6, ra_bits + 2 * 8 + 1, 6);
+ memcpy(fr->d_bits + 2 * 6, ra_bits + 3 * 8 + 1, 6);
+ memcpy(fr->d_bits + 3 * 6, ra_bits + 4 * 8 + 1, 6);
+
+ memcpy(fr->d_bits + 4 * 6, ra_bits + 6 * 8 + 1, 6);
+ memcpy(fr->d_bits + 5 * 6, ra_bits + 7 * 8 + 1, 6);
+ memcpy(fr->d_bits + 6 * 6, ra_bits + 8 * 8 + 1, 6);
+ memcpy(fr->d_bits + 7 * 6, ra_bits + 9 * 8 + 1, 6);
+
+ return 0;
+}
+
+/*! Encode a struct osmo_v110_decoded_frame into an 80-bit V.110 frame as ubits.
+ * \param[out] ra_bits caller-provided output buffer at leat 80 ubits large
+ * \param[in] n_bits length of ra_bits. Must be at least 80.
+ * \param[in] input data structure
+ * \returns number of bits written to ra_bits */
+int osmo_v110_encode_frame(ubit_t *ra_bits, size_t n_bits, const struct osmo_v110_decoded_frame *fr)
+{
+ if (n_bits < 80)
+ return -ENOSPC;
+
+ /* alignment pattern */
+ memset(ra_bits+0, 0, 8);
+ for (int i = 1; i < 10; i++)
+ ra_bits[i*8] = 1;
+
+ /* X1 .. X2 */
+ ra_bits[2 * 8 + 7] = fr->x_bits[0];
+ ra_bits[7 * 8 + 7] = fr->x_bits[1];
+
+ /* S1, S3, S4, S6, S8, S9 */
+ ra_bits[1 * 8 + 7] = fr->s_bits[0];
+ ra_bits[3 * 8 + 7] = fr->s_bits[2];
+ ra_bits[4 * 8 + 7] = fr->s_bits[3];
+ ra_bits[6 * 8 + 7] = fr->s_bits[5];
+ ra_bits[8 * 8 + 7] = fr->s_bits[7];
+ ra_bits[9 * 8 + 7] = fr->s_bits[8];
+
+ /* E1 .. E7 */
+ memcpy(ra_bits + 5 * 8 + 1, fr->e_bits, 7);
+
+ /* D-bits */
+ memcpy(ra_bits + 1 * 8 + 1, fr->d_bits + 0 * 6, 6);
+ memcpy(ra_bits + 2 * 8 + 1, fr->d_bits + 1 * 6, 6);
+ memcpy(ra_bits + 3 * 8 + 1, fr->d_bits + 2 * 6, 6);
+ memcpy(ra_bits + 4 * 8 + 1, fr->d_bits + 3 * 6, 6);
+
+ memcpy(ra_bits + 6 * 8 + 1, fr->d_bits + 4 * 6, 6);
+ memcpy(ra_bits + 7 * 8 + 1, fr->d_bits + 5 * 6, 6);
+ memcpy(ra_bits + 8 * 8 + 1, fr->d_bits + 6 * 6, 6);
+ memcpy(ra_bits + 9 * 8 + 1, fr->d_bits + 7 * 6, 6);
+
+ return 10 * 8;
+}
+
+/*! Print a encoded V.110 frame in the same table-like structure as the spec.
+ * \param outf output FILE stream to which to dump
+ * \param[in] fr unpacked bits to dump
+ * \param[in] in_len length of unpacked bits available at fr. */
+void osmo_v110_ubit_dump(FILE *outf, const ubit_t *fr, size_t in_len)
+{
+ if (in_len < 80)
+ fprintf(outf, "short input data\n");
+
+ for (unsigned int octet = 0; octet < 10; octet++) {
+ fprintf(outf, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ fr[octet * 8 + 0], fr[octet * 8 + 1], fr[octet * 8 + 2], fr[octet * 8 + 3],
+ fr[octet * 8 + 4], fr[octet * 8 + 5], fr[octet * 8 + 6], fr[octet * 8 + 7]);
+ }
+}
+
+/*************************************************************************
+ * RA1 synchronous rate adaptation
+ *************************************************************************/
+
+/* I actually couldn't find any reference as to the value of F(ill) bits */
+#define F 1
+
+/*! E1/E2/E3 bit values as per Table 5/V.110 */
+const ubit_t osmo_v110_e1e2e3[_NUM_OSMO_V110_SYNC_RA1][3] = {
+ [OSMO_V110_SYNC_RA1_600] = { 1, 0, 0 },
+ [OSMO_V110_SYNC_RA1_1200] = { 0, 1, 0 },
+ [OSMO_V110_SYNC_RA1_2400] = { 1, 1, 0 },
+ [OSMO_V110_SYNC_RA1_4800] = { 0, 1, 1 },
+ [OSMO_V110_SYNC_RA1_7200] = { 1, 0, 1 },
+ [OSMO_V110_SYNC_RA1_9600] = { 0, 1, 1 },
+ [OSMO_V110_SYNC_RA1_12000] = { 0, 0, 1 },
+ [OSMO_V110_SYNC_RA1_14400] = { 1, 0, 1 },
+ [OSMO_V110_SYNC_RA1_19200] = { 0, 1, 1 },
+ [OSMO_V110_SYNC_RA1_24000] = { 0, 0, 1 },
+ [OSMO_V110_SYNC_RA1_28800] = { 1, 0, 1 },
+ [OSMO_V110_SYNC_RA1_38400] = { 0, 1, 1 },
+};
+
+/*! Adapt from 6 synchronous 600bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 6.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_600_to_IR8000(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ if (in_len != 6)
+ return -EINVAL;
+
+ /* Table 6a / V.110 */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_600);
+ for (int i = 0; i < 6; i++)
+ memset(fr->d_bits + i*8, d_in[i], 8);
+
+ return 0;
+}
+
+static int v110_adapt_IR8000_to_600(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ if (out_len < 6)
+ return -ENOSPC;
+
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_600))
+ return -EINVAL;
+
+ for (int i = 0; i < 6; i++) {
+ /* we only use one of the bits, not some kind of consistency check or majority vote */
+ d_out[i] = fr->d_bits[i*8];
+ }
+
+ return 6;
+}
+
+/*! Adapt from 12 synchronous 1200bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 12.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_1200_to_IR8000(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ if (in_len != 12)
+ return -EINVAL;
+
+ /* Table 6b / V.110 */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_1200);
+ for (int i = 0; i < 12; i++)
+ memset(fr->d_bits + i*4, d_in[i], 4);
+
+ return 0;
+}
+
+static int v110_adapt_IR8000_to_1200(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ if (out_len < 12)
+ return -ENOSPC;
+
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_1200))
+ return -EINVAL;
+
+ for (int i = 0; i < 12; i++) {
+ /* we only use one of the bits, not some kind of consistency check or majority vote */
+ d_out[i] = fr->d_bits[i*4];
+ }
+
+ return 12;
+}
+
+/*! Adapt from 24 synchronous 2400bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 24.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_2400_to_IR8000(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ if (in_len != 24)
+ return -EINVAL;
+
+ /* Table 6c / V.110 */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_2400);
+ for (int i = 0; i < 24; i++) {
+ fr->d_bits[i*2 + 0] = d_in[i];
+ fr->d_bits[i*2 + 1] = d_in[i];
+ }
+
+ return 0;
+}
+
+static int v110_adapt_IR8000_to_2400(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ if (out_len < 24)
+ return -ENOSPC;
+
+ /* Table 6c / V.110 */
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_2400))
+ return -EINVAL;
+
+ for (int i = 0; i < 24; i++) {
+ /* we only use one of the bits, not some kind of consistency check or majority vote */
+ d_out[i] = fr->d_bits[i*2];
+ }
+
+ return 24;
+}
+
+/*! Adapt from 36 synchronous N x 3600bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 36.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_Nx3600_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ int d_idx = 0;
+
+ if (in_len != 36)
+ return -EINVAL;
+
+ /* Table 6d / V.110 (7200 is one of Nx3600) */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_7200);
+
+ memcpy(fr->d_bits + d_idx, d_in + 0, 10); d_idx += 10; /* D1..D10 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 10, 2); d_idx += 2; /* D11..D12 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 12, 2); d_idx += 2; /* D13..D14 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 14, 14); d_idx += 14; /* D15..D28 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 28, 2); d_idx += 2; /* D29..D30 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 30, 2); d_idx += 2; /* D31..D32 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 32, 4); d_idx += 4; /* D33..D36 */
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 0;
+}
+
+static int v110_adapt_IR_to_Nx3600(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ int d_idx = 0;
+
+ if (out_len < 36)
+ return -ENOSPC;
+
+ /* Table 6d / V.110 (7200 is one of Nx3600) */
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_7200))
+ return -EINVAL;
+
+ memcpy(d_out + 0, fr->d_bits + d_idx, 10); d_idx += 10; /* D1..D10 */
+ d_idx += 2;
+ memcpy(d_out + 10, fr->d_bits + d_idx, 2); d_idx += 2; /* D11..D12 */
+ d_idx += 2;
+ memcpy(d_out + 12, fr->d_bits + d_idx, 2); d_idx += 2; /* D13..D14 */
+ d_idx += 2;
+ memcpy(d_out + 14, fr->d_bits + d_idx, 14); d_idx += 14;/* D15..D28 */
+ d_idx += 2;
+ memcpy(d_out + 28, fr->d_bits + d_idx, 2); d_idx += 2; /* D29..D30 */
+ d_idx += 2;
+ memcpy(d_out + 30, fr->d_bits + d_idx, 2); d_idx += 2; /* D31..D32 */
+ d_idx += 2;
+ memcpy(d_out + 32, fr->d_bits + d_idx, 4); d_idx += 4; /* D33..D36 */
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 36;
+}
+
+
+/*! Adapt from 48 synchronous N x 4800bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 48.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_Nx4800_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ if (in_len != 48)
+ return -EINVAL;
+
+ /* Table 6e / V.110 (4800 is one of Nx4800) */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_4800);
+
+ memcpy(fr->d_bits, d_in, 48);
+
+ return 0;
+}
+
+static int v110_adapt_IR_to_Nx4800(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ if (out_len < 48)
+ return -ENOSPC;
+
+ /* Table 6e / V.110 (4800 is one of Nx4800) */
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_4800))
+ return -EINVAL;
+
+ memcpy(d_out, fr->d_bits, 48);
+
+ return 48;
+}
+
+/*! Adapt from 30 synchronous N x 12000bit/s input bits to a decoded V.110 frame.
+ * \param[out] fr caller-allocated output frame to which E+D bits are stored
+ * \param[in] d_in input user bits
+ * \param[in] in_len number of bits in d_in. Must be 30.
+ * \returns 0 on success; negative in case of error. */
+static int v110_adapt_Nx12000_to_IR(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len)
+{
+ int d_idx = 0;
+
+ if (in_len != 30)
+ return -EINVAL;
+
+ /* Table 6f / V.110 (12000 is one of Nx12000) */
+ osmo_v110_e1e2e3_set(fr->e_bits, OSMO_V110_SYNC_RA1_12000);
+
+ memcpy(fr->d_bits + d_idx, d_in + 0, 10); d_idx += 10; /* D1..D10 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 10, 2); d_idx += 2; /* D11..D12 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 12, 2); d_idx += 2; /* D13..D14 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ fr->d_bits[d_idx++] = d_in[14]; /* D15 */
+ memset(fr->d_bits + d_idx, F, 3); d_idx += 3;
+ memcpy(fr->d_bits + d_idx, d_in + 15, 10); d_idx += 10; /* D16..D25 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 25, 2); d_idx += 2; /* D26..D27 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ memcpy(fr->d_bits + d_idx, d_in + 27, 2); d_idx += 2; /* D28..D29 */
+ memset(fr->d_bits + d_idx, F, 2); d_idx += 2;
+ fr->d_bits[d_idx++] = d_in[29]; /* D30 */
+ memset(fr->d_bits + d_idx, F, 3); d_idx += 3;
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 0;
+}
+
+static int v110_adapt_IR_to_Nx12000(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr)
+{
+ int d_idx = 0;
+
+ if (out_len < 30)
+ return -ENOSPC;
+
+ /* Table 6f / V.110 (12000 is one of Nx12000) */
+ if (osmo_v110_e1e2e3_cmp(fr->e_bits, OSMO_V110_SYNC_RA1_12000))
+ return -EINVAL;
+
+ memcpy(d_out + 0, fr->d_bits + d_idx, 10); d_idx += 10; /* D1..D10 */
+ d_idx += 2;
+ memcpy(d_out + 10, fr->d_bits + d_idx, 2); d_idx += 2; /* D11..D12 */
+ d_idx += 2;
+ memcpy(d_out + 12, fr->d_bits + d_idx, 2); d_idx += 2; /* D13..D14 */
+ d_idx += 2;
+ d_out[14] = fr->d_bits[d_idx++]; /* D15 */
+ d_idx += 3;
+ memcpy(d_out + 15, fr->d_bits + d_idx, 10); d_idx += 10;/* D16..D25 */
+ d_idx += 2;
+ memcpy(d_out + 25, fr->d_bits + d_idx, 2); d_idx += 2; /* D26..D27 */
+ d_idx += 2;
+ memcpy(d_out + 27, fr->d_bits + d_idx, 2); d_idx += 2; /* D28..D29 */
+ d_idx += 2;
+ d_out[29] = fr->d_bits[d_idx++]; /* D30 */
+ d_idx += 3;
+
+ OSMO_ASSERT(d_idx == 48);
+
+ return 30;
+}
+
+/* definition of a synchronous V.110 RA1 rate adaptation. There is one for each supported tuple
+ * of user data rate and intermediate rate (IR). */
+struct osmo_v110_sync_ra1 {
+ unsigned int data_rate;
+ unsigned int intermediate_rate;
+ unsigned int user_data_chunk_bits;
+ /*! RA1 function in user bitrate -> intermediate rate direction */
+ int (*adapt_user_to_ir)(struct osmo_v110_decoded_frame *fr, const ubit_t *d_in, size_t in_len);
+ /*! RA1 function in intermediate rate -> user bitrate direction */
+ int (*adapt_ir_to_user)(ubit_t *d_out, size_t out_len, const struct osmo_v110_decoded_frame *fr);
+};
+
+/* all of the synchronous data signalling rates; see Table 1/V.110 */
+static const struct osmo_v110_sync_ra1 osmo_v110_sync_ra1_def[_NUM_OSMO_V110_SYNC_RA1] = {
+ [OSMO_V110_SYNC_RA1_600] = {
+ .data_rate = 600,
+ .intermediate_rate = 8000,
+ .user_data_chunk_bits = 6,
+ .adapt_user_to_ir = v110_adapt_600_to_IR8000,
+ .adapt_ir_to_user = v110_adapt_IR8000_to_600,
+ },
+ [OSMO_V110_SYNC_RA1_1200] = {
+ .data_rate = 1200,
+ .intermediate_rate = 8000,
+ .user_data_chunk_bits = 12,
+ .adapt_user_to_ir = v110_adapt_1200_to_IR8000,
+ .adapt_ir_to_user = v110_adapt_IR8000_to_1200,
+ },
+ [OSMO_V110_SYNC_RA1_2400] = {
+ .data_rate = 2400,
+ .intermediate_rate = 8000,
+ .user_data_chunk_bits = 24,
+ .adapt_user_to_ir = v110_adapt_2400_to_IR8000,
+ .adapt_ir_to_user = v110_adapt_IR8000_to_2400,
+ },
+ [OSMO_V110_SYNC_RA1_4800] = {
+ .data_rate = 4800,
+ .intermediate_rate = 8000,
+ .user_data_chunk_bits = 48,
+ .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
+ },
+ [OSMO_V110_SYNC_RA1_7200] = {
+ .data_rate = 7200,
+ .intermediate_rate = 16000,
+ .user_data_chunk_bits = 36,
+ .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
+ },
+ [OSMO_V110_SYNC_RA1_9600] = {
+ .data_rate = 9600,
+ .intermediate_rate = 16000,
+ .user_data_chunk_bits = 48,
+ .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
+ },
+ [OSMO_V110_SYNC_RA1_12000] = {
+ .data_rate = 12000,
+ .intermediate_rate = 32000,
+ .user_data_chunk_bits = 30,
+ .adapt_user_to_ir = v110_adapt_Nx12000_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx12000,
+ },
+ [OSMO_V110_SYNC_RA1_14400] = {
+ .data_rate = 14400,
+ .intermediate_rate = 32000,
+ .user_data_chunk_bits = 36,
+ .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
+ },
+ [OSMO_V110_SYNC_RA1_19200] = {
+ .data_rate = 19200,
+ .intermediate_rate = 32000,
+ .user_data_chunk_bits = 48,
+ .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
+ },
+ [OSMO_V110_SYNC_RA1_24000] = {
+ .data_rate = 24000,
+ .intermediate_rate = 64000,
+ .user_data_chunk_bits = 30,
+ .adapt_user_to_ir = v110_adapt_Nx12000_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx12000,
+ },
+ [OSMO_V110_SYNC_RA1_28800] = {
+ .data_rate = 28800,
+ .intermediate_rate = 64000,
+ .user_data_chunk_bits = 36,
+ .adapt_user_to_ir = v110_adapt_Nx3600_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx3600,
+ },
+ [OSMO_V110_SYNC_RA1_38400] = {
+ .data_rate = 38400,
+ .intermediate_rate = 64000,
+ .user_data_chunk_bits = 48,
+ .adapt_user_to_ir = v110_adapt_Nx4800_to_IR,
+ .adapt_ir_to_user = v110_adapt_IR_to_Nx4800,
+ },
+};
+
+/*! obtain the size (in number of bits) of the user data bits in one V.110
+ * frame for specified RA1 rate */
+int osmo_v110_sync_ra1_get_user_data_chunk_bitlen(enum osmo_v100_sync_ra1_rate rate)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].user_data_chunk_bits;
+}
+
+/*! obtain the user data rate (in bits/s) for specified RA1 rate */
+int osmo_v110_sync_ra1_get_user_data_rate(enum osmo_v100_sync_ra1_rate rate)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].data_rate;
+}
+
+/*! obtain the intermediate rate (in bits/s) for specified RA1 rate */
+int osmo_v110_sync_ra1_get_intermediate_rate(enum osmo_v100_sync_ra1_rate rate)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].intermediate_rate;
+}
+
+/*! perform V.110 RA1 function in user rate -> intermediate rate direction.
+ * \param[in] rate specification of the user bitrate
+ * \param[out] fr caller-allocated output buffer for the [decoded] V.110 frame generated
+ * \param[in] d_in input user data (unpacked bits)
+ * \param[in] in_len length of user input data (in number of bits)
+ * \returns 0 on success; negative in case of error */
+int osmo_v110_sync_ra1_user_to_ir(enum osmo_v100_sync_ra1_rate rate, struct osmo_v110_decoded_frame *fr,
+ const ubit_t *d_in, size_t in_len)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].adapt_user_to_ir(fr, d_in, in_len);
+}
+
+/*! perform V.110 RA1 function in intermediate rate -> user rate direction.
+ * \param[in] rate specification of the user bitrate
+ * \param[out] d_out caller-allocated output user data (unpacked bits)
+ * \param[out] out_len length of d_out output buffer
+ * \param[in] fr [decoded] V.110 frame used as input
+ * \returns number of unpacked bits written to d_out on success; negative in case of error */
+int osmo_v110_sync_ra1_ir_to_user(enum osmo_v100_sync_ra1_rate rate, ubit_t *d_out, size_t out_len,
+ const struct osmo_v110_decoded_frame *fr)
+{
+ if (rate < 0 || rate >= _NUM_OSMO_V110_SYNC_RA1)
+ return -EINVAL;
+
+ return osmo_v110_sync_ra1_def[rate].adapt_ir_to_user(d_out, out_len, fr);
+}
diff --git a/src/isdn/v110_ta.c b/src/isdn/v110_ta.c
new file mode 100644
index 00000000..6730b20c
--- /dev/null
+++ b/src/isdn/v110_ta.c
@@ -0,0 +1,881 @@
+/*! \file v110_ta.c
+ * TA (Terminal Adapter) implementation as per ITU-T V.110. */
+/*
+ * (C) 2022 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Initial (Work-in-Progress) implementation by Harald Welte,
+ * completed and co-authored by Vadim Yanitskiy.
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/isdn/v110.h>
+#include <osmocom/isdn/v110_ta.h>
+
+#define S(x) (1 << (x))
+
+#define V24_FLAGMASK_IS_ON(flags, circuit) \
+ (((flags) & S(circuit)) != 0)
+
+#define V24_FLAGMASK_IS_OFF(flags, circuit) \
+ (((flags) & S(circuit)) == 0)
+
+#define V24_FLAGMASK_SET_ON(flags, circuit) \
+ (flags) |= S(circuit)
+
+#define V24_FLAGMASK_SET_OFF(flags, circuit) \
+ (flags) &= ~S(circuit)
+
+/* inverse logic: ON = binary 0; OFF = binary 1 */
+#define V110_SX_BIT_ON 0
+#define V110_SX_BIT_OFF 1
+
+const struct value_string osmo_v110_ta_circuit_names[] = {
+ { OSMO_V110_TA_C_105, "105/RTS" },
+ { OSMO_V110_TA_C_106, "106/CTS" },
+ { OSMO_V110_TA_C_107, "107/DSR" },
+ { OSMO_V110_TA_C_108, "108/DTR" },
+ { OSMO_V110_TA_C_109, "109/DCD" },
+ { OSMO_V110_TA_C_133, "133" },
+ { 0, NULL }
+};
+
+const struct value_string osmo_v110_ta_circuit_descs[] = {
+ { OSMO_V110_TA_C_105, "Request to Send" },
+ { OSMO_V110_TA_C_106, "Clear to Send" },
+ { OSMO_V110_TA_C_107, "Data Set Ready" },
+ { OSMO_V110_TA_C_108, "Data Terminal Ready" },
+ { OSMO_V110_TA_C_109, "Data Carrier Detect" },
+ { OSMO_V110_TA_C_133, "Ready for receiving" },
+ { 0, NULL }
+};
+
+static const struct osmo_tdef v110_ta_tdef[] = {
+ { .T = OSMO_V110_TA_TIMER_X1,
+ .unit = OSMO_TDEF_MS, .default_val = 3000, /* suggested in 7.1.5 e) */
+ .desc = "ITU-T V.110 7.1.5 Loss of frame synchronization: sync recovery timer" },
+ { .T = OSMO_V110_TA_TIMER_T1,
+ .unit = OSMO_TDEF_MS, .default_val = 10000, /* suggested in 7.1.2.2 */
+ .desc = "ITU-T V.110 7.1.2 Connect TA to line: sync establishment timer" },
+ { .T = OSMO_V110_TA_TIMER_T2,
+ .unit = OSMO_TDEF_MS, .default_val = 5000, /* suggested in 7.1.4.1 */
+ .desc = "ITU-T V.110 7.1.4 Disconnect mode: disconnect confirmation timer" },
+ { /* end of list */ }
+};
+
+/*********************************************************************************
+ * V.110 TERMINAL ADAPTER FSM
+ *********************************************************************************/
+
+enum v110_ta_fsm_state {
+ V110_TA_ST_IDLE_READY, /* 7.1.1 Idle (or ready) state */
+ V110_TA_ST_CON_TA_TO_LINE, /* 7.1.2 Connect TA to line state */
+ V110_TA_ST_DATA_TRANSFER, /* 7.1.3 Data transfer state */
+ V110_TA_ST_DISCONNECTING, /* 7.1.4 Disconnect mode */
+ V110_TA_ST_RESYNCING, /* 7.1.5 Re-synchronizing state */
+};
+
+enum v110_ta_fsm_event {
+ V110_TA_EV_RX_FRAME_IND, /* a V.110 frame was received by the lower layer */
+ V110_TA_EV_TX_FRAME_RTS, /* a V.110 frame is to be sent by the lower layer */
+ V110_TA_EV_V24_STATUS_CHG, /* V.24 flag-mask has been updated by TE */
+ V110_TA_EV_SYNC_IND, /* the lower layer has synchronized to the frame clock */
+ V110_TA_EV_DESYNC_IND, /* the lower layer has lost frame clock synchronization */
+ V110_TA_EV_TIMEOUT, /* generic event for handling a timeout condition */
+};
+
+static const struct value_string v110_ta_fsm_event_names[] = {
+ { V110_TA_EV_RX_FRAME_IND, "RX_FRAME_IND" },
+ { V110_TA_EV_TX_FRAME_RTS, "TX_FRAME_RTS" },
+ { V110_TA_EV_V24_STATUS_CHG, "V24_STATUS_CHG" },
+ { V110_TA_EV_SYNC_IND, "SYNC_IND" },
+ { V110_TA_EV_DESYNC_IND, "DESYNC_IND" },
+ { V110_TA_EV_TIMEOUT, "TIMEOUT" },
+ { 0, NULL }
+};
+
+enum v110_ta_d_bit_mode {
+ V110_TA_DBIT_M_ALL_ZERO = 0, /* set all bits to binary '0' */
+ V110_TA_DBIT_M_ALL_ONE = 1, /* set all bits to binary '1' */
+ V110_TA_DBIT_M_FORWARD, /* forward D-bits to/from DTE */
+};
+
+struct v110_ta_state {
+ /*! V.24 status flags shared between DTE (user) and DCE (TA, us) */
+ unsigned int v24_flags;
+ struct {
+ /* what kind of D-bits to transmit in V.110 frames */
+ enum v110_ta_d_bit_mode d_bit_mode;
+ /* what to put in S-bits of transmitted V.110 frames */
+ ubit_t s_bits;
+ /* what to put in X-bits of transmitted V.110 frames */
+ ubit_t x_bits;
+ } tx;
+ struct {
+ enum v110_ta_d_bit_mode d_bit_mode;
+ } rx;
+};
+
+struct osmo_v110_ta {
+ const char *name;
+ struct osmo_tdef *Tdefs;
+ struct osmo_fsm_inst *fi;
+ struct osmo_v110_ta_cfg *cfg;
+ struct v110_ta_state state;
+};
+
+static inline bool v110_df_x_bits_are(const struct osmo_v110_decoded_frame *df, ubit_t cmp)
+{
+ return (df->x_bits[0] == cmp) && (df->x_bits[1] == cmp);
+}
+
+static inline bool v110_df_s_bits_are(const struct osmo_v110_decoded_frame *df, ubit_t cmp)
+{
+ /* ITU-T Table 2/V.110 (see also 5.1.2.3) defines the following S-bits:
+ * S1, S3, S4, S6, S8, S9 (6 bits total). However, fr->s_bits[] contains
+ * 9 (MAX_S_BITS) bits, including the undefined bits S2, S5, S7.
+ * Hence we must skip those undefined bits. */
+ static const uint8_t sbit_map[] = { 0, 2, 3, 5, 7, 8 };
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(sbit_map); i++) {
+ uint8_t idx = sbit_map[i];
+ if (df->s_bits[idx] != cmp)
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool v110_df_d_bits_are(const struct osmo_v110_decoded_frame *df, ubit_t cmp)
+{
+ for (unsigned int i = 0; i < MAX_D_BITS; i++) {
+ if (df->d_bits[i] != cmp)
+ return false;
+ }
+
+ return true;
+}
+
+/* handle one V.110 frame and forward user bits to the application */
+static void v110_ta_handle_frame(const struct osmo_v110_ta *ta,
+ const struct osmo_v110_decoded_frame *df)
+{
+ const struct osmo_v110_ta_cfg *cfg = ta->cfg;
+ const struct v110_ta_state *ts = &ta->state;
+ ubit_t user_bits[MAX_D_BITS];
+ int num_user_bits;
+ int rc;
+
+ switch (ts->rx.d_bit_mode) {
+ case V110_TA_DBIT_M_ALL_ZERO:
+ case V110_TA_DBIT_M_ALL_ONE:
+ /* generate as many user bits as needed for the configured rate */
+ num_user_bits = osmo_v110_sync_ra1_get_user_data_chunk_bitlen(cfg->rate);
+ OSMO_ASSERT(num_user_bits > 0);
+ /* set them all to binary '0' or binary '1' */
+ memset(&user_bits[0], (int)ts->rx.d_bit_mode, num_user_bits);
+ cfg->rx_cb(cfg->priv, &user_bits[0], num_user_bits);
+ break;
+ case V110_TA_DBIT_M_FORWARD:
+ rc = osmo_v110_sync_ra1_ir_to_user(cfg->rate, &user_bits[0], sizeof(user_bits), df);
+ if (rc > 0)
+ cfg->rx_cb(cfg->priv, &user_bits[0], rc);
+ /* XXX else: indicate an error somehow? */
+ break;
+ }
+}
+
+/* build one V.110 frame to transmit */
+static void v110_ta_build_frame(const struct osmo_v110_ta *ta,
+ struct osmo_v110_decoded_frame *df)
+{
+ const struct osmo_v110_ta_cfg *cfg = ta->cfg;
+ const struct v110_ta_state *ts = &ta->state;
+ ubit_t user_bits[MAX_D_BITS];
+ int num_user_bits;
+ int rc;
+
+ /* E-bits (E1/E2/E3 may be overwritten below) */
+ memset(df->e_bits, 1, sizeof(df->e_bits));
+ /* S-bits */
+ memset(df->s_bits, ts->tx.s_bits, sizeof(df->s_bits));
+ /* X-bits */
+ memset(df->x_bits, ts->tx.x_bits, sizeof(df->x_bits));
+
+ /* D-bits */
+ switch (ts->tx.d_bit_mode) {
+ case V110_TA_DBIT_M_ALL_ZERO:
+ case V110_TA_DBIT_M_ALL_ONE:
+ /* set them all to binary '0' or binary '1' */
+ memset(df->d_bits, (int)ts->tx.d_bit_mode, sizeof(df->d_bits));
+ break;
+ case V110_TA_DBIT_M_FORWARD:
+ /* how many user bits to retrieve */
+ num_user_bits = osmo_v110_sync_ra1_get_user_data_chunk_bitlen(cfg->rate);
+ OSMO_ASSERT(num_user_bits > 0);
+ /* retrieve user bits from the application */
+ cfg->tx_cb(cfg->priv, &user_bits[0], num_user_bits);
+ /* convert user bits to intermediate rate (store to df) */
+ rc = osmo_v110_sync_ra1_user_to_ir(cfg->rate, df, &user_bits[0], num_user_bits);
+ OSMO_ASSERT(rc == 0);
+ break;
+ }
+}
+
+static void v110_ta_flags_update(struct osmo_v110_ta *ta, unsigned int v24_flags)
+{
+ struct osmo_v110_ta_cfg *cfg = ta->cfg;
+
+ if (ta->state.v24_flags == v24_flags)
+ return;
+ if (cfg->status_update_cb != NULL)
+ cfg->status_update_cb(cfg->priv, v24_flags);
+ ta->state.v24_flags = v24_flags;
+}
+
+static const struct osmo_tdef_state_timeout v110_ta_fsm_timeouts[32] = {
+ [V110_TA_ST_RESYNCING] = { .T = OSMO_V110_TA_TIMER_X1 },
+ [V110_TA_ST_CON_TA_TO_LINE] = { .T = OSMO_V110_TA_TIMER_T1 },
+ [V110_TA_ST_DISCONNECTING] = { .T = OSMO_V110_TA_TIMER_T2 },
+};
+
+#define v110_ta_fsm_state_chg(state) \
+ osmo_tdef_fsm_inst_state_chg(fi, state, \
+ v110_ta_fsm_timeouts, \
+ ((struct osmo_v110_ta *)(fi->priv))->Tdefs, \
+ 0)
+
+/* ITU-T V.110 Section 7.1.1 */
+static void v110_ta_fsm_idle_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.1.2 During the idle (or ready) state the TA will transmit continuous binary 1s into the B-channel */
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE; /* circuit 103: continuous binary '1' */
+ ts->tx.s_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
+ ts->tx.x_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
+
+ /* 7.1.1.3 During the idle (or ready) state the TA (DCE) will transmit the following toward the DTE: */
+ /* - circuit 104: continuous binary '1' */
+ ts->rx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE;
+ /* - circuits 107, 106, 109 = OFF */
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_106);
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_107);
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_109);
+ v110_ta_flags_update(ta, v24_flags);
+}
+
+/* ITU-T V.110 Section 7.1.1 */
+static void v110_ta_fsm_idle_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ /* When the TA is to be switched to the data mode, circuit 108 must be ON */
+ if (V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108)) {
+ /* 7.12.2: Start timer T1 when switching to CON_TA_LINE */
+ v110_ta_fsm_state_chg(V110_TA_ST_CON_TA_TO_LINE);
+ }
+ break;
+ case V110_TA_EV_RX_FRAME_IND:
+ v110_ta_handle_frame(ta, (const struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* ITU-T V.110 Section 7.1.2 */
+static void v110_ta_fsm_connect_ta_to_line_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ /* 7.1.2.1 Switching to the data mode causes the TA to transmit the following towards the ISDN: */
+ /* a) frame synchronization pattern as described in 5.1.3.1 and 5.2.1 (done by the API user) */
+ /* b) circuit 103: continuous binary '1' */
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE;
+ /* c) status bits S = OFF and X = OFF */
+ ts->tx.s_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
+ ts->tx.x_bits = V110_SX_BIT_OFF; /* OFF is binary '1' */
+
+ /* 7.1.2.2 ... the receiver in the TA will begin to search for the frame synchronization
+ * pattern in the received bit stream (see 5.1.3.1 and 5.2.1) and start timer T1. */
+ OSMO_ASSERT(fi->T == OSMO_V110_TA_TIMER_T1);
+}
+
+/* ITU-T V.110 Section 7.1.2 */
+static void v110_ta_fsm_connect_ta_to_line(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ /* If circuit 108 is OFF, we go back to IDLE/READY */
+ if (V24_FLAGMASK_IS_OFF(ts->v24_flags, OSMO_V110_TA_C_108))
+ v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
+ break;
+ case V110_TA_EV_SYNC_IND:
+ /* 7.1.2.3 When the receiver recognizes the frame synchronization pattern, it causes the S-
+ * and X-bits in the transmitted frames to be turned ON (provided that circuit 108 is ON). */
+ OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108));
+ ts->tx.s_bits = V110_SX_BIT_ON;
+ ts->tx.x_bits = V110_SX_BIT_ON;
+ break;
+ case V110_TA_EV_RX_FRAME_IND:
+ {
+ const struct osmo_v110_decoded_frame *df = data;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.2.4 When the receiver recognizes that the status of bits S and X are ON */
+ if (v110_df_s_bits_are(df, V110_SX_BIT_ON) &&
+ v110_df_x_bits_are(df, V110_SX_BIT_ON)) {
+ /* ... it will perform the following functions: */
+ /* a) Turn ON circuit 107 toward the DTE and stop timer T1 */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_107);
+ osmo_timer_del(&fi->timer);
+ /* b) Then, circuit 103 may be connected to the data bits in the frame; however, the
+ * DTE must maintain a binary 1 condition on circuit 103 until circuit 106 is turned
+ * ON in the next portion of the sequence. */
+ /* c) Turn ON circuit 109 and connect the data bits to circuit 104. */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_109);
+ ts->rx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
+ /* d) After an interval of N bits (see 6.3), it will turn ON circuit 106. */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_106);
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
+ v110_ta_flags_update(ta, v24_flags);
+ /* Circuit 106 transitioning from OFF to ON will cause the transmitted data to
+ * transition from binary 1 to the data mode. */
+ v110_ta_fsm_state_chg(V110_TA_ST_DATA_TRANSFER);
+ }
+
+ v110_ta_handle_frame(ta, df);
+ break;
+ }
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_TIMEOUT:
+ v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* ITU-T V.110 Section 7.1.3 */
+static void v110_ta_fsm_data_transfer_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.3.1 While in the data transfer state, the following circuit conditions exist:
+ * a): 105, 107, 108, and 109 are in the ON condition */
+ /* XXX: OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_105)); */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_107);
+ /* XXX: OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108)); */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_109);
+ /* b) data is being transmitted on circuit 103 and received on circuit 104 */
+ ts->rx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_FORWARD;
+ /* c) circuits 133 (when implemented) and 106 are in the ON condition unless local out-of-band
+ * flow control is being used, either or both circuits may be in the ON or the OFF condition. */
+ if (!ta->cfg->flow_ctrl.end_to_end) {
+ /* XXX: OSMO_ASSERT(V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_133)); */
+ V24_FLAGMASK_SET_ON(v24_flags, OSMO_V110_TA_C_106);
+ }
+ v110_ta_flags_update(ta, v24_flags);
+
+ /* 7.1.3.2 While in the data transfer state, the following status bit conditions exist: */
+ /* a) status bits S in both directions are in the ON condition; */
+ ts->tx.s_bits = V110_SX_BIT_ON;
+ /* b) status bits X in both directions are in the ON condition unless end-to-end flow control
+ * is being used, in which case status bit X in either or both directions may be in the
+ * ON or the OFF condition. */
+ if (!ta->cfg->flow_ctrl.end_to_end)
+ ts->tx.x_bits = V110_SX_BIT_ON;
+}
+
+/* ITU-T V.110 Section 7.1.3 */
+static void v110_ta_fsm_data_transfer(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ /* 7.1.3.3 While in the data transfer state: */
+ /* a) the S status bits shall *not* be mapped to/from the interchange circuits */
+ /* b) the X status bits shall *not* be mapped according to Table 3,
+ * unless end-to-end flow control is implemented */
+ /* TODO: if (ta->cfg->flow_ctrl.end_to_end) { ... } */
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ /* 7.1.4.1 At the completion of the data transfer phase, the local DTE will indicate a
+ * disconnect request by turning OFF circuit 108 */
+ if (V24_FLAGMASK_IS_ON(ts->v24_flags, OSMO_V110_TA_C_108))
+ break;
+ v110_ta_fsm_state_chg(V110_TA_ST_DISCONNECTING);
+ break;
+ case V110_TA_EV_DESYNC_IND:
+ v110_ta_fsm_state_chg(V110_TA_ST_RESYNCING);
+ break;
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_RX_FRAME_IND:
+ {
+ const struct osmo_v110_decoded_frame *df = data;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.4.2 ... this TA will recognize the transition of the status bits S from
+ * ON to OFF and the data bits from data to binary 0 as a disconnect request */
+ if (v110_df_s_bits_are(df, V110_SX_BIT_OFF) && v110_df_d_bits_are(df, 0)) {
+ /* ... and it will turn OFF circuits 107 and 109. */
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_107);
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_109);
+ v110_ta_flags_update(ta, v24_flags);
+ /* DTE should respond by turning OFF circuit 108 */
+ break; /* XXX: shall we forward D-bits to DTE anyway? */
+ }
+
+ v110_ta_handle_frame(ta, df);
+ break;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* ITU-T V.110 Section 7.1.4 */
+static void v110_ta_fsm_disconnect_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ /* 7.1.4.1 At the completion of the data transfer phase, the local DTE will indicate a
+ * disconnect request by turning OFF circuit 108. This will cause the following to occur: */
+ /* a) the status bits S in the frame toward ISDN will turn OFF, status bits X are kept ON */
+ ts->tx.s_bits = V110_SX_BIT_OFF;
+ /* b) circuit 106 will be turned OFF */
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_106);
+ v110_ta_flags_update(ta, v24_flags);
+ /* c) the data bits in the frame will be set to binary 0. */
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ZERO;
+
+ /* To guard against the failure of the remote TA to respond to the disconnect request,
+ * the local TA may start a timer T2 (suggested value 5 s) which is stopped by the
+ * reception or transmission of any D-channel clearing message (DISCONNECT, RELEASE,
+ * RELEASE COMPLETE). */
+ OSMO_ASSERT(fi->T == OSMO_V110_TA_TIMER_T2);
+}
+
+/* ITU-T V.110 Section 7.1.4 */
+static void v110_ta_fsm_disconnect(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ break; /* nothing to do */
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_RX_FRAME_IND:
+ {
+ const struct osmo_v110_decoded_frame *df = data;
+
+ /* 7.1.4.3 The TA at the station that originated the disconnect request will
+ * recognize reception of S = OFF or the loss of framing signals as a disconnect
+ * acknowledgement and turn OFF circuits 107 and 109. */
+ if (v110_df_s_bits_are(df, V110_SX_BIT_OFF)) {
+ /* circuits 107 and 109 set to off in .onenter() */
+ v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
+ }
+
+ v110_ta_handle_frame(ta, df);
+ break;
+ }
+ case V110_TA_EV_DESYNC_IND:
+ case V110_TA_EV_TIMEOUT:
+ /* circuits 107 and 109 set to off in .onenter() */
+ v110_ta_fsm_state_chg(V110_TA_ST_IDLE_READY);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* ITU-T V.110 Section 7.1.5 */
+static void v110_ta_fsm_resyncing_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+
+ /* 7.1.5 In the event of loss of frame synchronization, the (local) TA should
+ * attempt to resynchronize as follows: */
+ /* a) Place circuit 104 in binary 1 condition (passes from the data mode) */
+ ts->rx.d_bit_mode = V110_TA_DBIT_M_ALL_ONE;
+ /* b) Turn OFF status bit X in the transmitted frame */
+ ts->tx.x_bits = V110_SX_BIT_OFF;
+
+ /* guard timeout, see 7.1.5 e) */
+ OSMO_ASSERT(fi->T == OSMO_V110_TA_TIMER_X1);
+}
+
+/* ITU-T V.110 Section 7.1.5 */
+static void v110_ta_fsm_resyncing(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct osmo_v110_ta *ta = (struct osmo_v110_ta *)fi->priv;
+ struct v110_ta_state *ts = &ta->state;
+ unsigned int v24_flags = ta->state.v24_flags;
+
+ switch (event) {
+ case V110_TA_EV_V24_STATUS_CHG:
+ break; /* TODO: handle circuit 108 being set to OFF? */
+ case V110_TA_EV_TX_FRAME_RTS:
+ v110_ta_build_frame(ta, (struct osmo_v110_decoded_frame *)data);
+ break;
+ case V110_TA_EV_SYNC_IND:
+ /* f) If resynchronization is achieved, the local TA should turn ON status bit X */
+ ts->tx.x_bits = V110_SX_BIT_ON;
+ v110_ta_fsm_state_chg(V110_TA_ST_DATA_TRANSFER);
+ break;
+ case V110_TA_EV_TIMEOUT:
+ /* e) If after an interval of X1 seconds the local TA cannot attain synchronization,
+ * it should send a disconnect request by turning OFF all of the status bits for several
+ * (at least three) frames with data bits set to binary 0 and then disconnect by turning
+ * OFF circuit 107 and transferring to the disconnected mode as discussed in 7.1.4.2. */
+ ts->tx.s_bits = V110_SX_BIT_OFF;
+ ts->tx.x_bits = V110_SX_BIT_OFF;
+ ts->tx.d_bit_mode = V110_TA_DBIT_M_ALL_ZERO;
+ /* TODO: actually Tx those frames (delay state transition) */
+ V24_FLAGMASK_SET_OFF(v24_flags, OSMO_V110_TA_C_107);
+ v110_ta_flags_update(ta, v24_flags);
+ v110_ta_fsm_state_chg(V110_TA_ST_DISCONNECTING);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int v110_ta_timer_cb(struct osmo_fsm_inst *fi)
+{
+ osmo_fsm_inst_dispatch(fi, V110_TA_EV_TIMEOUT, NULL);
+ return 0;
+}
+
+static const struct osmo_fsm_state v110_ta_states[] = {
+ [V110_TA_ST_IDLE_READY] = {
+ .name = "IDLE_READY",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_RX_FRAME_IND),
+ .out_state_mask = S(V110_TA_ST_IDLE_READY)
+ | S(V110_TA_ST_CON_TA_TO_LINE),
+ .action = &v110_ta_fsm_idle_ready,
+ .onenter = &v110_ta_fsm_idle_ready_onenter,
+ },
+ [V110_TA_ST_CON_TA_TO_LINE] = {
+ .name = "CONNECT_TA_TO_LINE",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_TIMEOUT)
+ | S(V110_TA_EV_SYNC_IND)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_RX_FRAME_IND),
+ .out_state_mask = S(V110_TA_ST_DATA_TRANSFER)
+ | S(V110_TA_ST_IDLE_READY),
+ .action = &v110_ta_fsm_connect_ta_to_line,
+ .onenter = &v110_ta_fsm_connect_ta_to_line_onenter,
+ },
+ [V110_TA_ST_DATA_TRANSFER] = {
+ .name = "DATA_TRANSFER",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_DESYNC_IND)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_RX_FRAME_IND),
+ .out_state_mask = S(V110_TA_ST_RESYNCING)
+ | S(V110_TA_ST_DISCONNECTING),
+ .action = &v110_ta_fsm_data_transfer,
+ .onenter = &v110_ta_fsm_data_transfer_onenter,
+ },
+ [V110_TA_ST_DISCONNECTING] = {
+ .name = "DISCONNECTING",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_TIMEOUT)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_RX_FRAME_IND)
+ | S(V110_TA_EV_DESYNC_IND),
+ .out_state_mask = S(V110_TA_ST_IDLE_READY),
+ .action = &v110_ta_fsm_disconnect,
+ .onenter = &v110_ta_fsm_disconnect_onenter,
+ },
+ [V110_TA_ST_RESYNCING] = {
+ .name = "RESYNCING",
+ .in_event_mask = S(V110_TA_EV_V24_STATUS_CHG)
+ | S(V110_TA_EV_TIMEOUT)
+ | S(V110_TA_EV_TX_FRAME_RTS)
+ | S(V110_TA_EV_SYNC_IND),
+ .out_state_mask = S(V110_TA_ST_IDLE_READY)
+ | S(V110_TA_ST_DATA_TRANSFER),
+ .action = &v110_ta_fsm_resyncing,
+ .onenter = &v110_ta_fsm_resyncing_onenter,
+ },
+};
+
+static struct osmo_fsm osmo_v110_ta_fsm = {
+ .name = "V110-TA",
+ .states = v110_ta_states,
+ .num_states = ARRAY_SIZE(v110_ta_states),
+ .timer_cb = v110_ta_timer_cb,
+ .log_subsys = DLGLOBAL,
+ .event_names = v110_ta_fsm_event_names,
+};
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&osmo_v110_ta_fsm) == 0);
+}
+
+/*! Allocate a V.110 TA (Terminal Adapter) instance.
+ * \param[in] ctx parent talloc context.
+ * \param[in] name name of the TA instance.
+ * \param[in] cfg initial configuration of the TA instance.
+ * \returns pointer to allocated TA instance; NULL on error. */
+struct osmo_v110_ta *osmo_v110_ta_alloc(void *ctx, const char *name,
+ const struct osmo_v110_ta_cfg *cfg)
+{
+ struct osmo_v110_ta *ta;
+
+ OSMO_ASSERT(cfg != NULL);
+ OSMO_ASSERT(cfg->rx_cb != NULL);
+ OSMO_ASSERT(cfg->tx_cb != NULL);
+
+ /* local (TE-TA) flow control is not implemented */
+ if (cfg->flow_ctrl.local != OSMO_V110_LOCAL_FLOW_CTRL_NONE) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Local (TE-TA) flow control is not implemented\n");
+ return NULL;
+ }
+
+ ta = talloc_zero(ctx, struct osmo_v110_ta);
+ if (ta == NULL)
+ return NULL;
+
+ ta->name = talloc_strdup(ta, name);
+ ta->cfg = talloc_memdup(ta, cfg, sizeof(*cfg));
+ if (ta->name == NULL || ta->cfg == NULL)
+ goto exit_free;
+
+ ta->Tdefs = talloc_memdup(ta, v110_ta_tdef, sizeof(v110_ta_tdef));
+ if (ta->Tdefs == NULL)
+ goto exit_free;
+ osmo_tdefs_reset(ta->Tdefs); /* apply default values */
+
+ ta->fi = osmo_fsm_inst_alloc(&osmo_v110_ta_fsm, ta, ta, LOGL_DEBUG, name);
+ if (ta->fi == NULL)
+ goto exit_free;
+
+ /* perform a loop transition to init the internal state */
+ osmo_fsm_inst_state_chg(ta->fi, V110_TA_ST_IDLE_READY, 0, 0);
+
+ return ta;
+
+exit_free:
+ if (ta->fi != NULL)
+ osmo_fsm_inst_free(ta->fi);
+ talloc_free(ta);
+ return NULL;
+}
+
+/*! Release memory taken by the given V.110 TA instance.
+ * \param[in] ta TA instance to be free()d. */
+void osmo_v110_ta_free(struct osmo_v110_ta *ta)
+{
+ if (ta == NULL)
+ return;
+ if (ta->fi != NULL)
+ osmo_fsm_inst_free(ta->fi);
+ talloc_free(ta); /* also free()s name and cfg */
+}
+
+/*! Configure a timer of the given V.110 TA instance.
+ * \param[in] ta TA instance to be configured.
+ * \param[in] timer a timer to be configured.
+ * \param[in] val_ms the new timeout value to set (in milliseconds).
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_set_timer_val_ms(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_timer timer,
+ unsigned long val_ms)
+{
+ return osmo_tdef_set(ta->Tdefs, (int)timer, val_ms, OSMO_TDEF_MS);
+}
+
+/*! Feed a [decoded] V.110 frame into the given TA instance.
+ *
+ * This function, like its _out counterpart, is intended to be used by the lower layers
+ * receiving V.110 frames over some medium. The caller of this function is responsible
+ * for finding the synchronization pattern (if needed), aligning to the frame boundaries,
+ * and decoding frames using osmo_v110_decode_frame() or osmo_csd_*_decode_frame().
+ *
+ * Bits E1/E2/E3 are expected to be set by the caller (if not being transmitted
+ * over the medium) in accordance with the configured synchronous user rate.
+ *
+ * Bits D1..D48 are passed to the bit rate adaption function RA1. The resulting output
+ * is then passed to the upper layer (application) via the configured .rx_cb(). Though,
+ * in certain states of the TA's FSM, bits D1..D48 are ignored and the upper layer
+ * gets a sequence of binary '0' or '1'.
+ *
+ * \param[in] ta TA instance to feed the given frame into.
+ * \param[in] in pointer to a [decoded] V.110 frame.
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_frame_in(struct osmo_v110_ta *ta, const struct osmo_v110_decoded_frame *in)
+{
+ return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_RX_FRAME_IND, (void *)in);
+}
+
+/*! Pull a [decoded] V.110 frame out of the given TA instance.
+ *
+ * This function, like its _in counterpart, is intended to be used by the lower layers
+ * transmitting V.110 frames over some medium. The caller of this function is responsible
+ * for encoding the output frame using osmo_v110_encode_frame() or osmo_csd_*_encode_frame().
+ *
+ * Bits E1/E2/E3 are set in accordance with the configured synchronous user rate.
+ * Bits E4/E5/E6/E7 are unconditionally set to binary '1'.
+ *
+ * Bits D1..D48 are set depending on the state of TA's FSM:
+ *
+ * - In data transfer mode, the user bits are obtained from the upper layer (application)
+ * via the configured .tx_cb(), and then passed to the bit rate adaption function RA1,
+ * which generates bits D1..D48.
+ * - In other modes, bits D1..D48 are all set to binary '0' or '1'.
+ *
+ * \param[in] ta TA instance to pull a frame from.
+ * \param[out] out where to store a [decoded] V.110 frame.
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_frame_out(struct osmo_v110_ta *ta, struct osmo_v110_decoded_frame *out)
+{
+ return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_TX_FRAME_RTS, (void *)out);
+}
+
+/*! Indicate a synchronization establishment event.
+ *
+ * This function is intended to be called when the lower layer
+ * achieves synchronization to the frame clock.
+ *
+ * \param[in] ta TA instance to indicate the event to.
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_sync_ind(struct osmo_v110_ta *ta)
+{
+ return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_SYNC_IND, NULL);
+}
+
+/*! Indicate a synchronization loss event.
+ *
+ * This function is intended to be called when the lower layer
+ * experiences a loss of synchronization with the frame clock.
+ *
+ * \param[in] ta TA instance to indicate the event to.
+ * \returns 0 in case of success; negative on error. */
+int osmo_v110_ta_desync_ind(struct osmo_v110_ta *ta)
+{
+ return osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_DESYNC_IND, NULL);
+}
+
+/*! Get the V.24 status bit-mask of the given TA instance.
+ * \param[in] ta TA instance to get the circuit bit-mask.
+ * \returns bitmask of OSMO_V110_TA_C_*. */
+unsigned int osmo_v110_ta_get_status(const struct osmo_v110_ta *ta)
+{
+ return ta->state.v24_flags;
+}
+
+/*! Set the V.24 status bit-mask of the given TA instance.
+ * \param[in] ta TA instance to update the circuit state.
+ * \param[in] status bit-mask of OSMO_V110_TA_C_*.
+ * \returns 0 on success; negative on error. */
+static int v110_ta_set_status(struct osmo_v110_ta *ta, unsigned int status)
+{
+ const unsigned int old_status = ta->state.v24_flags;
+ int rc = 0;
+
+ ta->state.v24_flags = status;
+ if (status != old_status)
+ rc = osmo_fsm_inst_dispatch(ta->fi, V110_TA_EV_V24_STATUS_CHG, NULL);
+
+ return rc;
+}
+
+/*! Get state of a V.24 circuit of the given TA instance.
+ * \param[in] ta TA instance to get the circuit state.
+ * \param[in] circuit a V.24 circuit, one of OSMO_V110_TA_C_*.
+ * \returns circuit state: active (true) or inactive (false). */
+bool osmo_v110_ta_get_circuit(const struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit)
+{
+ return V24_FLAGMASK_IS_ON(ta->state.v24_flags, circuit);
+}
+
+/*! Activate/deactivate a V.24 circuit of the given TA instance.
+ * \param[in] ta TA instance to update the circuit state.
+ * \param[in] circuit a V.24 circuit, one of OSMO_V110_TA_C_* (DTE->DCE).
+ * \param[in] active activate (true) or deactivate (false) the circuit.
+ * \returns 0 on success; negative on error. */
+int osmo_v110_ta_set_circuit(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit, bool active)
+{
+ unsigned int status = ta->state.v24_flags;
+
+ /* permit setting only DTE->DCE circuits */
+ switch (circuit) {
+ case OSMO_V110_TA_C_105:
+ case OSMO_V110_TA_C_108:
+ case OSMO_V110_TA_C_133:
+ break;
+ default:
+ LOGPFSML(ta->fi, LOGL_ERROR,
+ "Setting circuit %s is not permitted (wrong direction?)\n",
+ osmo_v110_ta_circuit_name(circuit));
+ return -EACCES;
+ }
+
+ if (active)
+ V24_FLAGMASK_SET_ON(status, circuit);
+ else
+ V24_FLAGMASK_SET_OFF(status, circuit);
+
+ return v110_ta_set_status(ta, status);
+}
diff --git a/src/pseudotalloc/Makefile.am b/src/pseudotalloc/Makefile.am
index 3c78bba8..030b281b 100644
--- a/src/pseudotalloc/Makefile.am
+++ b/src/pseudotalloc/Makefile.am
@@ -1,4 +1,5 @@
-AM_CFLAGS = -Wall -I. $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CPPFLAGS = -I. -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall
if ENABLE_PSEUDOTALLOC
diff --git a/src/pseudotalloc/pseudotalloc.c b/src/pseudotalloc/pseudotalloc.c
index 25425e57..895c6dd0 100644
--- a/src/pseudotalloc/pseudotalloc.c
+++ b/src/pseudotalloc/pseudotalloc.c
@@ -55,21 +55,21 @@ void talloc_set_name_const(const void *ptr, const char *name)
{
}
-char *talloc_strdup(const void *context, const char *p)
+void *talloc_memdup(const void *ctx, const void *p, size_t size)
{
- char *ptr;
- size_t len;
+ void *ptr;
- if (!p)
- return NULL;
- len = strlen(p);
+ ptr = talloc_size(ctx, size);
+ if (ptr && p)
+ memcpy(ptr, p, size);
+ return ptr;
+}
- ptr = talloc_size(context, len+1);
- if (!ptr)
+char *talloc_strdup(const void *ctx, const char *p)
+{
+ if (!p)
return NULL;
- memcpy(ptr, p, len+1);
-
- return ptr;
+ return talloc_memdup(ctx, p, strlen(p) + 1);
}
void *talloc_pool(const void *context, size_t size)
diff --git a/src/pseudotalloc/talloc.h b/src/pseudotalloc/talloc.h
index d257a981..38935991 100644
--- a/src/pseudotalloc/talloc.h
+++ b/src/pseudotalloc/talloc.h
@@ -50,7 +50,8 @@ int _talloc_free(void *ptr, const char *location);
void *talloc_named_const(const void *context, size_t size, const char *name);
void *talloc_named(const void *context, size_t size, const char *fmt, ...);
void talloc_set_name_const(const void *ptr, const char *name);
-char *talloc_strdup(const void *t, const char *p);
+void *talloc_memdup(const void *ctx, const void *p, size_t size);
+char *talloc_strdup(const void *ctx, const char *p);
void *talloc_pool(const void *context, size_t size);
#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
diff --git a/src/select.c b/src/select.c
deleted file mode 100644
index b997122e..00000000
--- a/src/select.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/*! \file select.c
- * select filedescriptor handling.
- * Taken from:
- * userspace logging daemon for the iptables ULOG target
- * of the linux 2.4 netfilter subsystem. */
-/*
- * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org>
- * All Rights Reserverd.
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
- */
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdbool.h>
-#include <errno.h>
-
-#include <osmocom/core/select.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-
-#include "../config.h"
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-
-/*! \addtogroup select
- * @{
- * select() loop abstraction
- *
- * \file select.c */
-
-/* keep a set of file descriptors per-thread, so that each thread can have its own
- * distinct set of file descriptors to interact with */
-static __thread int maxfd = 0;
-static __thread struct llist_head osmo_fds; /* TLS cannot use LLIST_HEAD() */
-static __thread int unregistered_count;
-
-/*! Set up an osmo-fd. Will not register it.
- * \param[inout] ofd Osmo FD to be set-up
- * \param[in] fd OS-level file descriptor number
- * \param[in] when bit-mask of OSMO_FD_{READ,WRITE,EXECEPT}
- * \param[in] cb Call-back function to be called
- * \param[in] data Private context pointer
- * \param[in] priv_nr Private number
- */
-void osmo_fd_setup(struct osmo_fd *ofd, int fd, unsigned int when,
- int (*cb)(struct osmo_fd *fd, unsigned int what),
- void *data, unsigned int priv_nr)
-{
- ofd->fd = fd;
- ofd->when = when;
- ofd->cb = cb;
- ofd->data = data;
- ofd->priv_nr = priv_nr;
-}
-
-/*! Check if a file descriptor is already registered
- * \param[in] fd osmocom file descriptor to be checked
- * \returns true if registered; otherwise false
- */
-bool osmo_fd_is_registered(struct osmo_fd *fd)
-{
- struct osmo_fd *entry;
- llist_for_each_entry(entry, &osmo_fds, list) {
- if (entry == fd) {
- return true;
- }
- }
-
- return false;
-}
-
-/*! Register a new file descriptor with select loop abstraction
- * \param[in] fd osmocom file descriptor to be registered
- * \returns 0 on success; negative in case of error
- */
-int osmo_fd_register(struct osmo_fd *fd)
-{
- int flags;
-
- /* make FD nonblocking */
- flags = fcntl(fd->fd, F_GETFL);
- if (flags < 0)
- return flags;
- flags |= O_NONBLOCK;
- flags = fcntl(fd->fd, F_SETFL, flags);
- if (flags < 0)
- return flags;
-
- /* set close-on-exec flag */
- flags = fcntl(fd->fd, F_GETFD);
- if (flags < 0)
- return flags;
- flags |= FD_CLOEXEC;
- flags = fcntl(fd->fd, F_SETFD, flags);
- if (flags < 0)
- return flags;
-
- /* Register FD */
- if (fd->fd > maxfd)
- maxfd = fd->fd;
-
-#ifdef BSC_FD_CHECK
- if (osmo_fd_is_registered(fd)) {
- fprintf(stderr, "Adding a osmo_fd that is already in the list.\n");
- return 0;
- }
-#endif
-
- llist_add_tail(&fd->list, &osmo_fds);
-
- return 0;
-}
-
-/*! Unregister a file descriptor from select loop abstraction
- * \param[in] fd osmocom file descriptor to be unregistered
- */
-void osmo_fd_unregister(struct osmo_fd *fd)
-{
- /* Note: when fd is inside the osmo_fds list (not registered before)
- * this function will crash! If in doubt, check file descriptor with
- * osmo_fd_is_registered() */
- unregistered_count++;
- llist_del(&fd->list);
-}
-
-/*! Close a file descriptor, mark it as closed + unregister from select loop abstraction
- * \param[in] fd osmocom file descriptor to be unregistered + closed
- *
- * If \a fd is registered, we unregister it from the select() loop
- * abstraction. We then close the fd and set it to -1, as well as
- * unsetting any 'when' flags */
-void osmo_fd_close(struct osmo_fd *fd)
-{
- if (osmo_fd_is_registered(fd))
- osmo_fd_unregister(fd);
- if (fd->fd != -1)
- close(fd->fd);
- fd->fd = -1;
- fd->when = 0;
-}
-
-/*! Populate the fd_sets and return the highest fd number
- * \param[in] _rset The readfds to populate
- * \param[in] _wset The wrtiefds to populate
- * \param[in] _eset The errorfds to populate
- *
- * \returns The highest file descriptor seen or 0 on an empty list
- */
-inline int osmo_fd_fill_fds(void *_rset, void *_wset, void *_eset)
-{
- fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
- struct osmo_fd *ufd;
- int highfd = 0;
-
- llist_for_each_entry(ufd, &osmo_fds, list) {
- if (ufd->when & OSMO_FD_READ)
- FD_SET(ufd->fd, readset);
-
- if (ufd->when & OSMO_FD_WRITE)
- FD_SET(ufd->fd, writeset);
-
- if (ufd->when & OSMO_FD_EXCEPT)
- FD_SET(ufd->fd, exceptset);
-
- if (ufd->fd > highfd)
- highfd = ufd->fd;
- }
-
- return highfd;
-}
-
-inline int osmo_fd_disp_fds(void *_rset, void *_wset, void *_eset)
-{
- struct osmo_fd *ufd, *tmp;
- int work = 0;
- fd_set *readset = _rset, *writeset = _wset, *exceptset = _eset;
-
-restart:
- unregistered_count = 0;
- llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) {
- int flags = 0;
-
- if (FD_ISSET(ufd->fd, readset)) {
- flags |= OSMO_FD_READ;
- FD_CLR(ufd->fd, readset);
- }
-
- if (FD_ISSET(ufd->fd, writeset)) {
- flags |= OSMO_FD_WRITE;
- FD_CLR(ufd->fd, writeset);
- }
-
- if (FD_ISSET(ufd->fd, exceptset)) {
- flags |= OSMO_FD_EXCEPT;
- FD_CLR(ufd->fd, exceptset);
- }
-
- if (flags) {
- work = 1;
- /* make sure to clear any log context before processing the next incoming message
- * as part of some file descriptor callback. This effectively prevents "context
- * leaking" from processing of one message into processing of the next message as part
- * of one iteration through the list of file descriptors here. See OS#3813 */
- log_reset_context();
- ufd->cb(ufd, flags);
- }
- /* ugly, ugly hack. If more than one filedescriptor was
- * unregistered, they might have been consecutive and
- * llist_for_each_entry_safe() is no longer safe */
- /* this seems to happen with the last element of the list as well */
- if (unregistered_count >= 1)
- goto restart;
- }
-
- return work;
-}
-
-static int _osmo_select_main(int polling)
-{
- fd_set readset, writeset, exceptset;
- int rc;
- struct timeval no_time = {0, 0};
-
- FD_ZERO(&readset);
- FD_ZERO(&writeset);
- FD_ZERO(&exceptset);
-
- /* prepare read and write fdsets */
- osmo_fd_fill_fds(&readset, &writeset, &exceptset);
-
- if (!polling)
- osmo_timers_prepare();
- rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest());
- if (rc < 0)
- return 0;
-
- /* fire timers */
- osmo_timers_update();
-
- OSMO_ASSERT(osmo_ctx->select);
-
- /* call registered callback functions */
- return osmo_fd_disp_fds(&readset, &writeset, &exceptset);
-}
-
-/*! select main loop integration
- * \param[in] polling should we pollonly (1) or block on select (0)
- * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
- */
-int osmo_select_main(int polling)
-{
- int rc = _osmo_select_main(polling);
-#ifndef EMBEDDED
- if (talloc_total_size(osmo_ctx->select) != 0) {
- osmo_panic("You cannot use the 'select' volatile "
- "context if you don't use osmo_select_main_ctx()!\n");
- }
-#endif
- return rc;
-}
-
-#ifndef EMBEDDED
-/*! select main loop integration with temporary select-dispatch talloc context
- * \param[in] polling should we pollonly (1) or block on select (0)
- * \returns 0 if no fd handled; 1 if fd handled; negative in case of error
- */
-int osmo_select_main_ctx(int polling)
-{
- int rc = _osmo_select_main(polling);
- /* free all the children of the volatile 'select' scope context */
- talloc_free_children(osmo_ctx->select);
- return rc;
-}
-#endif
-
-/*! find an osmo_fd based on the integer fd
- * \param[in] fd file descriptor to use as search key
- * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */
-struct osmo_fd *osmo_fd_get_by_fd(int fd)
-{
- struct osmo_fd *ofd;
-
- llist_for_each_entry(ofd, &osmo_fds, list) {
- if (ofd->fd == fd)
- return ofd;
- }
- return NULL;
-}
-
-/*! initialize the osmocom select abstraction for the current thread */
-void osmo_select_init(void)
-{
- INIT_LLIST_HEAD(&osmo_fds);
-}
-
-/* ensure main thread always has pre-initialized osmo_fds */
-static __attribute__((constructor)) void on_dso_load_select(void)
-{
- osmo_select_init();
-}
-
-#ifdef HAVE_SYS_TIMERFD_H
-#include <sys/timerfd.h>
-
-/*! disable the osmocom-wrapped timerfd */
-int osmo_timerfd_disable(struct osmo_fd *ofd)
-{
- const struct itimerspec its_null = {
- .it_value = { 0, 0 },
- .it_interval = { 0, 0 },
- };
- return timerfd_settime(ofd->fd, 0, &its_null, NULL);
-}
-
-/*! schedule the osmcoom-wrapped timerfd to occur first at \a first, then periodically at \a interval
- * \param[in] ofd Osmocom wrapped timerfd
- * \param[in] first Relative time at which the timer should first execute (NULL = \a interval)
- * \param[in] interval Time interval at which subsequent timer shall fire
- * \returns 0 on success; negative on error */
-int osmo_timerfd_schedule(struct osmo_fd *ofd, const struct timespec *first,
- const struct timespec *interval)
-{
- struct itimerspec its;
-
- if (ofd->fd < 0)
- return -EINVAL;
-
- /* first expiration */
- if (first)
- its.it_value = *first;
- else
- its.it_value = *interval;
- /* repeating interval */
- its.it_interval = *interval;
-
- return timerfd_settime(ofd->fd, 0, &its, NULL);
-}
-
-/*! setup osmocom-wrapped timerfd
- * \param[inout] ofd Osmocom-wrapped timerfd on which to operate
- * \param[in] cb Call-back function called when timerfd becomes readable
- * \param[in] data Opaque data to be passed on to call-back
- * \returns 0 on success; negative on error
- *
- * We simply initialize the data structures here, but do not yet
- * schedule the timer.
- */
-int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned int), void *data)
-{
- ofd->cb = cb;
- ofd->data = data;
- ofd->when = OSMO_FD_READ;
-
- if (ofd->fd < 0) {
- int rc;
-
- ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
- if (ofd->fd < 0)
- return ofd->fd;
-
- rc = osmo_fd_register(ofd);
- if (rc < 0) {
- close(ofd->fd);
- ofd->fd = -1;
- return rc;
- }
- }
- return 0;
-}
-
-#endif /* HAVE_SYS_TIMERFD_H */
-
-
-/*! @} */
-
-#endif /* _HAVE_SYS_SELECT_H */
diff --git a/src/sim/Makefile.am b/src/sim/Makefile.am
index 1a8e5084..0f6be576 100644
--- a/src/sim/Makefile.am
+++ b/src/sim/Makefile.am
@@ -1,27 +1,32 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
-LIBVERSION=1:1:1
+LIBVERSION=3:2:1
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -fPIC -Wall $(PCSC_CFLAGS) $(TALLOC_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -fPIC -Wall $(TALLOC_CFLAGS)
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
-if ENABLE_PCSC
-# FIXME: only build the PC/SC dependent part conditional, but always build other parts
-
noinst_HEADERS = sim_int.h gsm_int.h
+if !EMBEDDED
lib_LTLIBRARIES = libosmosim.la
-libosmosim_la_SOURCES = core.c reader.c reader_pcsc.c class_tables.c \
+libosmosim_la_SOURCES = core.c reader.c class_tables.c \
card_fs_sim.c card_fs_usim.c card_fs_uicc.c \
- card_fs_isim.c card_fs_tetra.c
-libosmosim_la_LDFLAGS = -version-info $(LIBVERSION)
+ card_fs_isim.c card_fs_hpsim.c card_fs_tetra.c
+libosmosim_la_LDFLAGS = \
+ -version-info $(LIBVERSION) \
+ -no-undefined \
+ $(NULL)
libosmosim_la_LIBADD = \
- $(top_builddir)/src/libosmocore.la \
+ $(top_builddir)/src/core/libosmocore.la \
$(top_builddir)/src/gsm/libosmogsm.la \
- $(TALLOC_LIBS) \
- $(PCSC_LIBS)
-
+ $(TALLOC_LIBS)
+if ENABLE_PCSC
+AM_CFLAGS += $(PCSC_CFLAGS)
+libosmosim_la_SOURCES += reader_pcsc.c
+libosmosim_la_LIBADD += $(PCSC_LIBS)
endif
+
+endif # !EMBEDDED
diff --git a/src/sim/card_fs_hpsim.c b/src/sim/card_fs_hpsim.c
new file mode 100644
index 00000000..2c115b75
--- /dev/null
+++ b/src/sim/card_fs_hpsim.c
@@ -0,0 +1,72 @@
+/*! \file card_fs_hpsim.c
+ * 3GPP HPSIM specific structures / routines. */
+/*
+ * (C) 2020 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/sim/sim.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gsm/gsm48.h>
+
+#include "sim_int.h"
+#include "gsm_int.h"
+
+/* TS 31.104 Version 15.0.0 Release 15 / Chapter 7.1.3 */
+const struct osim_card_sw ts31_104_sw[] = {
+ {
+ 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - Authentication error, incorrect MAC",
+ },
+ OSIM_CARD_SW_LAST
+};
+
+/* TS 31.104 Version 15.0.0 Release 15 / Chapter 4.2 */
+static const struct osim_file_desc hpsim_ef_in_adf_hpsim[] = {
+ EF_LIN_FIX_N(0x6F06, 0x06, "EF.ARR", 0, 1, 256,
+ "Access Rule TLV data objects"),
+ EF_TRANSP_N(0x6F07, 0x07, "EF.IMST", 0, 9, 9,
+ "IMSI"),
+ EF_TRANSP_N(0x6FAD, 0x03, "EF_AD", 0, 4, 8,
+ "Administrative Data"),
+};
+
+/* Annex E - TS 101 220 */
+static const uint8_t adf_hpsim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x0A };
+
+struct osim_card_app_profile *osim_aprof_hpsim(void *ctx)
+{
+ struct osim_card_app_profile *aprof;
+ struct osim_file_desc *iadf;
+
+ aprof = talloc_zero(ctx, struct osim_card_app_profile);
+ aprof->name = "3GPP HPSIM";
+ aprof->sw = ts31_104_sw;
+ aprof->aid_len = sizeof(adf_hpsim_aid);
+ memcpy(aprof->aid, adf_hpsim_aid, aprof->aid_len);
+
+ /* ADF.HPSIM with its EF siblings */
+ iadf = alloc_adf_with_ef(aprof, adf_hpsim_aid, sizeof(adf_hpsim_aid), "ADF.HPSIM",
+ hpsim_ef_in_adf_hpsim, ARRAY_SIZE(hpsim_ef_in_adf_hpsim));
+ aprof->adf = iadf;
+
+ return aprof;
+}
diff --git a/src/sim/card_fs_isim.c b/src/sim/card_fs_isim.c
index e6ba0d09..1a38da2f 100644
--- a/src/sim/card_fs_isim.c
+++ b/src/sim/card_fs_isim.c
@@ -1,7 +1,7 @@
/*! \file card_fs_isim.c
* 3GPP ISIM specific structures / routines. */
/*
- * (C) 2014 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2014-2020 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -34,22 +30,19 @@
#include "sim_int.h"
#include "gsm_int.h"
-/* TS 31.103 Version 11.2.0 Release 11 / Chapoter 7.1.3 */
+/* TS 31.103 Version 15.5.0 Release 15 / Chapter 7.1.3 */
const struct osim_card_sw ts31_103_sw[] = {
{
0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
.u.str = "Security management - Authentication error, incorrect MAC",
+ }, {
+ 0x9864, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - Authentication error, security context not supported",
},
OSIM_CARD_SW_LAST
};
-static const struct osim_card_sw *isim_card_sws[] = {
- ts31_103_sw,
- ts102221_uicc_sw,
- NULL
-};
-
-/* TS 31.103 Version 11.2.0 Release 11 / Chapoter 4.2 */
+/* TS 31.103 Version 15.5.0 Release 15 / Chapter 4.2 */
static const struct osim_file_desc isim_ef_in_adf_isim[] = {
EF_TRANSP_N(0x6F02, 0x02, "EF.IMPI", 0, 1, 256,
"IMS private user identity"),
@@ -81,28 +74,34 @@ static const struct osim_file_desc isim_ef_in_adf_isim[] = {
"Short message service parameters"),
EF_LIN_FIX_N(0x6FE7, SFI_NONE, "EF.UICCIARI", F_OPTIONAL, 1, 256,
"UICC IARI"),
+ EF_TRANSP_N(0x6FF7, SFI_NONE, "EF_FromPreferred", F_OPTIONAL, 1, 1,
+ "From Preferred"),
+ EF_TRANSP_N(0x6FF8, SFI_NONE, "EF_IMSConfigData", F_OPTIONAL, 3, 128,
+ "IMS Configuration Data"),
+ EF_TRANSP_N(0x6FFC, SFI_NONE, "EF_XCAPConfigData", F_OPTIONAL, 1, 128,
+ "XCAP Configuration Data"),
+ EF_LIN_FIX_N(0x6FFA, SFI_NONE, "EF_WebRTCURI", F_OPTIONAL, 3, 128,
+ "WebRTC URI"),
};
/* Annex E - TS 101 220 */
static const uint8_t adf_isim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x04 };
-struct osim_card_profile *osim_cprof_isim(void *ctx)
+struct osim_card_app_profile *osim_aprof_isim(void *ctx)
{
- struct osim_card_profile *cprof;
- struct osim_file_desc *mf;
-
- cprof = talloc_zero(ctx, struct osim_card_profile);
- cprof->name = "3GPP ISIM";
- cprof->sws = isim_card_sws;
-
- mf = alloc_df(cprof, 0x3f00, "MF");
+ struct osim_card_app_profile *aprof;
+ struct osim_file_desc *iadf;
- cprof->mf = mf;
+ aprof = talloc_zero(ctx, struct osim_card_app_profile);
+ aprof->name = "3GPP ISIM";
+ aprof->sw = ts31_103_sw;
+ aprof->aid_len = sizeof(adf_isim_aid);
+ memcpy(aprof->aid, adf_isim_aid, aprof->aid_len);
/* ADF.USIM with its EF siblings */
- add_adf_with_ef(mf, adf_isim_aid, sizeof(adf_isim_aid),
- "ADF.ISIM", isim_ef_in_adf_isim,
- ARRAY_SIZE(isim_ef_in_adf_isim));
+ iadf = alloc_adf_with_ef(aprof, adf_isim_aid, sizeof(adf_isim_aid), "ADF.ISIM",
+ isim_ef_in_adf_isim, ARRAY_SIZE(isim_ef_in_adf_isim));
+ aprof->adf = iadf;
- return cprof;
+ return aprof;
}
diff --git a/src/sim/card_fs_sim.c b/src/sim/card_fs_sim.c
index 3f541f7b..f07a2370 100644
--- a/src/sim/card_fs_sim.c
+++ b/src/sim/card_fs_sim.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
@@ -431,7 +427,7 @@ int osim_int_cprof_add_gsm(struct osim_file_desc *mf)
add_df_with_ef(gsm, 0x5F33, "DF.ACeS", NULL, 0);
add_df_with_ef(gsm, 0x5F3C, "DF.MExE", sim_ef_in_mexe,
ARRAY_SIZE(sim_ef_in_mexe));
- add_df_with_ef(gsm, 0x5F40, "DF.EIA/TIA-533", NULL, 0);
+ add_df_with_ef(gsm, 0x5F40, "DF.EIA-TIA-533", NULL, 0);
add_df_with_ef(gsm, 0x5F60, "DF.CTS", NULL, 0);
add_df_with_ef(gsm, 0x5F70, "DF.SoLSA", sim_ef_in_solsa,
ARRAY_SIZE(sim_ef_in_solsa));
diff --git a/src/sim/card_fs_tetra.c b/src/sim/card_fs_tetra.c
index 12853a52..597e38b7 100644
--- a/src/sim/card_fs_tetra.c
+++ b/src/sim/card_fs_tetra.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
diff --git a/src/sim/card_fs_uicc.c b/src/sim/card_fs_uicc.c
index af6061cf..87def0e4 100644
--- a/src/sim/card_fs_uicc.c
+++ b/src/sim/card_fs_uicc.c
@@ -1,7 +1,7 @@
/*! \file card_fs_uicc.c
* ETSI UICC specific structures / routines. */
/*
- * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2012-2020 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -17,16 +17,16 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/sim/sim.h>
+#include <osmocom/core/talloc.h>
#include <osmocom/gsm/tlv.h>
+#include "sim_int.h"
+#include "gsm_int.h"
+
/* TS 102 221 V10.0.0 / 10.2.1 */
const struct osim_card_sw ts102221_uicc_sw[] = {
{
@@ -171,6 +171,23 @@ const struct osim_card_sw ts102221_uicc_sw[] = {
OSIM_CARD_SW_LAST
};
+static const struct osim_card_sw *uicc_card_sws[] = {
+ ts102221_uicc_sw,
+ NULL
+};
+
+/* TS 102 221 Chapter 13.1 */
+static const struct osim_file_desc uicc_ef_in_mf[] = {
+ EF_LIN_FIX_N(0x2f00, SFI_NONE, "EF.DIR", 0, 1, 32,
+ "Application directory"),
+ EF_TRANSP_N(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10,
+ "ICC Identification"),
+ EF_TRANSP_N(0x2F05, SFI_NONE, "EF.PL", 0, 2, 20,
+ "Preferred Languages"),
+ EF_LIN_FIX_N(0x2F06, SFI_NONE, "EF.ARR", F_OPTIONAL, 1, 256,
+ "Access Rule Reference"),
+};
+
const struct value_string ts102221_fcp_vals[14] = {
{ UICC_FCP_T_FCP, "File control parameters" },
{ UICC_FCP_T_FILE_SIZE, "File size" },
@@ -209,3 +226,39 @@ const struct tlv_definition ts102221_fcp_tlv_def = {
/* Annex E - TS 101 220 */
static const uint8_t __attribute__((__unused__)) adf_uicc_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x01 };
+
+struct osim_card_profile *osim_cprof_uicc(void *ctx, bool have_df_gsm)
+{
+ struct osim_card_profile *cprof;
+ struct osim_file_desc *mf;
+ int rc;
+
+ cprof = talloc_zero(ctx, struct osim_card_profile);
+ cprof->name = "3GPP UICC";
+ cprof->sws = uicc_card_sws; // FIXME: extend later
+
+ mf = alloc_df(cprof, 0x3f00, "MF");
+
+ cprof->mf = mf;
+
+ /* Core UICC Files */
+ add_filedesc(mf, uicc_ef_in_mf, ARRAY_SIZE(uicc_ef_in_mf));
+
+ /* DF.TELECOM hierarchy as sub-directory of MF */
+ rc = osim_int_cprof_add_telecom(mf);
+ if (rc != 0) {
+ talloc_free(cprof);
+ return NULL;
+ }
+
+ if (have_df_gsm) {
+ /* DF.GSM as sub-directory of MF */
+ rc = osim_int_cprof_add_gsm(mf);
+ if (rc != 0) {
+ talloc_free(cprof);
+ return NULL;
+ }
+ }
+
+ return cprof;
+}
diff --git a/src/sim/card_fs_usim.c b/src/sim/card_fs_usim.c
index 9e9fc878..8cff3fc3 100644
--- a/src/sim/card_fs_usim.c
+++ b/src/sim/card_fs_usim.c
@@ -1,7 +1,7 @@
/*! \file card_fs_usim.c
* 3GPP USIM specific structures / routines. */
/*
- * (C) 2012-2014 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2012-2020 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -32,7 +28,7 @@
#include "sim_int.h"
#include "gsm_int.h"
-/* TS 31.102 Version 7.7.0 / Chapter 7.3 */
+/* TS 31.102 Version 15.7.0 Release 15 / Chapter 7.3 */
const struct osim_card_sw ts31_102_sw[] = {
{
0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
@@ -43,29 +39,17 @@ const struct osim_card_sw ts31_102_sw[] = {
}, {
0x9865, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
.u.str = "Security management - Key freshness error",
+ }, {
+ 0x9866, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - Authentication error, no memory space available",
+ }, {
+ 0x9867, 0xffff, SW_TYPE_STR, SW_CLS_ERROR,
+ .u.str = "Security management - Authentication error, no memory space available in EF_MUK",
},
OSIM_CARD_SW_LAST
};
-static const struct osim_card_sw *usim_card_sws[] = {
- ts31_102_sw,
- ts102221_uicc_sw,
- NULL
-};
-
-/* TS 102 221 Chapter 13.1 */
-static const struct osim_file_desc uicc_ef_in_mf[] = {
- EF_LIN_FIX_N(0x2f00, SFI_NONE, "EF.DIR", 0, 1, 32,
- "Application directory"),
- EF_TRANSP_N(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10,
- "ICC Identification"),
- EF_TRANSP_N(0x2F05, SFI_NONE, "EF.PL", 0, 2, 20,
- "Preferred Languages"),
- EF_LIN_FIX_N(0x2F06, SFI_NONE, "EF.ARR", F_OPTIONAL, 1, 256,
- "Access Rule Reference"),
-};
-
-/* 31.102 Chapter 4.4.3 */
+/* 31.102 Version 15.7.0 Release 15 / Chapter 4.4.3 */
static const struct osim_file_desc usim_ef_in_df_gsm_access[] = {
EF_TRANSP_N(0x4f20, 0x01, "EF.Kc", 0, 9, 9,
"Ciphering Key Kc"),
@@ -77,7 +61,7 @@ static const struct osim_file_desc usim_ef_in_df_gsm_access[] = {
"Investigation Scan"),
};
-/* 31.102 Chapter 4.2 */
+/* 31.102 Version 15.7.0 Release 15 / Chapter 4.2 */
static const struct osim_file_desc usim_ef_in_adf_usim[] = {
EF_TRANSP(0x6F05, 0x02, "EF.LI", 0, 2, 16,
"Language Indication", &gsm_lp_decode, NULL),
@@ -161,7 +145,7 @@ static const struct osim_file_desc usim_ef_in_adf_usim[] = {
"Key for hidden phone book entries"),
EF_LIN_FIX_N(0x6F4D, SFI_NONE, "EF.BDN", F_OPTIONAL, 15, 32,
"Barred Dialling Numbers"),
- EF_LIN_FIX_N(0x6F4E, SFI_NONE, "EF.EXT4", F_OPTIONAL, 13, 13,
+ EF_LIN_FIX_N(0x6F55, SFI_NONE, "EF.EXT4", F_OPTIONAL, 13, 13,
"Extension 4"),
EF_LIN_FIX_N(0x6F58, SFI_NONE, "EF.CMI", F_OPTIONAL, 2, 16,
"Comparison Method Information"),
@@ -261,6 +245,39 @@ static const struct osim_file_desc usim_ef_in_adf_usim[] = {
"UICC IARI"),
EF_TRANSP_N(0x6FEC, SFI_NONE, "EF.PWS", F_OPTIONAL, 1, 32,
"Public Warning System"),
+ EF_LIN_FIX_N(0x6FED, SFI_NONE, "EF_FDNURI", F_OPTIONAL, 1, 128,
+ "Fixed Dialling Numbers URI"),
+ EF_LIN_FIX_N(0x6FEE, SFI_NONE, "EF_BDNURI", F_OPTIONAL, 1, 128,
+ "Barred Dialling Numbers URI"),
+ EF_LIN_FIX_N(0x6FEF, SFI_NONE, "EF_SDNURI", F_OPTIONAL, 1, 128,
+ "Service Dialling Numbers URI"),
+ EF_LIN_FIX_N(0x6FF0, SFI_NONE, "EF_IWL", F_OPTIONAL, 18, 32,
+ "IMEI(SV) White Lists"),
+ EF_CYCLIC_N(0x6FF1, SFI_NONE, "EF_IPS", F_OPTIONAL, 4, 4,
+ "IMEI(SV) Pairing Status"),
+ EF_LIN_FIX_N(0x6FF2, SFI_NONE, "EF_IPD", F_OPTIONAL, 10, 16,
+ "IMEI(SV) of Pairing Device"),
+ EF_TRANSP_N(0x6FF3, SFI_NONE, "EF_ePDGId", F_OPTIONAL, 1, 128,
+ "Home ePDG Identifier"),
+ EF_TRANSP_N(0x6FF4, SFI_NONE, "EF_ePDGSelection", F_OPTIONAL, 1, 128,
+ "ePDG Selection Information"),
+ EF_TRANSP_N(0x6FF5, SFI_NONE, "EF_ePDGIdEm", F_OPTIONAL, 1, 128,
+ "Emergency ePDG Identifier"),
+ EF_TRANSP_N(0x6FF6, SFI_NONE, "EF_ePDGSelectionEm", F_OPTIONAL, 1, 128,
+ "ePDG Selection Information for Emergency Services"),
+ EF_TRANSP_N(0x6FF7, SFI_NONE, "EF_FromPreferred", F_OPTIONAL, 1, 1,
+ "From Preferred"),
+ EF_TRANSP_N(0x6FF8, SFI_NONE, "EF_IMSConfigData", F_OPTIONAL, 3, 128,
+ "IMS Configuration Data"),
+ /* EF TVCONFIG (TV Configuration) has no fixed FID */
+ EF_TRANSP_N(0x6FF9, SFI_NONE, "EF_3GPPPSDATAOFF", F_OPTIONAL, 4, 4,
+ "3GPP PS Data Off"),
+ EF_LIN_FIX_N(0x6FFA, SFI_NONE, "EF_3GPPPSDATAOFFservicelist", F_OPTIONAL, 1, 128,
+ "3GPP PS Data Off Service List"),
+ EF_TRANSP_N(0x6FFC, SFI_NONE, "EF_XCAPConfigData", F_OPTIONAL, 1, 128,
+ "XCAP Configuration Data"),
+ EF_TRANSP_N(0x6FFD, SFI_NONE, "EF_EARFCNList", F_OPTIONAL, 1, 128,
+ "EARFCN list for MTC/NB-IOT UEs"),
};
@@ -327,30 +344,86 @@ static const struct osim_file_desc usim_ef_in_df_hnb[] = {
"Oprator Home NodeB Name"),
};
-/* Annex E - TS 101 220 */
-static const uint8_t adf_usim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02 };
+/* 31.102 Chapter 4.4.8 */
+static const struct osim_file_desc usim_ef_in_df_prose[] = {
+ EF_LIN_FIX_N(0x4F01, 0x01, "EF.PROSE_MON", F_OPTIONAL, 1, 64,
+ "ProSe Monitoring Parameters"),
+ EF_LIN_FIX_N(0x4F02, 0x02, "EF.PROSE_ANN", F_OPTIONAL, 1, 64,
+ "ProSe Announcing Parameters"),
+ EF_LIN_FIX_N(0x4F03, 0x03, "EF.PROSEFUNC", F_OPTIONAL, 1, 64,
+ "HPLMN ProSe Function"),
+ EF_TRANSP_N(0x4F04, 0x04, "EF.PROSE_RADIO_COM", F_OPTIONAL, 1, 128,
+ "ProSe Direct Communication Radio Parameters"),
+ EF_TRANSP_N(0x4F05, 0x05, "EF.PROSE_RADIO_MON", F_OPTIONAL, 1, 128,
+ "ProSe Direct Discovery Monitoring Radio Parameters"),
+ EF_TRANSP_N(0x4F06, 0x06, "EF.PROSE_RADIO_ANN", F_OPTIONAL, 1, 128,
+ "ProSe Direct Discovery Announcing Radio Parameters"),
+ EF_LIN_FIX_N(0x4F07, 0x07, "EF.PROSE_POLICY", F_OPTIONAL, 1, 64,
+ "ProSe Direct Discovery Announcing Radio Parameters"),
+ EF_LIN_FIX_N(0x4F08, 0x08, "EF.PROSE_PLMN", F_OPTIONAL, 1, 64,
+ "ProSe PLMN Parametes"),
+ EF_TRANSP_N(0x4F09, 0x09, "EF.PROSE_GC", F_OPTIONAL, 8, 64,
+ "ProSe Direct Discovery Announcing Radio Parameters"),
+ EF_TRANSP_N(0x4F10, 0x10, "EF.PST", F_OPTIONAL, 1, 2,
+ "ProSe Service Table"),
+ EF_TRANSP_N(0x4F11, 0x11, "EF.PROSE_UIRC", F_OPTIONAL, 1, 128,
+ "ProSe UsageInformationReportingConfiguration"),
+ EF_LIN_FIX_N(0x4F12, 0x12, "EF.PROSE_GM_DISCOVERY", F_OPTIONAL, 1, 64,
+ "ProSe Group Member Discovery Parameters"),
+ EF_LIN_FIX_N(0x4F13, 0x13, "EF.PROSE_RELAY", F_OPTIONAL, 1, 64,
+ "ProSe Relay Parameters"),
+ EF_TRANSP_N(0x4F14, 0x14, "EF.PROSE_RELAY_DISCOVERY", F_OPTIONAL, 5, 64,
+ "ProSe Relay Discovery Parameters"),
+};
-struct osim_card_profile *osim_cprof_usim(void *ctx)
-{
- struct osim_card_profile *cprof;
- struct osim_file_desc *mf, *uadf;
- int rc;
+/* 31.102 Chapter 4.4.9 */
+static const struct osim_file_desc usim_ef_in_df_acdc[] = {
+ EF_TRANSP_N(0x4F01, 0x01, "EF.ACDC_LIST", F_OPTIONAL, 1, 128,
+ "ACDC List"),
+};
- cprof = talloc_zero(ctx, struct osim_card_profile);
- cprof->name = "3GPP USIM";
- cprof->sws = usim_card_sws;
+/* 31.102 Chapter 4.4.11 */
+static const struct osim_file_desc usim_ef_in_df_5gs[] = {
+ EF_TRANSP_N(0x4F01, 0x01, "EF.5GS3GPPLOCI", F_OPTIONAL, 20, 20,
+ "5GS 3GPP location information"),
+ EF_TRANSP_N(0x4F02, 0x02, "EF.5GSN3GPPLOCI", F_OPTIONAL, 20, 20,
+ "5GS non-3GPP location information"),
+ EF_LIN_FIX_N(0x4F03, 0x03, "EF.5GS3GPPNSC", F_OPTIONAL, 57, 57,
+ "5GS 3GPP Access NAS Security Context"),
+ EF_LIN_FIX_N(0x4F04, 0x04, "EF.5GSN3GPPNSC", F_OPTIONAL, 57, 57,
+ "5GS non-3GPP Access NAS Security Context"),
+ EF_TRANSP_N(0x4F05, 0x05, "EF.5GAUTHKEYS", F_OPTIONAL, 68, 68,
+ "5GS authentication keys"),
+ EF_TRANSP_N(0x4F06, 0x06, "EF.UAC_AIC", F_OPTIONAL, 4, 4,
+ "UAC Access Identities Configuration"),
+ EF_TRANSP_N(0x4F07, 0x07, "EF.SUCI_Calc_Info", F_OPTIONAL, 2, 64,
+ "Subscription Concealed Identifier Calculation Information"),
+ EF_LIN_FIX_N(0x4F08, 0x08, "EF.OPL5G", F_OPTIONAL, 10, 10,
+ "5GS Operator PLMN List"),
+ EF_TRANSP_N(0x4F09, 0x09, "EF.NSI", F_OPTIONAL, 1, 64,
+ "Network Specific Identifier"),
+ EF_TRANSP_N(0x4F0A, 0x0A, "EF.Routing_Indicator", F_OPTIONAL, 4, 4,
+ "Routing Indicator"),
+};
- mf = alloc_df(cprof, 0x3f00, "MF");
+/* Annex E - TS 101 220 */
+static const uint8_t adf_usim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02 };
- cprof->mf = mf;
+struct osim_card_app_profile *osim_aprof_usim(void *ctx)
+{
+ struct osim_card_app_profile *aprof;
+ struct osim_file_desc *uadf;
- /* Core UICC Files */
- add_filedesc(mf, uicc_ef_in_mf, ARRAY_SIZE(uicc_ef_in_mf));
+ aprof = talloc_zero(ctx, struct osim_card_app_profile);
+ aprof->name = "3GPP USIM";
+ aprof->sw = ts31_102_sw;
+ aprof->aid_len = sizeof(adf_usim_aid);
+ memcpy(aprof->aid, adf_usim_aid, aprof->aid_len);
/* ADF.USIM with its EF siblings */
- uadf = add_adf_with_ef(mf, adf_usim_aid, sizeof(adf_usim_aid),
- "ADF.USIM", usim_ef_in_adf_usim,
- ARRAY_SIZE(usim_ef_in_adf_usim));
+ uadf = alloc_adf_with_ef(aprof, adf_usim_aid, sizeof(adf_usim_aid),
+ "ADF.USIM", usim_ef_in_adf_usim, ARRAY_SIZE(usim_ef_in_adf_usim));
+ aprof->adf = uadf;
/* DFs under ADF.USIM */
add_df_with_ef(uadf, 0x5F3A, "DF.PHONEBOOK", NULL, 0);
@@ -360,6 +433,8 @@ struct osim_card_profile *osim_cprof_usim(void *ctx)
ARRAY_SIZE(usim_ef_in_df_mexe));
add_df_with_ef(uadf, 0x5F40, "DF.WLAN", usim_ef_in_df_wlan,
ARRAY_SIZE(usim_ef_in_df_wlan));
+ add_df_with_ef(uadf, 0x5FC0, "DF.5GS", usim_ef_in_df_5gs,
+ ARRAY_SIZE(usim_ef_in_df_5gs));
/* Home-NodeB (femtocell) */
add_df_with_ef(uadf, 0x5F50, "DF.HNB", usim_ef_in_df_hnb,
ARRAY_SIZE(usim_ef_in_df_hnb));
@@ -368,14 +443,10 @@ struct osim_card_profile *osim_cprof_usim(void *ctx)
ARRAY_SIZE(usim_ef_in_solsa));
/* OMA BCAST Smart Card Profile */
add_df_with_ef(uadf, 0x5F80, "DF.BCAST", NULL, 0);
+ add_df_with_ef(uadf, 0x5F90, "DF.ProSe", usim_ef_in_df_prose,
+ ARRAY_SIZE(usim_ef_in_df_prose));
+ add_df_with_ef(uadf, 0x5FA0, "DF.ACDC", usim_ef_in_df_acdc,
+ ARRAY_SIZE(usim_ef_in_df_acdc));
- /* DF.GSM and DF.TELECOM hierarchy as sub-directory of MF */
- rc = osim_int_cprof_add_gsm(mf);
- rc |= osim_int_cprof_add_telecom(mf);
- if (rc != 0) {
- talloc_free(cprof);
- return NULL;
- }
-
- return cprof;
+ return aprof;
}
diff --git a/src/sim/class_tables.c b/src/sim/class_tables.c
index 6f541ee2..29c1e40e 100644
--- a/src/sim/class_tables.c
+++ b/src/sim/class_tables.c
@@ -13,17 +13,13 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdint.h>
#include <osmocom/core/utils.h>
#include <osmocom/sim/class_tables.h>
-static const uint8_t iso7816_ins_tbl[] = {
+static const uint8_t iso7816_ins_tbl[256] = {
[0xB0] = 2, /* READ BIN */
[0xD0] = 3, /* WRITE BIN */
[0xD6] = 3, /* UPDATE BIN */
@@ -117,7 +113,7 @@ static const uint8_t uicc_ins_tbl_046[256] = {
[0xD6] = 3, /* UPDATE BINARY */
[0xB2] = 2, /* READ RECORD */
[0xDC] = 3, /* UPDATE RECORD */
- [0xA2] = 4, /* SEEK */
+ [0xA2] = 4, /* SEARCH RECORD */
[0x20] = 3, /* VERIFY PIN */
[0x24] = 3, /* CHANGE PIN */
[0x26] = 3, /* DISABLE PIN */
@@ -177,6 +173,24 @@ static int uicc046_cla_ins_helper(const struct osim_cla_ins_case *cic,
return 0;
}
+static int gp_cla_ins_helper(const struct osim_cla_ins_case *cic,
+ const uint8_t *hdr)
+{
+ uint8_t ins = hdr[1];
+ uint8_t p1 = hdr[2];
+
+ switch (ins) {
+ case 0xE2: /* STORE DATA */
+ switch (p1 & 0x01) {
+ case 1:
+ return 4;
+ default:
+ return 3;
+ }
+ }
+ return 0;
+}
+
/* ETSI TS 102 221, Table 10.5, CLA = 0x8x, 0xCx or 0xEx */
static const uint8_t uicc_ins_tbl_8ce[256] = {
[0xF2] = 2, /* STATUS */
@@ -184,6 +198,7 @@ static const uint8_t uicc_ins_tbl_8ce[256] = {
[0xCB] = 4, /* RETRIEVE DATA */
[0xDB] = 3, /* SET DATA */
[0xAA] = 3, /* TERMINAL CAPABILITY */
+ [0x78] = 4, /* GET IDENTITY */
};
/* ETSI TS 102 221, Table 10.5, CLA = 0x80 */
@@ -192,6 +207,22 @@ static const uint8_t uicc_ins_tbl_80[256] = {
[0xC2] = 4, /* ENVELOPE */
[0x12] = 2, /* FETCH */
[0x14] = 3, /* TERMINAL RESPONSE */
+ [0x76] = 4, /* SUSPEND UICC */
+ [0x7A] = 4, /* EXCHANGE CAPABILITIES */
+};
+
+/* Card Specification v2.3.1*/
+static const uint8_t gp_ins_tbl_8ce[256] = {
+ [0xE4] = 4, /* DELETE */
+ [0xE2] = 0x80, /* STORE DATA */
+ [0xCA] = 4, /* GET DATA */
+ [0xCB] = 4, /* GET DATA */
+ [0xF2] = 4, /* GET STATUS */
+ [0xE6] = 4, /* INSTALL */
+ [0xE8] = 4, /* LOAD */
+ [0xD8] = 4, /* PUT KEY */
+ [0xF0] = 3, /* SET STATUS */
+ [0xC0] = 2, /* GET RESPONSE */
};
static const struct osim_cla_ins_case uicc_ins_case[] = {
@@ -226,6 +257,21 @@ static const struct osim_cla_ins_case uicc_ins_case[] = {
.cla = 0xE0,
.cla_mask = 0xF0,
.ins_tbl = uicc_ins_tbl_8ce,
+ }, {
+ .cla = 0x80,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
+ .cla = 0xC0,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
+ .cla = 0xE0,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
},
};
@@ -273,7 +319,23 @@ static const struct osim_cla_ins_case uicc_sim_ins_case[] = {
.cla = 0xE0,
.cla_mask = 0xF0,
.ins_tbl = uicc_ins_tbl_8ce,
+ }, {
+ .cla = 0x80,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
+ .cla = 0xC0,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
+ .cla = 0xE0,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
},
+
};
const struct osim_cla_ins_card_profile osim_uicc_sim_cic_profile = {
@@ -288,6 +350,13 @@ const uint8_t usim_ins_case[256] = {
[0x88] = 4, /* AUTHENTICATE */
};
+/* https://learn.microsoft.com/en-us/windows-hardware/drivers/smartcard/discovery-process */
+static const uint8_t microsoft_discovery_ins_tbl[256] = {
+ [0xA4] = 4, /* SELECT FILE */
+ [0xCA] = 2, /* GET DATA */
+ [0xC0] = 2, /* GET RESPONSE */
+};
+
int osim_determine_apdu_case(const struct osim_cla_ins_card_profile *prof,
const uint8_t *hdr)
{
@@ -305,12 +374,23 @@ int osim_determine_apdu_case(const struct osim_cla_ins_card_profile *prof,
case 0x80:
return cic->helper(cic, hdr);
case 0x00:
- /* continue with fruther cic, rather than abort
+ /* continue with further cic, rather than abort
* now */
continue;
default:
return rc;
}
}
+ /* special handling for Microsoft who insists to use INS=0xCA in CLA=0x00 which is not
+ * really part of GSM SIM, ETSI UICC or 3GPP USIM specifications, but only ISO7816. Rather than adding
+ * it to each and every card profile, let's add the instructions listed at
+ * https://learn.microsoft.com/en-us/windows-hardware/drivers/smartcard/discovery-process explicitly
+ * here. They will only be used in case no more specific match was found in the actual profile above. */
+ if (cla == 0x00) {
+ rc = microsoft_discovery_ins_tbl[ins];
+ if (rc)
+ return rc;
+ }
+
return 0;
}
diff --git a/src/sim/core.c b/src/sim/core.c
index b93633c1..fa17e12d 100644
--- a/src/sim/core.c
+++ b/src/sim/core.c
@@ -1,7 +1,7 @@
/*! \file core.c
* Core routines for SIM/UICC/USIM access. */
/*
- * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2012-2020 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -17,20 +17,19 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
+#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/sim/sim.h>
+#include "sim_int.h"
+
struct osim_decoded_data *osim_file_decode(struct osim_file *file,
int len, uint8_t *data)
{
@@ -154,21 +153,19 @@ add_df_with_ef(struct osim_file_desc *parent,
}
struct osim_file_desc *
-add_adf_with_ef(struct osim_file_desc *parent,
+alloc_adf_with_ef(void *ctx,
const uint8_t *adf_name, uint8_t adf_name_len,
const char *name, const struct osim_file_desc *in,
int num)
{
struct osim_file_desc *df;
- df = alloc_df(parent, 0xffff, name);
+ df = alloc_df(ctx, 0xffff, name);
if (!df)
return NULL;
df->type = TYPE_ADF;
df->df_name = adf_name;
df->df_name_len = adf_name_len;
- df->parent = parent;
- llist_add_tail(&df->list, &parent->child_list);
add_filedesc(df, in, num);
return df;
@@ -187,6 +184,22 @@ osim_file_desc_find_name(struct osim_file_desc *parent, const char *name)
}
struct osim_file_desc *
+osim_file_desc_find_aid(struct osim_file_desc *parent, const uint8_t *aid, uint8_t aid_len)
+{
+ struct osim_file_desc *ofd;
+ llist_for_each_entry(ofd, &parent->child_list, list) {
+ if (ofd->type != TYPE_ADF)
+ continue;
+ if (aid_len > ofd->df_name_len)
+ continue;
+ if (!memcmp(ofd->df_name, aid, aid_len)) {
+ return ofd;
+ }
+ }
+ return NULL;
+}
+
+struct osim_file_desc *
osim_file_desc_find_fid(struct osim_file_desc *parent, uint16_t fid)
{
struct osim_file_desc *ofd;
@@ -213,6 +226,88 @@ osim_file_desc_find_sfid(struct osim_file_desc *parent, uint8_t sfid)
}
+/***********************************************************************
+ * Application Profiles + Applications
+ ***********************************************************************/
+
+static LLIST_HEAD(g_app_profiles);
+
+/*! Register an application profile. Typically called at early start-up. */
+void osim_app_profile_register(struct osim_card_app_profile *aprof)
+{
+ OSMO_ASSERT(!osim_app_profile_find_by_name(aprof->name));
+ OSMO_ASSERT(!osim_app_profile_find_by_aid(aprof->aid, aprof->aid_len));
+ llist_add_tail(&aprof->list, &g_app_profiles);
+}
+
+/*! Find any registered application profile based on its name (e.g. "ADF.USIM") */
+const struct osim_card_app_profile *
+osim_app_profile_find_by_name(const char *name)
+{
+ struct osim_card_app_profile *ap;
+
+ llist_for_each_entry(ap, &g_app_profiles, list) {
+ if (!strcmp(name, ap->name))
+ return ap;
+ }
+ return NULL;
+}
+
+/*! Find any registered application profile based on its AID */
+const struct osim_card_app_profile *
+osim_app_profile_find_by_aid(const uint8_t *aid, uint8_t aid_len)
+{
+ struct osim_card_app_profile *ap;
+
+ llist_for_each_entry(ap, &g_app_profiles, list) {
+ if (ap->aid_len > aid_len)
+ continue;
+ if (!memcmp(ap->aid, aid, ap->aid_len))
+ return ap;
+ }
+ return NULL;
+}
+
+struct osim_card_app_hdl *
+osim_card_hdl_find_app(struct osim_card_hdl *ch, const uint8_t *aid, uint8_t aid_len)
+{
+ struct osim_card_app_hdl *ah;
+
+ if (aid_len > MAX_AID_LEN)
+ return NULL;
+
+ llist_for_each_entry(ah, &ch->apps, list) {
+ if (!memcmp(ah->aid, aid, aid_len))
+ return ah;
+ }
+ return NULL;
+}
+
+/*! Add an application to a given card */
+int osim_card_hdl_add_app(struct osim_card_hdl *ch, const uint8_t *aid, uint8_t aid_len,
+ const char *label)
+{
+ struct osim_card_app_hdl *ah;
+
+ if (aid_len > MAX_AID_LEN)
+ return -EINVAL;
+
+ if (osim_card_hdl_find_app(ch, aid, aid_len))
+ return -EEXIST;
+
+ ah = talloc_zero(ch, struct osim_card_app_hdl);
+ if (!ah)
+ return -ENOMEM;
+
+ memcpy(ah->aid, aid, aid_len);
+ ah->aid_len = aid_len;
+ ah->prof = osim_app_profile_find_by_aid(ah->aid, ah->aid_len);
+ if (label)
+ ah->label = talloc_strdup(ah, label);
+ llist_add_tail(&ah->list, &ch->apps);
+ return 0;
+}
+
/*! Generate an APDU message and initialize APDU command header
* \param[in] cla CLASS byte
* \param[in] ins INSTRUCTION byte
@@ -268,14 +363,19 @@ struct msgb *osim_new_apdumsg(uint8_t cla, uint8_t ins, uint8_t p1,
}
-char *osim_print_sw_buf(char *buf, size_t buf_len, const struct osim_card_hdl *ch, uint16_t sw_in)
+char *osim_print_sw_buf(char *buf, size_t buf_len, const struct osim_chan_hdl *ch, uint16_t sw_in)
{
- const struct osim_card_sw *csw;
+ const struct osim_card_sw *csw = NULL;
- if (!ch || !ch->prof)
+ if (!ch)
goto ret_def;
- csw = osim_find_sw(ch->prof, sw_in);
+ if (ch->cur_app && ch->cur_app->prof)
+ csw = osim_app_profile_find_sw(ch->cur_app->prof, sw_in);
+
+ if (!csw && ch->card->prof)
+ csw = osim_cprof_find_sw(ch->card->prof, sw_in);
+
if (!csw)
goto ret_def;
@@ -298,13 +398,13 @@ ret_def:
return buf;
}
-char *osim_print_sw(const struct osim_card_hdl *ch, uint16_t sw_in)
+char *osim_print_sw(const struct osim_chan_hdl *ch, uint16_t sw_in)
{
static __thread char sw_print_buf[256];
return osim_print_sw_buf(sw_print_buf, sizeof(sw_print_buf), ch, sw_in);
}
-char *osim_print_sw_c(const void *ctx, const struct osim_card_hdl *ch, uint16_t sw_in)
+char *osim_print_sw_c(const void *ctx, const struct osim_chan_hdl *ch, uint16_t sw_in)
{
char *buf = talloc_size(ctx, 256);
if (!buf)
@@ -312,8 +412,8 @@ char *osim_print_sw_c(const void *ctx, const struct osim_card_hdl *ch, uint16_t
return osim_print_sw_buf(buf, 256, ch, sw_in);
}
-const struct osim_card_sw *osim_find_sw(const struct osim_card_profile *cp,
- uint16_t sw_in)
+/*! Find status word within given card profile */
+const struct osim_card_sw *osim_cprof_find_sw(const struct osim_card_profile *cp, uint16_t sw_in)
{
const struct osim_card_sw **sw_lists = cp->sws;
const struct osim_card_sw *sw_list, *sw;
@@ -327,10 +427,30 @@ const struct osim_card_sw *osim_find_sw(const struct osim_card_profile *cp,
return NULL;
}
-enum osim_card_sw_class osim_sw_class(const struct osim_card_profile *cp,
- uint16_t sw_in)
+/*! Find application-specific status word within given card application profile */
+const struct osim_card_sw *osim_app_profile_find_sw(const struct osim_card_app_profile *ap, uint16_t sw_in)
+{
+ const struct osim_card_sw *sw_list = ap->sw, *sw;
+
+ for (sw = sw_list; sw->code != 0 && sw->mask != 0; sw++) {
+ if ((sw_in & sw->mask) == sw->code)
+ return sw;
+ }
+ return NULL;
+}
+
+enum osim_card_sw_class osim_sw_class(const struct osim_chan_hdl *ch, uint16_t sw_in)
{
- const struct osim_card_sw *csw = osim_find_sw(cp, sw_in);
+ const struct osim_card_sw *csw = NULL;
+
+ OSMO_ASSERT(ch);
+ OSMO_ASSERT(ch->card);
+
+ if (ch->cur_app && ch->cur_app->prof)
+ csw = osim_app_profile_find_sw(ch->cur_app->prof, sw_in);
+
+ if (!csw && ch->card->prof)
+ csw = osim_cprof_find_sw(ch->card->prof, sw_in);
if (!csw)
return SW_CLS_NONE;
@@ -349,3 +469,12 @@ int default_decode(struct osim_decoded_data *dd,
return 0;
}
+
+int osim_init(void *ctx)
+{
+ osim_app_profile_register(osim_aprof_usim(ctx));
+ osim_app_profile_register(osim_aprof_isim(ctx));
+ osim_app_profile_register(osim_aprof_hpsim(ctx));
+
+ return 0;
+}
diff --git a/src/sim/reader.c b/src/sim/reader.c
index d1a9ae60..982b2eef 100644
--- a/src/sim/reader.c
+++ b/src/sim/reader.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -35,6 +31,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/sim/sim.h>
+#include "config.h"
#include "sim_int.h"
@@ -43,7 +40,7 @@ static int get_sw(struct msgb *resp)
{
int ret;
- if (!msgb_apdu_de(resp) || msgb_apdu_le(resp) < 2)
+ if (!resp->l2h || msgb_apdu_le(resp) < 2)
return -EIO;
ret = msgb_get_u16(resp);
@@ -122,7 +119,6 @@ transceive_again:
/* save SW */
sw = msgb_apdu_sw(tmsg);
- printf("sw = 0x%04x\n", sw);
msgb_apdu_sw(amsg) = sw;
switch (msgb_apdu_case(amsg)) {
@@ -242,9 +238,11 @@ struct osim_reader_hdl *osim_reader_open(enum osim_reader_driver driver, int idx
struct osim_reader_hdl *rh;
switch (driver) {
+#ifdef HAVE_PCSC
case OSIM_READER_DRV_PCSC:
ops = &pcsc_reader_ops;
break;
+#endif
default:
return NULL;
}
@@ -275,3 +273,24 @@ struct osim_card_hdl *osim_card_open(struct osim_reader_hdl *rh, enum osim_proto
return ch;
}
+
+int osim_card_reset(struct osim_card_hdl *card, bool cold_reset)
+{
+ struct osim_reader_hdl *rh = card->reader;
+
+ return rh->ops->card_reset(card, cold_reset);
+}
+
+int osim_card_close(struct osim_card_hdl *card)
+{
+ struct osim_reader_hdl *rh = card->reader;
+ int rc;
+
+ rc = rh->ops->card_close(card);
+
+ card->reader = NULL;
+ talloc_free(card);
+ rh->card = NULL;
+
+ return rc;
+}
diff --git a/src/sim/reader_pcsc.c b/src/sim/reader_pcsc.c
index f22103f4..c37072ee 100644
--- a/src/sim/reader_pcsc.c
+++ b/src/sim/reader_pcsc.c
@@ -17,10 +17,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -41,12 +37,9 @@
if (rv != SCARD_S_SUCCESS) { \
fprintf(stderr, text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv); \
goto end; \
-} else { \
- printf(text ": OK\n\n"); \
}
-
struct pcsc_reader_state {
SCARDCONTEXT hContext;
SCARDHANDLE hCard;
@@ -56,6 +49,27 @@ struct pcsc_reader_state {
char *name;
};
+static int pcsc_get_atr(struct osim_card_hdl *card)
+{
+ struct osim_reader_hdl *rh = card->reader;
+ struct pcsc_reader_state *st = rh->priv;
+ char pbReader[MAX_READERNAME];
+ DWORD dwReaderLen = sizeof(pbReader);
+ DWORD dwAtrLen = sizeof(card->atr);
+ DWORD dwState, dwProt;
+ long rc;
+
+ rc = SCardStatus(st->hCard, pbReader, &dwReaderLen, &dwState, &dwProt,
+ card->atr, &dwAtrLen);
+ PCSC_ERROR(rc, "SCardStatus");
+ card->atr_len = dwAtrLen;
+
+ return 0;
+
+end:
+ return -EIO;
+}
+
static struct osim_reader_hdl *pcsc_reader_open(int num, const char *id, void *ctx)
{
struct osim_reader_hdl *rh;
@@ -79,18 +93,22 @@ static struct osim_reader_hdl *pcsc_reader_open(int num, const char *id, void *c
rc = SCardListReaders(st->hContext, NULL, (LPSTR)&mszReaders, &dwReaders);
PCSC_ERROR(rc, "SCardListReaders");
+ /* SCARD_S_SUCCESS means there is at least one reader in the group */
num_readers = 0;
ptr = mszReaders;
- while (*ptr != '\0') {
+ while (*ptr != '\0' && num_readers != num) {
ptr += strlen(ptr)+1;
num_readers++;
}
- if (num_readers == 0)
+ if (num != num_readers) {
+ SCardFreeMemory(st->hContext, mszReaders);
goto end;
+ }
- st->name = talloc_strdup(rh, mszReaders);
+ st->name = talloc_strdup(rh, ptr);
st->dwActiveProtocol = -1;
+ SCardFreeMemory(st->hContext, mszReaders);
return rh;
end:
@@ -117,6 +135,7 @@ static struct osim_card_hdl *pcsc_card_open(struct osim_reader_hdl *rh,
card = talloc_zero(rh, struct osim_card_hdl);
INIT_LLIST_HEAD(&card->channels);
+ INIT_LLIST_HEAD(&card->apps);
card->reader = rh;
rh->card = card;
@@ -125,12 +144,42 @@ static struct osim_card_hdl *pcsc_card_open(struct osim_reader_hdl *rh,
chan->card = card;
llist_add(&chan->list, &card->channels);
+ pcsc_get_atr(card);
+
return card;
end:
return NULL;
}
+static int pcsc_card_reset(struct osim_card_hdl *card, bool cold_reset)
+{
+ struct pcsc_reader_state *st = card->reader->priv;
+ LONG rc;
+
+ rc = SCardReconnect(st->hCard, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0,
+ cold_reset ? SCARD_UNPOWER_CARD : SCARD_RESET_CARD,
+ &st->dwActiveProtocol);
+ PCSC_ERROR(rc, "SCardReconnect");
+
+ return 0;
+end:
+ return -EIO;
+}
+
+static int pcsc_card_close(struct osim_card_hdl *card)
+{
+ struct pcsc_reader_state *st = card->reader->priv;
+ LONG rc;
+
+ rc = SCardDisconnect(st->hCard, SCARD_UNPOWER_CARD);
+ PCSC_ERROR(rc, "SCardDisconnect");
+
+ return 0;
+end:
+ return -EIO;
+}
+
static int pcsc_transceive(struct osim_reader_hdl *rh, struct msgb *msg)
{
@@ -138,13 +187,10 @@ static int pcsc_transceive(struct osim_reader_hdl *rh, struct msgb *msg)
DWORD rlen = msgb_tailroom(msg);
LONG rc;
- printf("TX: %s\n", osmo_hexdump(msg->data, msg->len));
-
rc = SCardTransmit(st->hCard, st->pioSendPci, msg->data, msgb_length(msg),
&st->pioRecvPci, msg->tail, &rlen);
PCSC_ERROR(rc, "SCardEndTransaction");
- printf("RX: %s\n", osmo_hexdump(msg->tail, rlen));
msgb_put(msg, rlen);
msgb_apdu_le(msg) = rlen;
@@ -157,6 +203,8 @@ const struct osim_reader_ops pcsc_reader_ops = {
.name = "PC/SC",
.reader_open = pcsc_reader_open,
.card_open = pcsc_card_open,
+ .card_reset = pcsc_card_reset,
+ .card_close = pcsc_card_close,
.transceive = pcsc_transceive,
};
diff --git a/src/sim/sim_int.h b/src/sim/sim_int.h
index 885011ed..a96a9cd2 100644
--- a/src/sim/sim_int.h
+++ b/src/sim/sim_int.h
@@ -12,8 +12,6 @@ struct osim_decoded_element *
element_alloc_sub(struct osim_decoded_element *ee, const char *name,
enum osim_element_type type, enum osim_element_repr repr);
-extern const struct osim_card_sw ts102221_uicc_sw[0];
-
int default_decode(struct osim_decoded_data *dd,
const struct osim_file_desc *desc,
int len, uint8_t *data);
@@ -26,11 +24,15 @@ add_df_with_ef(struct osim_file_desc *parent,
const struct osim_file_desc *in, int num);
struct osim_file_desc *
-add_adf_with_ef(struct osim_file_desc *parent,
- const uint8_t *adf_name, uint8_t adf_name_len,
- const char *name, const struct osim_file_desc *in,
- int num);
+alloc_adf_with_ef(void *ctx, const uint8_t *adf_name, uint8_t adf_name_len,
+ const char *name, const struct osim_file_desc *in, int num);
extern const struct osim_reader_ops pcsc_reader_ops;
+void osim_app_profile_register(struct osim_card_app_profile *aprof);
+
+struct osim_card_app_profile *osim_aprof_usim(void *ctx);
+struct osim_card_app_profile *osim_aprof_isim(void *ctx);
+struct osim_card_app_profile *osim_aprof_hpsim(void *ctx);
+
#endif
diff --git a/src/socket.c b/src/socket.c
deleted file mode 100644
index 9b1d30ec..00000000
--- a/src/socket.c
+++ /dev/null
@@ -1,1320 +0,0 @@
-/*
- * (C) 2011-2017 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include "../config.h"
-
-/*! \addtogroup socket
- * @{
- * Osmocom socket convenience functions.
- *
- * \file socket.c */
-
-#ifdef HAVE_SYS_SOCKET_H
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/utils.h>
-
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <string.h>
-#include <errno.h>
-#include <netdb.h>
-#include <ifaddrs.h>
-
-#ifdef HAVE_LIBSCTP
-#include <netinet/sctp.h>
-#endif
-
-static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto,
- const char *host, uint16_t port, bool passive)
-{
- struct addrinfo hints, *result, *rp;
- char portbuf[6];
- int rc;
-
- snprintf(portbuf, sizeof(portbuf), "%u", port);
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = family;
- if (type == SOCK_RAW) {
- /* Workaround for glibc, that returns EAI_SERVICE (-8) if
- * SOCK_RAW and IPPROTO_GRE is used.
- * http://sourceware.org/bugzilla/show_bug.cgi?id=15015
- */
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_protocol = IPPROTO_UDP;
- } else {
- hints.ai_socktype = type;
- hints.ai_protocol = proto;
- }
-
- if (passive)
- hints.ai_flags |= AI_PASSIVE;
-
- rc = getaddrinfo(host, portbuf, &hints, &result);
- if (rc != 0) {
- LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
- host, port, strerror(errno));
- return NULL;
- }
-
- for (rp = result; rp != NULL; rp = rp->ai_next) {
- /* Workaround for glibc again */
- if (type == SOCK_RAW) {
- rp->ai_socktype = SOCK_RAW;
- rp->ai_protocol = proto;
- }
- }
-
- return result;
-}
-
-#ifdef HAVE_LIBSCTP
-/*! Retrieve an array of addrinfo with specified hints, one for each host in the hosts array.
- * \param[out] addrinfo array of addrinfo pointers, will be filled by the function on success.
- * Its size must be at least the one of hosts.
- * \param[in] family Socket family like AF_INET, AF_INET6.
- * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM.
- * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP.
- * \param[in] hosts array of char pointers (strings) containing the addresses to query.
- * \param[in] host_cnt length of the hosts array (in items).
- * \param[in] port port number in host byte order.
- * \param[in] passive whether to include the AI_PASSIVE flag in getaddrinfo() hints.
- * \returns 0 is returned on success together with a filled addrinfo array; negative on error
- */
-static int addrinfo_helper_multi(struct addrinfo **addrinfo, uint16_t family, uint16_t type, uint8_t proto,
- const char **hosts, size_t host_cnt, uint16_t port, bool passive)
-{
- int i, j;
-
- for (i = 0; i < host_cnt; i++) {
- addrinfo[i] = addrinfo_helper(family, type, proto, hosts[i], port, passive);
- if (!addrinfo[i]) {
- for (j = 0; j < i; j++)
- freeaddrinfo(addrinfo[j]);
- return -EINVAL;
- }
- }
- return 0;
-}
-#endif /* HAVE_LIBSCTP*/
-
-static int socket_helper(const struct addrinfo *rp, unsigned int flags)
-{
- int sfd, on = 1;
-
- sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- if (sfd == -1) {
- LOGP(DLGLOBAL, LOGL_ERROR,
- "unable to create socket: %s\n", strerror(errno));
- return sfd;
- }
- if (flags & OSMO_SOCK_F_NONBLOCK) {
- if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
- LOGP(DLGLOBAL, LOGL_ERROR,
- "cannot set this socket unblocking: %s\n",
- strerror(errno));
- close(sfd);
- sfd = -EINVAL;
- }
- }
- return sfd;
-}
-
-#ifdef HAVE_LIBSCTP
-/* Fill buf with a string representation of the address set, in the form:
- * buf_len == 0: "()"
- * buf_len == 1: "hostA"
- * buf_len >= 2: (hostA|hostB|...|...)
- */
-static int multiaddr_snprintf(char* buf, size_t buf_len, const char **hosts, size_t host_cnt)
-{
- int len = 0, offset = 0, rem = buf_len;
- int ret, i;
- char *after;
-
- if (buf_len < 3)
- return -EINVAL;
-
- if (host_cnt != 1) {
- ret = snprintf(buf, rem, "(");
- if (ret < 0)
- return ret;
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
- }
- for (i = 0; i < host_cnt; i++) {
- if (host_cnt == 1)
- after = "";
- else
- after = (i == (host_cnt - 1)) ? ")" : "|";
- ret = snprintf(buf + offset, rem, "%s%s", hosts[i] ? : "0.0.0.0", after);
- OSMO_SNPRINTF_RET(ret, rem, offset, len);
- }
-
- return len;
-}
-#endif /* HAVE_LIBSCTP */
-
-static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags)
-{
- int rc;
-
- /* Make sure to call 'listen' on a bound, connection-oriented sock */
- if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) {
- switch (type) {
- case SOCK_STREAM:
- case SOCK_SEQPACKET:
- rc = listen(fd, 10);
- if (rc < 0) {
- LOGP(DLGLOBAL, LOGL_ERROR, "unable to listen on socket: %s\n",
- strerror(errno));
- return rc;
- }
- break;
- }
- }
-
- if (flags & OSMO_SOCK_F_NO_MCAST_LOOP) {
- rc = osmo_sock_mcast_loop_set(fd, false);
- if (rc < 0) {
- LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable multicast loop: %s\n",
- strerror(errno));
- return rc;
- }
- }
-
- if (flags & OSMO_SOCK_F_NO_MCAST_ALL) {
- rc = osmo_sock_mcast_all_set(fd, false);
- if (rc < 0) {
- LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable receive of all multicast: %s\n",
- strerror(errno));
- /* do not abort here, as this is just an
- * optional additional optimization that only
- * exists on Linux only */
- }
- }
- return 0;
-}
-
-/*! Initialize a socket (including bind and/or connect)
- * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
- * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
- * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
- * \param[in] local_host local host name or IP address in string form
- * \param[in] local_port local port number in host byte order
- * \param[in] remote_host remote host name or IP address in string form
- * \param[in] remote_port remote port number in host byte order
- * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
- * \returns socket file descriptor on success; negative on error
- *
- * This function creates a new socket of the designated \a family, \a
- * type and \a proto and optionally binds it to the \a local_host and \a
- * local_port as well as optionally connects it to the \a remote_host
- * and \q remote_port, depending on the value * of \a flags parameter.
- *
- * As opposed to \ref osmo_sock_init(), this function allows to combine
- * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This
- * is useful if you want to connect to a remote host/port, but still
- * want to bind that socket to either a specific local alias IP and/or a
- * specific local source port.
- *
- * You must specify either \ref OSMO_SOCK_F_BIND, or \ref
- * OSMO_SOCK_F_CONNECT, or both.
- *
- * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to
- * non-blocking mode.
- */
-int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
- const char *local_host, uint16_t local_port,
- const char *remote_host, uint16_t remote_port, unsigned int flags)
-{
- struct addrinfo *result, *rp;
- int sfd = -1, rc, on = 1;
-
- if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
- LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
- "BIND or CONNECT flags\n");
- return -EINVAL;
- }
-
- /* figure out local side of socket */
- if (flags & OSMO_SOCK_F_BIND) {
- result = addrinfo_helper(family, type, proto, local_host, local_port, true);
- if (!result)
- return -EINVAL;
-
- for (rp = result; rp != NULL; rp = rp->ai_next) {
- sfd = socket_helper(rp, flags);
- if (sfd < 0)
- continue;
-
- if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) {
- rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
- &on, sizeof(on));
- if (rc < 0) {
- LOGP(DLGLOBAL, LOGL_ERROR,
- "cannot setsockopt socket:"
- " %s:%u: %s\n",
- local_host, local_port,
- strerror(errno));
- close(sfd);
- continue;
- }
- }
-
- if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
- LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
- local_host, local_port, strerror(errno));
- close(sfd);
- continue;
- }
- break;
- }
- freeaddrinfo(result);
- if (rp == NULL) {
- LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n",
- local_host, local_port);
- return -ENODEV;
- }
- }
-
- /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it
- was already closed and func returned. If OSMO_SOCK_F_BIND is not
- set, then sfd = -1 */
-
- /* figure out remote side of socket */
- if (flags & OSMO_SOCK_F_CONNECT) {
- result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
- if (!result) {
- if (sfd >= 0)
- close(sfd);
- return -EINVAL;
- }
-
- for (rp = result; rp != NULL; rp = rp->ai_next) {
- if (sfd < 0) {
- sfd = socket_helper(rp, flags);
- if (sfd < 0)
- continue;
- }
-
- rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
- if (rc != 0 && errno != EINPROGRESS) {
- LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
- remote_host, remote_port, strerror(errno));
- /* We want to maintain the bind socket if bind was enabled */
- if (!(flags & OSMO_SOCK_F_BIND)) {
- close(sfd);
- sfd = -1;
- }
- continue;
- }
- break;
- }
- freeaddrinfo(result);
- if (rp == NULL) {
- LOGP(DLGLOBAL, LOGL_ERROR, "no suitable remote addr found for: %s:%u\n",
- remote_host, remote_port);
- if (sfd >= 0)
- close(sfd);
- return -ENODEV;
- }
- }
-
- rc = osmo_sock_init_tail(sfd, type, flags);
- if (rc < 0) {
- close(sfd);
- sfd = -1;
- }
-
- return sfd;
-}
-
-#ifdef HAVE_LIBSCTP
-
-
-/* Build array of addresses taking first addrinfo result of the requested family
- * for each host in hosts. addrs4 or addrs6 are filled based on family type. */
-static int addrinfo_to_sockaddr(uint16_t family, const struct addrinfo **result,
- const char **hosts, int host_cont,
- struct sockaddr_in *addrs4, struct sockaddr_in6 *addrs6) {
- size_t host_idx;
- const struct addrinfo *rp;
- OSMO_ASSERT(family == AF_INET || family == AF_INET6);
-
- for (host_idx = 0; host_idx < host_cont; host_idx++) {
- for (rp = result[host_idx]; rp != NULL; rp = rp->ai_next) {
- if (rp->ai_family != family)
- continue;
- if (family == AF_INET)
- memcpy(&addrs4[host_idx], rp->ai_addr, sizeof(addrs4[host_idx]));
- else
- memcpy(&addrs6[host_idx], rp->ai_addr, sizeof(addrs6[host_idx]));
- break;
- }
- if (!rp) { /* No addr could be bound for this host! */
- LOGP(DLGLOBAL, LOGL_ERROR, "No suitable remote address found for host: %s\n",
- hosts[host_idx]);
- return -ENODEV;
- }
- }
- return 0;
-}
-
-/*! Initialize a socket (including bind and/or connect) with multiple local or remote addresses.
- * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
- * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
- * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
- * \param[in] local_hosts array of char pointers (strings), each containing local host name or IP address in string form
- * \param[in] local_hosts_cnt length of local_hosts (in items)
- * \param[in] local_port local port number in host byte order
- * \param[in] remote_host array of char pointers (strings), each containing remote host name or IP address in string form
- * \param[in] remote_hosts_cnt length of remote_hosts (in items)
- * \param[in] remote_port remote port number in host byte order
- * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
- * \returns socket file descriptor on success; negative on error
- *
- * This function is similar to \ref osmo_sock_init2(), but can be passed an
- * array of local or remote addresses for protocols supporting multiple
- * addresses per socket, like SCTP (currently only one supported). This function
- * should not be used by protocols not supporting this kind of features, but
- * rather \ref osmo_sock_init2() should be used instead.
- * See \ref osmo_sock_init2() for more information on flags and general behavior.
- */
-int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
- const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
- const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port,
- unsigned int flags)
-
-{
- struct addrinfo *result[OSMO_SOCK_MAX_ADDRS];
- int sfd = -1, rc, on = 1;
- int i;
- struct sockaddr_in addrs4[OSMO_SOCK_MAX_ADDRS];
- struct sockaddr_in6 addrs6[OSMO_SOCK_MAX_ADDRS];
- struct sockaddr *addrs;
- char strbuf[512];
-
- /* TODO: So far this function is only aimed for SCTP, but could be
- reused in the future for other protocols with multi-addr support */
- if (proto != IPPROTO_SCTP)
- return -ENOTSUP;
-
- /* TODO: Let's not support AF_UNSPEC for now. sctp_bindx() actually
- supports binding both types of addresses on a AF_INET6 soscket, but
- that would mean we could get both AF_INET and AF_INET6 addresses for
- each host, and makes complexity of this function increase a lot since
- we'd need to find out which subsets to use, use v4v6 mapped socket,
- etc. */
- if (family == AF_UNSPEC)
- return -ENOTSUP;
-
- if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
- LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
- "BIND or CONNECT flags\n");
- return -EINVAL;
- }
-
- if (((flags & OSMO_SOCK_F_BIND) && !local_hosts_cnt) ||
- ((flags & OSMO_SOCK_F_CONNECT) && !remote_hosts_cnt) ||
- local_hosts_cnt > OSMO_SOCK_MAX_ADDRS ||
- remote_hosts_cnt > OSMO_SOCK_MAX_ADDRS)
- return -EINVAL;
-
- /* figure out local side of socket */
- if (flags & OSMO_SOCK_F_BIND) {
- rc = addrinfo_helper_multi(result, family, type, proto, local_hosts,
- local_hosts_cnt, local_port, true);
- if (rc < 0)
- return -EINVAL;
-
- /* Since addrinfo_helper sets ai_family, socktype and
- ai_protocol in hints, we know all results will use same
- values, so simply pick the first one and pass it to create
- the socket:
- */
- sfd = socket_helper(result[0], flags);
- if (sfd < 0) {
- for (i = 0; i < local_hosts_cnt; i++)
- freeaddrinfo(result[i]);
- return sfd;
- }
-
- /* Since so far we only allow IPPROTO_SCTP in this function,
- no need to check below for "proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR" */
- rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
- &on, sizeof(on));
- if (rc < 0) {
- multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
- LOGP(DLGLOBAL, LOGL_ERROR,
- "cannot setsockopt socket:"
- " %s:%u: %s\n",
- strbuf, local_port,
- strerror(errno));
- for (i = 0; i < local_hosts_cnt; i++)
- freeaddrinfo(result[i]);
- close(sfd);
- return rc;
- }
-
- /* Build array of addresses taking first of same family for each host.
- TODO: Ideally we should use backtracking storing last used
- indexes and trying next combination if connect() fails .*/
- rc = addrinfo_to_sockaddr(family, (const struct addrinfo **)result,
- local_hosts, local_hosts_cnt, addrs4, addrs6);
- if (rc < 0) {
- for (i = 0; i < local_hosts_cnt; i++)
- freeaddrinfo(result[i]);
- close(sfd);
- return -ENODEV;
- }
-
- if (family == AF_INET)
- addrs = (struct sockaddr *)addrs4;
- else
- addrs = (struct sockaddr *)addrs6;
- if (sctp_bindx(sfd, addrs, local_hosts_cnt, SCTP_BINDX_ADD_ADDR) == -1) {
- multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt);
- LOGP(DLGLOBAL, LOGL_NOTICE, "unable to bind socket: %s:%u: %s\n",
- strbuf, local_port, strerror(errno));
- for (i = 0; i < local_hosts_cnt; i++)
- freeaddrinfo(result[i]);
- close(sfd);
- return -ENODEV;
- }
- for (i = 0; i < local_hosts_cnt; i++)
- freeaddrinfo(result[i]);
- }
-
- /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it
- was already closed and func returned. If OSMO_SOCK_F_BIND is not
- set, then sfd = -1 */
-
- /* figure out remote side of socket */
- if (flags & OSMO_SOCK_F_CONNECT) {
- rc = addrinfo_helper_multi(result, family, type, proto, remote_hosts,
- remote_hosts_cnt, remote_port, false);
- if (rc < 0) {
- if (sfd >= 0)
- close(sfd);
- return -EINVAL;
- }
-
- if (sfd < 0) {
- /* Since addrinfo_helper sets ai_family, socktype and
- ai_protocol in hints, we know all results will use same
- values, so simply pick the first one and pass it to create
- the socket:
- */
- sfd = socket_helper(result[0], flags);
- if (sfd < 0) {
- for (i = 0; i < remote_hosts_cnt; i++)
- freeaddrinfo(result[i]);
- return sfd;
- }
- }
-
- /* Build array of addresses taking first of same family for each host.
- TODO: Ideally we should use backtracking storing last used
- indexes and trying next combination if connect() fails .*/
- rc = addrinfo_to_sockaddr(family, (const struct addrinfo **)result,
- remote_hosts, remote_hosts_cnt, addrs4, addrs6);
- if (rc < 0) {
- for (i = 0; i < remote_hosts_cnt; i++)
- freeaddrinfo(result[i]);
- close(sfd);
- return -ENODEV;
- }
-
- if (family == AF_INET)
- addrs = (struct sockaddr *)addrs4;
- else
- addrs = (struct sockaddr *)addrs6;
- rc = sctp_connectx(sfd, addrs, remote_hosts_cnt, NULL);
- if (rc != 0 && errno != EINPROGRESS) {
- multiaddr_snprintf(strbuf, sizeof(strbuf), remote_hosts, remote_hosts_cnt);
- LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
- strbuf, remote_port, strerror(errno));
- for (i = 0; i < remote_hosts_cnt; i++)
- freeaddrinfo(result[i]);
- close(sfd);
- return -ENODEV;
- }
- for (i = 0; i < remote_hosts_cnt; i++)
- freeaddrinfo(result[i]);
- }
-
- rc = osmo_sock_init_tail(sfd, type, flags);
- if (rc < 0) {
- close(sfd);
- sfd = -1;
- }
-
- return sfd;
-}
-#endif /* HAVE_LIBSCTP */
-
-/*! Initialize a socket (including bind/connect)
- * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
- * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
- * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
- * \param[in] host remote host name or IP address in string form
- * \param[in] port remote port number in host byte order
- * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
- * \returns socket file descriptor on success; negative on error
- *
- * This function creates a new socket of the designated \a family, \a
- * type and \a proto and optionally binds or connects it, depending on
- * the value of \a flags parameter.
- */
-int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
- const char *host, uint16_t port, unsigned int flags)
-{
- struct addrinfo *result, *rp;
- int sfd, rc, on = 1;
-
- if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
- (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
- LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:"
- " %s:%u\n", host, port);
- return -EINVAL;
- }
-
- result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND);
- if (!result) {
- LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
- host, port, strerror(errno));
- return -EINVAL;
- }
-
- for (rp = result; rp != NULL; rp = rp->ai_next) {
- sfd = socket_helper(rp, flags);
- if (sfd == -1)
- continue;
-
- if (flags & OSMO_SOCK_F_CONNECT) {
- rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
- if (rc != 0 && errno != EINPROGRESS) {
- close(sfd);
- continue;
- }
- } else {
- if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) {
- rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
- &on, sizeof(on));
- if (rc < 0) {
- LOGP(DLGLOBAL, LOGL_ERROR,
- "cannot setsockopt socket:"
- " %s:%u: %s\n",
- host, port, strerror(errno));
- close(sfd);
- continue;
- }
- }
- if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
- LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket:"
- "%s:%u: %s\n",
- host, port, strerror(errno));
- close(sfd);
- continue;
- }
- }
- break;
- }
- freeaddrinfo(result);
-
- if (rp == NULL) {
- LOGP(DLGLOBAL, LOGL_ERROR, "no suitable addr found for: %s:%u\n",
- host, port);
- return -ENODEV;
- }
-
- if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) {
- rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
- if (rc < 0) {
- LOGP(DLGLOBAL, LOGL_ERROR,
- "cannot setsockopt socket: %s:%u: %s\n", host,
- port, strerror(errno));
- close(sfd);
- sfd = -1;
- }
- }
-
- rc = osmo_sock_init_tail(sfd, type, flags);
- if (rc < 0) {
- close(sfd);
- sfd = -1;
- }
-
- return sfd;
-}
-
-/*! fill \ref osmo_fd for a give sfd
- * \param[out] ofd file descriptor (will be filled in)
- * \param[in] sfd socket file descriptor
- * \returns socket fd on success; negative on error
- *
- * This function fills the \a ofd structure.
- */
-static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
-{
- int rc;
-
- if (sfd < 0)
- return sfd;
-
- ofd->fd = sfd;
- ofd->when = OSMO_FD_READ;
-
- rc = osmo_fd_register(ofd);
- if (rc < 0) {
- close(sfd);
- return rc;
- }
-
- return sfd;
-}
-
-/*! Initialize a socket and fill \ref osmo_fd
- * \param[out] ofd file descriptor (will be filled in)
- * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
- * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
- * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
- * \param[in] host remote host name or IP address in string form
- * \param[in] port remote port number in host byte order
- * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
- * \returns socket fd on success; negative on error
- *
- * This function creates (and optionall binds/connects) a socket using
- * \ref osmo_sock_init, but also fills the \a ofd structure.
- */
-int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
- const char *host, uint16_t port, unsigned int flags)
-{
- return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
-}
-
-/*! Initialize a socket and fill \ref osmo_fd
- * \param[out] ofd file descriptor (will be filled in)
- * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
- * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
- * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
- * \param[in] local_host local host name or IP address in string form
- * \param[in] local_port local port number in host byte order
- * \param[in] remote_host remote host name or IP address in string form
- * \param[in] remote_port remote port number in host byte order
- * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
- * \returns socket fd on success; negative on error
- *
- * This function creates (and optionall binds/connects) a socket using
- * \ref osmo_sock_init2, but also fills the \a ofd structure.
- */
-int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
- const char *local_host, uint16_t local_port,
- const char *remote_host, uint16_t remote_port, unsigned int flags)
-{
- return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
- local_port, remote_host, remote_port, flags));
-}
-
-/*! Initialize a socket and fill \ref sockaddr
- * \param[out] ss socket address (will be filled in)
- * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
- * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
- * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
- * \returns socket fd on success; negative on error
- *
- * This function creates (and optionall binds/connects) a socket using
- * \ref osmo_sock_init, but also fills the \a ss structure.
- */
-int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
- uint8_t proto, unsigned int flags)
-{
- char host[NI_MAXHOST];
- uint16_t port;
- struct sockaddr_in *sin;
- struct sockaddr_in6 *sin6;
- int s, sa_len;
-
- /* determine port and host from ss */
- switch (ss->sa_family) {
- case AF_INET:
- sin = (struct sockaddr_in *) ss;
- sa_len = sizeof(struct sockaddr_in);
- port = ntohs(sin->sin_port);
- break;
- case AF_INET6:
- sin6 = (struct sockaddr_in6 *) ss;
- sa_len = sizeof(struct sockaddr_in6);
- port = ntohs(sin6->sin6_port);
- break;
- default:
- return -EINVAL;
- }
-
- s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
- NULL, 0, NI_NUMERICHOST);
- if (s != 0) {
- LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:"
- " %s\n", strerror(errno));
- return s;
- }
-
- return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
-}
-
-static int sockaddr_equal(const struct sockaddr *a,
- const struct sockaddr *b, unsigned int len)
-{
- struct sockaddr_in *sin_a, *sin_b;
- struct sockaddr_in6 *sin6_a, *sin6_b;
-
- if (a->sa_family != b->sa_family)
- return 0;
-
- switch (a->sa_family) {
- case AF_INET:
- sin_a = (struct sockaddr_in *)a;
- sin_b = (struct sockaddr_in *)b;
- if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
- sizeof(struct in_addr)))
- return 1;
- break;
- case AF_INET6:
- sin6_a = (struct sockaddr_in6 *)a;
- sin6_b = (struct sockaddr_in6 *)b;
- if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
- sizeof(struct in6_addr)))
- return 1;
- break;
- }
- return 0;
-}
-
-/*! Determine if the given address is a local address
- * \param[in] addr Socket Address
- * \param[in] addrlen Length of socket address in bytes
- * \returns 1 if address is local, 0 otherwise.
- */
-int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
-{
- struct ifaddrs *ifaddr, *ifa;
-
- if (getifaddrs(&ifaddr) == -1) {
- LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
- " %s\n", strerror(errno));
- return -EIO;
- }
-
- for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
- if (!ifa->ifa_addr)
- continue;
- if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) {
- freeifaddrs(ifaddr);
- return 1;
- }
- }
-
- freeifaddrs(ifaddr);
- return 0;
-}
-
-/*! Convert sockaddr_in to IP address as char string and port as uint16_t.
- * \param[out] addr String buffer to write IP address to, or NULL.
- * \param[out] addr_len Size of \a addr.
- * \param[out] port Pointer to uint16_t to write the port number to, or NULL.
- * \param[in] sin Sockaddr to convert.
- * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL.
- */
-size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
- const struct sockaddr_in *sin)
-{
- if (port)
- *port = ntohs(sin->sin_port);
-
- if (addr)
- return osmo_strlcpy(addr, inet_ntoa(sin->sin_addr), addr_len);
-
- return 0;
-}
-
-/*! Convert sockaddr to IP address as char string and port as uint16_t.
- * \param[out] addr String buffer to write IP address to, or NULL.
- * \param[out] addr_len Size of \a addr.
- * \param[out] port Pointer to uint16_t to write the port number to, or NULL.
- * \param[in] sa Sockaddr to convert.
- * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL.
- */
-unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
- const struct sockaddr *sa)
-{
- const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
-
- return osmo_sockaddr_in_to_str_and_uint(addr, addr_len, port, sin);
-}
-
-/*! Initialize a unix domain socket (including bind/connect)
- * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
- * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
- * \param[in] socket_path path to identify the socket
- * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
- * \returns socket fd on success; negative on error
- *
- * This function creates a new unix domain socket, \a
- * type and \a proto and optionally binds or connects it, depending on
- * the value of \a flags parameter.
- */
-#if defined(__clang__) && defined(SUN_LEN)
-__attribute__((no_sanitize("undefined")))
-#endif
-int osmo_sock_unix_init(uint16_t type, uint8_t proto,
- const char *socket_path, unsigned int flags)
-{
- struct sockaddr_un local;
- int sfd, rc, on = 1;
- unsigned int namelen;
-
- if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
- (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
- return -EINVAL;
-
- local.sun_family = AF_UNIX;
- /* When an AF_UNIX socket is bound, sun_path should be NUL-terminated. See unix(7) man page. */
- if (osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path)) >= sizeof(local.sun_path)) {
- LOGP(DLGLOBAL, LOGL_ERROR, "Socket path exceeds maximum length of %zd bytes: %s\n",
- sizeof(local.sun_path), socket_path);
- return -ENOSPC;
- }
-
-#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
-
- sfd = socket(AF_UNIX, type, proto);
- if (sfd < 0)
- return -1;
-
- if (flags & OSMO_SOCK_F_CONNECT) {
- rc = connect(sfd, (struct sockaddr *)&local, namelen);
- if (rc < 0)
- goto err;
- } else {
- unlink(local.sun_path);
- rc = bind(sfd, (struct sockaddr *)&local, namelen);
- if (rc < 0)
- goto err;
- }
-
- if (flags & OSMO_SOCK_F_NONBLOCK) {
- if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
- LOGP(DLGLOBAL, LOGL_ERROR,
- "cannot set this socket unblocking: %s\n",
- strerror(errno));
- close(sfd);
- return -EINVAL;
- }
- }
-
- rc = osmo_sock_init_tail(sfd, type, flags);
- if (rc < 0) {
- close(sfd);
- sfd = -1;
- }
-
- return sfd;
-err:
- close(sfd);
- return -1;
-}
-
-/*! Initialize a unix domain socket and fill \ref osmo_fd
- * \param[out] ofd file descriptor (will be filled in)
- * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
- * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
- * \param[in] socket_path path to identify the socket
- * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
- * \returns socket fd on success; negative on error
- *
- * This function creates (and optionally binds/connects) a socket
- * using osmo_sock_unix_init, but also fills the ofd structure.
- */
-int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
- const char *socket_path, unsigned int flags)
-{
- return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
-}
-
-/*! Get the IP and/or port number on socket in separate string buffers.
- * \param[in] fd file descriptor of socket
- * \param[out] ip IP address (will be filled in when not NULL)
- * \param[in] ip_len length of the ip buffer
- * \param[out] port number (will be filled in when not NULL)
- * \param[in] port_len length of the port buffer
- * \param[in] local (true) or remote (false) name will get looked at
- * \returns 0 on success; negative otherwise
- */
-int osmo_sock_get_ip_and_port(int fd, char *ip, size_t ip_len, char *port, size_t port_len, bool local)
-{
- struct sockaddr sa;
- socklen_t len = sizeof(sa);
- char ipbuf[INET6_ADDRSTRLEN], portbuf[6];
- int rc;
-
- rc = local ? getsockname(fd, &sa, &len) : getpeername(fd, &sa, &len);
- if (rc < 0)
- return rc;
-
- rc = getnameinfo(&sa, len, ipbuf, sizeof(ipbuf),
- portbuf, sizeof(portbuf),
- NI_NUMERICHOST | NI_NUMERICSERV);
- if (rc < 0)
- return rc;
-
- if (ip)
- strncpy(ip, ipbuf, ip_len);
- if (port)
- strncpy(port, portbuf, port_len);
- return 0;
-}
-
-/*! Get local IP address on socket
- * \param[in] fd file descriptor of socket
- * \param[out] ip IP address (will be filled in)
- * \param[in] len length of the output buffer
- * \returns 0 on success; negative otherwise
- */
-int osmo_sock_get_local_ip(int fd, char *ip, size_t len)
-{
- return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, true);
-}
-
-/*! Get local port on socket
- * \param[in] fd file descriptor of socket
- * \param[out] port number (will be filled in)
- * \param[in] len length of the output buffer
- * \returns 0 on success; negative otherwise
- */
-int osmo_sock_get_local_ip_port(int fd, char *port, size_t len)
-{
- return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, true);
-}
-
-/*! Get remote IP address on socket
- * \param[in] fd file descriptor of socket
- * \param[out] ip IP address (will be filled in)
- * \param[in] len length of the output buffer
- * \returns 0 on success; negative otherwise
- */
-int osmo_sock_get_remote_ip(int fd, char *ip, size_t len)
-{
- return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, false);
-}
-
-/*! Get remote port on socket
- * \param[in] fd file descriptor of socket
- * \param[out] port number (will be filled in)
- * \param[in] len length of the output buffer
- * \returns 0 on success; negative otherwise
- */
-int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len)
-{
- return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, false);
-}
-
-/*! Get address/port information on socket in dyn-alloc string like "(r=1.2.3.4:5<->l=6.7.8.9:10)".
- * Usually, it is better to use osmo_sock_get_name2() for a static string buffer or osmo_sock_get_name_buf() for a
- * caller provided string buffer, to avoid the dynamic talloc allocation.
- * \param[in] ctx talloc context from which to allocate string buffer
- * \param[in] fd file descriptor of socket
- * \returns string identifying the connection of this socket, talloc'd from ctx.
- */
-char *osmo_sock_get_name(const void *ctx, int fd)
-{
- char str[OSMO_SOCK_NAME_MAXLEN];
- int rc;
- rc = osmo_sock_get_name_buf(str, sizeof(str), fd);
- if (rc <= 0)
- return NULL;
- return talloc_asprintf(ctx, "(%s)", str);
-}
-
-/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10".
- * This does not include braces like osmo_sock_get_name().
- * \param[out] str Destination string buffer.
- * \param[in] str_len sizeof(str).
- * \param[in] fd File descriptor of socket.
- * \return String length as returned by snprintf(), or negative on error.
- */
-int osmo_sock_get_name_buf(char *str, size_t str_len, int fd)
-{
- char hostbuf_l[INET6_ADDRSTRLEN], hostbuf_r[INET6_ADDRSTRLEN];
- char portbuf_l[6], portbuf_r[6];
- int rc;
-
- /* get local */
- if ((rc = osmo_sock_get_ip_and_port(fd, hostbuf_l, sizeof(hostbuf_l), portbuf_l, sizeof(portbuf_l), true))) {
- osmo_strlcpy(str, "<error-in-getsockname>", str_len);
- return rc;
- }
-
- /* get remote */
- if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) != 0)
- return snprintf(str, str_len, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l);
-
- return snprintf(str, str_len, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l);
-}
-
-/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
- * This does not include braces like osmo_sock_get_name().
- * \param[in] fd File descriptor of socket.
- * \return Static string buffer containing the result.
- */
-const char *osmo_sock_get_name2(int fd)
-{
- static __thread char str[OSMO_SOCK_NAME_MAXLEN];
- osmo_sock_get_name_buf(str, sizeof(str), fd);
- return str;
-}
-
-/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
- * This does not include braces like osmo_sock_get_name().
- * \param[in] fd File descriptor of socket.
- * \return Static string buffer containing the result.
- */
-char *osmo_sock_get_name2_c(const void *ctx, int fd)
-{
- char *str = talloc_size(ctx, OSMO_SOCK_NAME_MAXLEN);
- if (!str)
- return NULL;
- osmo_sock_get_name_buf(str, OSMO_SOCK_NAME_MAXLEN, fd);
- return str;
-}
-
-static int sock_get_domain(int fd)
-{
- int domain;
-#ifdef SO_DOMAIN
- socklen_t dom_len = sizeof(domain);
- int rc;
-
- rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
- if (rc < 0)
- return rc;
-#else
- /* This of course sucks, but what shall we do on OSs like
- * FreeBSD that don't seem to expose a method by which one can
- * learn the address family of a socket? */
- domain = AF_INET;
-#endif
- return domain;
-}
-
-
-/*! Activate or de-activate local loop-back of transmitted multicast packets
- * \param[in] fd file descriptor of related socket
- * \param[in] enable Enable (true) or disable (false) loop-back
- * \returns 0 on success; negative otherwise */
-int osmo_sock_mcast_loop_set(int fd, bool enable)
-{
- int domain, loop = 0;
-
- if (enable)
- loop = 1;
-
- domain = sock_get_domain(fd);
- if (domain < 0)
- return domain;
-
- switch (domain) {
- case AF_INET:
- return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
- case AF_INET6:
- return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
- default:
- return -EINVAL;
- }
-}
-
-/*! Set the TTL of outbound multicast packets
- * \param[in] fd file descriptor of related socket
- * \param[in] ttl TTL of to-be-sent multicast packets
- * \returns 0 on success; negative otherwise */
-int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
-{
- int domain, ttli = ttl;
-
- domain = sock_get_domain(fd);
- if (domain < 0)
- return domain;
-
- switch (domain) {
- case AF_INET:
- return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli));
- case AF_INET6:
- return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
- default:
- return -EINVAL;
- }
-}
-
-/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
- * \param[in] fd file descriptor of related socket
- * \param[in] enable Enable or Disable receiving of all packets
- * \returns 0 on success; negative otherwise */
-int osmo_sock_mcast_all_set(int fd, bool enable)
-{
- int domain, all = 0;
-
- if (enable)
- all = 1;
-
- domain = sock_get_domain(fd);
- if (domain < 0)
- return domain;
-
- switch (domain) {
- case AF_INET:
-#ifdef IP_MULTICAST_ALL
- return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
-#endif
- case AF_INET6:
- /* there seems no equivalent ?!? */
- default:
- return -EINVAL;
- }
-}
-
-/* FreeBSD calls the socket option differently */
-#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
-#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
-#endif
-
-/*! Subscribe to the given IP multicast group
- * \param[in] fd file descriptor of related scoket
- * \param[in] grp_addr ASCII representation of the multicast group address
- * \returns 0 on success; negative otherwise */
-int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
-{
- int rc, domain;
- struct ip_mreq mreq;
- struct ipv6_mreq mreq6;
- struct in6_addr i6a;
-
- domain = sock_get_domain(fd);
- if (domain < 0)
- return domain;
-
- switch (domain) {
- case AF_INET:
- memset(&mreq, 0, sizeof(mreq));
- mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
- mreq.imr_interface.s_addr = htonl(INADDR_ANY);
- return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-#ifdef IPV6_ADD_MEMBERSHIP
- case AF_INET6:
- memset(&mreq6, 0, sizeof(mreq6));
- rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
- if (rc < 0)
- return -EINVAL;
- mreq6.ipv6mr_multiaddr = i6a;
- return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
-#endif
- default:
- return -EINVAL;
- }
-}
-
-/*! Determine the matching local IP-address for a given remote IP-Address.
- * \param[out] local_ip caller provided memory for resulting local IP-address
- * \param[in] remote_ip remote IP-address
- * \param[in] fd file descriptor of related scoket
- * \returns 0 on success; negative otherwise
- *
- * The function accepts IPv4 and IPv6 address strings. The caller must provide
- * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as
- * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */
-int osmo_sock_local_ip(char *local_ip, const char *remote_ip)
-{
- int sfd;
- int rc;
- struct addrinfo addrinfo_hint;
- struct addrinfo *addrinfo = NULL;
- struct sockaddr_in local_addr;
- socklen_t local_addr_len;
- uint16_t family;
-
- /* Find out the address family (AF_INET or AF_INET6?) */
- memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint));
- addrinfo_hint.ai_family = PF_UNSPEC;
- addrinfo_hint.ai_flags = AI_NUMERICHOST;
- rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo);
- if (rc)
- return -EINVAL;
- family = addrinfo->ai_family;
- freeaddrinfo(addrinfo);
-
- /* Connect a dummy socket to trick the kernel into determining the
- * ip-address of the interface that would be used if we would send
- * out an actual packet */
- sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT);
- if (sfd < 0)
- return -EINVAL;
-
- /* Request the IP address of the interface that the kernel has
- * actually choosen. */
- memset(&local_addr, 0, sizeof(local_addr));
- local_addr_len = sizeof(local_addr);
- rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len);
- close(sfd);
- if (rc < 0)
- return -EINVAL;
- if (local_addr.sin_family == AF_INET)
- inet_ntop(AF_INET, &local_addr.sin_addr, local_ip, INET_ADDRSTRLEN);
- else if (local_addr.sin_family == AF_INET6)
- inet_ntop(AF_INET6, &local_addr.sin_addr, local_ip, INET6_ADDRSTRLEN);
- else
- return -EINVAL;
-
- return 0;
-}
-
-#endif /* HAVE_SYS_SOCKET_H */
-
-/*! @} */
diff --git a/src/stat_item.c b/src/stat_item.c
deleted file mode 100644
index 67165754..00000000
--- a/src/stat_item.c
+++ /dev/null
@@ -1,359 +0,0 @@
-/*! \file stat_item.c
- * utility routines for keeping statistical values */
-/*
- * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
- * (C) 2015 by sysmocom - s.f.m.c. GmbH
- *
- * All Rights Reserved
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-/*! \addtogroup osmo_stat_item
- * @{
- *
- * This osmo_stat_item module adds instrumentation capabilities to
- * gather measurement and statistical values in a similar fashion to
- * what we have as \ref osmo_counter_group.
- *
- * As opposed to counters, osmo_stat_item do not increment but consist
- * of a configurable-sized FIFO, which can store not only the current
- * (most recent) value, but also historic values.
- *
- * The only supported value type is an int32_t.
- *
- * Getting values from the osmo_stat_item does not modify its state to
- * allow for multiple independent back-ends retrieving values (e.g. VTY
- * and statd).
- *
- * Each value stored in the FIFO of an osmo_stat_item has an associated
- * value_id. The value_id is derived from an application-wide globally
- * incrementing counter, so (until the counter wraps) more recent
- * values will have higher values.
- *
- * When a new value is set, the oldest value in the FIFO gets silently
- * overwritten. Lost values are skipped when getting values from the
- * item.
- *
- */
-
-#include <stdint.h>
-#include <string.h>
-
-#include <osmocom/core/utils.h>
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/stat_item.h>
-
-/*! global list of stat_item groups */
-static LLIST_HEAD(osmo_stat_item_groups);
-/*! counter for assigning globally unique value identifiers */
-static int32_t global_value_id = 0;
-
-/*! talloc context from which we allocate */
-static void *tall_stat_item_ctx;
-
-/*! Allocate a new group of counters according to description.
- * Allocate a group of stat items described in \a desc from talloc context \a ctx,
- * giving the new group the index \a idx.
- * \param[in] ctx \ref talloc context
- * \param[in] desc Statistics item group description
- * \param[in] idx Index of new stat item group
- */
-struct osmo_stat_item_group *osmo_stat_item_group_alloc(void *ctx,
- const struct osmo_stat_item_group_desc *desc,
- unsigned int idx)
-{
- unsigned int group_size;
- unsigned long items_size = 0;
- unsigned int item_idx;
- void *items;
-
- struct osmo_stat_item_group *group;
-
- group_size = sizeof(struct osmo_stat_item_group) +
- desc->num_items * sizeof(struct osmo_stat_item *);
-
- if (!ctx)
- ctx = tall_stat_item_ctx;
-
- group = talloc_zero_size(ctx, group_size);
- if (!group)
- return NULL;
-
- group->desc = desc;
- group->idx = idx;
-
- /* Get combined size of all items */
- for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
- unsigned int size;
- size = sizeof(struct osmo_stat_item) +
- sizeof(struct osmo_stat_item_value) *
- desc->item_desc[item_idx].num_values;
- /* Align to pointer size */
- size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
-
- /* Store offsets into the item array */
- group->items[item_idx] = (void *)items_size;
-
- items_size += size;
- }
-
- items = talloc_zero_size(group, items_size);
- if (!items) {
- talloc_free(group);
- return NULL;
- }
-
- /* Update item pointers */
- for (item_idx = 0; item_idx < desc->num_items; item_idx++) {
- struct osmo_stat_item *item = (struct osmo_stat_item *)
- ((uint8_t *)items + (unsigned long)group->items[item_idx]);
- unsigned int i;
-
- group->items[item_idx] = item;
- item->last_offs = desc->item_desc[item_idx].num_values - 1;
- item->last_value_index = -1;
- item->desc = &desc->item_desc[item_idx];
-
- for (i = 0; i <= item->last_offs; i++) {
- item->values[i].value = desc->item_desc[item_idx].default_value;
- item->values[i].id = OSMO_STAT_ITEM_NOVALUE_ID;
- }
- }
-
- llist_add(&group->list, &osmo_stat_item_groups);
-
- return group;
-}
-
-/*! Free the memory for the specified group of stat items */
-void osmo_stat_item_group_free(struct osmo_stat_item_group *grp)
-{
- llist_del(&grp->list);
- talloc_free(grp);
-}
-
-/*! Increase the stat_item to the given value.
- * This function adds a new value for the given stat_item at the end of
- * the FIFO.
- * \param[in] item The stat_item whose \a value we want to set
- * \param[in] value The numeric value we want to store at end of FIFO
- */
-void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value)
-{
- int32_t oldvalue = item->values[item->last_offs].value;
- osmo_stat_item_set(item, oldvalue + value);
-}
-
-/*! Descrease the stat_item to the given value.
- * This function adds a new value for the given stat_item at the end of
- * the FIFO.
- * \param[in] item The stat_item whose \a value we want to set
- * \param[in] value The numeric value we want to store at end of FIFO
- */
-void osmo_stat_item_dec(struct osmo_stat_item *item, int32_t value)
-{
- int32_t oldvalue = item->values[item->last_offs].value;
- osmo_stat_item_set(item, oldvalue - value);
-}
-
-/*! Set the a given stat_item to the given value.
- * This function adds a new value for the given stat_item at the end of
- * the FIFO.
- * \param[in] item The stat_item whose \a value we want to set
- * \param[in] value The numeric value we want to store at end of FIFO
- */
-void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)
-{
- item->last_offs += 1;
- if (item->last_offs >= item->desc->num_values)
- item->last_offs = 0;
-
- global_value_id += 1;
- if (global_value_id == OSMO_STAT_ITEM_NOVALUE_ID)
- global_value_id += 1;
-
- item->values[item->last_offs].value = value;
- item->values[item->last_offs].id = global_value_id;
-}
-
-/*! Retrieve the next value from the osmo_stat_item object.
- * If a new value has been set, it is returned. The idx is used to decide
- * which value to return.
- * On success, *idx is updated to refer to the next unread value. If
- * values have been missed due to FIFO overflow, *idx is incremented by
- * (1 + num_lost).
- * This way, the osmo_stat_item object can be kept stateless from the reader's
- * perspective and therefore be used by several backends simultaneously.
- *
- * \param val the osmo_stat_item object
- * \param idx identifies the next value to be read
- * \param value a pointer to store the value
- * \returns the increment of the index (0: no value has been read,
- * 1: one value has been taken,
- * (1+n): n values have been skipped, one has been taken)
- */
-int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_idx,
- int32_t *value)
-{
- const struct osmo_stat_item_value *next_value;
- const struct osmo_stat_item_value *item_value = NULL;
- int idx_delta;
- int next_offs;
-
- next_offs = item->last_offs;
- next_value = &item->values[next_offs];
-
- while (next_value->id - *next_idx >= 0 &&
- next_value->id != OSMO_STAT_ITEM_NOVALUE_ID)
- {
- item_value = next_value;
-
- next_offs -= 1;
- if (next_offs < 0)
- next_offs = item->desc->num_values - 1;
- if (next_offs == item->last_offs)
- break;
- next_value = &item->values[next_offs];
- }
-
- if (!item_value)
- /* All items have been read */
- return 0;
-
- *value = item_value->value;
-
- idx_delta = item_value->id + 1 - *next_idx;
-
- *next_idx = item_value->id + 1;
-
- return idx_delta;
-}
-
-/*! Skip/discard all values of this item and update \a idx accordingly */
-int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx)
-{
- int discarded = item->values[item->last_offs].id + 1 - *idx;
- *idx = item->values[item->last_offs].id + 1;
-
- return discarded;
-}
-
-/*! Skip all values of all items and update \a idx accordingly */
-int osmo_stat_item_discard_all(int32_t *idx)
-{
- int discarded = global_value_id + 1 - *idx;
- *idx = global_value_id + 1;
-
- return discarded;
-}
-
-/*! Initialize the stat item module. Call this once from your program.
- * \param[in] tall_ctx Talloc context from which this module allocates */
-int osmo_stat_item_init(void *tall_ctx)
-{
- tall_stat_item_ctx = tall_ctx;
-
- return 0;
-}
-
-/*! Search for item group based on group name and index
- * \param[in] name Name of stats_item_group we want to find
- * \param[in] idx Index of the group we want to find
- * \returns pointer to group, if found; NULL otherwise */
-struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx(
- const char *name, const unsigned int idx)
-{
- struct osmo_stat_item_group *statg;
-
- llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
- if (!statg->desc)
- continue;
-
- if (!strcmp(statg->desc->group_name_prefix, name) &&
- statg->idx == idx)
- return statg;
- }
- return NULL;
-}
-
-/*! Search for item based on group + item name
- * \param[in] statg group in which to search for the item
- * \param[in] name name of item to search within \a statg
- * \returns pointer to item, if found; NULL otherwise */
-const struct osmo_stat_item *osmo_stat_item_get_by_name(
- const struct osmo_stat_item_group *statg, const char *name)
-{
- int i;
- const struct osmo_stat_item_desc *item_desc;
-
- if (!statg->desc)
- return NULL;
-
- for (i = 0; i < statg->desc->num_items; i++) {
- item_desc = &statg->desc->item_desc[i];
-
- if (!strcmp(item_desc->name, name)) {
- return statg->items[i];
- }
- }
- return NULL;
-}
-
-/*! Iterate over all items in group, call user-supplied function on each
- * \param[in] statg stat_item group over whose items to iterate
- * \param[in] handle_item Call-back function, aborts if rc < 0
- * \param[in] data Private data handed through to \a handle_item
- */
-int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg,
- osmo_stat_item_handler_t handle_item, void *data)
-{
- int rc = 0;
- int i;
-
- for (i = 0; i < statg->desc->num_items; i++) {
- struct osmo_stat_item *item = statg->items[i];
- rc = handle_item(statg, item, data);
- if (rc < 0)
- return rc;
- }
-
- return rc;
-}
-
-/*! Iterate over all stat_item groups in system, call user-supplied function on each
- * \param[in] handle_group Call-back function, aborts if rc < 0
- * \param[in] data Private data handed through to \a handle_group
- */
-int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data)
-{
- struct osmo_stat_item_group *statg;
- int rc = 0;
-
- llist_for_each_entry(statg, &osmo_stat_item_groups, list) {
- rc = handle_group(statg, data);
- if (rc < 0)
- return rc;
- }
-
- return rc;
-}
-
-/*! @} */
diff --git a/src/usb/Makefile.am b/src/usb/Makefile.am
new file mode 100644
index 00000000..c7d7a2a2
--- /dev/null
+++ b/src/usb/Makefile.am
@@ -0,0 +1,24 @@
+# This is _NOT_ the library release version, it's an API version.
+# Please read chapter "Library interface versions" of the libtool documentation
+# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
+LIBVERSION=0:1:0
+
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -fPIC -Wall $(LIBUSB_CFLAGS) $(TALLOC_CFLAGS)
+AM_LDFLAGS = $(COVERAGE_LDFLAGS)
+
+if ENABLE_LIBUSB
+
+lib_LTLIBRARIES = libosmousb.la
+
+libosmousb_la_SOURCES = osmo_libusb.c
+libosmousb_la_LDFLAGS = \
+ -version-info $(LIBVERSION) \
+ -no-undefined \
+ $(NULL)
+libosmousb_la_LIBADD = \
+ $(top_builddir)/src/core/libosmocore.la \
+ $(TALLOC_LIBS) \
+ $(LIBUSB_LIBS)
+
+endif
diff --git a/src/usb/osmo_libusb.c b/src/usb/osmo_libusb.c
new file mode 100644
index 00000000..a249d100
--- /dev/null
+++ b/src/usb/osmo_libusb.c
@@ -0,0 +1,776 @@
+/* libosmocore integration with libusb-1.0
+ *
+ * (C) 2019-2019 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <poll.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/talloc.h>
+
+#include <libusb.h>
+
+#include <osmocom/usb/libusb.h>
+
+/***********************************************************************
+ * logging integration
+ ***********************************************************************/
+
+#define DLUSB DLINP
+
+#ifdef LIBUSB_LOG_CB_CONTEXT /* introduced in 1.0.23 */
+static const int usb2logl[] = {
+ [LIBUSB_LOG_LEVEL_NONE] = LOGL_FATAL,
+ [LIBUSB_LOG_LEVEL_ERROR] = LOGL_ERROR,
+ [LIBUSB_LOG_LEVEL_WARNING] = LOGL_NOTICE,
+ [LIBUSB_LOG_LEVEL_INFO] = LOGL_INFO,
+ [LIBUSB_LOG_LEVEL_DEBUG] = LOGL_DEBUG,
+};
+
+/* called by libusb if it wants to log something */
+static void libosmo_usb_log_cb(libusb_context *luctx, enum libusb_log_level level_usb, const char *str)
+{
+ int level = LOGL_NOTICE;
+
+ if (level_usb < ARRAY_SIZE(usb2logl))
+ level = usb2logl[level_usb];
+
+ LOGP(DLUSB, level, "%s", str);
+}
+#endif /* LIBUSB_LOG_CB_CONTEXT */
+
+/***********************************************************************
+ * select loop integration
+ ***********************************************************************/
+
+static int osmo_usb_fd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ libusb_context *luctx = ofd->data;
+
+ /* we assume that we're running Linux v2.6.27 with timerfd support here
+ * and hence don't have to perform manual timeout handling. See
+ * "Notes on time-based events" at
+ * http://libusb.sourceforge.net/api-1.0/group__libusb__poll.html */
+ struct timeval zero_tv = { 0, 0 };
+ libusb_handle_events_timeout(luctx, &zero_tv);
+
+ return 0;
+}
+
+/* called by libusb if it wants to add a file-descriptor */
+static void osmo_usb_added_cb(int fd, short events, void *user_data)
+{
+ struct osmo_fd *ofd = talloc_zero(OTC_GLOBAL, struct osmo_fd);
+ libusb_context *luctx = user_data;
+ unsigned int when = 0;
+ int rc;
+
+ if (events & POLLIN)
+ when |= OSMO_FD_READ;
+ if (events & POLLOUT)
+ when |= OSMO_FD_WRITE;
+
+ osmo_fd_setup(ofd, fd, when, osmo_usb_fd_cb, luctx, 0);
+ rc = osmo_fd_register(ofd);
+ if (rc)
+ LOGP(DLUSB, LOGL_ERROR, "osmo_fd_register() failed with rc=%d\n", rc);
+}
+
+/* called by libusb if it wants to remove a file-descriptor */
+static void osmo_usb_removed_cb(int fd, void *user_data)
+{
+ struct osmo_fd *ofd = osmo_fd_get_by_fd(fd);
+ if (!ofd)
+ return;
+ osmo_fd_unregister(ofd);
+ talloc_free(ofd);
+}
+
+/***********************************************************************
+ * utility functions
+ ***********************************************************************/
+
+/*! obtain the string representation of the USB device path of given device.
+ * \param[out] buf Output string buffer
+ * \param[in] bufsize Size of output string buffer in bytes
+ * \param[in] dev USB device whose bus path we want to obtain
+ * \returns pointer to 'buf' in case of success; NULL in case of error */
+char *osmo_libusb_dev_get_path_buf(char *buf, size_t bufsize, libusb_device *dev)
+{
+#if (defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102) || \
+ (defined(LIBUSBX_API_VERSION) && LIBUSBX_API_VERSION >= 0x01000102)
+ struct osmo_strbuf sb = { .buf = buf, .len = bufsize };
+ uint8_t path[8];
+ int r,j;
+ r = libusb_get_port_numbers(dev, path, sizeof(path));
+ if (r > 0) {
+ OSMO_STRBUF_PRINTF(sb, "%d-%d", libusb_get_bus_number(dev), path[0]);
+ for (j = 1; j < r; j++){
+ OSMO_STRBUF_PRINTF(sb, ".%d", path[j]);
+ }
+ }
+ return buf;
+#else
+# warning "libusb too old - building without USB path support!"
+ return NULL;
+#endif
+}
+
+/*! obtain the string representation of the USB device path of given device.
+ * \param[in] talloc context from which to dynamically allocate output string buffer
+ * \param[in] dev USB device whose bus path we want to obtain
+ * \returns pointer to 'buf' in case of success; NULL in case of error */
+char *osmo_libusb_dev_get_path_c(void *ctx, libusb_device *dev)
+{
+ char *buf = talloc_zero_size(ctx, USB_MAX_PATH_LEN);
+ if (!buf)
+ return NULL;
+ return osmo_libusb_dev_get_path_buf(buf, USB_MAX_PATH_LEN, dev);
+}
+
+static int match_dev_id(const struct libusb_device_descriptor *desc, const struct dev_id *id)
+{
+ if ((desc->idVendor == id->vendor_id) && (desc->idProduct == id->product_id))
+ return 1;
+ return 0;
+}
+
+static int match_dev_ids(const struct libusb_device_descriptor *desc, const struct dev_id *ids)
+{
+ const struct dev_id *id;
+
+ for (id = ids; id->vendor_id || id->product_id; id++) {
+ if (match_dev_id(desc, id))
+ return 1;
+ }
+ return 0;
+}
+
+/*! Find USB devices matching the specified list of USB VendorID/ProductIDs
+ * \param[in] ctx talloc context from which to allocate output data
+ * \param[in] luctx libusb context on which to operate
+ * \param[in] dev_ids zero-terminated array of VendorId/ProductId tuples
+ * \returns array of up to 256 libusb_device pointers; NULL in case of error */
+libusb_device **osmo_libusb_find_matching_usb_devs(void *ctx, struct libusb_context *luctx,
+ const struct dev_id *dev_ids)
+{
+ libusb_device **list;
+ libusb_device **out = talloc_zero_array(ctx, libusb_device *, 256);
+ libusb_device **cur = out;
+ unsigned int i;
+ int rc;
+
+ if (!out)
+ return NULL;
+
+ rc = libusb_get_device_list(luctx, &list);
+ if (rc <= 0) {
+ perror("No USB devices found");
+ talloc_free(out);
+ return NULL;
+ }
+
+ for (i = 0; list[i] != NULL; i++) {
+ struct libusb_device_descriptor dev_desc;
+ libusb_device *dev = list[i];
+
+ rc = libusb_get_device_descriptor(dev, &dev_desc);
+ if (rc < 0) {
+ perror("Couldn't get device descriptor\n");
+ libusb_unref_device(dev);
+ continue;
+ }
+
+ if (match_dev_ids(&dev_desc, dev_ids)) {
+ *cur = dev;
+ cur++;
+ /* overflow check */
+ if (cur >= out + 256)
+ break;
+ } else
+ libusb_unref_device(dev);
+ }
+ if (cur == out) {
+ libusb_free_device_list(list, 1);
+ talloc_free(out);
+ return NULL;
+ }
+
+ libusb_free_device_list(list, 0);
+ return out;
+}
+
+/*! Find a USB device of matching VendorID/ProductID at given path.
+ * \param[in] luctx libusb context on which to operate
+ * \param[in] dev_ids zer-oterminated array of VendorId/ProductId tuples
+ * \param[in] path string representation of USB path
+ * \returns libusb_device if there was exactly one match; NULL otherwise */
+libusb_device *osmo_libusb_find_matching_dev_path(struct libusb_context *luctx,
+ const struct dev_id *dev_ids,
+ const char *path)
+{
+ libusb_device **list;
+ libusb_device *match = NULL;
+ unsigned int i;
+ int rc;
+
+ rc = libusb_get_device_list(luctx, &list);
+ if (rc <= 0)
+ return NULL;
+
+ for (i = 0; list[i] != NULL; i++) {
+ struct libusb_device_descriptor dev_desc;
+ libusb_device *dev = list[i];
+ char pathbuf[128];
+
+ rc = libusb_get_device_descriptor(dev, &dev_desc);
+ if (rc < 0) {
+ LOGP(DLUSB, LOGL_ERROR, "couldn't get device descriptor\n");
+ continue;
+ }
+
+ /* check if device doesn't match */
+ if (!match_dev_ids(&dev_desc, dev_ids))
+ continue;
+
+ /* check if path doesn't match */
+ if (path) {
+ osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), dev);
+ if (strcmp(pathbuf, path))
+ continue;
+ }
+
+ if (match) {
+ /* we already have a match, but now found a second -> FAIL */
+ libusb_free_device_list(list, 1);
+ LOGP(DLUSB, LOGL_ERROR, "Found more than one matching USB device\n");
+ return NULL;
+ } else
+ match = dev;
+ }
+
+ if (!match) {
+ /* no match: free the list with automatic unref of all devices */
+ libusb_free_device_list(list, 1);
+ return NULL;
+ }
+
+ /* unref all devices *except* the match we found */
+ for (i = 0; list[i] != NULL; i++) {
+ libusb_device *dev = list[i];
+ if (dev != match)
+ libusb_unref_device(dev);
+ }
+ /* free the list *without* automatic unref of all devices */
+ libusb_free_device_list(list, 0);
+ return match;
+}
+
+/*! Find a USB device of matching VendorID/ProductID and given iSerial string.
+ * \param[in] luctx libusb context on which to operate
+ * \param[in] dev_ids zer-oterminated array of VendorId/ProductId tuples
+ * \param[in] serial string representation of serial number
+ * \returns libusb_device if there was exactly one match; NULL otherwise */
+libusb_device *osmo_libusb_find_matching_dev_serial(struct libusb_context *luctx,
+ const struct dev_id *dev_ids,
+ const char *serial)
+{
+ libusb_device **list;
+ libusb_device *match = NULL;
+ unsigned int i;
+ int rc;
+
+ rc = libusb_get_device_list(luctx, &list);
+ if (rc <= 0)
+ return NULL;
+
+ for (i = 0; list[i] != NULL; i++) {
+ struct libusb_device_descriptor dev_desc;
+ libusb_device *dev = list[i];
+
+ rc = libusb_get_device_descriptor(dev, &dev_desc);
+ if (rc < 0) {
+ LOGP(DLUSB, LOGL_ERROR, "couldn't get device descriptor\n");
+ continue;
+ }
+
+ /* check if device doesn't match */
+ if (!match_dev_ids(&dev_desc, dev_ids))
+ continue;
+
+ /* check if serial number string doesn't match */
+ if (serial) {
+ char strbuf[256];
+ libusb_device_handle *devh;
+ rc = libusb_open(dev, &devh);
+ if (rc < 0) {
+ LOGP(DLUSB, LOGL_ERROR, "Cannot open USB Device: %s\n",
+ libusb_strerror(rc));
+ /* there's no point in continuing here, as we don't know if there
+ * are multiple matches if we cannot read the iSerial string of all
+ * devices with matching vid/pid */
+ libusb_free_device_list(list, 1);
+ return NULL;
+ }
+ rc = libusb_get_string_descriptor_ascii(devh, dev_desc.iSerialNumber,
+ (uint8_t *) strbuf, sizeof(strbuf));
+ if (rc < 0) {
+ LOGP(DLUSB, LOGL_ERROR, "Cannot read USB Descriptor: %s\n",
+ libusb_strerror(rc));
+ libusb_close(devh);
+ continue;
+ }
+ libusb_close(devh);
+ if (strcmp(strbuf, serial))
+ continue;
+ }
+
+ if (match) {
+ /* we already have a match, but now found a second -> FAIL */
+ libusb_free_device_list(list, 1);
+ LOGP(DLUSB, LOGL_ERROR, "Found more than one matching USB device\n");
+ return NULL;
+ } else
+ match = dev;
+ }
+
+ if (!match) {
+ /* no match: free the list with automatic unref of all devices */
+ libusb_free_device_list(list, 1);
+ return NULL;
+ }
+
+ /* unref all devices *except* the match we found */
+ for (i = 0; list[i] != NULL; i++) {
+ libusb_device *dev = list[i];
+ if (dev != match)
+ libusb_unref_device(dev);
+ }
+ /* free the list *without* automatic unref of all devices */
+ libusb_free_device_list(list, 0);
+ return match;
+}
+
+
+/*! find a matching interface among all interfaces of the given USB device.
+ * \param[in] dev USB device in which we shall search
+ * \param[in] class USB Interface Class to look for
+ * \param[in] sub_class USB Interface Subclass to look for
+ * \param[in] protocol USB Interface Protocol to look for
+ * \param[out] out User-allocated array for storing matches
+ * \param[in] out_len Length of out array
+ * \returns number of matching interfaces; negative in case of error */
+int osmo_libusb_dev_find_matching_interfaces(libusb_device *dev, int class, int sub_class,
+ int protocol, struct usb_interface_match *out,
+ unsigned int out_len)
+{
+ struct libusb_device_descriptor dev_desc;
+ int rc, i, out_idx = 0;
+ uint8_t addr;
+ char pathbuf[USB_MAX_PATH_LEN];
+ char *path;
+
+ rc = libusb_get_device_descriptor(dev, &dev_desc);
+ if (rc < 0) {
+ perror("Couldn't get device descriptor\n");
+ return -EIO;
+ }
+
+ addr = libusb_get_device_address(dev);
+ path = osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), dev);
+
+ /* iterate over all configurations */
+ for (i = 0; i < dev_desc.bNumConfigurations; i++) {
+ struct libusb_config_descriptor *conf_desc;
+ int j;
+
+ rc = libusb_get_config_descriptor(dev, i, &conf_desc);
+ if (rc < 0) {
+ fprintf(stderr, "Couldn't get config descriptor %u\n", i);
+ continue;
+ }
+ /* iterate over all interfaces */
+ for (j = 0; j < conf_desc->bNumInterfaces; j++) {
+ const struct libusb_interface *intf = &conf_desc->interface[j];
+ int k;
+ /* iterate over all alternate settings */
+ for (k = 0; k < intf->num_altsetting; k++) {
+ const struct libusb_interface_descriptor *if_desc;
+ if_desc = &intf->altsetting[k];
+ if (class >= 0 && if_desc->bInterfaceClass != class)
+ continue;
+ if (sub_class >= 0 && if_desc->bInterfaceSubClass != sub_class)
+ continue;
+ if (protocol >= 0 && if_desc->bInterfaceProtocol != protocol)
+ continue;
+ /* MATCH! */
+ out[out_idx].usb_dev = dev;
+ out[out_idx].vendor = dev_desc.idVendor;
+ out[out_idx].product = dev_desc.idProduct;
+ out[out_idx].addr = addr;
+ OSMO_STRLCPY_ARRAY(out[out_idx].path, path);
+ out[out_idx].path[sizeof(out[out_idx].path)-1] = '\0';
+ out[out_idx].configuration = conf_desc->bConfigurationValue;
+ out[out_idx].interface = if_desc->bInterfaceNumber;
+ out[out_idx].altsetting = if_desc->bAlternateSetting;
+ out[out_idx].class = if_desc->bInterfaceClass;
+ out[out_idx].sub_class = if_desc->bInterfaceSubClass;
+ out[out_idx].protocol = if_desc->bInterfaceProtocol;
+ out[out_idx].string_idx = if_desc->iInterface;
+ out_idx++;
+ if (out_idx >= out_len)
+ return out_idx;
+ }
+ }
+ }
+ return out_idx;
+}
+
+/*! find matching interfaces among a list devices of specified VendorId/ProductID tuples.
+ * \param[in] luctx libusb context on which to operate
+ * \param[in] dev_ids zero-terminated array of VendorId/ProductId tuples
+ * \param[in] class USB Interface Class to look for
+ * \param[in] sub_class USB Interface Subclass to look for
+ * \param[in] protocol USB Interface Protocol to look for
+ * \param[out] out User-allocated array for storing matches
+ * \param[in] out_len Length of out array
+ * \returns number of matching interfaces; negative in case of error */
+int osmo_libusb_find_matching_interfaces(libusb_context *luctx, const struct dev_id *dev_ids,
+ int class, int sub_class, int protocol,
+ struct usb_interface_match *out, unsigned int out_len)
+{
+ struct usb_interface_match *out_cur = out;
+ unsigned int out_len_remain = out_len;
+ libusb_device **list;
+ libusb_device **dev;
+
+ list = osmo_libusb_find_matching_usb_devs(NULL, luctx, dev_ids);
+ if (!list)
+ return 0;
+
+ for (dev = list; *dev; dev++) {
+ int rc;
+
+#if 0
+ struct libusb_device_descriptor dev_desc;
+ uint8_t ports[8];
+ uint8_t addr;
+ rc = libusb_get_device_descriptor(*dev, &dev_desc);
+ if (rc < 0) {
+ perror("Cannot get device descriptor");
+ continue;
+ }
+
+ addr = libusb_get_device_address(*dev);
+
+ rc = libusb_get_port_numbers(*dev, ports, sizeof(ports));
+ if (rc < 0) {
+ perror("Cannot get device path");
+ continue;
+ }
+
+ printf("Found USB Device %04x:%04x at address %d\n",
+ dev_desc.idVendor, dev_desc.idProduct, addr);
+#endif
+
+ rc = osmo_libusb_dev_find_matching_interfaces(*dev, class, sub_class,
+ protocol, out_cur, out_len_remain);
+ if (rc < 0)
+ continue;
+ out_cur += rc;
+ out_len_remain -= rc;
+
+ }
+
+ /* unref / free list */
+ for (dev = list; *dev; dev++)
+ libusb_unref_device(*dev);
+ talloc_free(list);
+
+ return out_len - out_len_remain;
+}
+
+/*! open matching USB device and claim interface
+ * \param[in] ctx talloc context to use for related allocations
+ * \param[in] luctx libusb context on which to operate
+ * \param[in] ifm interface match describing interface to claim
+ * \returns libusb device chandle on success; NULL on error */
+libusb_device_handle *osmo_libusb_open_claim_interface(void *ctx, libusb_context *luctx,
+ const struct usb_interface_match *ifm)
+{
+ int rc, config;
+ struct dev_id dev_ids[] = { { ifm->vendor, ifm->product }, { 0, 0 } };
+ libusb_device **list;
+ libusb_device **dev;
+ libusb_device_handle *usb_devh = NULL;
+
+ list = osmo_libusb_find_matching_usb_devs(ctx, luctx, dev_ids);
+ if (!list) {
+ perror("No USB device with matching VID/PID");
+ return NULL;
+ }
+
+ for (dev = list; *dev; dev++) {
+ int addr;
+ char pathbuf[USB_MAX_PATH_LEN];
+ char *path;
+
+ addr = libusb_get_device_address(*dev);
+ path = osmo_libusb_dev_get_path_buf(pathbuf, sizeof(pathbuf), *dev);
+ if ((ifm->addr && addr == ifm->addr) ||
+ (strlen(ifm->path) && !strcmp(path, ifm->path)) ||
+ (!ifm->addr && !strlen(ifm->path) && !list[1] /* only one device */)) {
+ rc = libusb_open(*dev, &usb_devh);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot open device: %s\n", libusb_error_name(rc));
+ usb_devh = NULL;
+ break;
+ }
+ rc = libusb_get_configuration(usb_devh, &config);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot get current configuration: %s\n", libusb_error_name(rc));
+ libusb_close(usb_devh);
+ usb_devh = NULL;
+ break;
+ }
+ if (config != ifm->configuration) {
+ rc = libusb_set_configuration(usb_devh, ifm->configuration);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot set configuration: %s\n", libusb_error_name(rc));
+ libusb_close(usb_devh);
+ usb_devh = NULL;
+ break;
+ }
+ }
+ rc = libusb_claim_interface(usb_devh, ifm->interface);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot claim interface: %s\n", libusb_error_name(rc));
+ libusb_close(usb_devh);
+ usb_devh = NULL;
+ break;
+ }
+ rc = libusb_set_interface_alt_setting(usb_devh, ifm->interface, ifm->altsetting);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot set interface altsetting: %s\n", libusb_error_name(rc));
+ libusb_release_interface(usb_devh, ifm->interface);
+ libusb_close(usb_devh);
+ usb_devh = NULL;
+ break;
+ }
+ }
+ }
+
+ /* unref / free list */
+ for (dev = list; *dev; dev++)
+ libusb_unref_device(*dev);
+ talloc_free(list);
+
+ return usb_devh;
+}
+
+void osmo_libusb_match_init(struct osmo_usb_matchspec *cfg, int if_class, int if_subclass, int if_proto)
+{
+ cfg->dev.vendor_id = -1;
+ cfg->dev.product_id = -1;
+ cfg->dev.path = NULL;
+
+ cfg->config_id = -1;
+
+ cfg->intf.class = if_class;
+ cfg->intf.subclass = if_subclass;
+ cfg->intf.proto = if_proto;
+
+ cfg->intf.num = cfg->intf.altsetting = -1;
+}
+
+
+/*! high-level all-in-one function for USB device, config + interface matching + opening.
+ * This function offers the highest level of API among all libosmousb helper functions. It
+ * is intended as a one-stop shop for everything related to grabbing an interface.
+ *
+ * 1) looks for a device matching either the VID/PID from 'cfg' or 'default_dev_ids',
+ * if more than one is found, the user is expected to fill in cfg->dev.path to disambiguate.
+ * 2) find any interfaces on the device that match the specification in 'cfg'. The match
+ * could be done based on any of (class, subclass, proto, interface number). If there
+ * are multiple matches, the caller must disambiguate by specifying the interface number.
+ * 3) open the USB device; set the configuration (if needed); claim the interface and set
+ * the altsetting
+ *
+ * \param[in] cfg user-supplied match configuration (from command line or config file)
+ * \param[in] default_dev_ids Default list of supported VendorId/ProductIds
+ * \returns libusb_device_handle on success, NULL on error
+ */
+libusb_device_handle *osmo_libusb_find_open_claim(const struct osmo_usb_matchspec *cfg,
+ const struct dev_id *default_dev_ids)
+{
+ struct usb_interface_match if_matches[16];
+ struct usb_interface_match *ifm = NULL;
+ libusb_device_handle *usb_devh = NULL;
+ struct dev_id user_dev_ids[2] = {
+ { cfg->dev.vendor_id, cfg->dev.product_id },
+ { 0, 0 }
+ };
+ const struct dev_id *dev_ids = default_dev_ids;
+ libusb_device *dev;
+ int rc, i;
+
+ /* Stage 1: Find a device matching either the user-specified VID/PID or
+ * the list of IDs in default_dev_ids plus optionally the user-specified path */
+ if (cfg->dev.vendor_id != -1 || cfg->dev.product_id != -1)
+ dev_ids = user_dev_ids;
+ dev = osmo_libusb_find_matching_dev_path(NULL, dev_ids, cfg->dev.path);
+ if (!dev)
+ goto close_exit;
+
+ /* Stage 2: Find any interfaces matching the class/subclass/proto as specified */
+ rc = osmo_libusb_dev_find_matching_interfaces(dev, cfg->intf.class, cfg->intf.subclass,
+ cfg->intf.proto, if_matches, sizeof(if_matches));
+ if (rc < 1) {
+ LOGP(DLUSB, LOGL_NOTICE, "can't find matching USB interface at device\n");
+ goto close_exit;
+ } else if (rc == 1) {
+ ifm = if_matches;
+ } else if (rc > 1) {
+ if (cfg->intf.num == -1) {
+ LOGP(DLUSB, LOGL_ERROR, "Found %d matching USB interfaces, you "
+ "have to specify the interface number\n", rc);
+ goto close_exit;
+ }
+ for (i = 0; i < rc; i++) {
+ if (if_matches[i].interface == cfg->intf.num) {
+ ifm = &if_matches[i];
+ break;
+ }
+ /* FIXME: match altsetting */
+ }
+ }
+ if (!ifm) {
+ LOGP(DLUSB, LOGL_NOTICE, "Couldn't find matching interface\n");
+ goto close_exit;
+ }
+
+ /* Stage 3: Open device; set config (if required); claim interface; set altsetting */
+ usb_devh = osmo_libusb_open_claim_interface(NULL, NULL, ifm);
+ if (!usb_devh) {
+ LOGP(DLUSB, LOGL_ERROR, "can't open USB device (permissions issue?)\n");
+ goto close_exit;
+ }
+ return usb_devh;
+close_exit:
+ /* release if_matches */
+ if (usb_devh)
+ libusb_close(usb_devh);
+
+ return NULL;
+}
+
+/*! obtain the endpoint addresses for a given USB interface.
+ * \param[in] devh USB device handle on which to operate
+ * \param[in] if_num USB Interface number on which to operate
+ * \param[out] out user-provided storage for OUT endpoint number
+ * \param[out] in user-provided storage for IN endpoint number
+ * \param[out] irq user-provided storage for IRQ endpoint number
+ * \returns 0 in case of success; negative in case of error */
+int osmo_libusb_get_ep_addrs(libusb_device_handle *devh, unsigned int if_num,
+ uint8_t *out, uint8_t *in, uint8_t *irq)
+{
+ libusb_device *dev = libusb_get_device(devh);
+ struct libusb_config_descriptor *cdesc;
+ const struct libusb_interface_descriptor *idesc;
+ const struct libusb_interface *iface;
+ int rc, l;
+
+ rc = libusb_get_active_config_descriptor(dev, &cdesc);
+ if (rc < 0)
+ return rc;
+
+ iface = &cdesc->interface[if_num];
+ /* FIXME: we assume there's no altsetting */
+ idesc = &iface->altsetting[0];
+
+ for (l = 0; l < idesc->bNumEndpoints; l++) {
+ const struct libusb_endpoint_descriptor *edesc = &idesc->endpoint[l];
+ switch (edesc->bmAttributes & 3) {
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ if (edesc->bEndpointAddress & 0x80) {
+ if (in)
+ *in = edesc->bEndpointAddress;
+ } else {
+ if (out)
+ *out = edesc->bEndpointAddress;
+ }
+ break;
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ if (irq)
+ *irq = edesc->bEndpointAddress;
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+/***********************************************************************
+ * initialization
+ ***********************************************************************/
+
+int osmo_libusb_init(libusb_context **pluctx)
+{
+ libusb_context *luctx = NULL;
+ const struct libusb_pollfd **pfds;
+
+ int rc;
+
+ rc = libusb_init(pluctx);
+ if (rc != 0) {
+ LOGP(DLUSB, LOGL_ERROR, "Error initializing libusb: %s\n", libusb_strerror(rc));
+ return rc;
+ }
+
+ if (pluctx)
+ luctx = *pluctx;
+
+#ifdef LIBUSB_LOG_CB_CONTEXT /* introduced in 1.0.23 */
+ libusb_set_log_cb(luctx, &libosmo_usb_log_cb, LIBUSB_LOG_CB_CONTEXT);
+#endif
+
+ libusb_set_pollfd_notifiers(luctx, osmo_usb_added_cb, osmo_usb_removed_cb, luctx);
+
+ /* get the initial file descriptors which were created even before during libusb_init() */
+ pfds = libusb_get_pollfds(luctx);
+ if (pfds) {
+ const struct libusb_pollfd **pfds2 = pfds;
+ const struct libusb_pollfd *pfd;
+ /* synthesize 'add' call-backs. not sure why libusb doesn't do that by itself? */
+ for (pfd = *pfds2; pfd; pfd = *++pfds2)
+ osmo_usb_added_cb(pfd->fd, pfd->events, luctx);
+ libusb_free_pollfds(pfds);
+ }
+
+ return 0;
+}
+
+void osmo_libusb_exit(libusb_context *luctx)
+{
+ /* we just assume libusb is cleaning up all the osmo_Fd's we've allocated */
+ libusb_exit(luctx);
+}
diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am
index abed92ac..c314a141 100644
--- a/src/vty/Makefile.am
+++ b/src/vty/Makefile.am
@@ -1,10 +1,10 @@
# This is _NOT_ the library release version, it's an API version.
# Please read chapter "Library interface versions" of the libtool documentation
# before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html
-LIBVERSION=8:0:4
+LIBVERSION=13:0:0
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
-AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS)
if ENABLE_VTY
lib_LTLIBRARIES = libosmovty.la
@@ -12,7 +12,7 @@ lib_LTLIBRARIES = libosmovty.la
libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \
telnet_interface.c logging_vty.c stats_vty.c \
fsm_vty.c talloc_ctx_vty.c \
- tdef_vty.c
+ cpu_sched_vty.c tdef_vty.c
libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
-libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS)
+libosmovty_la_LIBADD = $(top_builddir)/src/core/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS)
endif
diff --git a/src/vty/command.c b/src/vty/command.c
index 6a9d18af..1719690b 100644
--- a/src/vty/command.c
+++ b/src/vty/command.c
@@ -37,14 +37,19 @@ Boston, MA 02110-1301, USA. */
#include <unistd.h>
#include <ctype.h>
#include <time.h>
+#include <limits.h>
+#include <signal.h>
#include <sys/time.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <osmocom/vty/vector.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
+#include <osmocom/core/logging.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
#include <osmocom/core/utils.h>
#ifndef MAXPATHLEN
@@ -62,6 +67,9 @@ Boston, MA 02110-1301, USA. */
void *tall_vty_cmd_ctx;
+/* Set by on_dso_load_starttime() for "show uptime". */
+static struct timespec starttime;
+
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
vector cmdvec;
@@ -69,6 +77,21 @@ vector cmdvec;
/* Host information structure. */
struct host host;
+struct vty_parent_node {
+ struct llist_head entry;
+
+ /*! private data, specified by creator */
+ void *priv;
+ void *index;
+
+ /*! Node status of this vty */
+ int node;
+
+ /*! When reading from a config file, these are the indenting characters expected for children of
+ * this VTY node. */
+ char *indent;
+};
+
/* Standard command node structures. */
struct cmd_node auth_node = {
AUTH_NODE,
@@ -200,18 +223,6 @@ static int cmp_desc(const void *p, const void *q)
return strcmp(a->cmd, b->cmd);
}
-static int is_config_child(struct vty *vty)
-{
- if (vty->node <= CONFIG_NODE)
- return 0;
- else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE)
- return 1;
- else if (host.app_info->is_config_node)
- return host.app_info->is_config_node(vty, vty->node);
- else
- return vty->node > CONFIG_NODE;
-}
-
/*! Sort each node's command element according to command string. */
void sort_node(void)
{
@@ -631,81 +642,209 @@ static char *xml_escape(const char *inp)
return out;
}
+typedef int (*print_func_t)(void *data, const char *fmt, ...);
+
+static const struct value_string cmd_attr_desc[] = {
+ { CMD_ATTR_DEPRECATED, "This command is deprecated" },
+ { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" },
+ { CMD_ATTR_IMMEDIATE, "This command applies immediately" },
+ { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" },
+ /* CMD_ATTR_LIB_COMMAND is intentionally skipped */
+ { 0, NULL }
+};
+
+/* Public attributes (to be printed in the VTY / XML reference) */
+#define CMD_ATTR_PUBLIC_MASK \
+ (CMD_ATTR_HIDDEN | CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT)
+
+/* Get a flag character for a global VTY command attribute */
+static char cmd_attr_get_flag(unsigned int attr)
+{
+ switch (attr) {
+ case CMD_ATTR_HIDDEN:
+ return '^';
+ case CMD_ATTR_IMMEDIATE:
+ return '!';
+ case CMD_ATTR_NODE_EXIT:
+ return '@';
+ default:
+ return '.';
+ }
+}
+
+/* Description of attributes shared between the lib commands */
+static const char * const cmd_lib_attr_desc[32] = {
+ /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \
+ * "Brief but meaningful description", */
+ [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \
+ "This command applies on ASP restart",
+ [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \
+ "This command applies on IPA link establishment",
+ [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \
+ "This command applies on E1 line update",
+};
+
+/* Flag letters of attributes shared between the lib commands.
+ * NOTE: uppercase letters only, the rest is reserved for applications. */
+static const char cmd_lib_attr_letters[32] = {
+ /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */
+ [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A',
+ [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I',
+ [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L',
+};
+
/*
- * Write one cmd_element as XML to the given VTY.
+ * Write one cmd_element as XML via a print_func_t.
*/
-static int vty_dump_element(struct cmd_element *cmd, struct vty *vty)
+static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_func,
+ void *data, const char *newline)
{
char *xml_string = xml_escape(cmd->string);
+ unsigned int i;
- vty_out(vty, " <command id='%s'>%s", xml_string, VTY_NEWLINE);
- vty_out(vty, " <params>%s", VTY_NEWLINE);
+ print_func(data, " <command id='%s'>%s", xml_string, newline);
- int j;
- for (j = 0; j < vector_count(cmd->strvec); ++j) {
- vector descvec = vector_slot(cmd->strvec, j);
- int i;
- for (i = 0; i < vector_active(descvec); ++i) {
+ /* Print global attributes and their description */
+ if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */
+ print_func(data, " <attributes scope='global'>%s", newline);
+
+ for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
+ char *xml_att_desc;
+ char flag;
+
+ if (~cmd->attr & cmd_attr_desc[i].value)
+ continue;
+
+ xml_att_desc = xml_escape(cmd_attr_desc[i].str);
+ print_func(data, " <attribute doc='%s'",
+ xml_att_desc, newline);
+ talloc_free(xml_att_desc);
+
+ flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
+ if (flag != '.')
+ print_func(data, " flag='%c'", flag);
+ print_func(data, " />%s", newline);
+ }
+
+ print_func(data, " </attributes>%s", newline);
+ }
+
+ /* Print application specific attributes and their description */
+ if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */
+ const char * const *desc;
+ const char *letters;
+
+ if (cmd->attr & CMD_ATTR_LIB_COMMAND) {
+ print_func(data, " <attributes scope='library'>%s", newline);
+ letters = &cmd_lib_attr_letters[0];
+ desc = &cmd_lib_attr_desc[0];
+ } else {
+ print_func(data, " <attributes scope='application'>%s", newline);
+ letters = &host.app_info->usr_attr_letters[0];
+ desc = &host.app_info->usr_attr_desc[0];
+ }
+
+ for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) {
+ char *xml_att_desc;
+ char flag;
+
+ /* Skip attribute if *not* set */
+ if (~cmd->usrattr & ((unsigned)1 << i))
+ continue;
+
+ xml_att_desc = xml_escape(desc[i]);
+ print_func(data, " <attribute doc='%s'", xml_att_desc);
+ talloc_free(xml_att_desc);
+
+ if ((flag = letters[i]) != '\0')
+ print_func(data, " flag='%c'", flag);
+ print_func(data, " />%s", newline);
+ }
+
+ print_func(data, " </attributes>%s", newline);
+ }
+
+ print_func(data, " <params>%s", newline);
+
+ for (i = 0; i < vector_count(cmd->strvec); ++i) {
+ vector descvec = vector_slot(cmd->strvec, i);
+ int j;
+ for (j = 0; j < vector_active(descvec); ++j) {
char *xml_param, *xml_doc;
- struct desc *desc = vector_slot(descvec, i);
+ struct desc *desc = vector_slot(descvec, j);
if (desc == NULL)
continue;
xml_param = xml_escape(desc->cmd);
xml_doc = xml_escape(desc->str);
- vty_out(vty, " <param name='%s' doc='%s' />%s",
- xml_param, xml_doc, VTY_NEWLINE);
+ print_func(data, " <param name='%s' doc='%s' />%s",
+ xml_param, xml_doc, newline);
talloc_free(xml_param);
talloc_free(xml_doc);
}
}
- vty_out(vty, " </params>%s", VTY_NEWLINE);
- vty_out(vty, " </command>%s", VTY_NEWLINE);
+ print_func(data, " </params>%s", newline);
+ print_func(data, " </command>%s", newline);
talloc_free(xml_string);
return 0;
}
-static bool vty_command_is_common(struct cmd_element *cmd);
+static bool vty_command_is_common(const struct cmd_element *cmd);
/*
- * Dump all nodes and commands associated with a given node as XML to the VTY.
+ * Dump all nodes and commands associated with a given node as XML via a print_func_t.
+ *
+ * (gflag_mask, match = false) - print only those commands with non-matching flags.
+ * (gflag_mask, match = true) - print only those commands with matching flags.
+ *
+ * Some examples:
+ *
+ * Print all commands except deprecated and hidden (default mode):
+ * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, false)
+ * Print only deprecated and hidden commands:
+ * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, true)
+ * Print all commands except deprecated (expert mode):
+ * (CMD_ATTR_DEPRECATED, false)
+ * Print only hidden commands:
+ * (CMD_ATTR_HIDDEN, true)
*/
-static int vty_dump_nodes(struct vty *vty)
+static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline,
+ unsigned char gflag_mask, bool match)
{
int i, j;
int same_name_count;
- vty_out(vty, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", VTY_NEWLINE);
+ print_func(data, "<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>%s", newline);
/* Only once, list all common node commands. Use the CONFIG node to find common node commands. */
- vty_out(vty, " <node id='_common_cmds_'>%s", VTY_NEWLINE);
- vty_out(vty, " <name>Common Commands</name>%s", VTY_NEWLINE);
- vty_out(vty, " <description>These commands are available on all VTY nodes. They are listed"
- " here only once, to unclutter the VTY reference.</description>%s", VTY_NEWLINE);
+ print_func(data, " <node id='_common_cmds_'>%s", newline);
+ print_func(data, " <name>Common Commands</name>%s", newline);
+ print_func(data, " <description>These commands are available on all VTY nodes. They are listed"
+ " here only once, to unclutter the VTY reference.</description>%s", newline);
for (i = 0; i < vector_active(cmdvec); ++i) {
- struct cmd_node *cnode;
- cnode = vector_slot(cmdvec, i);
+ const struct cmd_node *cnode = vector_slot(cmdvec, i);
if (!cnode)
continue;
if (cnode->node != CONFIG_NODE)
continue;
for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
- struct cmd_element *elem;
- elem = vector_slot(cnode->cmd_vector, j);
+ const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
if (!vty_command_is_common(elem))
continue;
- if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
- vty_dump_element(elem, vty);
+ if (!match && (elem->attr & gflag_mask) != 0x00)
+ continue;
+ if (match && (elem->attr & gflag_mask) == 0x00)
+ continue;
+ vty_dump_element(elem, print_func, data, newline);
}
}
- vty_out(vty, " </node>%s", VTY_NEWLINE);
+ print_func(data, " </node>%s", newline);
for (i = 0; i < vector_active(cmdvec); ++i) {
- struct cmd_node *cnode;
- cnode = vector_slot(cmdvec, i);
+ const struct cmd_node *cnode = vector_slot(cmdvec, i);
if (!cnode)
continue;
if (vector_active(cnode->cmd_vector) < 1)
@@ -716,37 +855,124 @@ static int vty_dump_nodes(struct vty *vty)
* 'name', the second becomes 'name_2', then 'name_3', ... */
same_name_count = 1;
for (j = 0; j < i; ++j) {
- struct cmd_node *cnode2;
- cnode2 = vector_slot(cmdvec, j);
+ const struct cmd_node *cnode2 = vector_slot(cmdvec, j);
if (!cnode2)
continue;
if (strcmp(cnode->name, cnode2->name) == 0)
same_name_count ++;
}
- vty_out(vty, " <node id='%s", cnode->name);
+ print_func(data, " <node id='%s", cnode->name);
if (same_name_count > 1 || !*cnode->name)
- vty_out(vty, "_%d", same_name_count);
- vty_out(vty, "'>%s", VTY_NEWLINE);
- vty_out(vty, " <name>%s</name>%s", cnode->name, VTY_NEWLINE);
+ print_func(data, "_%d", same_name_count);
+ print_func(data, "'>%s", newline);
+ print_func(data, " <name>%s</name>%s", cnode->name, newline);
for (j = 0; j < vector_active(cnode->cmd_vector); ++j) {
- struct cmd_element *elem;
- elem = vector_slot(cnode->cmd_vector, j);
+ const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j);
if (vty_command_is_common(elem))
continue;
- if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
- vty_dump_element(elem, vty);
+ if (!match && (elem->attr & gflag_mask) != 0x00)
+ continue;
+ if (match && (elem->attr & gflag_mask) == 0x00)
+ continue;
+ vty_dump_element(elem, print_func, data, newline);
}
- vty_out(vty, " </node>%s", VTY_NEWLINE);
+ print_func(data, " </node>%s", newline);
}
- vty_out(vty, "</vtydoc>%s", VTY_NEWLINE);
+ print_func(data, "</vtydoc>%s", newline);
return 0;
}
+static int print_func_vty(void *data, const char *format, ...)
+{
+ struct vty *vty = data;
+ va_list args;
+ int rc;
+ va_start(args, format);
+ rc = vty_out_va(vty, format, args);
+ va_end(args);
+ return rc;
+}
+
+static int vty_dump_xml_ref_to_vty(struct vty *vty)
+{
+ unsigned char gflag_mask = CMD_ATTR_DEPRECATED;
+ if (!vty->expert_mode)
+ gflag_mask |= CMD_ATTR_HIDDEN;
+ return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE, gflag_mask, false);
+}
+
+static int print_func_stream(void *data, const char *format, ...)
+{
+ va_list args;
+ int rc;
+ va_start(args, format);
+ rc = vfprintf((FILE*)data, format, args);
+ va_end(args);
+ return rc;
+}
+
+const struct value_string vty_ref_gen_mode_names[] = {
+ { VTY_REF_GEN_MODE_DEFAULT, "default" },
+ { VTY_REF_GEN_MODE_EXPERT, "expert" },
+ { VTY_REF_GEN_MODE_HIDDEN, "hidden" },
+ { 0, NULL }
+};
+
+const struct value_string vty_ref_gen_mode_desc[] = {
+ { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" },
+ { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" },
+ { VTY_REF_GEN_MODE_HIDDEN, "hidden commands only" },
+ { 0, NULL }
+};
+
+/*! Print the XML reference of all VTY nodes to the given stream.
+ * \param[out] stream Output stream to print the XML reference to.
+ * \param[in] mode The XML reference generation mode.
+ * \returns always 0 for now, no errors possible.
+ */
+int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode)
+{
+ unsigned char gflag_mask;
+ bool match = false;
+
+ switch (mode) {
+ case VTY_REF_GEN_MODE_EXPERT:
+ /* All commands except deprecated */
+ gflag_mask = CMD_ATTR_DEPRECATED;
+ break;
+ case VTY_REF_GEN_MODE_HIDDEN:
+ /* Only hidden commands */
+ gflag_mask = CMD_ATTR_HIDDEN;
+ match = true;
+ break;
+ case VTY_REF_GEN_MODE_DEFAULT:
+ default:
+ /* All commands except deprecated and hidden */
+ gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN;
+ break;
+ }
+
+ return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match);
+}
+
+/*! Print the XML reference of all VTY nodes to the given stream.
+ * \param[out] stream Output stream to print the XML reference to.
+ * \returns always 0 for now, no errors possible.
+ *
+ * NOTE: this function is deprecated because it does not allow to
+ * specify the XML reference generation mode (default mode
+ * is hard-coded). Use vty_dump_xml_ref_mode() instead.
+ */
+int vty_dump_xml_ref(FILE *stream)
+{
+ return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT);
+}
+
/* Check if a command with given string exists at given node */
static int check_element_exists(struct cmd_node *cnode, const char *cmdstring)
{
@@ -784,6 +1010,16 @@ void install_element(int ntype, struct cmd_element *cmd)
cmd->cmdsize = cmd_cmdsize(cmd->strvec);
}
+/*! Install a library command into a node
+ * \param[in] ntype Node Type
+ * \param[in] cmd element to be installed
+ */
+void install_lib_element(int ntype, struct cmd_element *cmd)
+{
+ cmd->attr |= CMD_ATTR_LIB_COMMAND;
+ install_element(ntype, cmd);
+}
+
/* Install a command into VIEW and ENABLE node */
void install_element_ve(struct cmd_element *cmd)
{
@@ -791,6 +1027,13 @@ void install_element_ve(struct cmd_element *cmd)
install_element(ENABLE_NODE, cmd);
}
+/* Install a library command into VIEW and ENABLE node */
+void install_lib_element_ve(struct cmd_element *cmd)
+{
+ cmd->attr |= CMD_ATTR_LIB_COMMAND;
+ install_element_ve(cmd);
+}
+
#ifdef VTY_CRYPT_PW
static unsigned char itoa64[] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@@ -864,6 +1107,7 @@ static int config_write_host(struct vty *vty)
static vector cmd_node_vector(vector v, enum node_type ntype)
{
struct cmd_node *cnode = vector_slot(v, ntype);
+ OSMO_ASSERT(cnode != NULL);
return cnode->cmd_vector;
}
@@ -1121,7 +1365,6 @@ static enum match_type cmd_ipv6_prefix_match(const char *str)
int colons = 0, nums = 0, double_colon = 0;
int mask;
const char *sp = NULL;
- char *endptr = NULL;
if (str == NULL)
return PARTLY_MATCH;
@@ -1219,11 +1462,7 @@ static enum match_type cmd_ipv6_prefix_match(const char *str)
if (state < STATE_MASK)
return PARTLY_MATCH;
- mask = strtol(str, &endptr, 10);
- if (*endptr != '\0')
- return NO_MATCH;
-
- if (mask < 0 || mask > 128)
+ if (osmo_str_to_int(&mask, str, 10, 0, 128))
return NO_MATCH;
/* I don't know why mask < 13 makes command match partly.
@@ -1239,21 +1478,69 @@ static enum match_type cmd_ipv6_prefix_match(const char *str)
#endif /* HAVE_IPV6 */
-#define DECIMAL_STRLEN_MAX 10
-static int cmd_range_match(const char *range, const char *str)
+#if ULONG_MAX == 18446744073709551615UL
+#define DECIMAL_STRLEN_MAX_UNSIGNED 20
+#elif ULONG_MAX == 4294967295UL
+#define DECIMAL_STRLEN_MAX_UNSIGNED 10
+#else
+#error "ULONG_MAX not defined!"
+#endif
+
+#if LONG_MAX == 9223372036854775807L
+#define DECIMAL_STRLEN_MAX_SIGNED 19
+#elif LONG_MAX == 2147483647L
+#define DECIMAL_STRLEN_MAX_SIGNED 10
+#else
+#error "LONG_MAX not defined!"
+#endif
+
+/* This function is aimed at quickly guessing & filtering the numeric base a
+ * string can contain, by no means validates the entire value.
+ * Returns 16 if string follows pattern "({+,-}0x[digits])"
+ * Returns 10 if string follows pattern "({+,-}[digits])"
+ * Returns a negative value if something other is detected (error)
+*/
+static int check_base(const char *str)
+{
+ const char *ptr = str;
+ /* Skip any space */
+ while (isspace(*ptr)) ptr++;
+
+ /* Skip optional sign: */
+ if (*ptr == '+' || *ptr == '-')
+ ptr++;
+ if (*ptr == '0') {
+ ptr++;
+ if (*ptr == '\0' || isdigit(*ptr))
+ return 10;
+ if (!(*ptr == 'x' || *ptr == 'X'))
+ return -1;
+ ptr++;
+ if (isxdigit(*ptr))
+ return 16;
+ return -1;
+ }
+ return 10;
+}
+
+int vty_cmd_range_match(const char *range, const char *str)
{
char *p;
- char buf[DECIMAL_STRLEN_MAX + 1];
+ char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1];
char *endptr = NULL;
+ int min_base, max_base, val_base;
if (str == NULL)
return 1;
+ if ((val_base = check_base(str)) < 0)
+ return 0;
+
if (range[1] == '-') {
signed long min = 0, max = 0, val;
- val = strtol(str, &endptr, 10);
+ val = strtol(str, &endptr, val_base);
if (*endptr != '\0')
return 0;
@@ -1261,11 +1548,13 @@ static int cmd_range_match(const char *range, const char *str)
p = strchr(range, '-');
if (p == NULL)
return 0;
- if (p - range > DECIMAL_STRLEN_MAX)
+ if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
return 0;
strncpy(buf, range, p - range);
buf[p - range] = '\0';
- min = -strtol(buf, &endptr, 10);
+ if ((min_base = check_base(buf)) < 0)
+ return 0;
+ min = -strtol(buf, &endptr, min_base);
if (*endptr != '\0')
return 0;
@@ -1273,11 +1562,13 @@ static int cmd_range_match(const char *range, const char *str)
p = strchr(range, '>');
if (p == NULL)
return 0;
- if (p - range > DECIMAL_STRLEN_MAX)
+ if (p - range > DECIMAL_STRLEN_MAX_SIGNED)
return 0;
strncpy(buf, range, p - range);
buf[p - range] = '\0';
- max = strtol(buf, &endptr, 10);
+ if ((max_base = check_base(buf)) < 0)
+ return 0;
+ max = strtol(buf, &endptr, max_base);
if (*endptr != '\0')
return 0;
@@ -1286,7 +1577,10 @@ static int cmd_range_match(const char *range, const char *str)
} else {
unsigned long min, max, val;
- val = strtoul(str, &endptr, 10);
+ if (str[0] == '-')
+ return 0;
+
+ val = strtoul(str, &endptr, val_base);
if (*endptr != '\0')
return 0;
@@ -1294,11 +1588,13 @@ static int cmd_range_match(const char *range, const char *str)
p = strchr(range, '-');
if (p == NULL)
return 0;
- if (p - range > DECIMAL_STRLEN_MAX)
+ if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
return 0;
strncpy(buf, range, p - range);
buf[p - range] = '\0';
- min = strtoul(buf, &endptr, 10);
+ if ((min_base = check_base(buf)) < 0)
+ return 0;
+ min = strtoul(buf, &endptr, min_base);
if (*endptr != '\0')
return 0;
@@ -1306,11 +1602,13 @@ static int cmd_range_match(const char *range, const char *str)
p = strchr(range, '>');
if (p == NULL)
return 0;
- if (p - range > DECIMAL_STRLEN_MAX)
+ if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED)
return 0;
strncpy(buf, range, p - range);
buf[p - range] = '\0';
- max = strtoul(buf, &endptr, 10);
+ if ((max_base = check_base(buf)) < 0)
+ return 0;
+ max = strtoul(buf, &endptr, max_base);
if (*endptr != '\0')
return 0;
@@ -1318,6 +1616,14 @@ static int cmd_range_match(const char *range, const char *str)
return 0;
}
+ /* Don't allow ranges specified by min and max with different bases */
+ if (min_base != max_base)
+ return 0;
+ /* arg value passed must match the base of the range */
+ if (min_base != val_base)
+ return 0;
+
+ /* Everything's fine */
return 1;
}
@@ -1361,7 +1667,7 @@ cmd_match(const char *str, const char *command,
return VARARG_MATCH;
else if (CMD_RANGE(str))
{
- if (cmd_range_match(str, command))
+ if (vty_cmd_range_match(str, command))
return RANGE_MATCH;
}
#ifdef HAVE_IPV6
@@ -1558,7 +1864,7 @@ is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
}
break;
case RANGE_MATCH:
- if (cmd_range_match
+ if (vty_cmd_range_match
(str, command)) {
if (matched
&& strcmp(matched,
@@ -1651,7 +1957,7 @@ static const char *cmd_entry_function_desc(const char *src, const char *dst)
return dst;
if (CMD_RANGE(dst)) {
- if (cmd_range_match(dst, src))
+ if (vty_cmd_range_match(dst, src))
return dst;
else
return NULL;
@@ -1840,7 +2146,9 @@ cmd_describe_command_real(vector vline, struct vty *vty, int *status)
if (!cmd_element)
continue;
- if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN))
+ if (cmd_element->attr & CMD_ATTR_DEPRECATED)
+ continue;
+ if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN))
continue;
strvec = cmd_element->strvec;
@@ -2145,6 +2453,7 @@ static bool vty_pop_parent(struct vty *vty)
llist_del(&parent->entry);
vty->node = parent->node;
vty->priv = parent->priv;
+ vty->index = parent->index;
if (vty->indent)
talloc_free(vty->indent);
vty->indent = parent->indent;
@@ -2175,38 +2484,23 @@ static void vty_clear_parents(struct vty *vty)
int vty_go_parent(struct vty *vty)
{
switch (vty->node) {
- case AUTH_NODE:
- case VIEW_NODE:
- case ENABLE_NODE:
- case CONFIG_NODE:
- vty_clear_parents(vty);
- break;
-
- case AUTH_ENABLE_NODE:
- vty->node = VIEW_NODE;
- vty_clear_parents(vty);
- break;
+ case AUTH_NODE:
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ case CONFIG_NODE:
+ vty_clear_parents(vty);
+ break;
- case CFG_LOG_NODE:
- case VTY_NODE:
- vty->node = CONFIG_NODE;
- vty_clear_parents(vty);
- break;
+ case AUTH_ENABLE_NODE:
+ vty->node = VIEW_NODE;
+ vty_clear_parents(vty);
+ break;
- default:
- if (host.app_info->go_parent_cb) {
- host.app_info->go_parent_cb(vty);
- vty_pop_parent(vty);
- }
- else if (is_config_child(vty)) {
- vty->node = CONFIG_NODE;
- vty_clear_parents(vty);
- }
- else {
- vty->node = VIEW_NODE;
- vty_clear_parents(vty);
- }
- break;
+ default:
+ if (host.app_info->go_parent_cb)
+ host.app_info->go_parent_cb(vty);
+ vty_pop_parent(vty);
+ break;
}
return vty->node;
@@ -2365,9 +2659,31 @@ cmd_execute_command_real(vector vline, struct vty *vty,
if (matched_element->daemon)
rc = CMD_SUCCESS_DAEMON;
- else /* Execute matched command. */
+ else {
+ /* Execute matched command. */
+ struct vty_parent_node this_node = {
+ .node = vty->node,
+ .priv = vty->priv,
+ .index = vty->index,
+ .indent = vty->indent,
+ };
+ struct vty_parent_node *parent = vty_parent(vty);
rc = (*matched_element->func) (matched_element, vty, argc, argv);
+ /* If we have stepped down into a child node, push a parent frame.
+ * The causality is such: we don't expect every single node entry implementation to push
+ * a parent node entry onto vty->parent_nodes. Instead we expect vty_go_parent() to *pop*
+ * a parent node. Hence if the node changed without the parent node changing, we must
+ * have stepped into a child node. */
+ if (vty->node != this_node.node && parent == vty_parent(vty)
+ && vty->node > CONFIG_NODE) {
+ /* Push the parent node. */
+ parent = talloc_zero(vty, struct vty_parent_node);
+ *parent = this_node;
+ llist_add(&parent->entry, &vty->parent_nodes);
+ }
+ }
+
rc_free_deopt_ctx:
/* Now after we called the command func, we can free temporary strings */
talloc_free(cmd_deopt_ctx);
@@ -2624,6 +2940,7 @@ int config_from_file(struct vty *vty, FILE * fp)
this_node = (struct vty_parent_node){
.node = vty->node,
.priv = vty->priv,
+ .index = vty->index,
.indent = vty->indent,
};
@@ -2680,7 +2997,7 @@ return_invalid_indent:
/* Configration from terminal */
DEFUN(config_terminal,
config_terminal_cmd,
- "configure terminal",
+ "configure [terminal]",
"Configuration from vty interface\n" "Configuration terminal\n")
{
if (vty_config_lock(vty))
@@ -2694,7 +3011,10 @@ DEFUN(config_terminal,
}
/* Enable command */
-DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
+DEFUN(enable, config_enable_cmd,
+ "enable [expert-mode]",
+ "Turn on privileged mode command\n"
+ "Enable the expert mode (show hidden commands)\n")
{
/* If enable password is NULL, change to ENABLE_NODE */
if ((host.enable == NULL && host.enable_encrypt == NULL) ||
@@ -2703,6 +3023,8 @@ DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
else
vty->node = AUTH_ENABLE_NODE;
+ vty->expert_mode = argc > 0;
+
return CMD_SUCCESS;
}
@@ -2712,6 +3034,9 @@ DEFUN(disable,
{
if (vty->node == ENABLE_NODE)
vty->node = VIEW_NODE;
+
+ vty->expert_mode = false;
+
return CMD_SUCCESS;
}
@@ -2763,6 +3088,18 @@ gDEFUN(config_exit,
return CMD_SUCCESS;
}
+DEFUN(shutdown,
+ shutdown_cmd, "shutdown", "Request a shutdown of the program\n")
+{
+ LOGP(DLGLOBAL, LOGL_INFO, "Shutdown requested from telnet\n");
+ vty_out(vty, "%s is shutting down. Bye!%s", host.app_info->name, VTY_NEWLINE);
+
+ /* Same exit path as if it was killed by the service manager */
+ kill(getpid(), SIGTERM);
+
+ return CMD_SUCCESS;
+}
+
/* Show version. */
DEFUN(show_version,
show_version_cmd, "show version", SHOW_STR "Displays program version\n")
@@ -2778,7 +3115,24 @@ DEFUN(show_version,
DEFUN(show_online_help,
show_online_help_cmd, "show online-help", SHOW_STR "Online help\n")
{
- vty_dump_nodes(vty);
+ vty_dump_xml_ref_to_vty(vty);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_pid,
+ show_pid_cmd, "show pid", SHOW_STR "Displays the process ID\n")
+{
+ vty_out(vty, "%d%s", getpid(), VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_uptime,
+ show_uptime_cmd, "show uptime", SHOW_STR "Displays how long the program has been running\n")
+{
+ vty_out(vty, "%s has been running for ", host.app_info->name);
+ vty_out_uptime(vty, &starttime);
+ vty_out_newline(vty);
+
return CMD_SUCCESS;
}
@@ -2786,34 +3140,235 @@ DEFUN(show_online_help,
gDEFUN(config_help,
config_help_cmd, "help", "Description of the interactive help system\n")
{
- vty_out(vty,
- "This VTY provides advanced help features. When you need help,%s\
-anytime at the command line please press '?'.%s\
-%s\
-If nothing matches, the help list will be empty and you must backup%s\
- until entering a '?' shows the available options.%s\
-Two styles of help are provided:%s\
-1. Full help is available when you are ready to enter a%s\
-command argument (e.g. 'show ?') and describes each possible%s\
-argument.%s\
-2. Partial help is provided when an abbreviated argument is entered%s\
- and you want to know what arguments match the input%s\
- (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
- VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+ vty_out(vty, "This VTY provides advanced help features. When you need help,%s"
+ "anytime at the command line please press '?'.%s%s"
+ "If nothing matches, the help list will be empty and you must backup%s"
+ " until entering a '?' shows the available options.%s"
+ "Two styles of help are provided:%s"
+ "1. Full help is available when you are ready to enter a%s"
+ "command argument (e.g. 'show ?') and describes each possible%s"
+ "argument.%s"
+ "2. Partial help is provided when an abbreviated argument is entered%s"
+ " and you want to know what arguments match the input%s"
+ " (e.g. 'show me?'.)%s%s",
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+ VTY_NEWLINE);
return CMD_SUCCESS;
}
+enum {
+ ATTR_TYPE_GLOBAL = (1 << 0),
+ ATTR_TYPE_LIB = (1 << 1),
+ ATTR_TYPE_APP = (1 << 2),
+};
+
+static void print_attr_list(struct vty *vty, unsigned int attr_mask)
+{
+ const char *desc;
+ unsigned int i;
+ bool found;
+ char flag;
+
+ if (attr_mask & ATTR_TYPE_GLOBAL) {
+ vty_out(vty, " Global attributes:%s", VTY_NEWLINE);
+
+ for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) {
+ flag = cmd_attr_get_flag(cmd_attr_desc[i].value);
+ desc = cmd_attr_desc[i].str;
+
+ /* Skip attributes without flags */
+ if (flag != '.')
+ vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
+ }
+ }
+
+ if (attr_mask & ATTR_TYPE_LIB) {
+ vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE);
+
+ for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
+ if ((desc = cmd_lib_attr_desc[i]) == NULL)
+ continue;
+ found = true;
+
+ flag = cmd_lib_attr_letters[i];
+ if (flag == '\0')
+ flag = '.';
+
+ vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
+ }
+
+ if (!found)
+ vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
+ }
+
+ if (attr_mask & ATTR_TYPE_APP) {
+ vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE);
+
+ for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) {
+ if ((desc = host.app_info->usr_attr_desc[i]) == NULL)
+ continue;
+ found = true;
+
+ flag = host.app_info->usr_attr_letters[i];
+ if (flag == '\0')
+ flag = '.';
+
+ vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE);
+ }
+
+ if (!found)
+ vty_out(vty, " (no attributes)%s", VTY_NEWLINE);
+ }
+}
+
+gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd,
+ "show vty-attributes",
+ SHOW_STR "List of VTY attributes\n")
+{
+ print_attr_list(vty, 0xff);
+ return CMD_SUCCESS;
+}
+
+gDEFUN(show_vty_attr, show_vty_attr_cmd,
+ "show vty-attributes (application|library|global)",
+ SHOW_STR "List of VTY attributes\n"
+ "Application specific attributes only\n"
+ "Library specific attributes only\n"
+ "Global attributes only\n")
+{
+ unsigned int attr_mask = 0;
+
+ if (argv[0][0] == 'g') /* global */
+ attr_mask |= ATTR_TYPE_GLOBAL;
+ else if (argv[0][0] == 'l') /* library */
+ attr_mask |= ATTR_TYPE_LIB;
+ else if (argv[0][0] == 'a') /* application */
+ attr_mask |= ATTR_TYPE_APP;
+
+ print_attr_list(vty, attr_mask);
+ return CMD_SUCCESS;
+}
+
+/* Compose flag bit-mask for all commands within the given node */
+static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode)
+{
+ unsigned int flag_mask = 0x00;
+ unsigned int f, i;
+
+ for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
+ for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
+ const struct cmd_element *cmd;
+ char flag_letter;
+
+ if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
+ continue;
+ if (cmd->attr & CMD_ATTR_DEPRECATED)
+ continue;
+ if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
+ continue;
+ if (~cmd->usrattr & ((unsigned)1 << f))
+ continue;
+
+ if (cmd->attr & CMD_ATTR_LIB_COMMAND)
+ flag_letter = cmd_lib_attr_letters[f];
+ else
+ flag_letter = host.app_info->usr_attr_letters[f];
+
+ if (flag_letter == '\0')
+ continue;
+
+ flag_mask |= (1 << f);
+ break;
+ }
+ }
+
+ return flag_mask;
+}
+
+/* Compose global flag char-mask for the given command (e.g. "!" or "@") */
+static const char *cmd_gflag_mask(const struct cmd_element *cmd)
+{
+ static char char_mask[8 + 1];
+ char *ptr = &char_mask[0];
+
+ /* Mutually exclusive global attributes */
+ if (cmd->attr & CMD_ATTR_HIDDEN)
+ *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN);
+ else if (cmd->attr & CMD_ATTR_IMMEDIATE)
+ *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE);
+ else if (cmd->attr & CMD_ATTR_NODE_EXIT)
+ *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT);
+ else
+ *(ptr++) = '.';
+
+ *ptr = '\0';
+
+ return char_mask;
+}
+
+/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */
+static const char *cmd_flag_mask(const struct cmd_element *cmd,
+ unsigned int flag_mask)
+{
+ static char char_mask[VTY_CMD_USR_ATTR_NUM + 1];
+ char *ptr = &char_mask[0];
+ char flag_letter;
+ unsigned int f;
+
+ for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) {
+ if (~flag_mask & ((unsigned)1 << f))
+ continue;
+ if (~cmd->usrattr & ((unsigned)1 << f)) {
+ *(ptr++) = '.';
+ continue;
+ }
+
+ if (cmd->attr & CMD_ATTR_LIB_COMMAND)
+ flag_letter = cmd_lib_attr_letters[f];
+ else
+ flag_letter = host.app_info->usr_attr_letters[f];
+
+ *(ptr++) = flag_letter ? flag_letter : '.';
+ }
+
+ *ptr = '\0';
+
+ return char_mask;
+}
+
/* Help display function for all node. */
-gDEFUN(config_list, config_list_cmd, "list", "Print command list\n")
+gDEFUN(config_list, config_list_cmd,
+ "list [with-flags]",
+ "Print command list\n"
+ "Also print the VTY attribute flags\n")
{
unsigned int i;
struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
+ unsigned int flag_mask = 0x00;
struct cmd_element *cmd;
- for (i = 0; i < vector_active(cnode->cmd_vector); i++)
- if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
- && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
+ if (argc > 0)
+ flag_mask = node_flag_mask(cnode, vty->expert_mode);
+
+ for (i = 0; i < vector_active(cnode->cmd_vector); i++) {
+ if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL)
+ continue;
+ if (cmd->attr & CMD_ATTR_DEPRECATED)
+ continue;
+ if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN))
+ continue;
+ if (!argc)
vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
+ else {
+ vty_out(vty, " %s %s %s%s",
+ cmd_gflag_mask(cmd),
+ cmd_flag_mask(cmd, flag_mask),
+ cmd->string, VTY_NEWLINE);
+ }
+ }
+
return CMD_SUCCESS;
}
@@ -3302,16 +3857,7 @@ DEFUN(config_terminal_length, config_terminal_length_cmd,
"Set number of lines on a screen\n"
"Number of lines on screen (0 for no pausing)\n")
{
- int lines;
- char *endptr = NULL;
-
- lines = strtol(argv[0], &endptr, 10);
- if (lines < 0 || lines > 512 || *endptr != '\0') {
- vty_out(vty, "length is malformed%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- vty->lines = lines;
-
+ vty->lines = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -3330,16 +3876,7 @@ DEFUN(service_terminal_length, service_terminal_length_cmd,
"System wide terminal length configuration\n"
"Number of lines of VTY (0 means no line control)\n")
{
- int lines;
- char *endptr = NULL;
-
- lines = strtol(argv[0], &endptr, 10);
- if (lines < 0 || lines > 512 || *endptr != '\0') {
- vty_out(vty, "length is malformed%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- host.lines = lines;
-
+ host.lines = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -3760,7 +4297,12 @@ DEFUN(no_banner_motd,
/* Set config filename. Called from vty.c */
void host_config_set(const char *filename)
{
- host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
+ osmo_talloc_replace_string(tall_vty_cmd_ctx, &host.config, filename);
+}
+
+const char *host_config_file(void)
+{
+ return host.config;
}
/*! Deprecated, now happens implicitly when calling install_node().
@@ -3780,29 +4322,34 @@ void vty_install_default(int node)
/*! Install common commands like 'exit' and 'list'. */
static void install_basic_node_commands(int node)
{
- install_element(node, &config_help_cmd);
- install_element(node, &config_list_cmd);
+ install_lib_element(node, &config_help_cmd);
+ install_lib_element(node, &config_list_cmd);
+
+ install_lib_element(node, &show_vty_attr_all_cmd);
+ install_lib_element(node, &show_vty_attr_cmd);
- install_element(node, &config_write_terminal_cmd);
- install_element(node, &config_write_file_cmd);
- install_element(node, &config_write_memory_cmd);
- install_element(node, &config_write_cmd);
- install_element(node, &show_running_config_cmd);
+ install_lib_element(node, &config_write_terminal_cmd);
+ install_lib_element(node, &config_write_file_cmd);
+ install_lib_element(node, &config_write_memory_cmd);
+ install_lib_element(node, &config_write_cmd);
+ install_lib_element(node, &show_running_config_cmd);
- install_element(node, &config_exit_cmd);
+ install_lib_element(node, &config_exit_cmd);
if (node >= CONFIG_NODE) {
/* It's not a top node. */
- install_element(node, &config_end_cmd);
+ install_lib_element(node, &config_end_cmd);
}
}
/*! Return true if a node is installed by install_basic_node_commands(), so
* that we can avoid repeating them for each and every node during 'show
* running-config' */
-static bool vty_command_is_common(struct cmd_element *cmd)
+static bool vty_command_is_common(const struct cmd_element *cmd)
{
if (cmd == &config_help_cmd
+ || cmd == &show_vty_attr_all_cmd
+ || cmd == &show_vty_attr_cmd
|| cmd == &config_list_cmd
|| cmd == &config_write_terminal_cmd
|| cmd == &config_write_file_cmd
@@ -3880,55 +4427,92 @@ void cmd_init(int terminal)
install_node(&config_node, config_write_host);
/* Each node's basic commands. */
- install_element(VIEW_NODE, &show_version_cmd);
- install_element(VIEW_NODE, &show_online_help_cmd);
+ install_lib_element(VIEW_NODE, &show_pid_cmd);
+ install_lib_element(VIEW_NODE, &show_uptime_cmd);
+ install_lib_element(VIEW_NODE, &show_version_cmd);
+ install_lib_element(VIEW_NODE, &show_online_help_cmd);
if (terminal) {
- install_element(VIEW_NODE, &config_list_cmd);
- install_element(VIEW_NODE, &config_exit_cmd);
- install_element(VIEW_NODE, &config_help_cmd);
- install_element(VIEW_NODE, &config_enable_cmd);
- install_element(VIEW_NODE, &config_terminal_length_cmd);
- install_element(VIEW_NODE, &config_terminal_no_length_cmd);
- install_element(VIEW_NODE, &echo_cmd);
+ install_lib_element(VIEW_NODE, &config_list_cmd);
+ install_lib_element(VIEW_NODE, &config_exit_cmd);
+ install_lib_element(VIEW_NODE, &config_help_cmd);
+ install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd);
+ install_lib_element(VIEW_NODE, &show_vty_attr_cmd);
+ install_lib_element(VIEW_NODE, &config_enable_cmd);
+ install_lib_element(VIEW_NODE, &config_terminal_length_cmd);
+ install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd);
+ install_lib_element(VIEW_NODE, &echo_cmd);
}
if (terminal) {
- install_element(ENABLE_NODE, &config_disable_cmd);
- install_element(ENABLE_NODE, &config_terminal_cmd);
- install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
+ install_lib_element(ENABLE_NODE, &config_disable_cmd);
+ install_lib_element(ENABLE_NODE, &config_terminal_cmd);
+ install_lib_element(ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
+ install_lib_element(ENABLE_NODE, &shutdown_cmd);
}
- install_element (ENABLE_NODE, &show_startup_config_cmd);
- install_element(ENABLE_NODE, &show_version_cmd);
- install_element(ENABLE_NODE, &show_online_help_cmd);
+ install_lib_element(ENABLE_NODE, &show_startup_config_cmd);
+ install_lib_element(ENABLE_NODE, &show_version_cmd);
+ install_lib_element(ENABLE_NODE, &show_online_help_cmd);
if (terminal) {
- install_element(ENABLE_NODE, &config_terminal_length_cmd);
- install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
- install_element(ENABLE_NODE, &echo_cmd);
+ install_lib_element(ENABLE_NODE, &config_terminal_length_cmd);
+ install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd);
+ install_lib_element(ENABLE_NODE, &echo_cmd);
}
- install_element(CONFIG_NODE, &hostname_cmd);
- install_element(CONFIG_NODE, &no_hostname_cmd);
+ install_lib_element(CONFIG_NODE, &hostname_cmd);
+ install_lib_element(CONFIG_NODE, &no_hostname_cmd);
if (terminal) {
- install_element(CONFIG_NODE, &password_cmd);
- install_element(CONFIG_NODE, &password_text_cmd);
- install_element(CONFIG_NODE, &enable_password_cmd);
- install_element(CONFIG_NODE, &enable_password_text_cmd);
- install_element(CONFIG_NODE, &no_enable_password_cmd);
+ install_lib_element(CONFIG_NODE, &password_cmd);
+ install_lib_element(CONFIG_NODE, &password_text_cmd);
+ install_lib_element(CONFIG_NODE, &enable_password_cmd);
+ install_lib_element(CONFIG_NODE, &enable_password_text_cmd);
+ install_lib_element(CONFIG_NODE, &no_enable_password_cmd);
#ifdef VTY_CRYPT_PW
- install_element(CONFIG_NODE, &service_password_encrypt_cmd);
- install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
+ install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd);
+ install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
#endif
- install_element(CONFIG_NODE, &banner_motd_default_cmd);
- install_element(CONFIG_NODE, &banner_motd_file_cmd);
- install_element(CONFIG_NODE, &no_banner_motd_cmd);
- install_element(CONFIG_NODE, &service_terminal_length_cmd);
- install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
+ install_lib_element(CONFIG_NODE, &banner_motd_default_cmd);
+ install_lib_element(CONFIG_NODE, &banner_motd_file_cmd);
+ install_lib_element(CONFIG_NODE, &no_banner_motd_cmd);
+ install_lib_element(CONFIG_NODE, &service_terminal_length_cmd);
+ install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd);
}
srand(time(NULL));
}
+static __attribute__((constructor)) void on_dso_load_starttime(void)
+{
+ osmo_clock_gettime(CLOCK_MONOTONIC, &starttime);
+}
+
+/* FIXME: execute this section in the unit test instead */
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ unsigned int i, j;
+
+ /* Check total number of the library specific attributes */
+ OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32);
+
+ /* Check for duplicates in the list of library specific flags */
+ for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) {
+ if (cmd_lib_attr_letters[i] == '\0')
+ continue;
+
+ /* Some flag characters are reserved for global attributes */
+ const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
+ for (j = 0; j < ARRAY_SIZE(rafc); j++)
+ OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]);
+
+ /* Only upper case flag letters are allowed for libraries */
+ OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A');
+ OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z');
+
+ for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++)
+ OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]);
+ }
+}
+
/*! @} */
diff --git a/src/vty/cpu_sched_vty.c b/src/vty/cpu_sched_vty.c
new file mode 100644
index 00000000..7198f747
--- /dev/null
+++ b/src/vty/cpu_sched_vty.c
@@ -0,0 +1,682 @@
+/*! \file cpu_sched_vty.c
+ * Implementation to CPU / Threading / Scheduler properties from VTY configuration.
+ */
+/* (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPLv2+
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sched.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <inttypes.h>
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/tdef_vty.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/linuxlist.h>
+
+/*! \addtogroup Tdef_VTY
+ *
+ * CPU Scheduling related VTY API.
+ *
+ * @{
+ * \file cpu_sched_vty.c
+ */
+
+enum sched_vty_thread_id {
+ SCHED_VTY_THREAD_SELF,
+ SCHED_VTY_THREAD_ALL,
+ SCHED_VTY_THREAD_ID,
+ SCHED_VTY_THREAD_NAME,
+ SCHED_VTY_THREAD_UNKNOWN,
+};
+
+struct cpu_affinity_it {
+ struct llist_head entry;
+ enum sched_vty_thread_id tid_type;
+ char bufname[64];
+ cpu_set_t *cpuset;
+ size_t cpuset_size;
+ bool delay;
+};
+
+struct sched_vty_opts {
+ void *tall_ctx;
+ int sched_rr_prio;
+ struct llist_head cpu_affinity_li;
+ pthread_mutex_t cpu_affinity_li_mutex;
+};
+
+static struct sched_vty_opts *sched_vty_opts;
+
+static struct cmd_node sched_node = {
+ L_CPU_SCHED_NODE,
+ "%s(config-cpu-sched)# ",
+ 1,
+};
+
+/* returns number of configured CPUs in the system, or negative otherwise */
+static int get_num_cpus(void) {
+ static unsigned int num_cpus = 0;
+ long ln;
+
+ if (num_cpus)
+ return num_cpus;
+
+ /* This is expensive (goes across /sys, so let's do it only once. It is
+ * guaranteed it won't change during process span anyway). */
+ ln = sysconf(_SC_NPROCESSORS_CONF);
+ if (ln < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "sysconf(_SC_NPROCESSORS_CONF) failed: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ num_cpus = (unsigned int) ln;
+ return num_cpus;
+}
+
+/* Parses string with CPU hex Affinity Mask, with right-most bit being CPU0, and
+ * fills a cpuset of size cpuset_size.
+ */
+static int parse_cpu_hex_mask(const char *str, cpu_set_t *cpuset, size_t cpuset_size)
+{
+ int len = strlen(str);
+ const char *ptr = str + len - 1;
+ int cpu = 0;
+
+ /* skip optional '0x' prefix format */
+ if (len >= 2 && str[0] == '0' && str[1] == 'x')
+ str += 2;
+ CPU_ZERO_S(cpuset_size, cpuset);
+
+ while (ptr >= str) {
+ char c = *ptr;
+ uint8_t val;
+
+ if (c >= '0' && c <= '9') {
+ val = c - '0';
+ } else {
+ c = (char)tolower((int)c);
+ if (c >= 'a' && c <= 'f')
+ val = c + (10 - 'a');
+ else
+ return -1;
+ }
+ if (val & 0x01)
+ CPU_SET_S(cpu, cpuset_size, cpuset);
+ if (val & 0x02)
+ CPU_SET_S(cpu + 1, cpuset_size, cpuset);
+ if (val & 0x04)
+ CPU_SET_S(cpu + 2, cpuset_size, cpuset);
+ if (val & 0x08)
+ CPU_SET_S(cpu + 3, cpuset_size, cpuset);
+ ptr--;
+ cpu += 4;
+ }
+
+ return 0;
+}
+
+/* Generates a hexstring in str from cpuset of size cpuset_size */
+static int generate_cpu_hex_mask(char *str, size_t str_buf_size,
+ cpu_set_t *cpuset, size_t cpuset_size)
+{
+ char *ptr = str;
+ int cpu;
+ bool first_nonzero_found = false;
+
+ /* 2 char per byte, + '0x' prefix + '\0' */
+ if (cpuset_size * 2 + 2 + 1 > str_buf_size)
+ return -1;
+
+ *ptr++ = '0';
+ *ptr++ = 'x';
+
+ for (cpu = cpuset_size*8 - 4; cpu >= 0; cpu -= 4) {
+ uint8_t val = 0;
+
+ if (CPU_ISSET_S(cpu, cpuset_size, cpuset))
+ val |= 0x01;
+ if (CPU_ISSET_S(cpu + 1, cpuset_size, cpuset))
+ val |= 0x02;
+ if (CPU_ISSET_S(cpu + 2, cpuset_size, cpuset))
+ val |= 0x04;
+ if (CPU_ISSET_S(cpu + 3, cpuset_size, cpuset))
+ val |= 0x08;
+
+ if (val < 10)
+ *ptr = '0' + val;
+ else
+ *ptr = ('a' - 10) + val;
+ if (val)
+ first_nonzero_found = true;
+ if (first_nonzero_found)
+ ptr++;
+
+ }
+ if (!first_nonzero_found)
+ *ptr++ = '0';
+ *ptr = '\0';
+ return 0;
+}
+
+/* Checks whther a thread identified by tid exists and belongs to the running process */
+static bool proc_tid_exists(pid_t tid)
+{
+ DIR *proc_dir;
+ struct dirent *entry;
+ char dirname[100];
+ int tid_it;
+ bool found = false;
+
+ snprintf(dirname, sizeof(dirname), "/proc/%ld/task", (long int)getpid());
+ proc_dir = opendir(dirname);
+ if (!proc_dir)
+ return false; /*FIXME; print error */
+
+ while ((entry = readdir(proc_dir))) {
+ if (entry->d_name[0] == '.')
+ continue;
+ tid_it = atoi(entry->d_name);
+ if (tid_it == tid) {
+ found = true;
+ break;
+ }
+ }
+
+ closedir(proc_dir);
+ return found;
+}
+
+/* Checks whther a thread identified by name exists and belongs to the running
+ * process, and returns its disocevered TID in res_pid.
+ */
+static bool proc_name_exists(const char *name, pid_t *res_pid)
+{
+ DIR *proc_dir;
+ struct dirent *entry;
+ char path[100];
+ char buf[17]; /* 15 + \n + \0 */
+ int tid_it;
+ int fd;
+ pid_t mypid = getpid();
+ bool found = false;
+ int rc;
+
+ *res_pid = 0;
+
+ snprintf(path, sizeof(path), "/proc/%ld/task", (long int)mypid);
+ proc_dir = opendir(path);
+ if (!proc_dir)
+ return false;
+
+ while ((entry = readdir(proc_dir)))
+ {
+ if (entry->d_name[0] == '.')
+ continue;
+
+ tid_it = atoi(entry->d_name);
+ snprintf(path, sizeof(path), "/proc/%ld/task/%ld/comm", (long int)mypid, (long int) tid_it);
+ if ((fd = open(path, O_RDONLY)) == -1)
+ continue;
+ rc = read(fd, buf, sizeof(buf) - 1);
+ if (rc >= 0) {
+ /* Last may char contain a '\n', get rid of it */
+ if (rc > 0 && buf[rc - 1] == '\n')
+ buf[rc - 1] = '\0';
+ else
+ buf[rc] = '\0';
+ if (strcmp(name, buf) == 0) {
+ *res_pid = tid_it;
+ found = true;
+ }
+ }
+ close(fd);
+
+ if (found)
+ break;
+ }
+
+ closedir(proc_dir);
+ return found;
+}
+
+/* Parse VTY THREADNAME variable, return its type and fill discovered res_pid if required */
+static enum sched_vty_thread_id procname2pid(pid_t *res_pid, const char *str, bool applynow)
+{
+ size_t i, len;
+ bool is_pid = true;
+
+ if (strcmp(str, "all") == 0) {
+ *res_pid = 0;
+ return SCHED_VTY_THREAD_ALL;
+ }
+
+ if (strcmp(str, "self") == 0) {
+ *res_pid = 0;
+ return SCHED_VTY_THREAD_SELF;
+ }
+
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if (!isdigit(str[i])) {
+ is_pid = false;
+ break;
+ }
+ }
+ if (is_pid) {
+ int64_t val;
+ if (osmo_str_to_int64(&val, str, 0, 0, INT64_MAX))
+ return SCHED_VTY_THREAD_UNKNOWN;
+ *res_pid = (pid_t)val;
+ if (*res_pid != val)
+ return SCHED_VTY_THREAD_UNKNOWN;
+ if (!applynow || proc_tid_exists(*res_pid))
+ return SCHED_VTY_THREAD_ID;
+ else
+ return SCHED_VTY_THREAD_UNKNOWN;
+ }
+
+ if (len > 15) {
+ /* Thread names only allow up to 15+1 null chars, see man pthread_setname_np */
+ return SCHED_VTY_THREAD_UNKNOWN;
+ }
+
+ if (applynow) {
+ if (proc_name_exists(str, res_pid))
+ return SCHED_VTY_THREAD_NAME;
+ else
+ return SCHED_VTY_THREAD_UNKNOWN;
+ } else {
+ /* assume a thread will be named after it */
+ *res_pid = 0;
+ return SCHED_VTY_THREAD_NAME;
+ }
+}
+
+/* Wrapper for sched_setaffinity applying to single thread or all threads in process based on tid_type. */
+static int my_sched_setaffinity(enum sched_vty_thread_id tid_type, pid_t pid,
+ cpu_set_t *cpuset, size_t cpuset_size)
+{
+ DIR *proc_dir;
+ struct dirent *entry;
+ char dirname[100];
+ char str_mask[1024];
+ int tid_it;
+ int rc = 0;
+
+ if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), cpuset, cpuset_size) < 0)
+ str_mask[0] = '\0';
+
+ if (tid_type != SCHED_VTY_THREAD_ALL) {
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Setting CPU affinity mask for tid %lu to: %s\n",
+ (unsigned long) pid, str_mask);
+
+ rc = sched_setaffinity(pid, sizeof(cpu_set_t), cpuset);
+ return rc;
+ }
+
+ snprintf(dirname, sizeof(dirname), "/proc/%ld/task", (long int)getpid());
+ proc_dir = opendir(dirname);
+ if (!proc_dir)
+ return -EINVAL;
+
+ while ((entry = readdir(proc_dir)))
+ {
+ if (entry->d_name[0] == '.')
+ continue;
+ tid_it = atoi(entry->d_name);
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Setting CPU affinity mask for tid %lu to: %s\n",
+ (unsigned long) tid_it, str_mask);
+
+ rc = sched_setaffinity(tid_it, sizeof(cpu_set_t), cpuset);
+ if (rc == -1)
+ break;
+ }
+
+ closedir(proc_dir);
+ return rc;
+
+}
+
+DEFUN_ATTR(cfg_sched_cpu_affinity, cfg_sched_cpu_affinity_cmd,
+ "cpu-affinity (self|all|<0-4294967295>|THREADNAME) CPUHEXMASK [delay]",
+ "Set CPU affinity mask on a (group of) thread(s)\n"
+ "Set CPU affinity mask on thread running the VTY\n"
+ "Set CPU affinity mask on all process' threads\n"
+ "Set CPU affinity mask on a thread with specified PID\n"
+ "Set CPU affinity mask on a thread with specified thread name\n"
+ "CPU affinity mask\n"
+ "If set, delay applying the affinity mask now and let the app handle it at a later point\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ const char* str_who = argv[0];
+ const char *str_mask = argv[1];
+ bool applynow = (argc != 3);
+ int rc;
+ pid_t pid;
+ enum sched_vty_thread_id tid_type;
+ struct cpu_affinity_it *it, *it_next;
+ cpu_set_t *cpuset;
+ size_t cpuset_size;
+
+ tid_type = procname2pid(&pid, str_who, applynow);
+ if (tid_type == SCHED_VTY_THREAD_UNKNOWN) {
+ vty_out(vty, "%% Failed parsing target thread %s%s",
+ str_who, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (tid_type == SCHED_VTY_THREAD_ID && !applynow) {
+ vty_out(vty, "%% It makes no sense to delay applying cpu-affinity on tid %lu%s",
+ (unsigned long)pid, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (tid_type == SCHED_VTY_THREAD_ALL && !applynow) {
+ vty_out(vty, "%% It makes no sense to delay applying cpu-affinity on all threads%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ cpuset = CPU_ALLOC(get_num_cpus());
+ cpuset_size = CPU_ALLOC_SIZE(get_num_cpus());
+ if (parse_cpu_hex_mask(str_mask, cpuset, cpuset_size) < 0) {
+ vty_out(vty, "%% Failed parsing CPU Affinity Mask %s%s",
+ str_mask, VTY_NEWLINE);
+ CPU_FREE(cpuset);
+ return CMD_WARNING;
+ }
+
+ if (applynow) {
+ rc = my_sched_setaffinity(tid_type, pid, cpuset, cpuset_size);
+ if (rc == -1) {
+ vty_out(vty, "%% Failed setting sched CPU Affinity Mask %s: %s%s",
+ str_mask, strerror(errno), VTY_NEWLINE);
+ CPU_FREE(cpuset);
+ return CMD_WARNING;
+ }
+ }
+
+ /* Keep history of cmds applied to be able to rewrite config. If PID was passed
+ directly it makes no sense to store it since PIDs are temporary */
+ if (tid_type == SCHED_VTY_THREAD_SELF ||
+ tid_type == SCHED_VTY_THREAD_ALL ||
+ tid_type == SCHED_VTY_THREAD_NAME) {
+ pthread_mutex_lock(&sched_vty_opts->cpu_affinity_li_mutex);
+
+ /* Drop previous entries matching, since they will be overwritten */
+ llist_for_each_entry_safe(it, it_next, &sched_vty_opts->cpu_affinity_li, entry) {
+ if (strcmp(it->bufname, str_who) == 0) {
+ llist_del(&it->entry);
+ CPU_FREE(it->cpuset);
+ talloc_free(it);
+ break;
+ }
+ }
+ it = talloc_zero(sched_vty_opts->tall_ctx, struct cpu_affinity_it);
+ OSMO_STRLCPY_ARRAY(it->bufname, str_who);
+ it->tid_type = tid_type;
+ it->cpuset = cpuset;
+ it->cpuset_size = cpuset_size;
+ it->delay = !applynow;
+ llist_add_tail(&it->entry, &sched_vty_opts->cpu_affinity_li);
+
+ pthread_mutex_unlock(&sched_vty_opts->cpu_affinity_li_mutex);
+ } else {
+ /* We don't need cpuset for later, free it: */
+ CPU_FREE(cpuset);
+ }
+ return CMD_SUCCESS;
+}
+
+static int set_sched_rr(unsigned int prio)
+{
+ struct sched_param param;
+ int rc;
+ memset(&param, 0, sizeof(param));
+ param.sched_priority = prio;
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Setting SCHED_RR priority %d\n", param.sched_priority);
+ rc = sched_setscheduler(getpid(), SCHED_RR, &param);
+ if (rc == -1) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Setting SCHED_RR priority %d failed: %s\n",
+ param.sched_priority, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+DEFUN_ATTR(cfg_sched_policy, cfg_sched_policy_cmd,
+ "policy rr <1-32>",
+ "Set the scheduling policy to use for the process\n"
+ "Use the SCHED_RR real-time scheduling algorithm\n"
+ "Set the SCHED_RR real-time priority\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ sched_vty_opts->sched_rr_prio = atoi(argv[0]);
+
+ if (set_sched_rr(sched_vty_opts->sched_rr_prio) < 0) {
+ vty_out(vty, "%% Failed setting SCHED_RR priority %d%s",
+ sched_vty_opts->sched_rr_prio, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_sched,
+ cfg_sched_cmd,
+ "cpu-sched", "Configure CPU Scheduler related settings")
+{
+ vty->index = NULL;
+ vty->node = L_CPU_SCHED_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_sched_threads, show_sched_threads_cmd,
+ "show cpu-sched threads",
+ SHOW_STR
+ "Show Sched section information\n"
+ "Show information about running threads)\n")
+{
+ DIR *proc_dir;
+ struct dirent *entry;
+ char path[100];
+ char name[17];
+ char str_mask[1024];
+ int tid_it;
+ int fd;
+ pid_t mypid = getpid();
+ int rc;
+ cpu_set_t *cpuset;
+ size_t cpuset_size;
+
+ vty_out(vty, "Thread list for PID %lu:%s", (unsigned long) mypid, VTY_NEWLINE);
+
+ snprintf(path, sizeof(path), "/proc/%ld/task", (long int)mypid);
+ proc_dir = opendir(path);
+ if (!proc_dir) {
+ vty_out(vty, "%% Failed opening dir%s%s", path, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ while ((entry = readdir(proc_dir)))
+ {
+ if (entry->d_name[0] == '.')
+ continue;
+
+ tid_it = atoi(entry->d_name);
+ snprintf(path, sizeof(path), "/proc/%ld/task/%ld/comm", (long int)mypid, (long int)tid_it);
+ if ((fd = open(path, O_RDONLY)) != -1) {
+ rc = read(fd, name, sizeof(name) - 1);
+ if (rc >= 0) {
+ /* Last may char contain a '\n', get rid of it */
+ if (rc > 0 && name[rc - 1] == '\n')
+ name[rc - 1] = '\0';
+ else
+ name[rc] = '\0';
+ }
+ close(fd);
+ } else {
+ name[0] = '\0';
+ }
+
+ str_mask[0] = '\0';
+ cpuset = CPU_ALLOC(get_num_cpus());
+ cpuset_size = CPU_ALLOC_SIZE(get_num_cpus());
+ CPU_ZERO_S(cpuset_size, cpuset);
+ if (sched_getaffinity(tid_it, cpuset_size, cpuset) == 0) {
+ if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), cpuset, cpuset_size) < 0)
+ str_mask[0] = '\0';
+ }
+ CPU_FREE(cpuset);
+
+ vty_out(vty, " TID: %lu, NAME: '%s', cpu-affinity: %s%s",
+ (unsigned long) tid_it, name, str_mask, VTY_NEWLINE);
+ }
+
+ closedir(proc_dir);
+ return CMD_SUCCESS;
+}
+
+static int config_write_sched(struct vty *vty)
+{
+ struct cpu_affinity_it *it;
+ char str_mask[1024];
+
+ /* Only add the node if there's something to write under it */
+ if (sched_vty_opts->sched_rr_prio || !llist_empty(&sched_vty_opts->cpu_affinity_li))
+ vty_out(vty, "cpu-sched%s", VTY_NEWLINE);
+
+ if (sched_vty_opts->sched_rr_prio)
+ vty_out(vty, " policy rr %d%s", sched_vty_opts->sched_rr_prio, VTY_NEWLINE);
+
+ llist_for_each_entry(it, &sched_vty_opts->cpu_affinity_li, entry) {
+ if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), it->cpuset, it->cpuset_size) < 0)
+ OSMO_STRLCPY_ARRAY(str_mask, "ERROR");
+ vty_out(vty, " cpu-affinity %s %s%s%s", it->bufname, str_mask,
+ it->delay ? " delay" : "", VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/*! Initialize sched VTY nodes
+ * \param[in] tall_ctx Talloc context to use internally by vty_sched subsystem.
+ * \return 0 on success, non-zero on error.
+ */
+int osmo_cpu_sched_vty_init(void *tall_ctx)
+{
+ OSMO_ASSERT(!sched_vty_opts); /* assert only called once */
+
+ sched_vty_opts = talloc_zero(tall_ctx, struct sched_vty_opts);
+ sched_vty_opts->tall_ctx = tall_ctx;
+ INIT_LLIST_HEAD(&sched_vty_opts->cpu_affinity_li);
+ pthread_mutex_init(&sched_vty_opts->cpu_affinity_li_mutex, NULL);
+
+ install_lib_element(CONFIG_NODE, &cfg_sched_cmd);
+ install_node(&sched_node, config_write_sched);
+
+ install_lib_element(L_CPU_SCHED_NODE, &cfg_sched_policy_cmd);
+ install_lib_element(L_CPU_SCHED_NODE, &cfg_sched_cpu_affinity_cmd);
+
+ install_lib_element_ve(&show_sched_threads_cmd);
+
+ /* Initialize amount of cpus now */
+ if (get_num_cpus() < 0)
+ return -1;
+
+ return 0;
+}
+
+/*! Apply cpu-affinity on calling thread based on VTY configuration
+ * \return 0 on success, non-zero on error.
+ */
+int osmo_cpu_sched_vty_apply_localthread(void)
+{
+ struct cpu_affinity_it *it, *it_match = NULL;
+ char name[16]; /* 15 + \0 */
+ char str_mask[1024];
+ bool has_name = false;
+ int rc = 0;
+
+ /* Assert subsystem was inited and structs are preset */
+ if (!sched_vty_opts) {
+ LOGP(DLGLOBAL, LOGL_FATAL, "Setting cpu-affinity mask impossible: no opts!\n");
+ return 0;
+ }
+
+#ifdef HAVE_PTHREAD_GETNAME_NP
+ if (pthread_getname_np(pthread_self(), name, sizeof(name)) == 0)
+ has_name = true;
+#endif
+
+ /* Get latest matching mask for the thread */
+ pthread_mutex_lock(&sched_vty_opts->cpu_affinity_li_mutex);
+ llist_for_each_entry(it, &sched_vty_opts->cpu_affinity_li, entry) {
+ switch (it->tid_type) {
+ case SCHED_VTY_THREAD_SELF:
+ continue; /* self to the VTY thread, not us */
+ case SCHED_VTY_THREAD_ALL:
+ it_match = it;
+ break;
+ case SCHED_VTY_THREAD_NAME:
+ if (!has_name)
+ continue;
+ if (strcmp(name, it->bufname) != 0)
+ continue;
+ it_match = it;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ }
+
+ if (it_match) {
+ rc = my_sched_setaffinity(SCHED_VTY_THREAD_SELF, 0, it_match->cpuset, it_match->cpuset_size);
+ if (rc == -1) {
+ if (generate_cpu_hex_mask(str_mask, sizeof(str_mask),
+ it_match->cpuset, it_match->cpuset_size) < 0)
+ str_mask[0] = '\0';
+ LOGP(DLGLOBAL, LOGL_FATAL, "Setting cpu-affinity mask %s failed: %s\n",
+ str_mask, strerror(errno));
+ }
+ }
+ pthread_mutex_unlock(&sched_vty_opts->cpu_affinity_li_mutex);
+ return rc;
+}
+
+/*! @} */
diff --git a/src/vty/fsm_vty.c b/src/vty/fsm_vty.c
index 9bde241c..da6038fa 100644
--- a/src/vty/fsm_vty.c
+++ b/src/vty/fsm_vty.c
@@ -14,16 +14,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
#include <string.h>
-#include "../../config.h"
+#include "config.h"
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
@@ -51,62 +47,82 @@ extern struct llist_head osmo_g_fsms;
/*! Print information about a FSM [class] to the given VTY
* \param vty The VTY to which to print
+ * \param[in] prefix prefix to print at start of each line (typically indenting)
* \param[in] fsm The FSM class to print
*/
-void vty_out_fsm(struct vty *vty, struct osmo_fsm *fsm)
+void vty_out_fsm2(struct vty *vty, const char *prefix, struct osmo_fsm *fsm)
{
unsigned int i;
const struct value_string *evt_name;
- vty_out(vty, "FSM Name: '%s', Log Subsys: '%s'%s", fsm->name,
+ vty_out(vty, "%sFSM Name: '%s', Log Subsys: '%s'%s", prefix, fsm->name,
log_category_name(fsm->log_subsys), VTY_NEWLINE);
/* list the events */
if (fsm->event_names) {
for (evt_name = fsm->event_names; evt_name->str != NULL; evt_name++) {
- vty_out(vty, " Event %02u (0x%08x): '%s'%s", evt_name->value,
- (1 << evt_name->value), evt_name->str, VTY_NEWLINE);
+ vty_out(vty, "%s Event %02u (0x%08x): '%s'%s", prefix, evt_name->value,
+ (1U << evt_name->value), evt_name->str, VTY_NEWLINE);
}
} else
- vty_out(vty, " No event names are defined for this FSM! Please fix!%s", VTY_NEWLINE);
+ vty_out(vty, "%s No event names are defined for this FSM! Please fix!%s", prefix, VTY_NEWLINE);
/* list the states */
- vty_out(vty, " Number of States: %u%s", fsm->num_states, VTY_NEWLINE);
+ vty_out(vty, "%s Number of States: %u%s", prefix, fsm->num_states, VTY_NEWLINE);
for (i = 0; i < fsm->num_states; i++) {
const struct osmo_fsm_state *state = &fsm->states[i];
- vty_out(vty, " State %-20s InEvtMask: 0x%08x, OutStateMask: 0x%08x%s",
+ vty_out(vty, "%s State %-20s InEvtMask: 0x%08x, OutStateMask: 0x%08x%s", prefix,
state->name, state->in_event_mask, state->out_state_mask,
VTY_NEWLINE);
}
}
+/*! Print information about a FSM [class] to the given VTY
+ * \param vty The VTY to which to print
+ * \param[in] fsm The FSM class to print
+ */
+void vty_out_fsm(struct vty *vty, struct osmo_fsm *fsm)
+{
+ vty_out_fsm2(vty, "", fsm);
+}
+
/*! Print a FSM instance to the given VTY
* \param vty The VTY to which to print
+ * \param[in] prefix prefix to print at start of each line (typically indenting)
* \param[in] fsmi The FSM instance to print
*/
-void vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi)
+void vty_out_fsm_inst2(struct vty *vty, const char *prefix, struct osmo_fsm_inst *fsmi)
{
struct osmo_fsm_inst *child;
- vty_out(vty, "FSM Instance Name: '%s', ID: '%s'%s",
+ vty_out(vty, "%sFSM Instance Name: '%s', ID: '%s'%s", prefix,
fsmi->name, fsmi->id, VTY_NEWLINE);
- vty_out(vty, " Log-Level: '%s', State: '%s'%s",
+ vty_out(vty, "%s Log-Level: '%s', State: '%s'%s", prefix,
log_level_str(fsmi->log_level),
osmo_fsm_state_name(fsmi->fsm, fsmi->state),
VTY_NEWLINE);
if (fsmi->T)
- vty_out(vty, " Timer: %u%s", fsmi->T, VTY_NEWLINE);
+ vty_out(vty, "%s Timer: %u%s", prefix, fsmi->T, VTY_NEWLINE);
if (fsmi->proc.parent) {
- vty_out(vty, " Parent: '%s', Term-Event: '%s'%s",
+ vty_out(vty, "%s Parent: '%s', Term-Event: '%s'%s", prefix,
fsmi->proc.parent->name,
osmo_fsm_event_name(fsmi->proc.parent->fsm,
fsmi->proc.parent_term_event),
VTY_NEWLINE);
}
llist_for_each_entry(child, &fsmi->proc.children, proc.child) {
- vty_out(vty, " Child: '%s'%s", child->name, VTY_NEWLINE);
+ vty_out(vty, "%s Child: '%s'%s", prefix, child->name, VTY_NEWLINE);
}
}
+/*! Print a FSM instance to the given VTY
+ * \param vty The VTY to which to print
+ * \param[in] fsmi The FSM instance to print
+ */
+void vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi)
+{
+ vty_out_fsm_inst2(vty, "", fsmi);
+}
+
#define SH_FSM_STR SHOW_STR "Show information about finite state machines\n"
#define SH_FSMI_STR SHOW_STR "Show information about finite state machine instances\n"
@@ -196,9 +212,9 @@ void osmo_fsm_vty_add_cmds(void)
if (osmo_fsm_vty_cmds_installed)
return;
- install_element_ve(&show_fsm_cmd);
- install_element_ve(&show_fsms_cmd);
- install_element_ve(&show_fsm_inst_cmd);
- install_element_ve(&show_fsm_insts_cmd);
+ install_lib_element_ve(&show_fsm_cmd);
+ install_lib_element_ve(&show_fsms_cmd);
+ install_lib_element_ve(&show_fsm_inst_cmd);
+ install_lib_element_ve(&show_fsm_insts_cmd);
osmo_fsm_vty_cmds_installed = true;
}
diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c
index 88ee330a..678ae686 100644
--- a/src/vty/logging_vty.c
+++ b/src/vty/logging_vty.c
@@ -15,16 +15,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
#include <string.h>
-#include "../../config.h"
+#include "config.h"
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
@@ -33,6 +29,7 @@
#include <osmocom/core/strrb.h>
#include <osmocom/core/loggingrb.h>
#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/application.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
@@ -130,7 +127,7 @@ DEFUN(enable_logging,
conn = (struct telnet_connection *) vty->priv;
if (conn->dbg) {
- vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
+ vty_out(vty, "%% Logging already enabled.%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -157,7 +154,7 @@ struct log_target *osmo_log_vty2tgt(struct vty *vty)
conn = (struct telnet_connection *) vty->priv;
if (!conn->dbg)
- vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ vty_out(vty, "%% Logging was not enabled.%s", VTY_NEWLINE);
return conn->dbg;
}
@@ -224,6 +221,22 @@ DEFUN(logging_prnt_ext_timestamp,
RET_WITH_UNLOCK(CMD_SUCCESS);
}
+DEFUN(logging_prnt_tid,
+ logging_prnt_tid_cmd,
+ "logging print thread-id (0|1)",
+ LOGGING_STR "Log output settings\n"
+ "Configure log message logging Thread ID\n"
+ "Don't prefix each log message\n"
+ "Prefix each log message with current Thread ID\n")
+{
+ struct log_target *tgt;
+
+ ACQUIRE_VTY_LOG_TGT_WITH_LOCK(vty, tgt);
+
+ log_set_print_tid(tgt, atoi(argv[0]));
+ RET_WITH_UNLOCK(CMD_SUCCESS);
+}
+
DEFUN(logging_prnt_cat,
logging_prnt_cat_cmd,
"logging print category (0|1)",
@@ -337,6 +350,9 @@ static void gen_logging_level_cmd_strs(struct cmd_element *cmd,
osmo_talloc_asprintf(tall_log_ctx, cmd_str, ") %s", level_args);
osmo_talloc_asprintf(tall_log_ctx, doc_str, "%s", level_strs);
+ talloc_set_name_const(cmd_str, "vty_log_level_cmd_str");
+ talloc_set_name_const(doc_str, "vty_log_level_doc_str");
+
cmd->string = cmd_str;
cmd->doc = doc_str;
}
@@ -351,21 +367,25 @@ DEFUN(logging_level,
int category = log_parse_category(argv[0]);
int level = log_parse_level(argv[1]);
- ACQUIRE_VTY_LOG_TGT_WITH_LOCK(vty, tgt);
-
if (level < 0) {
- vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
- RET_WITH_UNLOCK(CMD_WARNING);
+ vty_out(vty, "%% Invalid level '%s'%s", argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
}
if (category < 0) {
- vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
- RET_WITH_UNLOCK(CMD_WARNING);
+ vty_out(vty, "%% Invalid category '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
}
+ ACQUIRE_VTY_LOG_TGT_WITH_LOCK(vty, tgt);
+
tgt->categories[category].enabled = 1;
tgt->categories[category].loglevel = level;
+#if !defined(EMBEDDED)
+ log_cache_update(category, 1, level);
+#endif
+
RET_WITH_UNLOCK(CMD_SUCCESS);
}
@@ -390,6 +410,9 @@ DEFUN(logging_level_set_all, logging_level_set_all_cmd,
cat->enabled = 1;
cat->loglevel = level;
+#if !defined(EMBEDDED)
+ log_cache_update(i, 1, level);
+#endif
}
RET_WITH_UNLOCK(CMD_SUCCESS);
}
@@ -575,7 +598,7 @@ gDEFUN(cfg_description, cfg_description_cmd,
char **dptr = vty->index_sub;
if (!dptr) {
- vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
+ vty_out(vty, "%% vty->index_sub == NULL%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -596,7 +619,7 @@ gDEFUN(cfg_no_description, cfg_no_description_cmd,
char **dptr = vty->index_sub;
if (!dptr) {
- vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
+ vty_out(vty, "%% vty->index_sub == NULL%s", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -728,6 +751,64 @@ DEFUN(cfg_no_log_syslog, cfg_no_log_syslog_cmd,
}
#endif /* HAVE_SYSLOG_H */
+DEFUN(cfg_log_systemd_journal, cfg_log_systemd_journal_cmd,
+ "log systemd-journal [raw]",
+ LOG_STR "Logging to systemd-journal\n"
+ "Offload rendering of the meta information (location, category) to systemd\n")
+{
+#ifdef ENABLE_SYSTEMD_LOGGING
+ struct log_target *tgt;
+ bool raw = argc > 0;
+
+ log_tgt_mutex_lock();
+ tgt = log_target_find(LOG_TGT_TYPE_SYSTEMD, NULL);
+ if (tgt == NULL) {
+ tgt = log_target_create_systemd(raw);
+ if (tgt == NULL) {
+ vty_out(vty, "%% Unable to create systemd-journal "
+ "log target%s", VTY_NEWLINE);
+ RET_WITH_UNLOCK(CMD_WARNING);
+ }
+ log_add_target(tgt);
+ } else if (tgt->sd_journal.raw != raw) {
+ log_target_systemd_set_raw(tgt, raw);
+ }
+
+ vty->index = tgt;
+ vty->node = CFG_LOG_NODE;
+
+ RET_WITH_UNLOCK(CMD_SUCCESS);
+#else
+ vty_out(vty, "%% systemd-journal logging is not available "
+ "in this build of libosmocore%s", VTY_NEWLINE);
+ return CMD_WARNING;
+#endif /* ENABLE_SYSTEMD_LOGGING */
+}
+
+DEFUN(cfg_no_log_systemd_journal, cfg_no_log_systemd_journal_cmd,
+ "no log systemd-journal",
+ NO_STR LOG_STR "Logging to systemd-journal\n")
+{
+#ifdef ENABLE_SYSTEMD_LOGGING
+ struct log_target *tgt;
+
+ log_tgt_mutex_lock();
+ tgt = log_target_find(LOG_TGT_TYPE_SYSTEMD, NULL);
+ if (!tgt) {
+ vty_out(vty, "%% No systemd-journal logging active%s", VTY_NEWLINE);
+ RET_WITH_UNLOCK(CMD_WARNING);
+ }
+
+ log_target_destroy(tgt);
+
+ RET_WITH_UNLOCK(CMD_SUCCESS);
+#else
+ vty_out(vty, "%% systemd-journal logging is not available "
+ "in this build of libosmocore%s", VTY_NEWLINE);
+ return CMD_WARNING;
+#endif /* ENABLE_SYSTEMD_LOGGING */
+}
+
DEFUN(cfg_log_gsmtap, cfg_log_gsmtap_cmd,
"log gsmtap [HOSTNAME]",
LOG_STR "Logging via GSMTAP\n"
@@ -756,9 +837,31 @@ DEFUN(cfg_log_gsmtap, cfg_log_gsmtap_cmd,
RET_WITH_UNLOCK(CMD_SUCCESS);
}
+DEFUN(cfg_no_log_gsmtap, cfg_no_log_gsmtap_cmd,
+ "no log gsmtap [HOSTNAME]",
+ NO_STR LOG_STR "Logging via GSMTAP\n"
+ "Host name to send the GSMTAP logging to (UDP port 4729)\n")
+{
+ const char *hostname = argc ? argv[0] : "127.0.0.1";
+ struct log_target *tgt;
+
+ log_tgt_mutex_lock();
+ tgt = log_target_find(LOG_TGT_TYPE_GSMTAP, hostname);
+ if (tgt == NULL) {
+ vty_out(vty, "%% Unable to find GSMTAP log target for %s%s",
+ hostname, VTY_NEWLINE);
+ RET_WITH_UNLOCK(CMD_WARNING);
+ }
+
+ log_target_destroy(tgt);
+
+ RET_WITH_UNLOCK(CMD_SUCCESS);
+}
+
DEFUN(cfg_log_stderr, cfg_log_stderr_cmd,
- "log stderr",
- LOG_STR "Logging via STDERR of the process\n")
+ "log stderr [blocking-io]",
+ LOG_STR "Logging via STDERR of the process\n"
+ "Use blocking, synchronous I/O\n")
{
struct log_target *tgt;
@@ -774,6 +877,11 @@ DEFUN(cfg_log_stderr, cfg_log_stderr_cmd,
log_add_target(tgt);
}
+ if (argc > 0 && !strcmp(argv[0], "blocking-io"))
+ log_target_file_switch_to_stream(tgt);
+ else
+ log_target_file_switch_to_wqueue(tgt);
+
vty->index = tgt;
vty->node = CFG_LOG_NODE;
@@ -794,13 +902,15 @@ DEFUN(cfg_no_log_stderr, cfg_no_log_stderr_cmd,
}
log_target_destroy(tgt);
+ osmo_stderr_target = NULL;
RET_WITH_UNLOCK(CMD_SUCCESS);
}
DEFUN(cfg_log_file, cfg_log_file_cmd,
- "log file .FILENAME",
- LOG_STR "Logging to text file\n" "Filename\n")
+ "log file FILENAME [blocking-io]",
+ LOG_STR "Logging to text file\n" "Filename\n"
+ "Use blocking, synchronous I/O\n")
{
const char *fname = argv[0];
struct log_target *tgt;
@@ -810,13 +920,18 @@ DEFUN(cfg_log_file, cfg_log_file_cmd,
if (!tgt) {
tgt = log_target_create_file(fname);
if (!tgt) {
- vty_out(vty, "%% Unable to create file `%s'%s",
+ vty_out(vty, "%% Unable to create file '%s'%s",
fname, VTY_NEWLINE);
RET_WITH_UNLOCK(CMD_WARNING);
}
log_add_target(tgt);
}
+ if (argc > 1 && !strcmp(argv[1], "blocking-io"))
+ log_target_file_switch_to_stream(tgt);
+ else
+ log_target_file_switch_to_wqueue(tgt);
+
vty->index = tgt;
vty->node = CFG_LOG_NODE;
@@ -825,7 +940,7 @@ DEFUN(cfg_log_file, cfg_log_file_cmd,
DEFUN(cfg_no_log_file, cfg_no_log_file_cmd,
- "no log file .FILENAME",
+ "no log file FILENAME",
NO_STR LOG_STR "Logging to text file\n" "Filename\n")
{
const char *fname = argv[0];
@@ -834,7 +949,7 @@ DEFUN(cfg_no_log_file, cfg_no_log_file_cmd,
log_tgt_mutex_lock();
tgt = log_target_find(LOG_TGT_TYPE_FILE, fname);
if (!tgt) {
- vty_out(vty, "%% No such log file `%s'%s",
+ vty_out(vty, "%% No such log file '%s'%s",
fname, VTY_NEWLINE);
RET_WITH_UNLOCK(CMD_WARNING);
}
@@ -900,7 +1015,10 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt)
return 1;
break;
case LOG_TGT_TYPE_STDERR:
- vty_out(vty, "log stderr%s", VTY_NEWLINE);
+ if (tgt->tgt_file.wqueue)
+ vty_out(vty, "log stderr%s", VTY_NEWLINE);
+ else
+ vty_out(vty, "log stderr blocking-io%s", VTY_NEWLINE);
break;
case LOG_TGT_TYPE_SYSLOG:
#ifdef HAVE_SYSLOG_H
@@ -911,7 +1029,10 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt)
#endif
break;
case LOG_TGT_TYPE_FILE:
- vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE);
+ if (tgt->tgt_file.wqueue)
+ vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE);
+ else
+ vty_out(vty, "log file %s blocking-io%s", tgt->tgt_file.fname, VTY_NEWLINE);
break;
case LOG_TGT_TYPE_STRRB:
vty_out(vty, "log alarms %zu%s",
@@ -921,6 +1042,11 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt)
vty_out(vty, "log gsmtap %s%s",
tgt->tgt_gsmtap.hostname, VTY_NEWLINE);
break;
+ case LOG_TGT_TYPE_SYSTEMD:
+ vty_out(vty, "log systemd-journal%s%s",
+ tgt->sd_journal.raw ? " raw" : "",
+ VTY_NEWLINE);
+ break;
}
vty_out(vty, " logging filter all %u%s",
@@ -935,6 +1061,8 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt)
tgt->print_category_hex ? 1 : 0, VTY_NEWLINE);
vty_out(vty, " logging print category %d%s",
tgt->print_category ? 1 : 0, VTY_NEWLINE);
+ vty_out(vty, " logging print thread-id %d%s",
+ tgt->print_tid ? 1 : 0, VTY_NEWLINE);
if (tgt->print_ext_timestamp)
vty_out(vty, " logging print extended-timestamp 1%s", VTY_NEWLINE);
else
@@ -942,8 +1070,9 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt)
tgt->print_timestamp ? 1 : 0, VTY_NEWLINE);
if (tgt->print_level)
vty_out(vty, " logging print level 1%s", VTY_NEWLINE);
- vty_out(vty, " logging print file %s%s",
+ vty_out(vty, " logging print file %s%s%s",
get_value_string(logging_print_file_args, tgt->print_filename2),
+ tgt->print_filename_pos == LOG_FILENAME_POS_LINE_END ? " last" : "",
VTY_NEWLINE);
if (tgt->loglevel) {
@@ -998,22 +1127,20 @@ static int config_write_log(struct vty *vty)
static int log_deprecated_func(struct cmd_element *cmd, struct vty *vty, int argc, const char *argv[])
{
vty_out(vty, "%% Ignoring deprecated '%s'%s", cmd->string, VTY_NEWLINE);
- return CMD_WARNING;
+ return CMD_SUCCESS; /* Otherwise the process would terminate */
}
void logging_vty_add_deprecated_subsys(void *ctx, const char *name)
{
struct cmd_element *cmd = talloc_zero(ctx, struct cmd_element);
OSMO_ASSERT(cmd);
- cmd->string = talloc_asprintf(cmd, "logging level %s (debug|info|notice|error|fatal)",
- name);
- printf("%s\n", cmd->string);
+ cmd->string = talloc_asprintf(cmd, "logging level %s " LOG_LEVEL_ARGS, name);
cmd->func = log_deprecated_func;
cmd->doc = LEVEL_STR
"Deprecated Category\n";
cmd->attr = CMD_ATTR_DEPRECATED;
- install_element(CFG_LOG_NODE, cmd);
+ install_lib_element(CFG_LOG_NODE, cmd);
}
/* logp (<categories>) (debug|...|fatal) .LOGMESSAGE*/
@@ -1025,6 +1152,23 @@ DEFUN(vty_logp,
int category = log_parse_category(argv[0]);
int level = log_parse_level(argv[1]);
char *str = argv_concat(argv, argc, 2);
+
+ if (level < 0) {
+ vty_out(vty, "%% Invalid level '%s'%s", argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (category < 0) {
+ vty_out(vty, "%% Invalid category '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Properly handle library specific sub-systems */
+ if ((unsigned int) category >= osmo_log_info->num_cat_user) {
+ category -= osmo_log_info->num_cat_user - 1;
+ category *= -1;
+ }
+
LOGP(category, level, "%s\n", str);
return CMD_SUCCESS;
}
@@ -1050,26 +1194,30 @@ static void gen_vty_logp_cmd_strs(struct cmd_element *cmd)
osmo_talloc_asprintf(tall_log_ctx, doc_str,
"Arbitrary message to log on given category and log level\n");
+ talloc_set_name_const(cmd_str, "vty_logp_cmd_str");
+ talloc_set_name_const(doc_str, "vty_logp_doc_str");
+
cmd->string = cmd_str;
cmd->doc = doc_str;
}
/*! Register logging related commands to the VTY. Call this once from
* your application if you want to support those commands. */
-void logging_vty_add_cmds()
+void logging_vty_add_cmds(void)
{
- install_element_ve(&enable_logging_cmd);
- install_element_ve(&disable_logging_cmd);
- install_element_ve(&logging_fltr_all_cmd);
- install_element_ve(&logging_use_clr_cmd);
- install_element_ve(&logging_prnt_timestamp_cmd);
- install_element_ve(&logging_prnt_ext_timestamp_cmd);
- install_element_ve(&logging_prnt_cat_cmd);
- install_element_ve(&logging_prnt_cat_hex_cmd);
- install_element_ve(&logging_prnt_level_cmd);
- install_element_ve(&logging_prnt_file_cmd);
- install_element_ve(&logging_set_category_mask_cmd);
- install_element_ve(&logging_set_category_mask_old_cmd);
+ install_lib_element_ve(&enable_logging_cmd);
+ install_lib_element_ve(&disable_logging_cmd);
+ install_lib_element_ve(&logging_fltr_all_cmd);
+ install_lib_element_ve(&logging_use_clr_cmd);
+ install_lib_element_ve(&logging_prnt_timestamp_cmd);
+ install_lib_element_ve(&logging_prnt_ext_timestamp_cmd);
+ install_lib_element_ve(&logging_prnt_tid_cmd);
+ install_lib_element_ve(&logging_prnt_cat_cmd);
+ install_lib_element_ve(&logging_prnt_cat_hex_cmd);
+ install_lib_element_ve(&logging_prnt_level_cmd);
+ install_lib_element_ve(&logging_prnt_file_cmd);
+ install_lib_element_ve(&logging_set_category_mask_cmd);
+ install_lib_element_ve(&logging_set_category_mask_old_cmd);
/* logging level (<categories>) (debug|...|fatal) */
gen_logging_level_cmd_strs(&logging_level_cmd,
@@ -1079,47 +1227,51 @@ void logging_vty_add_cmds()
gen_logging_level_cmd_strs(&deprecated_logging_level_everything_cmd,
"everything", EVERYTHING_STR);
- install_element_ve(&logging_level_cmd);
- install_element_ve(&logging_level_set_all_cmd);
- install_element_ve(&logging_level_force_all_cmd);
- install_element_ve(&no_logging_level_force_all_cmd);
- install_element_ve(&deprecated_logging_level_everything_cmd);
- install_element_ve(&deprecated_logging_level_all_cmd);
- install_element_ve(&deprecated_logging_level_all_everything_cmd);
+ install_lib_element_ve(&logging_level_cmd);
+ install_lib_element_ve(&logging_level_set_all_cmd);
+ install_lib_element_ve(&logging_level_force_all_cmd);
+ install_lib_element_ve(&no_logging_level_force_all_cmd);
+ install_lib_element_ve(&deprecated_logging_level_everything_cmd);
+ install_lib_element_ve(&deprecated_logging_level_all_cmd);
+ install_lib_element_ve(&deprecated_logging_level_all_everything_cmd);
gen_vty_logp_cmd_strs(&vty_logp_cmd);
- install_element_ve(&vty_logp_cmd);
+ install_lib_element_ve(&vty_logp_cmd);
- install_element_ve(&show_logging_vty_cmd);
- install_element_ve(&show_alarms_cmd);
+ install_lib_element_ve(&show_logging_vty_cmd);
+ install_lib_element_ve(&show_alarms_cmd);
install_node(&cfg_log_node, config_write_log);
- install_element(CFG_LOG_NODE, &logging_fltr_all_cmd);
- install_element(CFG_LOG_NODE, &logging_use_clr_cmd);
- install_element(CFG_LOG_NODE, &logging_prnt_timestamp_cmd);
- install_element(CFG_LOG_NODE, &logging_prnt_ext_timestamp_cmd);
- install_element(CFG_LOG_NODE, &logging_prnt_cat_cmd);
- install_element(CFG_LOG_NODE, &logging_prnt_cat_hex_cmd);
- install_element(CFG_LOG_NODE, &logging_prnt_level_cmd);
- install_element(CFG_LOG_NODE, &logging_prnt_file_cmd);
- install_element(CFG_LOG_NODE, &logging_level_cmd);
- install_element(CFG_LOG_NODE, &logging_level_set_all_cmd);
- install_element(CFG_LOG_NODE, &logging_level_force_all_cmd);
- install_element(CFG_LOG_NODE, &no_logging_level_force_all_cmd);
- install_element(CFG_LOG_NODE, &deprecated_logging_level_everything_cmd);
- install_element(CFG_LOG_NODE, &deprecated_logging_level_all_cmd);
- install_element(CFG_LOG_NODE, &deprecated_logging_level_all_everything_cmd);
-
- install_element(CONFIG_NODE, &cfg_log_stderr_cmd);
- install_element(CONFIG_NODE, &cfg_no_log_stderr_cmd);
- install_element(CONFIG_NODE, &cfg_log_file_cmd);
- install_element(CONFIG_NODE, &cfg_no_log_file_cmd);
- install_element(CONFIG_NODE, &cfg_log_alarms_cmd);
- install_element(CONFIG_NODE, &cfg_no_log_alarms_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_fltr_all_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_use_clr_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_prnt_timestamp_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_prnt_ext_timestamp_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_prnt_tid_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_prnt_cat_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_prnt_cat_hex_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_prnt_level_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_prnt_file_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_level_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_level_set_all_cmd);
+ install_lib_element(CFG_LOG_NODE, &logging_level_force_all_cmd);
+ install_lib_element(CFG_LOG_NODE, &no_logging_level_force_all_cmd);
+ install_lib_element(CFG_LOG_NODE, &deprecated_logging_level_everything_cmd);
+ install_lib_element(CFG_LOG_NODE, &deprecated_logging_level_all_cmd);
+ install_lib_element(CFG_LOG_NODE, &deprecated_logging_level_all_everything_cmd);
+
+ install_lib_element(CONFIG_NODE, &cfg_log_stderr_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_no_log_stderr_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_log_file_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_no_log_file_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_log_alarms_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_no_log_alarms_cmd);
#ifdef HAVE_SYSLOG_H
- install_element(CONFIG_NODE, &cfg_log_syslog_cmd);
- install_element(CONFIG_NODE, &cfg_log_syslog_local_cmd);
- install_element(CONFIG_NODE, &cfg_no_log_syslog_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_log_syslog_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_log_syslog_local_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_no_log_syslog_cmd);
#endif
- install_element(CONFIG_NODE, &cfg_log_gsmtap_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_log_systemd_journal_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_no_log_systemd_journal_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_log_gsmtap_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_no_log_gsmtap_cmd);
}
diff --git a/src/vty/stats_vty.c b/src/vty/stats_vty.c
index 296519c3..f940018f 100644
--- a/src/vty/stats_vty.c
+++ b/src/vty/stats_vty.c
@@ -1,5 +1,5 @@
/*
- * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2022 by Harald Welte <laforge@gnumonks.org>
* (C) 2009-2014 by Holger Hans Peter Freyther
* (C) 2015 by sysmocom - s.f.m.c. GmbH
* All Rights Reserved
@@ -16,16 +16,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
#include <string.h>
-#include "../../config.h"
+#include "config.h"
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
@@ -37,11 +33,15 @@
#include <osmocom/core/stats.h>
#include <osmocom/core/counter.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stats_tcp.h>
#define CFG_STATS_STR "Configure stats sub-system\n"
#define CFG_REPORTER_STR "Configure a stats reporter\n"
#define SHOW_STATS_STR "Show statistical values\n"
+#define SKIP_ZERO_STR "Skip items with total count zero\n"
+
+#define STATS_STR "Stats related commands\n"
/*! \file stats_vty.c
* VTY interface for statsd / statistic items
@@ -245,15 +245,42 @@ DEFUN(cfg_stats_reporter_disable, cfg_stats_reporter_disable_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_stats_reporter_flush_period, cfg_stats_reporter_flush_period_cmd,
+ "flush-period <0-65535>",
+ CFG_STATS_STR "Send all stats even if they have not changed (i.e. force the flush)"
+ "every N-th reporting interval. Set to 0 to disable regular flush (default).\n"
+ "0 to disable regular flush (default), 1 to flush every time, 2 to flush every 2nd time, etc\n")
+{
+ int rc;
+ unsigned int period = atoi(argv[0]);
+ struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty);
+ OSMO_ASSERT(srep);
+
+ rc = osmo_stats_reporter_set_flush_period(srep, period);
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to set force flush period: %s%s",
+ strerror(-rc), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_stats_reporter_statsd, cfg_stats_reporter_statsd_cmd,
- "stats reporter statsd",
- CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n")
+ "stats reporter statsd [NAME]",
+ CFG_STATS_STR CFG_REPORTER_STR
+ "Report to a STATSD server\n"
+ "Name of the reporter\n")
{
struct osmo_stats_reporter *srep;
+ const char *name = NULL;
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
+ if (argc > 0)
+ name = argv[0];
+
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, name);
if (!srep) {
- srep = osmo_stats_reporter_create_statsd(NULL);
+ srep = osmo_stats_reporter_create_statsd(name);
if (!srep) {
vty_out(vty, "%% Unable to create statsd reporter%s",
VTY_NEWLINE);
@@ -269,34 +296,22 @@ DEFUN(cfg_stats_reporter_statsd, cfg_stats_reporter_statsd_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_stats_interval, cfg_stats_interval_cmd,
- "stats interval <1-65535>",
- CFG_STATS_STR "Set the reporting interval\n"
- "Interval in seconds\n")
-{
- int rc;
- int interval = atoi(argv[0]);
- rc = osmo_stats_set_interval(interval);
- if (rc < 0) {
- vty_out(vty, "%% Unable to set interval: %s%s",
- strerror(-rc), VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- return CMD_SUCCESS;
-}
-
-
DEFUN(cfg_no_stats_reporter_statsd, cfg_no_stats_reporter_statsd_cmd,
- "no stats reporter statsd",
- NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n")
+ "no stats reporter statsd [NAME]",
+ NO_STR CFG_STATS_STR CFG_REPORTER_STR
+ "Report to a STATSD server\n"
+ "Name of the reporter\n")
{
struct osmo_stats_reporter *srep;
+ const char *name = NULL;
+
+ if (argc > 0)
+ name = argv[0];
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, name);
if (!srep) {
- vty_out(vty, "%% No statsd logging active%s",
- VTY_NEWLINE);
+ vty_out(vty, "%% There is no such statsd reporter with name '%s'%s",
+ name ? name : "", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -306,14 +321,20 @@ DEFUN(cfg_no_stats_reporter_statsd, cfg_no_stats_reporter_statsd_cmd,
}
DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd,
- "stats reporter log",
- CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n")
+ "stats reporter log [NAME]",
+ CFG_STATS_STR CFG_REPORTER_STR
+ "Report to the logger\n"
+ "Name of the reporter\n")
{
struct osmo_stats_reporter *srep;
+ const char *name = NULL;
+
+ if (argc > 0)
+ name = argv[0];
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, name);
if (!srep) {
- srep = osmo_stats_reporter_create_log(NULL);
+ srep = osmo_stats_reporter_create_log(name);
if (!srep) {
vty_out(vty, "%% Unable to create log reporter%s",
VTY_NEWLINE);
@@ -330,15 +351,21 @@ DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd,
}
DEFUN(cfg_no_stats_reporter_log, cfg_no_stats_reporter_log_cmd,
- "no stats reporter log",
- NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n")
+ "no stats reporter log [NAME]",
+ NO_STR CFG_STATS_STR CFG_REPORTER_STR
+ "Report to the logger\n"
+ "Name of the reporter\n")
{
struct osmo_stats_reporter *srep;
+ const char *name = NULL;
+
+ if (argc > 0)
+ name = argv[0];
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
+ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, name);
if (!srep) {
- vty_out(vty, "%% No log reporting active%s",
- VTY_NEWLINE);
+ vty_out(vty, "%% There is no such log reporter with name '%s'%s",
+ name ? name : "", VTY_NEWLINE);
return CMD_WARNING;
}
@@ -347,27 +374,77 @@ DEFUN(cfg_no_stats_reporter_log, cfg_no_stats_reporter_log_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_stats_interval, cfg_stats_interval_cmd,
+ "stats interval <0-65535>",
+ CFG_STATS_STR "Set the reporting interval\n"
+ "Interval in seconds (0 disables the reporting interval)\n")
+{
+ int rc;
+ int interval = atoi(argv[0]);
+ rc = osmo_stats_set_interval(interval);
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to set interval: %s%s",
+ strerror(-rc), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tcp_stats_interval, cfg_tcp_stats_interval_cmd,
+ "stats-tcp interval <0-65535>",
+ CFG_STATS_STR "Set the tcp socket stats polling interval\n"
+ "Interval in seconds (0 disables the polling interval)\n")
+{
+ int rc;
+ int interval = atoi(argv[0]);
+ rc = osmo_stats_tcp_set_interval(interval);
+ if (rc < 0) {
+ vty_out(vty, "%% Unable to set interval: %s%s",
+ strerror(-rc), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_tcp_stats_batch_size, cfg_tcp_stats_batch_size_cmd,
+ "stats-tcp batch-size <1-65535>",
+ CFG_STATS_STR "Set the number of tcp sockets that are processed per stats polling interval\n"
+ "Number of sockets per interval\n")
+{
+ osmo_tcp_stats_config->batch_size = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
DEFUN(show_stats,
show_stats_cmd,
- "show stats",
- SHOW_STR SHOW_STATS_STR)
+ "show stats [skip-zero]",
+ SHOW_STR SHOW_STATS_STR SKIP_ZERO_STR)
{
- vty_out_statistics_full(vty, "");
+ bool skip_zero = false;
+ if (argc > 0)
+ skip_zero = true;
+
+ vty_out_statistics_full2(vty, "", skip_zero);
return CMD_SUCCESS;
}
DEFUN(show_stats_level,
show_stats_level_cmd,
- "show stats level (global|peer|subscriber)",
+ "show stats level (global|peer|subscriber) [skip-zero]",
SHOW_STR SHOW_STATS_STR
"Set the maximum group level\n"
"Show global groups only\n"
"Show global and network peer related groups\n"
- "Show global, peer, and subscriber groups\n")
+ "Show global, peer, and subscriber groups\n" SKIP_ZERO_STR)
{
int level = get_string_value(stats_class_strs, argv[0]);
- vty_out_statistics_partial(vty, "", level);
+ bool skip_zero = false;
+ if (argc > 1)
+ skip_zero = true;
+ vty_out_statistics_partial2(vty, "", level, skip_zero);
return CMD_SUCCESS;
}
@@ -393,13 +470,6 @@ static int asciidoc_handle_counter(struct osmo_counter *counter, void *sctx_)
static void asciidoc_counter_generate(struct vty *vty)
{
- if (osmo_counters_count() == 0)
- {
- vty_out(vty, "// there are no ungrouped osmo_counters%s",
- VTY_NEWLINE);
- return;
- }
-
vty_out(vty, "// ungrouped osmo_counters%s", VTY_NEWLINE);
vty_out(vty, ".ungrouped osmo counters%s", VTY_NEWLINE);
vty_out(vty, "[options=\"header\"]%s", VTY_NEWLINE);
@@ -460,10 +530,11 @@ static int asciidoc_osmo_stat_item_handler(
{
struct vty *vty = sctx_;
- char *name = osmo_asciidoc_escape(item->desc->name);
- char *description = osmo_asciidoc_escape(item->desc->description);
+ const struct osmo_stat_item_desc *desc = osmo_stat_item_get_desc(item);
+ char *name = osmo_asciidoc_escape(desc->name);
+ char *description = osmo_asciidoc_escape(desc->description);
char *group_name_prefix = osmo_asciidoc_escape(statg->desc->group_name_prefix);
- char *unit = osmo_asciidoc_escape(item->desc->unit);
+ char *unit = osmo_asciidoc_escape(desc->unit);
/* | Name | Reference | Description | Unit | */
vty_out(vty, "| %s | <<%s_%s>> | %s | %s%s",
@@ -520,48 +591,97 @@ DEFUN(show_stats_asciidoc_table,
vty_out(vty, "// generating tables for rate_ctr_group%s", VTY_NEWLINE);
rate_ctr_for_each_group(asciidoc_rate_ctr_group_handler, vty);
- vty_out(vty, "== Osmo Stat Items%s%s", VTY_NEWLINE, VTY_NEWLINE);
+ vty_out(vty, "=== Osmo Stat Items%s%s", VTY_NEWLINE, VTY_NEWLINE);
vty_out(vty, "// generating tables for osmo_stat_items%s", VTY_NEWLINE);
osmo_stat_item_for_each_group(asciidoc_osmo_stat_item_group_handler, vty);
- vty_out(vty, "== Osmo Counters%s%s", VTY_NEWLINE, VTY_NEWLINE);
- vty_out(vty, "// generating tables for osmo_counters%s", VTY_NEWLINE);
- asciidoc_counter_generate(vty);
+ if (osmo_counters_count() == 0)
+ {
+ vty_out(vty, "// there are no ungrouped osmo_counters%s",
+ VTY_NEWLINE);
+ } else {
+ vty_out(vty, "=== Osmo Counters%s%s", VTY_NEWLINE, VTY_NEWLINE);
+ vty_out(vty, "// generating tables for osmo_counters%s", VTY_NEWLINE);
+ asciidoc_counter_generate(vty);
+ }
return CMD_SUCCESS;
}
+struct rctr_vty_ctx {
+ struct vty *vty;
+ bool skip_zero;
+};
+
static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
{
- struct vty *vty = sctx_;
- vty_out(vty, "%s %u:%s", ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE);
- vty_out_rate_ctr_group_fmt(vty, "%25n: %10c (%S/s %M/m %H/h %D/d) %d", ctrg);
+ struct rctr_vty_ctx *sctx = sctx_;
+ struct vty *vty = sctx->vty;
+ vty_out(vty, "%s %u", ctrg->desc->group_description, ctrg->idx);
+ if (ctrg->name != NULL)
+ vty_out(vty, " (%s)", ctrg->name);
+ vty_out(vty, ":%s", VTY_NEWLINE);
+ vty_out_rate_ctr_group_fmt2(vty, "%25n: %10c (%S/s %M/m %H/h %D/d) %d", ctrg, sctx->skip_zero);
return 0;
}
DEFUN(show_rate_counters,
show_rate_counters_cmd,
- "show rate-counters",
- SHOW_STR "Show all rate counters\n")
+ "show rate-counters [skip-zero]",
+ SHOW_STR "Show all rate counters\n" SKIP_ZERO_STR)
+{
+ struct rctr_vty_ctx rctx = { .vty = vty, .skip_zero = false };
+ if (argc > 0)
+ rctx.skip_zero = true;
+ rate_ctr_for_each_group(rate_ctr_group_handler, &rctx);
+ return CMD_SUCCESS;
+}
+
+DEFUN(stats_report,
+ stats_report_cmd,
+ "stats report",
+ STATS_STR "Manurally trigger reporting of stats\n")
{
- rate_ctr_for_each_group(rate_ctr_group_handler, vty);
+ osmo_stats_report();
+ return CMD_SUCCESS;
+}
+
+static int reset_rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_)
+{
+ rate_ctr_group_reset(ctrg);
+ return 0;
+}
+
+DEFUN(stats_reset,
+ stats_reset_cmd,
+ "stats reset",
+ STATS_STR "Reset all rate counter stats\n")
+{
+ rate_ctr_for_each_group(reset_rate_ctr_group_handler, NULL);
return CMD_SUCCESS;
}
static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_reporter *srep)
{
- if (srep == NULL)
- return 0;
+ const char *type = NULL;
switch (srep->type) {
case OSMO_STATS_REPORTER_STATSD:
- vty_out(vty, "stats reporter statsd%s", VTY_NEWLINE);
+ type = "statsd";
break;
case OSMO_STATS_REPORTER_LOG:
- vty_out(vty, "stats reporter log%s", VTY_NEWLINE);
+ type = "log";
break;
+ default:
+ /* don't try to save unknown stats reporters to the VTY. Imagine some
+ * application registering a new application specific stats reporter that
+ * this VTY code knows nothing about! */
+ return 0;
}
- vty_out(vty, " disable%s", VTY_NEWLINE);
+ vty_out(vty, "stats reporter %s", type);
+ if (srep->name != NULL)
+ vty_out(vty, " %s", srep->name);
+ vty_out(vty, "%s", VTY_NEWLINE);
if (srep->have_net_config) {
if (srep->dest_addr_str)
@@ -589,8 +709,14 @@ static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_report
else
vty_out(vty, " no prefix%s", VTY_NEWLINE);
+ if (srep->flush_period > 0)
+ vty_out(vty, " flush-period %d%s",
+ srep->flush_period, VTY_NEWLINE);
+
if (srep->enabled)
vty_out(vty, " enable%s", VTY_NEWLINE);
+ else
+ vty_out(vty, " disable%s", VTY_NEWLINE);
return 1;
}
@@ -599,13 +725,15 @@ static int config_write_stats(struct vty *vty)
{
struct osmo_stats_reporter *srep;
- /* TODO: loop through all reporters */
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL);
- config_write_stats_reporter(vty, srep);
- srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL);
- config_write_stats_reporter(vty, srep);
-
vty_out(vty, "stats interval %d%s", osmo_stats_config->interval, VTY_NEWLINE);
+ if (osmo_tcp_stats_config->interval != TCP_STATS_DEFAULT_INTERVAL)
+ vty_out(vty, "stats-tcp interval %d%s", osmo_tcp_stats_config->interval, VTY_NEWLINE);
+ if (osmo_tcp_stats_config->batch_size != TCP_STATS_DEFAULT_BATCH_SIZE)
+ vty_out(vty, "stats-tcp batch-size %d%s", osmo_tcp_stats_config->batch_size, VTY_NEWLINE);
+
+ /* Loop through all reporters */
+ llist_for_each_entry(srep, &osmo_stats_reporter_list, list)
+ config_write_stats_reporter(vty, srep);
return 1;
}
@@ -614,31 +742,37 @@ static int config_write_stats(struct vty *vty)
* Call this once during your application initialization if you would
* like to have stats VTY commands enabled.
*/
-void osmo_stats_vty_add_cmds()
+void osmo_stats_vty_add_cmds(void)
{
- install_element_ve(&show_stats_cmd);
- install_element_ve(&show_stats_level_cmd);
+ install_lib_element_ve(&show_stats_cmd);
+ install_lib_element_ve(&show_stats_level_cmd);
- install_element(CONFIG_NODE, &cfg_stats_reporter_statsd_cmd);
- install_element(CONFIG_NODE, &cfg_no_stats_reporter_statsd_cmd);
- install_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd);
- install_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd);
- install_element(CONFIG_NODE, &cfg_stats_interval_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_stats_reporter_statsd_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_no_stats_reporter_statsd_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_stats_interval_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_tcp_stats_interval_cmd);
+ install_lib_element(CONFIG_NODE, &cfg_tcp_stats_batch_size_cmd);
install_node(&cfg_stats_node, config_write_stats);
- install_element(CFG_STATS_NODE, &cfg_stats_reporter_local_ip_cmd);
- install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_local_ip_cmd);
- install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_ip_cmd);
- install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_port_cmd);
- install_element(CFG_STATS_NODE, &cfg_stats_reporter_mtu_cmd);
- install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_mtu_cmd);
- install_element(CFG_STATS_NODE, &cfg_stats_reporter_prefix_cmd);
- install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_prefix_cmd);
- install_element(CFG_STATS_NODE, &cfg_stats_reporter_level_cmd);
- install_element(CFG_STATS_NODE, &cfg_stats_reporter_enable_cmd);
- install_element(CFG_STATS_NODE, &cfg_stats_reporter_disable_cmd);
-
- install_element_ve(&show_stats_asciidoc_table_cmd);
- install_element_ve(&show_rate_counters_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_local_ip_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_no_stats_reporter_local_ip_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_ip_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_port_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_mtu_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_no_stats_reporter_mtu_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_prefix_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_no_stats_reporter_prefix_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_level_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_enable_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_disable_cmd);
+ install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_flush_period_cmd);
+
+ install_lib_element_ve(&show_stats_asciidoc_table_cmd);
+ install_lib_element_ve(&show_rate_counters_cmd);
+
+ install_lib_element(ENABLE_NODE, &stats_report_cmd);
+ install_lib_element(ENABLE_NODE, &stats_reset_cmd);
}
diff --git a/src/vty/talloc_ctx_vty.c b/src/vty/talloc_ctx_vty.c
index 16cb7635..ea8ebe70 100644
--- a/src/vty/talloc_ctx_vty.c
+++ b/src/vty/talloc_ctx_vty.c
@@ -17,17 +17,13 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
#include <regex.h>
#include <string.h>
-#include <talloc.h>
+#include <osmocom/core/talloc.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
@@ -151,6 +147,8 @@ static void talloc_ctx_walk(const char *ctx, const char *depth,
/* Determine a context for report */
if (!strncmp(ctx, "app", 3))
talloc_ctx = host.app_info->tall_ctx;
+ else if (!strcmp(ctx, "global"))
+ talloc_ctx = OTC_GLOBAL;
else if (!strncmp(ctx, "all", 3))
talloc_ctx = NULL;
@@ -167,11 +165,12 @@ static void talloc_ctx_walk(const char *ctx, const char *depth,
}
#define BASE_CMD_STR \
- "show talloc-context (application|all) (full|brief|DEPTH)"
+ "show talloc-context (application|global|all) (full|brief|DEPTH)"
#define BASE_CMD_DESCR \
SHOW_STR "Show talloc memory hierarchy\n" \
"Application's context\n" \
+ "Global context (OTC_GLOBAL)\n" \
"All contexts, if NULL-context tracking is enabled\n" \
"Display a full talloc memory hierarchy\n" \
"Display a brief talloc memory hierarchy\n" \
@@ -247,7 +246,7 @@ DEFUN(show_talloc_ctx_tree, show_talloc_ctx_tree_cmd,
*/
void osmo_talloc_vty_add_cmds(void)
{
- install_element_ve(&show_talloc_ctx_cmd);
- install_element_ve(&show_talloc_ctx_tree_cmd);
- install_element_ve(&show_talloc_ctx_filter_cmd);
+ install_lib_element_ve(&show_talloc_ctx_cmd);
+ install_lib_element_ve(&show_talloc_ctx_tree_cmd);
+ install_lib_element_ve(&show_talloc_ctx_filter_cmd);
}
diff --git a/src/vty/tdef_vty.c b/src/vty/tdef_vty.c
index 4549a61c..bd209ae0 100644
--- a/src/vty/tdef_vty.c
+++ b/src/vty/tdef_vty.c
@@ -50,10 +50,9 @@
*/
struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *tdefs, const char *T_str)
{
- long l;
+ int l;
int T;
struct osmo_tdef *t;
- char *endptr;
const char *T_nr_str;
int sign = 1;
@@ -77,9 +76,7 @@ struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *t
return NULL;
}
- errno = 0;
- l = strtol(T_nr_str, &endptr, 10);
- if (errno || *endptr || l > INT_MAX || l < 0) {
+ if (osmo_str_to_int(&l, T_nr_str, 10, 0, INT_MAX)) {
vty_out(vty, "%% Invalid T timer argument (should be 'T1234' or 'X1234'): '%s'%s", T_str, VTY_NEWLINE);
return NULL;
}
@@ -245,7 +242,7 @@ void osmo_tdef_vty_out_all(struct vty *vty, struct osmo_tdef *tdefs, const char
/*! Write current timer configuration arguments to the vty. Skip all entries that reflect their default value.
* The passed prefix string must contain both necessary indent and the VTY command the specific implementation is using.
- * See tdef_vty_test_config_subnode.c and tdef_vty_test_dynamic.c for examples.
+ * See tdef_vty_config_subnode_test.c and tdef_vty_dynamic_test.c for examples.
* \param[in] vty VTY context.
* \param[in] tdefs Array of timers to print, ended with a fully zero-initialized entry.
* \param[in] prefix_fmt Arbitrary string to start each line with, with variable printf like arguments.
@@ -361,10 +358,10 @@ static char *timer_doc_string(const char *prefix, const char *suffix)
* The given timer definitions group is stored in a global pointer, so this can be done only once per main() scope.
* It would also be possible to have distinct timer groups on separate VTY subnodes, with a "manual" implementation, but
* not with this API.
- * \param[in] parent_node VTY node id at which to add the timer group commands, e.g. CONFIG_NODE.
+ * \param[in] parent_cfg_node VTY node at which to add the timer configuration commands, e.g. CONFIG_NODE.
* \param[in] groups Global timer groups definition.
*/
-void osmo_tdef_vty_groups_init(enum node_type parent_node, struct osmo_tdef_group *groups)
+void osmo_tdef_vty_groups_init(unsigned int parent_cfg_node, struct osmo_tdef_group *groups)
{
struct osmo_tdef_group *g;
OSMO_ASSERT(!global_tdef_groups);
@@ -379,8 +376,8 @@ void osmo_tdef_vty_groups_init(enum node_type parent_node, struct osmo_tdef_grou
cfg_timer_cmd.string = timer_command_string("timer", OSMO_TDEF_VTY_ARG_SET_OPTIONAL);
cfg_timer_cmd.doc = timer_doc_string("Configure or show timers\n", OSMO_TDEF_VTY_DOC_SET);
- install_element_ve(&show_timer_cmd);
- install_element(parent_node, &cfg_timer_cmd);
+ install_lib_element_ve(&show_timer_cmd);
+ install_lib_element(parent_cfg_node, &cfg_timer_cmd);
}
/*! Write the global osmo_tdef_group configuration to VTY, as previously passed to osmo_tdef_vty_groups_init().
diff --git a/src/vty/telnet_interface.c b/src/vty/telnet_interface.c
index 9aa36fe4..8fa5dbff 100644
--- a/src/vty/telnet_interface.c
+++ b/src/vty/telnet_interface.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <sys/socket.h>
@@ -46,7 +42,7 @@
* process in order to enable interactive command-line introspection,
* interaction and configuration.
*
- * You typically call \ref telnet_init or \ref telnet_init_dynif once
+ * You typically call telnet_init_default once
* from your application code to enable this.
*/
@@ -64,26 +60,14 @@ static struct osmo_fd server_socket = {
.priv_nr = 0,
};
-/*! Initialize telnet based VTY interface listening to 127.0.0.1
- * \param[in] tall_ctx \ref talloc context
- * \param[in] priv private data to be passed to callback
- * \param[in] port TCP port number to bind to
- */
-int telnet_init(void *tall_ctx, void *priv, int port)
-{
- return telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port);
-}
-
-/*! Initialize telnet based VTY interface
- * \param[in] tall_ctx \ref talloc context
- * \param[in] priv private data to be passed to callback
- * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...)
- * \param[in] port TCP port number to bind to
- */
-int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
+/* Helper for deprecating telnet_init_dynif(), which previously held this code */
+static int _telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
{
int rc;
+ if (port < 0)
+ return -EINVAL;
+
tall_telnet_ctx = talloc_named_const(tall_ctx, 1,
"telnet_connection");
@@ -98,22 +82,45 @@ int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
if (rc < 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind telnet at %s %d\n",
ip, port);
- return -1;
+ return rc;
}
LOGP(DLGLOBAL, LOGL_NOTICE, "Available via telnet %s %d\n", ip, port);
return 0;
}
+/*! Initialize telnet based VTY interface listening to 127.0.0.1
+ * \param[in] tall_ctx \ref talloc context
+ * \param[in] priv private data to be passed to callback
+ * \param[in] port TCP port number to bind to
+ * \deprecated use telnet_init_default() instead
+ */
+int telnet_init(void *tall_ctx, void *priv, int port)
+{
+ return _telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port);
+}
+
+/*! Initialize telnet based VTY interface
+ * \param[in] tall_ctx \ref talloc context
+ * \param[in] priv private data to be passed to callback
+ * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...)
+ * \param[in] port TCP port number to bind to
+ * \deprecated use telnet_init_default() instead
+ */
+int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
+{
+ return _telnet_init_dynif(tall_ctx, priv, ip, port);
+}
+
/*! Initializes telnet based VTY interface using the configured bind addr/port.
* \param[in] tall_ctx \ref talloc context
* \param[in] priv private data to be passed to callback
- * \param[in] default_port TCP port number to bind to if not explicitely configured
+ * \param[in] default_port TCP port number to bind to if not explicitly configured
*/
int telnet_init_default(void *tall_ctx, void *priv, int default_port)
{
- return telnet_init_dynif(tall_ctx, priv, vty_get_bind_addr(),
- vty_get_bind_port(default_port));
+ return _telnet_init_dynif(tall_ctx, priv, vty_get_bind_addr(),
+ vty_get_bind_port(default_port));
}
@@ -256,7 +263,7 @@ void vty_event(enum event event, int sock, struct vty *vty)
}
/*! Close all telnet connections and release the telnet socket */
-void telnet_exit(void)
+void telnet_exit(void)
{
struct telnet_connection *tc, *tc2;
diff --git a/src/vty/utils.c b/src/vty/utils.c
index 0358d9bd..0f5a34e4 100644
--- a/src/vty/utils.c
+++ b/src/vty/utils.c
@@ -18,13 +18,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License 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 <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
@@ -48,6 +45,7 @@ struct vty_out_context {
struct vty *vty;
const char *prefix;
int max_level;
+ bool skip_zero;
};
static int rate_ctr_handler(
@@ -57,6 +55,9 @@ static int rate_ctr_handler(
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
+ if (vctx->skip_zero && ctr->current == 0)
+ return 0;
+
vty_out(vty, " %s%s: %8" PRIu64 " "
"(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s",
vctx->prefix, desc->description, ctr->current,
@@ -73,17 +74,24 @@ static int rate_ctr_handler(
* \param[in] vty The VTY to which it should be printed
* \param[in] prefix Any additional log prefix ahead of each line
* \param[in] ctrg Rate counter group to be printed
+ * \param[in] skip_zero Skip all zero-valued counters
*/
-void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
- struct rate_ctr_group *ctrg)
+void vty_out_rate_ctr_group2(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg, bool skip_zero)
{
- struct vty_out_context vctx = {vty, prefix};
+ struct vty_out_context vctx = {vty, prefix, 0, skip_zero};
vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE);
rate_ctr_for_each_counter(ctrg, rate_ctr_handler, &vctx);
}
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg)
+{
+ vty_out_rate_ctr_group2(vty, prefix, ctrg, false);
+}
+
static char *
pad_append_str(char *s, const char *a, int minwidth)
{
@@ -107,7 +115,12 @@ static int rate_ctr_handler_fmt(
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
const char *fmt = vctx->prefix;
- char *s = talloc_strdup(vty, "");
+ char *s;
+
+ if (vctx->skip_zero && ctr->current == 0)
+ return 0;
+
+ s = talloc_strdup(vty, "");
OSMO_ASSERT(s);
while (*fmt) {
@@ -209,14 +222,20 @@ static int rate_ctr_handler_fmt(
* \param[in] vty The VTY to which it should be printed
* \param[in] ctrg Rate counter group to be printed
* \param[in] fmt A format which may contain the above directives.
+ * \param[in] skip_zero Skip all zero-valued counters
*/
-void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt,
- struct rate_ctr_group *ctrg)
+void vty_out_rate_ctr_group_fmt2(struct vty *vty, const char *fmt,
+ struct rate_ctr_group *ctrg, bool skip_zero)
{
- struct vty_out_context vctx = {vty, fmt};
+ struct vty_out_context vctx = {vty, fmt, 0, skip_zero};
rate_ctr_for_each_counter(ctrg, rate_ctr_handler_fmt, &vctx);
}
+void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt,
+ struct rate_ctr_group *ctrg)
+{
+ vty_out_rate_ctr_group_fmt2(vty, fmt, ctrg, false);
+}
static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_)
{
struct vty_out_context *vctx = vctx_;
@@ -225,12 +244,10 @@ static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_)
if (ctrg->desc->class_id > vctx->max_level)
return 0;
- if (ctrg->idx)
- vty_out(vty, "%s%s (%d):%s", vctx->prefix,
- ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE);
- else
- vty_out(vty, "%s%s:%s", vctx->prefix,
- ctrg->desc->group_description, VTY_NEWLINE);
+ vty_out(vty, "%s%s (%d)", vctx->prefix, ctrg->desc->group_description, ctrg->idx);
+ if (ctrg->name)
+ vty_out(vty, "('%s')", ctrg->name);
+ vty_out(vty, ":%s", VTY_NEWLINE);
rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vctx);
@@ -249,14 +266,15 @@ static int osmo_stat_item_handler(
{
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
- const char *unit =
- item->desc->unit != OSMO_STAT_ITEM_NO_UNIT ?
- item->desc->unit : "";
+ const struct osmo_stat_item_desc *desc = osmo_stat_item_get_desc(item);
+ int32_t value = osmo_stat_item_get_last(item);
+ const char *unit = (desc->unit != OSMO_STAT_ITEM_NO_UNIT) ? desc->unit : "";
+
+ if (vctx->skip_zero && value == 0)
+ return 0;
vty_out(vty, " %s%s: %8" PRIi32 " %s%s",
- vctx->prefix, item->desc->description,
- osmo_stat_item_get_last(item),
- unit, VTY_NEWLINE);
+ vctx->prefix, desc->description, value, unit, VTY_NEWLINE);
return 0;
}
@@ -265,17 +283,24 @@ static int osmo_stat_item_handler(
* \param[in] vty The VTY to which it should be printed
* \param[in] prefix Any additional log prefix ahead of each line
* \param[in] statg Stat item group to be printed
+ * \param[in] skip_zero Skip all zero-valued counters
*/
-void vty_out_stat_item_group(struct vty *vty, const char *prefix,
- struct osmo_stat_item_group *statg)
+void vty_out_stat_item_group2(struct vty *vty, const char *prefix,
+ struct osmo_stat_item_group *statg, bool skip_zero)
{
- struct vty_out_context vctx = {vty, prefix};
+ struct vty_out_context vctx = {vty, prefix, 0, skip_zero};
vty_out(vty, "%s%s:%s", prefix, statg->desc->group_description,
VTY_NEWLINE);
osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, &vctx);
}
+void vty_out_stat_item_group(struct vty *vty, const char *prefix,
+ struct osmo_stat_item_group *statg)
+{
+ return vty_out_stat_item_group2(vty, prefix, statg, false);
+}
+
static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vctx_)
{
struct vty_out_context *vctx = vctx_;
@@ -284,13 +309,10 @@ static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void
if (statg->desc->class_id > vctx->max_level)
return 0;
- if (statg->idx)
- vty_out(vty, "%s%s (%d):%s", vctx->prefix,
- statg->desc->group_description, statg->idx,
- VTY_NEWLINE);
- else
- vty_out(vty, "%s%s:%s", vctx->prefix,
- statg->desc->group_description, VTY_NEWLINE);
+ vty_out(vty, "%s%s (%d)", vctx->prefix, statg->desc->group_description, statg->idx);
+ if (statg->name)
+ vty_out(vty, "('%s')", statg->name);
+ vty_out(vty, ":%s", VTY_NEWLINE);
osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vctx);
@@ -308,21 +330,22 @@ static int handle_counter(struct osmo_counter *counter, void *vctx_)
struct vty_out_context *vctx = vctx_;
struct vty *vty = vctx->vty;
const char *description = counter->description;
+ unsigned long value = osmo_counter_get(counter);
+
+ if (vctx->skip_zero && value == 0)
+ return 0;
if (!counter->description)
description = counter->name;
- vty_out(vty, " %s%s: %8lu%s",
- vctx->prefix, description,
- osmo_counter_get(counter), VTY_NEWLINE);
+ vty_out(vty, " %s%s: %8lu%s", vctx->prefix, description, value, VTY_NEWLINE);
return 0;
}
-void vty_out_statistics_partial(struct vty *vty, const char *prefix,
- int max_level)
+void vty_out_statistics_partial2(struct vty *vty, const char *prefix, int max_level, bool skip_zero)
{
- struct vty_out_context vctx = {vty, prefix, max_level};
+ struct vty_out_context vctx = {vty, prefix, max_level, skip_zero};
vty_out(vty, "%sUngrouped counters:%s", prefix, VTY_NEWLINE);
osmo_counters_for_each(handle_counter, &vctx);
@@ -330,9 +353,19 @@ void vty_out_statistics_partial(struct vty *vty, const char *prefix,
osmo_stat_item_for_each_group(osmo_stat_item_group_handler, &vctx);
}
+void vty_out_statistics_partial(struct vty *vty, const char *prefix, int max_level)
+{
+ return vty_out_statistics_partial2(vty, prefix, max_level, false);
+}
+
+void vty_out_statistics_full2(struct vty *vty, const char *prefix, bool skip_zero)
+{
+ vty_out_statistics_partial2(vty, prefix, INT_MAX, skip_zero);
+}
+
void vty_out_statistics_full(struct vty *vty, const char *prefix)
{
- vty_out_statistics_partial(vty, prefix, INT_MAX);
+ vty_out_statistics_full2(vty, prefix, false);
}
/*! Generate a VTY command string from value_string */
@@ -382,6 +415,7 @@ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals,
if (ret < 0)
goto err;
OSMO_SNPRINTF_RET(ret, rem, offset, len);
+ (void)len; /* suppress warnings about len being set but not used */
err:
str[size-1] = '\0';
return str;
diff --git a/src/vty/vector.c b/src/vty/vector.c
index f9e5ec3e..34b161d8 100644
--- a/src/vty/vector.c
+++ b/src/vty/vector.c
@@ -16,11 +16,6 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Zebra; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
diff --git a/src/vty/vty.c b/src/vty/vty.c
index babe0ef6..3a549b43 100644
--- a/src/vty/vty.c
+++ b/src/vty/vty.c
@@ -66,6 +66,8 @@
#include <osmocom/vty/command.h>
#include <osmocom/vty/buffer.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/utils.h>
#ifndef MAXPATHLEN
#define MAXPATHLEN 4096
@@ -128,6 +130,7 @@ struct vty *vty_new(void)
goto out_obuf;
new->max = VTY_BUFSIZ;
+ new->fd = -1;
return new;
@@ -205,6 +208,12 @@ static void vty_auth(struct vty *vty)
}
}
+void vty_flush(struct vty *vty)
+{
+ if (vty->obuf)
+ buffer_flush_all(vty->obuf, vty->fd);
+}
+
/*! Close a given vty interface. */
void vty_close(struct vty *vty)
{
@@ -230,8 +239,8 @@ void vty_close(struct vty *vty)
/* Unset vector. */
vector_unset(vtyvec, vty->fd);
- /* Close socket. */
- if (vty->fd > 0) {
+ /* Close socket (ignore standard I/O streams). */
+ if (vty->fd > 2) {
close(vty->fd);
vty->fd = -1;
}
@@ -330,6 +339,25 @@ int vty_out_newline(struct vty *vty)
return 0;
}
+/*! calculates the time difference of a give timespec to the current time
+ * and prints in a human readable format (days, hours, minutes, seconds).
+ */
+int vty_out_uptime(struct vty *vty, const struct timespec *starttime)
+{
+ struct timespec now;
+ struct timespec uptime;
+
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ timespecsub(&now, starttime, &uptime);
+
+ int d = uptime.tv_sec / (3600 * 24);
+ int h = uptime.tv_sec / 3600 % 24;
+ int m = uptime.tv_sec / 60 % 60;
+ int s = uptime.tv_sec % 60;
+
+ return vty_out(vty, "%dd %dh %dm %ds", d, h, m, s);
+}
+
/*! return the current index of a given VTY */
void *vty_current_index(struct vty *vty)
{
@@ -457,6 +485,7 @@ static int vty_command(struct vty *vty)
static const char telnet_backward_char = 0x08;
static const char telnet_space_char = ' ';
+static const char telnet_escape_char = 0x1B;
/* Basic function to write buffer to vty. */
static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
@@ -856,6 +885,19 @@ static void vty_down_level(struct vty *vty)
vty->cp = 0;
}
+/* When '^L' is typed, clear all lines above the current one. */
+static void vty_clear_screen(struct vty *vty)
+{
+ vty_out(vty, "%c%s%c%s",
+ telnet_escape_char,
+ "[2J", /* Erase Screen */
+ telnet_escape_char,
+ "[H" /* Cursor Home */
+ );
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+}
+
/* When '^Z' is received from vty, move down to the enable mode. */
static void vty_end_config(struct vty *vty)
{
@@ -1118,7 +1160,7 @@ static void vty_describe_command(struct vty *vty)
int ret;
vector vline;
vector describe;
- unsigned int i, width, desc_width;
+ unsigned int i, cmd_width, desc_width;
struct desc *desc, *desc_cr = NULL;
vline = cmd_make_strvec(vty->buf);
@@ -1142,19 +1184,17 @@ static void vty_describe_command(struct vty *vty)
vty_prompt(vty);
vty_redraw_line(vty);
return;
- break;
case CMD_ERR_NO_MATCH:
cmd_free_strvec(vline);
vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
vty_prompt(vty);
vty_redraw_line(vty);
return;
- break;
}
/* Get width of command string. */
- width = 0;
- for (i = 0; i < vector_active(describe); i++)
+ cmd_width = 0;
+ for (i = 0; i < vector_active(describe); i++) {
if ((desc = vector_slot(describe, i)) != NULL) {
unsigned int len;
@@ -1165,15 +1205,16 @@ static void vty_describe_command(struct vty *vty)
if (desc->cmd[0] == '.')
len--;
- if (width < len)
- width = len;
+ if (cmd_width < len)
+ cmd_width = len;
}
+ }
/* Get width of description string. */
- desc_width = vty->width - (width + 6);
+ desc_width = vty->width - (cmd_width + 6);
/* Print out description. */
- for (i = 0; i < vector_active(describe); i++)
+ for (i = 0; i < vector_active(describe); i++) {
if ((desc = vector_slot(describe, i)) != NULL) {
if (desc->cmd[0] == '\0')
continue;
@@ -1189,19 +1230,20 @@ static void vty_describe_command(struct vty *vty)
'.' ? desc->cmd + 1 : desc->cmd,
VTY_NEWLINE);
else if (desc_width >= strlen(desc->str))
- vty_out(vty, " %-*s %s%s", width,
+ vty_out(vty, " %-*s %s%s", cmd_width,
desc->cmd[0] ==
'.' ? desc->cmd + 1 : desc->cmd,
desc->str, VTY_NEWLINE);
else
- vty_describe_fold(vty, width, desc_width, desc);
+ vty_describe_fold(vty, cmd_width, desc_width, desc);
#if 0
- vty_out(vty, " %-*s %s%s", width
+ vty_out(vty, " %-*s %s%s", cmd_width
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
desc->str ? desc->str : "", VTY_NEWLINE);
#endif /* 0 */
}
+ }
if ((desc = desc_cr)) {
if (!desc->str)
@@ -1209,11 +1251,11 @@ static void vty_describe_command(struct vty *vty)
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
VTY_NEWLINE);
else if (desc_width >= strlen(desc->str))
- vty_out(vty, " %-*s %s%s", width,
+ vty_out(vty, " %-*s %s%s", cmd_width,
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
desc->str, VTY_NEWLINE);
else
- vty_describe_fold(vty, width, desc_width, desc);
+ vty_describe_fold(vty, cmd_width, desc_width, desc);
}
cmd_free_strvec(vline);
@@ -1400,6 +1442,9 @@ int vty_read(struct vty *vty)
case CONTROL('K'):
vty_kill_line(vty);
break;
+ case CONTROL('L'):
+ vty_clear_screen(vty);
+ break;
case CONTROL('N'):
vty_next_line(vty);
break;
@@ -1460,19 +1505,27 @@ int vty_read(struct vty *vty)
return 0;
}
-/* Read up configuration file */
-static int
-vty_read_file(FILE *confp, void *priv)
+/* Read up configuration from a file stream */
+/*! Read up VTY configuration from a file stream
+ * \param[in] confp file pointer of the stream for the configuration file
+ * \param[in] priv private data to be passed to \ref vty_read_file
+ * \returns Zero on success, non-zero on error
+ */
+int vty_read_config_filep(FILE *confp, void *priv)
{
int ret;
struct vty *vty;
vty = vty_new();
- vty->fd = 0;
vty->type = VTY_FILE;
vty->node = CONFIG_NODE;
vty->priv = priv;
+ /* By default, write to stderr. Otherwise, during parsing of the logging
+ * configuration, all invocations to vty_out() would make the process
+ * write() to its own stdin (fd=0)! */
+ vty->fd = fileno(stderr);
+
ret = config_from_file(vty, confp);
if (ret != CMD_SUCCESS) {
@@ -1789,6 +1842,8 @@ void vty_init_vtysh(void)
/* Install vty's own commands like `who' command. */
void vty_init(struct vty_app_info *app_info)
{
+ unsigned int i, j;
+
tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
@@ -1797,6 +1852,36 @@ void vty_init(struct vty_app_info *app_info)
host.app_info = app_info;
+ /* Check for duplicate flags in application specific attributes (if any) */
+ for (i = 0; i < ARRAY_SIZE(app_info->usr_attr_letters); i++) {
+ if (app_info->usr_attr_letters[i] == '\0')
+ continue;
+
+ /* Some flag characters are reserved for global attributes */
+ const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED;
+ for (j = 0; j < ARRAY_SIZE(rafc); j++) {
+ if (app_info->usr_attr_letters[i] != rafc[j])
+ continue;
+ fprintf(stderr, "Attribute flag character '%c' is reserved "
+ "for globals! Please fix.\n", app_info->usr_attr_letters[i]);
+ }
+
+ /* Upper case flag letters are reserved for libraries */
+ if (app_info->usr_attr_letters[i] >= 'A' &&
+ app_info->usr_attr_letters[i] <= 'Z') {
+ fprintf(stderr, "Attribute flag letter '%c' is reserved "
+ "for libraries! Please fix.\n", app_info->usr_attr_letters[i]);
+ }
+
+ for (j = i + 1; j < ARRAY_SIZE(app_info->usr_attr_letters); j++) {
+ if (app_info->usr_attr_letters[j] != app_info->usr_attr_letters[i])
+ continue;
+ fprintf(stderr, "Found duplicate flag letter '%c' in application "
+ "specific attributes (index %u vs %u)! Please fix.\n",
+ app_info->usr_attr_letters[i], i, j);
+ }
+ }
+
/* For further configuration read, preserve current directory. */
vty_save_cwd();
@@ -1805,18 +1890,18 @@ void vty_init(struct vty_app_info *app_info)
/* Install bgp top node. */
install_node(&vty_node, vty_config_write);
- install_element_ve(&config_who_cmd);
- install_element_ve(&show_history_cmd);
- install_element(CONFIG_NODE, &line_vty_cmd);
- install_element(CONFIG_NODE, &service_advanced_vty_cmd);
- install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
- install_element(CONFIG_NODE, &show_history_cmd);
- install_element(ENABLE_NODE, &terminal_monitor_cmd);
- install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
+ install_lib_element_ve(&config_who_cmd);
+ install_lib_element_ve(&show_history_cmd);
+ install_lib_element(CONFIG_NODE, &line_vty_cmd);
+ install_lib_element(CONFIG_NODE, &service_advanced_vty_cmd);
+ install_lib_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
+ install_lib_element(CONFIG_NODE, &show_history_cmd);
+ install_lib_element(ENABLE_NODE, &terminal_monitor_cmd);
+ install_lib_element(ENABLE_NODE, &terminal_no_monitor_cmd);
- install_element(VTY_NODE, &vty_login_cmd);
- install_element(VTY_NODE, &no_vty_login_cmd);
- install_element(VTY_NODE, &vty_bind_cmd);
+ install_lib_element(VTY_NODE, &vty_login_cmd);
+ install_lib_element(VTY_NODE, &no_vty_login_cmd);
+ install_lib_element(VTY_NODE, &vty_bind_cmd);
}
/*! Read the configuration file using the VTY code
@@ -1832,7 +1917,7 @@ int vty_read_config_file(const char *file_name, void *priv)
if (!cfile)
return -ENOENT;
- rc = vty_read_file(cfile, priv);
+ rc = vty_read_config_filep(cfile, priv);
fclose(cfile);
host_config_set(file_name);
diff --git a/tapset/Makefile.am b/tapset/Makefile.am
new file mode 100644
index 00000000..a07a3b1d
--- /dev/null
+++ b/tapset/Makefile.am
@@ -0,0 +1,22 @@
+.PHONY: clean-local install-data-hook uninstall-local
+
+EXTRA_DIST = libosmocore.stp
+TAPSET_FILES = $(EXTRA_DIST)
+TAPSET_INSTALL_DIR = $(DESTDIR)@ABS_TAPSET_DIR@
+
+if ENABLE_SYSTEMTAP
+all-local: $(TAPSET_FILES)
+
+clean-local:
+
+install-data-hook:
+ $(MKDIR_P) $(TAPSET_INSTALL_DIR)
+ $(INSTALL_DATA) $(TAPSET_FILES) $(TAPSET_INSTALL_DIR)
+
+uninstall-local:
+ @list='$(TAPSET_FILES)'; for p in $$list; do \
+ echo " rm -f '$(TAPSET_INSTALL_DIR)/$$p'"; \
+ rm -f "$(TAPSET_INSTALL_DIR)/$$p"; \
+ done
+
+endif
diff --git a/tapset/libosmocore.stp b/tapset/libosmocore.stp
new file mode 100644
index 00000000..a3e8f211
--- /dev/null
+++ b/tapset/libosmocore.stp
@@ -0,0 +1,29 @@
+/* libosmocore tapset
+ *
+ * This file is part of libosmocore.
+ *
+ * Each probe defines the probe name and a full probestr which consist of the probe name and between
+ * brackets all argument names and values.
+ */
+
+probe libosmocore_log_start = process("libosmocore").mark("log_start")
+{
+ count = $arg1;
+ probestr = sprintf("%s(count=%d), $$name, count);
+}
+
+probe libosmocore_log_done = process("libosmocore").mark("log_done")
+{
+ probestr = sprintf("%s", $$name);
+}
+
+probe libosmocore_stats_start = process("libosmocore").mark("statsd_start")
+{
+ count = $arg1;
+ probestr = sprintf("%s(count=%d), $$name, count);
+}
+
+probe libosmocore_stats_done = process("libosmocore").mark("statsd_done")
+{
+ probestr = sprintf("%s", $$name);
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 76249966..3b4325ee 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,7 +1,7 @@
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS)
AM_LDFLAGS = -no-install
-LDADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS)
+LDADD = $(top_builddir)/src/core/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS)
if ENABLE_SERCOM_STUB
noinst_LIBRARIES = libsercomstub.a
@@ -9,31 +9,56 @@ LDADD += $(top_builddir)/tests/libsercomstub.a
endif
check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
- smscb/smscb_test bits/bitrev_test a5/a5_test \
- conv/conv_test auth/milenage_test lapd/lapd_test \
+ bits/bitrev_test a5/a5_test \
+ conv/conv_test auth/milenage_test auth/tuak_test \
+ lapd/lapd_test \
gsm0808/gsm0808_test gsm0408/gsm0408_test \
gprs/gprs_test kasumi/kasumi_test gea/gea_test \
logging/logging_test codec/codec_test \
+ logging/logging_gsmtap_test \
loggingrb/loggingrb_test strrb/strrb_test \
- comp128/comp128_test smscb/gsm0341_test \
+ comp128/comp128_test \
bitvec/bitvec_test msgb/msgb_test bits/bitcomp_test \
bits/bitfield_test \
- tlv/tlv_test gsup/gsup_test oap/oap_test \
+ tlv/tlv_test oap/oap_test \
write_queue/wqueue_test socket/socket_test \
coding/coding_test conv/conv_gsm0503_test \
abis/abis_test endian/endian_test sercomm/sercomm_test \
prbs/prbs_test gsm23003/gsm23003_test \
+ gsm23236/gsm23236_test \
codec/codec_ecu_fr_test timer/clk_override_test \
oap/oap_client_test gsm29205/gsm29205_test \
logging/logging_vty_test \
vty/vty_transcript_test \
- tdef/tdef_test tdef/tdef_vty_test_config_root \
- tdef/tdef_vty_test_config_subnode \
- tdef/tdef_vty_test_dynamic \
+ tdef/tdef_test tdef/tdef_vty_config_root_test \
+ tdef/tdef_vty_config_subnode_test \
+ tdef/tdef_vty_dynamic_test \
sockaddr_str/sockaddr_str_test \
use_count/use_count_test \
context/context_test \
gsm0502/gsm0502_test \
+ dtx/dtx_gsm0503_test \
+ i460_mux/i460_mux_test \
+ bitgen/bitgen_test \
+ gad/gad_test \
+ bsslap/bsslap_test \
+ bssmap_le/bssmap_le_test \
+ it_q/it_q_test \
+ time_cc/time_cc_test \
+ gsm48/rest_octets_test \
+ base64/base64_test \
+ iuup/iuup_test \
+ smscb/smscb_test \
+ smscb/gsm0341_test \
+ smscb/cbsp_test \
+ auth/xor2g_test \
+ v110/frame_test \
+ v110/ra1_test \
+ v110/ta_test \
+ gsm44021/frame_csd_test \
+ osmo_io/osmo_io_test \
+ soft_uart/soft_uart_test \
+ rlp/rlp_test \
$(NULL)
if ENABLE_MSGFILE
@@ -60,43 +85,62 @@ check_PROGRAMS += \
$(NULL)
endif
-if ENABLE_STATS_TEST
-check_PROGRAMS += stats/stats_test
+if !EMBEDDED
+check_PROGRAMS += \
+ gsup/gsup_test \
+ stats/stats_test \
+ stats/stats_vty_test \
+ exec/exec_test \
+ $(NULL)
endif
if ENABLE_GB
-check_PROGRAMS += gb/bssgp_fc_test gb/gprs_bssgp_test gb/gprs_ns_test fr/fr_test
+check_PROGRAMS += gb/bssgp_fc_test gb/gprs_bssgp_test gb/gprs_bssgp_rim_test gb/gprs_ns_test gb/gprs_ns2_test fr/fr_test
endif
+base64_base64_test_SOURCES = base64/base64_test.c
+
utils_utils_test_SOURCES = utils/utils_test.c
-utils_utils_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+utils_utils_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
stats_stats_test_SOURCES = stats/stats_test.c
-stats_stats_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+stats_stats_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+stats_stats_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/core
+
+stats_stats_vty_test_SOURCES = stats/stats_vty_test.c
+stats_stats_vty_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
a5_a5_test_SOURCES = a5/a5_test.c
-a5_a5_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libgsmint.la
+a5_a5_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
kasumi_kasumi_test_SOURCES = kasumi/kasumi_test.c
-kasumi_kasumi_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libgsmint.la
+kasumi_kasumi_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
comp128_comp128_test_SOURCES = comp128/comp128_test.c
-comp128_comp128_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+comp128_comp128_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
auth_milenage_test_SOURCES = auth/milenage_test.c
-auth_milenage_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+auth_milenage_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+auth_tuak_test_SOURCES = auth/tuak_test.c
+auth_tuak_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
+auth_tuak_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src
+
+auth_xor2g_test_SOURCES = auth/xor2g_test.c
+auth_xor2g_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
abis_abis_test_SOURCES = abis/abis_test.c
-abis_abis_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+abis_abis_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
ctrl_ctrl_test_SOURCES = ctrl/ctrl_test.c
-ctrl_ctrl_test_LDADD = $(LDADD) \
+ctrl_ctrl_test_LDADD = \
$(top_builddir)/src/ctrl/libosmoctrl.la \
$(top_builddir)/src/gsm/libosmogsm.la \
- $(top_builddir)/src/vty/libosmovty.la
+ $(top_builddir)/src/vty/libosmovty.la \
+ $(LDADD)
gea_gea_test_SOURCES = gea/gea_test.c
-gea_gea_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gea_gea_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
bits_bitrev_test_SOURCES = bits/bitrev_test.c
@@ -107,83 +151,118 @@ bits_bitcomp_test_SOURCES = bits/bitcomp_test.c
bits_bitfield_test_SOURCES = bits/bitfield_test.c
conv_conv_test_SOURCES = conv/conv_test.c conv/conv.c
-conv_conv_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libgsmint.la
+conv_conv_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
gsm0502_gsm0502_test_SOURCES = gsm0502/gsm0502_test.c
-gsm0502_gsm0502_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm0502_gsm0502_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+dtx_dtx_gsm0503_test_SOURCES = dtx/dtx_gsm0503_test.c
+dtx_dtx_gsm0503_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la \
+ $(top_builddir)/src/coding/libosmocoding.la \
+ $(LDADD)
conv_conv_gsm0503_test_SOURCES = conv/conv_gsm0503_test.c conv/conv.c conv/gsm0503_test_vectors.c
-conv_conv_gsm0503_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libgsmint.la
+conv_conv_gsm0503_test_LDADD = $(top_builddir)/src/gsm/libgsmint.la $(LDADD)
conv_conv_gsm0503_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/tests/conv
gsm0808_gsm0808_test_SOURCES = gsm0808/gsm0808_test.c
-gsm0808_gsm0808_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm0808_gsm0808_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gsm29205_gsm29205_test_SOURCES = gsm29205/gsm29205_test.c
-gsm29205_gsm29205_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm29205_gsm29205_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gsm0408_gsm0408_test_SOURCES = gsm0408/gsm0408_test.c
-gsm0408_gsm0408_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm0408_gsm0408_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+gsm48_rest_octets_test_SOURCES = gsm48/rest_octets_test.c
+gsm48_rest_octets_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gprs_gprs_test_SOURCES = gprs/gprs_test.c
-gprs_gprs_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gprs_gprs_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
lapd_lapd_test_SOURCES = lapd/lapd_test.c
-lapd_lapd_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+lapd_lapd_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la \
+ $(top_builddir)/src/isdn/libosmoisdn.la \
+ $(LDADD)
msgb_msgb_test_SOURCES = msgb/msgb_test.c
msgfile_msgfile_test_SOURCES = msgfile/msgfile_test.c
smscb_smscb_test_SOURCES = smscb/smscb_test.c
-smscb_smscb_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+smscb_smscb_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
smscb_gsm0341_test_SOURCES = smscb/gsm0341_test.c
-smscb_gsm0341_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+smscb_gsm0341_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+smscb_cbsp_test_SOURCES = smscb/cbsp_test.c
+smscb_cbsp_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
sms_sms_test_SOURCES = sms/sms_test.c
-sms_sms_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+sms_sms_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
timer_timer_test_SOURCES = timer/timer_test.c
timer_clk_override_test_SOURCES = timer/clk_override_test.c
ussd_ussd_test_SOURCES = ussd/ussd_test.c
-ussd_ussd_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+ussd_ussd_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gb_bssgp_fc_test_SOURCES = gb/bssgp_fc_test.c
-gb_bssgp_fc_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la \
+gb_bssgp_fc_test_LDADD = $(top_builddir)/src/gb/libosmogb.la \
$(top_builddir)/src/vty/libosmovty.la \
- $(top_builddir)/src/gsm/libosmogsm.la
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD)
gb_gprs_bssgp_test_SOURCES = gb/gprs_bssgp_test.c
-gb_gprs_bssgp_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \
- $(top_builddir)/src/vty/libosmovty.la \
- $(top_builddir)/src/gsm/libosmogsm.la
+gb_gprs_bssgp_test_LDADD = $(top_builddir)/src/vty/libosmovty.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(top_builddir)/src/gb/libosmogb.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
+
+gb_gprs_bssgp_rim_test_SOURCES = gb/gprs_bssgp_rim_test.c
+gb_gprs_bssgp_rim_test_LDADD = $(top_builddir)/src/gb/libosmogb.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
gb_gprs_ns_test_SOURCES = gb/gprs_ns_test.c
-gb_gprs_ns_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \
+gb_gprs_ns_test_LDADD = $(top_builddir)/src/gb/libosmogb.la \
$(top_builddir)/src/vty/libosmovty.la \
- $(top_builddir)/src/gsm/libosmogsm.la
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
+
+gb_gprs_ns2_test_SOURCES = gb/gprs_ns2_test.c
+gb_gprs_ns2_test_LDADD = $(top_builddir)/src/vty/libosmovty.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(top_builddir)/src/core/libosmocore.la \
+ $(top_builddir)/src/gb/libosmogb-test.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
logging_logging_test_SOURCES = logging/logging_test.c
logging_logging_vty_test_SOURCES = logging/logging_vty_test.c
-logging_logging_vty_test_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+logging_logging_vty_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
+
+logging_logging_gsmtap_test_SOURCES = logging/logging_gsmtap_test.c
vty_vty_transcript_test_SOURCES = vty/vty_transcript_test.c
-vty_vty_transcript_test_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+vty_vty_transcript_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
fr_fr_test_SOURCES = fr/fr_test.c
-fr_fr_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \
+fr_fr_test_LDADD = $(top_builddir)/src/gb/libosmogb.la \
$(top_builddir)/src/vty/libosmovty.la \
- $(top_builddir)/src/gsm/libosmogsm.la
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD) \
+ $(LIBRARY_DLSYM)
codec_codec_test_SOURCES = codec/codec_test.c
-codec_codec_test_LDADD = $(LDADD) $(top_builddir)/src/codec/libosmocodec.la
+codec_codec_test_LDADD = $(top_builddir)/src/codec/libosmocodec.la $(LDADD)
codec_codec_ecu_fr_test_SOURCES = codec/codec_ecu_fr_test.c
-codec_codec_ecu_fr_test_LDADD = $(LDADD) $(top_builddir)/src/codec/libosmocodec.la
+codec_codec_ecu_fr_test_LDADD = $(top_builddir)/src/codec/libosmocodec.la $(LDADD)
loggingrb_loggingrb_test_SOURCES = loggingrb/loggingrb_test.c
loggingrb_loggingrb_test_LDADD = $(LDADD)
@@ -191,30 +270,31 @@ loggingrb_loggingrb_test_LDADD = $(LDADD)
strrb_strrb_test_SOURCES = strrb/strrb_test.c
vty_vty_test_SOURCES = vty/vty_test.c
-vty_vty_test_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+vty_vty_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
sim_sim_test_SOURCES = sim/sim_test.c
-sim_sim_test_LDADD = $(LDADD) $(top_builddir)/src/sim/libosmosim.la \
- $(top_builddir)/src/gsm/libosmogsm.la
+sim_sim_test_LDADD = $(top_builddir)/src/sim/libosmosim.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD)
tlv_tlv_test_SOURCES = tlv/tlv_test.c
-tlv_tlv_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+tlv_tlv_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
gsup_gsup_test_SOURCES = gsup/gsup_test.c
-gsup_gsup_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsup_gsup_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
oap_oap_test_SOURCES = oap/oap_test.c
-oap_oap_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+oap_oap_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
oap_oap_client_test_SOURCES = oap/oap_client_test.c
-oap_oap_client_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+oap_oap_client_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
fsm_fsm_test_SOURCES = fsm/fsm_test.c
fsm_fsm_test_LDADD = \
- $(LDADD) \
$(top_builddir)/src/ctrl/libosmoctrl.la \
$(top_builddir)/src/gsm/libosmogsm.la \
- $(top_builddir)/src/vty/libosmovty.la
+ $(top_builddir)/src/vty/libosmovty.la \
+ $(LDADD)
fsm_fsm_dealloc_test_SOURCES = fsm/fsm_dealloc_test.c
fsm_fsm_dealloc_test_LDADD = $(LDADD)
@@ -224,10 +304,11 @@ write_queue_wqueue_test_SOURCES = write_queue/wqueue_test.c
socket_socket_test_SOURCES = socket/socket_test.c
coding_coding_test_SOURCES = coding/coding_test.c
-coding_coding_test_LDADD = $(LDADD) \
+coding_coding_test_LDADD = \
$(top_builddir)/src/gsm/libosmogsm.la \
$(top_builddir)/src/codec/libosmocodec.la \
- $(top_builddir)/src/coding/libosmocoding.la
+ $(top_builddir)/src/coding/libosmocoding.la \
+ $(LDADD)
endian_endian_test_SOURCES = endian/endian_test.c
@@ -236,19 +317,22 @@ sercomm_sercomm_test_SOURCES = sercomm/sercomm_test.c
prbs_prbs_test_SOURCES = prbs/prbs_test.c
gsm23003_gsm23003_test_SOURCES = gsm23003/gsm23003_test.c
-gsm23003_gsm23003_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
+gsm23003_gsm23003_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+gsm23236_gsm23236_test_SOURCES = gsm23236/gsm23236_test.c
+gsm23236_gsm23236_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
tdef_tdef_test_SOURCES = tdef/tdef_test.c
tdef_tdef_test_LDADD = $(LDADD)
-tdef_tdef_vty_test_config_root_SOURCES = tdef/tdef_vty_test_config_root.c
-tdef_tdef_vty_test_config_root_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+tdef_tdef_vty_config_root_test_SOURCES = tdef/tdef_vty_config_root_test.c
+tdef_tdef_vty_config_root_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
-tdef_tdef_vty_test_config_subnode_SOURCES = tdef/tdef_vty_test_config_subnode.c
-tdef_tdef_vty_test_config_subnode_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+tdef_tdef_vty_config_subnode_test_SOURCES = tdef/tdef_vty_config_subnode_test.c
+tdef_tdef_vty_config_subnode_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
-tdef_tdef_vty_test_dynamic_SOURCES = tdef/tdef_vty_test_dynamic.c
-tdef_tdef_vty_test_dynamic_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la
+tdef_tdef_vty_dynamic_test_SOURCES = tdef/tdef_vty_dynamic_test.c
+tdef_tdef_vty_dynamic_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(LDADD)
sockaddr_str_sockaddr_str_test_SOURCES = sockaddr_str/sockaddr_str_test.c
sockaddr_str_sockaddr_str_test_LDADD = $(LDADD)
@@ -259,6 +343,55 @@ use_count_use_count_test_LDADD = $(LDADD)
context_context_test_SOURCES = context/context_test.c
context_context_test_LDADD = $(LDADD)
+exec_exec_test_SOURCES = exec/exec_test.c
+exec_exec_test_LDADD = $(LDADD)
+
+i460_mux_i460_mux_test_SOURCES = i460_mux/i460_mux_test.c
+i460_mux_i460_mux_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
+
+bitgen_bitgen_test_SOURCES = bitgen/bitgen_test.c
+bitgen_bitgen_test_LDADD = $(LDADD)
+
+gad_gad_test_SOURCES = gad/gad_test.c
+gad_gad_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+bsslap_bsslap_test_SOURCES = bsslap/bsslap_test.c
+bsslap_bsslap_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+bssmap_le_bssmap_le_test_SOURCES = bssmap_le/bssmap_le_test.c
+bssmap_le_bssmap_le_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+it_q_it_q_test_SOURCES = it_q/it_q_test.c
+it_q_it_q_test_LDADD = $(LDADD)
+
+time_cc_time_cc_test_SOURCES = time_cc/time_cc_test.c
+time_cc_time_cc_test_LDADD = $(LDADD)
+
+iuup_iuup_test_SOURCES = iuup/iuup_test.c
+iuup_iuup_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+v110_frame_test_SOURCES = v110/frame_test.c
+v110_frame_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
+
+v110_ra1_test_SOURCES = v110/ra1_test.c
+v110_ra1_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
+
+v110_ta_test_SOURCES = v110/ta_test.c
+v110_ta_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la $(LDADD)
+
+gsm44021_frame_csd_test_SOURCES = gsm44021/frame_csd_test.c
+gsm44021_frame_csd_test_LDADD = $(top_builddir)/src/isdn/libosmoisdn.la \
+ $(top_builddir)/src/gsm/libosmogsm.la \
+ $(LDADD)
+
+osmo_io_osmo_io_test_SOURCES = osmo_io/osmo_io_test.c
+
+soft_uart_soft_uart_test_SOURCES = soft_uart/soft_uart_test.c
+
+rlp_rlp_test_SOURCES = rlp/rlp_test.c
+rlp_rlp_test_LDADD = $(top_builddir)/src/gsm/libosmogsm.la $(LDADD)
+
+
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
@@ -279,21 +412,31 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
timer/timer_test.ok sms/sms_test.ok ussd/ussd_test.ok \
- smscb/smscb_test.ok bits/bitrev_test.ok a5/a5_test.ok \
+ bits/bitrev_test.ok a5/a5_test.ok \
conv/conv_test.ok auth/milenage_test.ok ctrl/ctrl_test.ok \
- lapd/lapd_test.ok gsm0408/gsm0408_test.ok \
+ auth/tuak_test.ok \
+ auth/xor2g_test.ok \
+ lapd/lapd_test.ok \
+ gsm0408/gsm0408_test.ok gsm0408/gsm0408_test.err \
gsm0808/gsm0808_test.ok gb/bssgp_fc_tests.err \
gb/bssgp_fc_tests.ok gb/bssgp_fc_tests.sh \
- gb/gprs_bssgp_test.ok gb/gprs_ns_test.ok gea/gea_test.ok \
+ gb/gprs_bssgp_test.ok gea/gea_test.ok \
+ gb/gprs_bssgp_rim_test.ok \
+ gb/gprs_ns2_vty.vty gb/osmoappdesc.py gb/osmo-ns-dummy.cfg \
+ gb/gprs_ns_test.ok \
+ gb/gprs_ns_test.err \
+ gb/gprs_ns2_test.ok \
+ gb/gprs_ns2_test.err \
gprs/gprs_test.ok kasumi/kasumi_test.ok \
msgfile/msgfile_test.ok msgfile/msgconfig.cfg \
logging/logging_test.ok logging/logging_test.err \
logging/logging_vty_test.vty \
+ logging/logging_gsmtap_test.err \
fr/fr_test.ok loggingrb/logging_test.ok \
loggingrb/logging_test.err strrb/strrb_test.ok \
codec/codec_test.ok \
codec/codec_ecu_fr_test.ok \
- vty/vty_test.ok \
+ vty/vty_test.ok vty/vty_test.err \
vty/fail_not_de-indented.cfg \
vty/fail_tabs_and_spaces.cfg \
vty/fail_too_much_indent.cfg \
@@ -306,8 +449,11 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
vty/ok_more_spaces.cfg \
vty/ok_tabs_and_spaces.cfg \
vty/ok_tabs.cfg \
+ vty/ok_deprecated_logging.cfg \
comp128/comp128_test.ok bits/bitfield_test.ok \
- utils/utils_test.ok utils/utils_test.err stats/stats_test.ok \
+ utils/utils_test.ok utils/utils_test.err \
+ stats/stats_test.ok stats/stats_test.err \
+ stats/stats_vty_test.vty \
bitvec/bitvec_test.ok msgb/msgb_test.ok bits/bitcomp_test.ok \
sim/sim_test.ok tlv/tlv_test.ok abis/abis_test.ok \
gsup/gsup_test.ok gsup/gsup_test.err \
@@ -321,34 +467,280 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
conv/conv_gsm0503_test.ok endian/endian_test.ok \
sercomm/sercomm_test.ok prbs/prbs_test.ok \
gsm29205/gsm29205_test.ok gsm23003/gsm23003_test.ok \
+ gsm23236/gsm23236_test.ok \
timer/clk_override_test.ok \
oap/oap_client_test.ok oap/oap_client_test.err \
vty/vty_transcript_test.vty \
tdef/tdef_test.ok \
+ tdef/tdef_test.err \
tdef/tdef_test_range_64bit.ok \
- tdef/tdef_vty_test_config_root.vty \
- tdef/tdef_vty_test_config_subnode.vty \
- tdef/tdef_vty_test_dynamic.vty \
+ tdef/tdef_vty_config_root_test.vty \
+ tdef/tdef_vty_config_subnode_test.vty \
+ tdef/tdef_vty_dynamic_test.vty \
sockaddr_str/sockaddr_str_test.ok \
use_count/use_count_test.ok use_count/use_count_test.err \
context/context_test.ok \
gsm0502/gsm0502_test.ok \
+ dtx/dtx_gsm0503_test.ok \
+ exec/exec_test.ok exec/exec_test.err \
+ i460_mux/i460_mux_test.ok \
+ bitgen/bitgen_test.ok \
+ gad/gad_test.ok \
+ bsslap/bsslap_test.ok \
+ bssmap_le/bssmap_le_test.ok \
+ it_q/it_q_test.ok \
+ time_cc/time_cc_test.ok \
+ gsm48/rest_octets_test.ok \
+ base64/base64_test.ok \
+ iuup/iuup_test.ok \
+ iuup/iuup_test.err \
+ smscb/smscb_test.ok \
+ smscb/gsm0341_test.ok \
+ smscb/cbsp_test.ok \
+ v110/frame_test.ok \
+ v110/ra1_test.ok \
+ v110/ta_test.err \
+ gsm44021/frame_csd_test.ok \
+ osmo_io/osmo_io_test.ok osmo_io/osmo_io_test.err \
+ soft_uart/soft_uart_test.ok \
+ rlp/rlp_test.ok \
+ socket/socket_sctp_test.ok socket/socket_sctp_test.err \
$(NULL)
+if ENABLE_LIBSCTP
+if ENABLE_SCTP_TESTS
+check_PROGRAMS += socket/socket_sctp_test
+socket_socket_sctp_test_SOURCES = socket/socket_sctp_test.c
+endif
+endif
+
DISTCLEANFILES = atconfig atlocal conv/gsm0503_test_vectors.c
BUILT_SOURCES = conv/gsm0503_test_vectors.c
noinst_HEADERS = conv/conv.h
TESTSUITE = $(srcdir)/testsuite
+update_exp: $(check_PROGRAMS)
+ a5/a5_test \
+ >$(srcdir)/a5/a5_test.ok
+ abis/abis_test \
+ >$(srcdir)/abis/abis_test.ok
+if ENABLE_CTRL
+ ctrl/ctrl_test \
+ >$(srcdir)/ctrl/ctrl_test.ok
+endif
+ kasumi/kasumi_test \
+ >$(srcdir)/kasumi/kasumi_test.ok
+ bits/bitrev_test \
+ >$(srcdir)/bits/bitrev_test.ok
+ bitvec/bitvec_test \
+ >$(srcdir)/bitvec/bitvec_test.ok
+ bits/bitcomp_test \
+ >$(srcdir)/bits/bitcomp_test.ok
+ bits/bitfield_test \
+ >$(srcdir)/bits/bitfield_test.ok
+ conv/conv_test \
+ >$(srcdir)/conv/conv_test.ok
+ conv/conv_gsm0503_test \
+ >$(srcdir)/conv/conv_gsm0503_test.ok
+ coding/coding_test \
+ >$(srcdir)/coding/coding_test.ok
+ msgb/msgb_test \
+ >$(srcdir)/msgb/msgb_test.ok
+ gea/gea_test \
+ >$(srcdir)/gea/gea_test.ok
+if ENABLE_MSGFILE
+ msgfile/msgfile_test $(srcdir)/msgfile/msgconfig.cfg \
+ >$(srcdir)/msgfile/msgfile_test.ok
+endif
+ sms/sms_test \
+ >$(srcdir)/sms/sms_test.ok
+ smscb/smscb_test \
+ >$(srcdir)/smscb/smscb_test.ok
+ smscb/gsm0341_test \
+ >$(srcdir)/smscb/gsm0341_test.ok
+ smscb/cbsp_test \
+ >$(srcdir)/smscb/cbsp_test.ok
+ ussd/ussd_test \
+ >$(srcdir)/ussd/ussd_test.ok
+ auth/milenage_test \
+ >$(srcdir)/auth/milenage_test.ok
+ auth/tuak_test \
+ >$(srcdir)/auth/tuak_test.ok
+ comp128/comp128_test \
+ >$(srcdir)/comp128/comp128_test.ok
+ lapd/lapd_test \
+ >$(srcdir)/lapd/lapd_test.ok
+ gsm0502/gsm0502_test \
+ >$(srcdir)/gsm0502/gsm0502_test.ok
+ dtx/dtx_gsm0503_test \
+ >$(srcdir)/dtx/dtx_gsm0503_test.ok
+ gsm0808/gsm0808_test \
+ >$(srcdir)/gsm0808/gsm0808_test.ok
+ gsm29205/gsm29205_test \
+ >$(srcdir)/gsm29205/gsm29205_test.ok
+ gsm0408/gsm0408_test \
+ 2>$(srcdir)/gsm0408/gsm0408_test.err \
+ 1>$(srcdir)/gsm0408/gsm0408_test.ok
+ gsm48/rest_octets_test \
+ >$(srcdir)/gsm48/rest_octets_test.ok
+ gprs/gprs_test \
+ >$(srcdir)/gprs/gprs_test.ok
+ logging/logging_test \
+ >$(srcdir)/logging/logging_test.ok \
+ 2>$(srcdir)/logging/logging_test.err
+ logging/logging_gsmtap_test \
+ 2>&1 |grep -v "enqueueing message failed" >$(srcdir)/logging/logging_gsmtap_test.err
+ codec/codec_test \
+ >$(srcdir)/codec/codec_test.ok
+ codec/codec_ecu_fr_test \
+ >$(srcdir)/codec/codec_ecu_fr_test.ok
+if ENABLE_GB
+ fr/fr_test \
+ >$(srcdir)/fr/fr_test.ok
+endif
+ loggingrb/loggingrb_test \
+ >$(srcdir)/loggingrb/logging_test.ok \
+ 2>$(srcdir)/loggingrb/logging_test.err
+ strrb/strrb_test \
+ >$(srcdir)/strrb/strrb_test.ok
+if ENABLE_VTY
+ vty/vty_test $(srcdir)/vty \
+ >$(srcdir)/vty/vty_test.ok \
+ 2>$(srcdir)/vty/vty_test.err
+endif
+if ENABLE_GB
+ gb/gprs_bssgp_test \
+ >$(srcdir)/gb/gprs_bssgp_test.ok
+ gb/gprs_bssgp_rim_test \
+ >$(srcdir)/gb/gprs_bssgp_rim_test.ok
+ gb/gprs_ns_test \
+ >$(srcdir)/gb/gprs_ns_test.ok \
+ 2>$(srcdir)/gb/gprs_ns_test.err
+ gb/gprs_ns2_test \
+ >$(srcdir)/gb/gprs_ns2_test.ok \
+ 2>$(srcdir)/gb/gprs_ns2_test.err
+endif
+if ENABLE_UTILITIES
+ utils/utils_test \
+ >$(srcdir)/utils/utils_test.ok
+endif
+if !EMBEDDED
+ stats/stats_test \
+ >$(srcdir)/stats/stats_test.ok \
+ 2>$(srcdir)/stats/stats_test.err
+endif
+ write_queue/wqueue_test \
+ >$(srcdir)/write_queue/wqueue_test.ok
+if ENABLE_GB
+ $(srcdir)/gb/bssgp_fc_tests.sh gb \
+ >$(srcdir)/gb/bssgp_fc_tests.ok \
+ 2>$(srcdir)/gb/bssgp_fc_tests.err
+endif
+if ENABLE_PCSC
+ sim/sim_test \
+ >$(srcdir)/sim/sim_test.ok
+endif
+ timer/timer_test \
+ >$(srcdir)/timer/timer_test.ok
+ timer/clk_override_test \
+ >$(srcdir)/timer/clk_override_test.ok
+ tlv/tlv_test \
+ >$(srcdir)/tlv/tlv_test.ok
+if !EMBEDDED
+ gsup/gsup_test \
+ >$(srcdir)/gsup/gsup_test.ok \
+ 2>$(srcdir)/gsup/gsup_test.err
+endif
+if ENABLE_CTRL
+ fsm/fsm_test \
+ >$(srcdir)/fsm/fsm_test.ok \
+ 2>$(srcdir)/fsm/fsm_test.err
+ fsm/fsm_dealloc_test \
+ 2>$(srcdir)/fsm/fsm_dealloc_test.err
+endif
+ oap/oap_test \
+ >$(srcdir)/oap/oap_test.ok
+ oap/oap_client_test \
+ >$(srcdir)/oap/oap_client_test.ok \
+ 2>$(srcdir)/oap/oap_client_test.err
+ socket/socket_test \
+ >$(srcdir)/socket/socket_test.ok \
+ 2>$(srcdir)/socket/socket_test.err
+ socket/socket_sctp_test \
+ >$(srcdir)/socket/socket_sctp_test.ok \
+ 2>$(srcdir)/socket/socket_sctp_test.err
+ $(srcdir)/osmo-auc-gen/osmo-auc-gen_test.sh ../utils/osmo-auc-gen \
+ >$(srcdir)/osmo-auc-gen/osmo-auc-gen_test.ok \
+ 2>$(srcdir)/osmo-auc-gen/osmo-auc-gen_test.err
+ endian/endian_test \
+ >$(srcdir)/endian/endian_test.ok
+ sercomm/sercomm_test \
+ >$(srcdir)/sercomm/sercomm_test.ok
+ prbs/prbs_test \
+ >$(srcdir)/prbs/prbs_test.ok
+ gsm23003/gsm23003_test \
+ >$(srcdir)/gsm23003/gsm23003_test.ok
+ gsm23236/gsm23236_test \
+ >$(srcdir)/gsm23236/gsm23236_test.ok
+ tdef/tdef_test \
+ >$(srcdir)/tdef/tdef_test.ok \
+ 2>$(srcdir)/tdef/tdef_test.err
+ sockaddr_str/sockaddr_str_test \
+ >$(srcdir)/sockaddr_str/sockaddr_str_test.ok
+ use_count/use_count_test \
+ >$(srcdir)/use_count/use_count_test.ok \
+ 2>$(srcdir)/use_count/use_count_test.err
+ context/context_test \
+ >$(srcdir)/context/context_test.ok
+if !EMBEDDED
+ exec/exec_test \
+ >$(srcdir)/exec/exec_test.ok \
+ 2>$(srcdir)/exec/exec_test.err
+endif
+ i460_mux/i460_mux_test \
+ >$(srcdir)/i460_mux/i460_mux_test.ok
+ bitgen/bitgen_test \
+ >$(srcdir)/bitgen/bitgen_test.ok
+ gad/gad_test \
+ >$(srcdir)/gad/gad_test.ok
+ bsslap/bsslap_test \
+ >$(srcdir)/bsslap/bsslap_test.ok
+ bssmap_le/bssmap_le_test \
+ >$(srcdir)/bssmap_le/bssmap_le_test.ok
+ it_q/it_q_test \
+ >$(srcdir)/it_q/it_q_test.ok
+ time_cc/time_cc_test \
+ >$(srcdir)/time_cc/time_cc_test.ok
+ iuup/iuup_test \
+ >$(srcdir)/iuup/iuup_test.ok \
+ 2>$(srcdir)/iuup/iuup_test.err
+ v110/frame_test \
+ >$(srcdir)/v110/frame_test.ok
+ v110/ra1_test \
+ >$(srcdir)/v110/ra1_test.ok
+ v110/ta_test \
+ 2>$(srcdir)/v110/ta_test.err
+ gsm44021/frame_csd_test \
+ >$(srcdir)/gsm44021/frame_csd_test.ok
+ osmo_io/osmo_io_test \
+ >$(srcdir)/osmo_io/osmo_io_test.ok \
+ 2>$(srcdir)/osmo_io/osmo_io_test.err
+ soft_uart/soft_uart_test \
+ >$(srcdir)/soft_uart/soft_uart.ok
+ rlp/rlp_test \
+ >$(srcdir)/rlp/rlp_test.ok \
+ $(NULL)
+
+
check-local: atconfig $(TESTSUITE)
[ -e /proc/cpuinfo ] && cat /proc/cpuinfo
- $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
+ $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) ENABLE_URING=$(ENABLE_URING) ENABLE_URING_TESTS=$(ENABLE_URING_TESTS)
$(MAKE) $(AM_MAKEFLAGS) ext-tests
installcheck-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
- $(TESTSUITEFLAGS)
+ $(TESTSUITEFLAGS) ENABLE_URING=$(ENABLE_URING) ENABLE_URING_TESTS=$(ENABLE_URING_TESTS)
clean-local:
test ! -f '$(TESTSUITE)' || \
@@ -361,7 +753,7 @@ $(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
mv $@.tmp $@
conv/gsm0503_test_vectors.c: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py
- $(AM_V_GEN)python $(top_srcdir)/utils/conv_gen.py gen_vectors gsm \
+ $(AM_V_GEN)python3 $(top_srcdir)/utils/conv_gen.py gen_vectors gsm \
--target-path $(builddir)/conv
if ENABLE_EXT_TESTS
@@ -378,37 +770,61 @@ endif
# pass -u to osmo_verify_transcript_vty.py by doing:
# make vty-test U=-u
-vty-test-logging:
+if ENABLE_GB
+vty-test-ns2: $(top_builddir)/utils/osmo-ns-dummy
+ $(MAKE) -C $(top_builddir)/utils osmo-ns-dummy
+ osmo_verify_transcript_vty.py -v \
+ -p 42042 \
+ -r "$(top_builddir)/utils/osmo-ns-dummy -p 42042" \
+ $(U) $(srcdir)/gb/gprs_ns2*.vty
+ osmotestvty.py -p $(abs_top_srcdir)/tests/gb -w $(abs_top_builddir)/tests/gb -v
+ osmotestconfig.py -p $(abs_top_srcdir)/tests/gb -w $(abs_top_builddir)/tests/gb -v
+else
+vty-test-ns2:
+ echo "Not running vty-test-ns2 because osmo-ns-dummy is not built (--disable-gb)"
+endif
+
+vty-test-logging: $(top_builddir)/tests/logging/logging_vty_test
osmo_verify_transcript_vty.py -v \
-p 42042 \
-r "$(top_builddir)/tests/logging/logging_vty_test" \
$(U) $(srcdir)/logging/*.vty
-vty-test-vty:
+vty-test-vty: $(top_builddir)/tests/vty/vty_transcript_test
osmo_verify_transcript_vty.py -v \
-p 42042 \
-r "$(top_builddir)/tests/vty/vty_transcript_test" \
$(U) $(srcdir)/vty/*.vty
-vty-test-tdef:
+vty-test-tdef: $(top_builddir)/tests/tdef/tdef_vty_config_root_test \
+ $(top_builddir)/tests/tdef/tdef_vty_config_subnode_test \
+ $(top_builddir)/tests/tdef/tdef_vty_dynamic_test
+ osmo_verify_transcript_vty.py -v \
+ -p 42042 \
+ -r "$(top_builddir)/tests/tdef/tdef_vty_config_root_test" \
+ $(U) $(srcdir)/tdef/tdef_vty_config_root_test.vty
osmo_verify_transcript_vty.py -v \
-p 42042 \
- -r "$(top_builddir)/tests/tdef/tdef_vty_test_config_root" \
- $(U) $(srcdir)/tdef/tdef_vty_test_config_root.vty
+ -r "$(top_builddir)/tests/tdef/tdef_vty_config_subnode_test" \
+ $(U) $(srcdir)/tdef/tdef_vty_config_subnode_test.vty
osmo_verify_transcript_vty.py -v \
-p 42042 \
- -r "$(top_builddir)/tests/tdef/tdef_vty_test_config_subnode" \
- $(U) $(srcdir)/tdef/tdef_vty_test_config_subnode.vty
+ -r "$(top_builddir)/tests/tdef/tdef_vty_dynamic_test" \
+ $(U) $(srcdir)/tdef/tdef_vty_dynamic_test.vty
+
+vty-test-stats: $(top_builddir)/tests/stats/stats_vty_test
osmo_verify_transcript_vty.py -v \
-p 42042 \
- -r "$(top_builddir)/tests/tdef/tdef_vty_test_dynamic" \
- $(U) $(srcdir)/tdef/tdef_vty_test_dynamic.vty
+ -r "$(top_builddir)/tests/stats/stats_vty_test" \
+ $(U) $(srcdir)/stats/*.vty
# don't run vty tests concurrently so that the ports don't conflict
vty-test:
$(MAKE) vty-test-logging
$(MAKE) vty-test-vty
$(MAKE) vty-test-tdef
+ $(MAKE) vty-test-ns2
+ $(MAKE) vty-test-stats
ctrl-test:
echo "No CTRL tests exist currently"
diff --git a/tests/a5/a5_test.c b/tests/a5/a5_test.c
index 69f10355..74586a68 100644
--- a/tests/a5/a5_test.c
+++ b/tests/a5/a5_test.c
@@ -66,7 +66,7 @@ static inline bool test_a53(const char * kc, uint32_t count, const char * block1
_a5_3(key, count, dlout, NULL, false);
_a5_3(key, count, NULL, ulout, false);
- return print_a5(3, 8, "DL", dlout, block1) & print_a5(3, 8, "UL", ulout, block2);
+ return print_a5(3, 8, "DL", dlout, block1) && print_a5(3, 8, "UL", ulout, block2);
}
static inline bool test_a54(const char * kc, uint32_t count, const char * block1, const char * block2)
@@ -78,7 +78,7 @@ static inline bool test_a54(const char * kc, uint32_t count, const char * block1
_a5_4(key, count, dlout, NULL, false);
_a5_4(key, count, NULL, ulout, false);
- return print_a5(4, 8, "DL", dlout, block1) & print_a5(4, 8, "UL", ulout, block2);
+ return print_a5(4, 8, "DL", dlout, block1) && print_a5(4, 8, "UL", ulout, block2);
}
diff --git a/tests/abis/abis_test.c b/tests/abis/abis_test.c
index ca6daed8..ac470fc7 100644
--- a/tests/abis/abis_test.c
+++ b/tests/abis/abis_test.c
@@ -1,6 +1,6 @@
/*
* (C) 2012 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2017 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
+ * (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
@@ -171,7 +167,7 @@ static inline void chk_descr(struct msgb *msg, const char *f_id, const char *f_v
}
}
-static void test_sw_descr()
+static void test_sw_descr(void)
{
const char *f_id = "TEST.L0L", *f_ver = "0.1.666~deadbeeffacefeed-dirty";
uint8_t chain[] = { 0x42, 0x12, 0x00, 0x03, 0x01, 0x02, 0x03, 0x13, 0x00, 0x03, 0x03, 0x04, 0x05, 0x42, 0x12,
@@ -197,6 +193,52 @@ static void test_sw_descr()
msgb_free(msg);
}
+/* Test decode IPAC_DLCX_IND obtained from SYS#5915 */
+static void test_dec_ipac_dlc_indx(void)
+{
+/* Radio Signalling Link (RSL)
+ 0111 111. = Message discriminator: ip.access Vendor Specific messages (63)
+ .... ...0 = T bit: Not considered transparent by BTS
+ .111 0110 = Message type: ip.access DLCX INDication (0x76)
+ Channel number IE
+ Element identifier: Channel Number (0x01)
+ 0000 1... = C-bits: Bm + ACCH (1)
+ .... .110 = Time slot number (TN): 6
+ Element identifier: Connection Identifier (0xf8)
+ ip.access Connection ID: 0
+ Element identifier: Connection Statistics (0xf6)
+ [1 byte length here, val = 28 (0x1c)]
+ Packets Sent: 1202
+ Octets Sent: 45052
+ Packets Received: 556
+ Octets Received: 24580
+ Packets Lost: 0
+ Inter-arrival Jitter: 0
+ Average Tx Delay: 0
+ Cause IE
+ Element identifier: Cause (0x1a)
+ Length: 1
+ 0... .... = Extension: No Extension
+ .000 .... = Class: Normal event (0)
+ .000 1111 = Cause Value: normal event, unspecified (15)
+*/
+ const uint8_t hex[] = {
+ 0x7e, 0x76, 0x01, 0x0e, 0xf8, 0x00, 0x00, 0xf6, 0x1c, 0x00, 0x00, 0x04, 0xb2, 0x00, 0x00, 0xaf,
+ 0xfc, 0x00, 0x00, 0x02, 0x2c, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, 0x0f
+ };
+ struct abis_rsl_dchan_hdr *dh = (struct abis_rsl_dchan_hdr *)&hex[0];
+
+ struct tlv_parsed tp;
+ int rc;
+
+ printf("Testing decoding IPAC_DLCX_IND\n");
+
+ rc = rsl_tlv_parse(&tp, dh->data, sizeof(hex) - sizeof(*dh));
+
+ OSMO_ASSERT(rc == 3);
+}
+
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "abis_test");
@@ -206,6 +248,7 @@ int main(int argc, char **argv)
test_simple_sw_config();
test_simple_sw_short();
test_dual_sw_config();
+ test_dec_ipac_dlc_indx();
printf("OK.\n");
diff --git a/tests/abis/abis_test.ok b/tests/abis/abis_test.ok
index e6b626be..2ee647bb 100644
--- a/tests/abis/abis_test.ok
+++ b/tests/abis/abis_test.ok
@@ -38,4 +38,5 @@ len: 13
file_id: 09 07 05
file_ver: 06 07 08
test_dual_sw_config(): OK
+Testing decoding IPAC_DLCX_IND
OK.
diff --git a/tests/auth/tuak_test.c b/tests/auth/tuak_test.c
new file mode 100644
index 00000000..a00ab2ce
--- /dev/null
+++ b/tests/auth/tuak_test.c
@@ -0,0 +1,309 @@
+
+#include <stdint.h>
+#include <osmocom/core/utils.h>
+#include "gsm/tuak/tuak.h"
+
+/* user-friendly test specification, uses hex-strings for all parameters for
+ * copy+pasting from the spec. */
+struct tuak_testspec {
+ const char *name;
+ struct {
+ const char *k;
+ const char *rand;
+ const char *sqn;
+ const char *amf;
+ const char *top;
+ unsigned int keccak_iterations;
+ } in;
+ struct {
+ const char *topc;
+ const char *f1;
+ const char *f1star;
+ const char *f2;
+ const char *f3;
+ const char *f4;
+ const char *f5;
+ const char *f5star;
+ } out;
+};
+
+static const struct tuak_testspec testspecs[] = {
+ {
+ .name = "TS 35.233 Section 6.3 Test Set 1",
+ .in = {
+ .k = "abababababababababababababababab",
+ .rand = "42424242424242424242424242424242",
+ .sqn = "111111111111",
+ .amf = "ffff",
+ .top = "5555555555555555555555555555555555555555555555555555555555555555",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "bd04d9530e87513c5d837ac2ad954623a8e2330c115305a73eb45d1f40cccbff",
+ .f1 = "f9a54e6aeaa8618d",
+ .f1star = "e94b4dc6c7297df3",
+ .f2 = "657acd64",
+ .f3 = "d71a1e5c6caffe986a26f783e5c78be1",
+ .f4 = "be849fa2564f869aecee6f62d4337e72",
+ .f5 = "719f1e9b9054",
+ .f5star = "e7af6b3d0e38",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.4 Test Set 2",
+ .in = {
+ .k = "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0",
+ .rand = "0123456789abcdef0123456789abcdef",
+ .sqn = "0123456789ab",
+ .amf = "abcd",
+ .top = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "305425427e18c503c8a4b294ea72c95d0c36c6c6b29d0c65de5974d5977f8524",
+ .f1 = "c0b8c2d4148ec7aa5f1d78a97e4d1d58",
+ .f1star = "ef81af7290f7842c6ceafa537fa0745b",
+ .f2 = "e9d749dc4eea0035",
+ .f3 = "a4cb6f6529ab17f8337f27baa8234d47",
+ .f4 = "2274155ccf4199d5e2abcbf621907f90",
+ .f5 = "480a9345cc1e",
+ .f5star = "f84eb338848c",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.5 Test Set 3",
+ .in = {
+ .k = "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0",
+ .rand = "0123456789abcdef0123456789abcdef",
+ .sqn = "0123456789ab",
+ .amf = "abcd",
+ .top = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "305425427e18c503c8a4b294ea72c95d0c36c6c6b29d0c65de5974d5977f8524",
+ .f1 = "d97b75a1776065271b1e212bc3b1bf173f438b21e6c64a55a96c372e085e5cc5",
+ .f1star = "427bbf07c6e3a86c54f8c5216499f3909a6fd4a164c9fe235b1550258111b821",
+ .f2 = "07021c73e7635c7d",
+ .f3 = "4d59ac796834eb85d11fa148a5058c3c",
+ .f4 = "126d47500136fdc5ddfd14f19ebf16749ce4b6435323fbb5715a3a796a6082bd",
+ .f5 = "1d6622c4e59a",
+ .f5star = "f84eb338848c",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.6 Test Set 4",
+ .in = {
+ .k = "b8da837a50652d6ac7c97da14f6acc61",
+ .rand = "6887e55425a966bd86c9661a5fa72be8",
+ .sqn = "0dea2ee2c5af",
+ .amf = "df1e",
+ .top = "0952be13556c32ebc58195d9dd930493e12a9003669988ffde5fa1f0fe35cc01",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "2bc16eb657a68e1f446f08f57c0efb1d493527a2e652ce281eb6ca0e4487760a",
+ .f1 = "749214087958dd8f58bfcdf869d8ae3f",
+ .f1star = "619e865afe80e382aee13063f9dfb56d",
+ .f2 = "4041ce438e3e38e8aa96562eed83ac43",
+ .f3 = "3e3bc01bea0cd914c4c2c83ce2d92757",
+ .f4 = "666a8e6f577b1aa77b7fd53cebb8a3d6",
+ .f5 = "1f880d005119",
+ .f5star = "45e617d77fe5",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.7 Test Set 5",
+ .in = {
+ .k = "1574ca56881d05c189c82880f789c9cd4244955f4426aa2b69c29f15770e5aa5",
+ .rand = "c570aac68cde651fb1e3088322498bef",
+ .sqn = "c89bb71f3a41",
+ .amf = "297d",
+ .top = "e59f6eb10ea406813f4991b0b9e02f181edf4c7e17b480f66d34da35ee88c95e",
+ .keccak_iterations = 1,
+ },
+ .out = {
+ .topc = "3c6052e41532a28a47aa3cbb89f223e8f3aaa976aecd48bc3e7d6165a55eff62",
+ .f1 = "d7340dad02b4cb01",
+ .f1star = "c6021e2e66accb15",
+ .f2 = "84d89b41db1867ffd4c7ba1d82163f4d526a20fbae5418fbb526940b1eeb905c",
+ .f3 = "d419676afe5ab58c1d8bee0d43523a4d2f52ef0b31a4676a0c334427a988fe65",
+ .f4 = "205533e505661b61d05cc0eac87818f4",
+ .f5 = "d7b3d2d4980a",
+ .f5star = "ca9655264986",
+ },
+ }, {
+ .name = "TS 35.233 Section 6.8 Test Set 6",
+ .in = {
+ .k = "1574ca56881d05c189c82880f789c9cd4244955f4426aa2b69c29f15770e5aa5",
+ .rand = "c570aac68cde651fb1e3088322498bef",
+ .sqn = "c89bb71f3a41",
+ .amf = "297d",
+ .top = "e59f6eb10ea406813f4991b0b9e02f181edf4c7e17b480f66d34da35ee88c95e",
+ .keccak_iterations = 2,
+ },
+ .out = {
+ .topc = "b04a66f26c62fcd6c82de22a179ab65506ecf47f56245cd149966cfa9cec7a51",
+ .f1 = "90d2289ed1ca1c3dbc2247bb480d431ac71d2e4a7677f6e997cfddb0cbad88b7",
+ .f1star = "427355dbac30e825063aba61b556e87583abac638e3ab01c4c884ad9d458dc2f",
+ .f2 = "d67e6e64590d22eecba7324afa4af4460c93f01b24506d6e12047d789a94c867",
+ .f3 = "ede57edfc57cdffe1aae75066a1b7479bbc3837438e88d37a801cccc9f972b89",
+ .f4 = "48ed9299126e5057402fe01f9201cf25249f9c5c0ed2afcf084755daff1d3999",
+ .f5 = "6aae8d18c448",
+ .f5star = "8c5f33b61f4e",
+ },
+ },
+};
+
+
+struct tuak_testset {
+ const char *name;
+ struct {
+ uint8_t k[32];
+ uint8_t k_len_bytes;
+ uint8_t rand[16];
+ uint8_t sqn[6];
+ uint8_t amf[2];
+ uint8_t top[32];
+ unsigned int keccak_iterations;
+ } in;
+ struct {
+ uint8_t topc[32];
+ uint8_t mac_a[32];
+ uint8_t mac_s[32];
+ uint8_t mac_len_bytes;
+
+ uint8_t res[32];
+ uint8_t res_len_bytes;
+
+ uint8_t ck[32];
+ uint8_t ck_len_bytes;
+ uint8_t ik[32];
+ uint8_t ik_len_bytes;
+ uint8_t ak[6];
+ uint8_t f5star[6];
+ } out;
+};
+
+static void expect_equal(const char *name, const uint8_t *actual, const uint8_t *expected, size_t len)
+{
+ if (!memcmp(actual, expected, len)) {
+ printf("\t%s: %s\r\n", name, osmo_hexdump_nospc(actual, len));
+ } else {
+ char buf[len*2+1];
+ printf("\t%s: %s != %s\r\n", name, osmo_hexdump_nospc(actual, len),
+ osmo_hexdump_buf(buf, sizeof(buf), expected, len, "", true));
+ }
+}
+
+static void execute_testset(const struct tuak_testset *tset)
+{
+ uint8_t topc[32];
+
+ printf("==> %s\n", tset->name);
+
+ tuak_set_keccak_iterations(tset->in.keccak_iterations);
+ tuak_opc_gen(topc, tset->in.k, tset->in.k_len_bytes, tset->in.top);
+ expect_equal("TOPc", topc, tset->out.topc, sizeof(topc));
+
+ if (tset->out.mac_len_bytes) {
+ uint8_t mac_a[32];
+ uint8_t mac_s[32];
+
+ tuak_f1(topc, tset->in.k, tset->in.k_len_bytes, tset->in.rand, tset->in.sqn, tset->in.amf,
+ mac_a, tset->out.mac_len_bytes, tset->in.keccak_iterations);
+ expect_equal("MAC_A", mac_a, tset->out.mac_a, tset->out.mac_len_bytes);
+
+ tuak_f1star(topc, tset->in.k, tset->in.k_len_bytes, tset->in.rand, tset->in.sqn, tset->in.amf,
+ mac_s, tset->out.mac_len_bytes, tset->in.keccak_iterations);
+ expect_equal("MAC_S", mac_s, tset->out.mac_s, tset->out.mac_len_bytes);
+ }
+
+ if (tset->out.ck_len_bytes || tset->out.ik_len_bytes || tset->out.res_len_bytes) {
+ uint8_t res[32];
+ uint8_t ck[32];
+ uint8_t ik[32];
+ uint8_t ak[6];
+
+ tuak_f2345(topc, tset->in.k, tset->in.k_len_bytes, tset->in.rand,
+ tset->out.res_len_bytes ? res : NULL, tset->out.res_len_bytes,
+ tset->out.ck_len_bytes ? ck : NULL, tset->out.ck_len_bytes,
+ tset->out.ik_len_bytes ? ik : NULL, tset->out.ik_len_bytes,
+ ak, tset->in.keccak_iterations);
+
+ if (tset->out.res_len_bytes)
+ expect_equal("RES", res, tset->out.res, tset->out.res_len_bytes);
+
+ if (tset->out.ck_len_bytes)
+ expect_equal("CK", ck, tset->out.ck, tset->out.ck_len_bytes);
+
+ if (tset->out.ik_len_bytes)
+ expect_equal("IK", ik, tset->out.ik, tset->out.ik_len_bytes);
+
+ expect_equal("AK", ak, tset->out.ak, 6);
+ }
+}
+
+/* convert string-testspec to binary-testset and execute it */
+static void execute_testspec(const struct tuak_testspec *tcase)
+{
+ struct tuak_testset _tset, *tset = &_tset;
+
+ tset->name = tcase->name;
+ tset->in.keccak_iterations = tcase->in.keccak_iterations;
+
+ osmo_hexparse(tcase->in.k, tset->in.k, sizeof(tset->in.k));
+ tset->in.k_len_bytes = strlen(tcase->in.k)/2;
+ OSMO_ASSERT(tset->in.k_len_bytes == 16 || tset->in.k_len_bytes == 32);
+
+ osmo_hexparse(tcase->in.rand, tset->in.rand, sizeof(tset->in.rand));
+ OSMO_ASSERT(strlen(tcase->in.rand)/2 == 16);
+
+ osmo_hexparse(tcase->in.sqn, tset->in.sqn, sizeof(tset->in.sqn));
+ OSMO_ASSERT(strlen(tcase->in.sqn)/2 == 6);
+
+ osmo_hexparse(tcase->in.amf, tset->in.amf, sizeof(tset->in.amf));
+ OSMO_ASSERT(strlen(tcase->in.amf)/2 == 2);
+
+ osmo_hexparse(tcase->in.top, tset->in.top, sizeof(tset->in.top));
+ OSMO_ASSERT(strlen(tcase->in.top)/2 == 32);
+
+ osmo_hexparse(tcase->out.topc, tset->out.topc, sizeof(tset->out.topc));
+ OSMO_ASSERT(strlen(tcase->out.topc)/2 == 32);
+
+ osmo_hexparse(tcase->out.f1, tset->out.mac_a, sizeof(tset->out.mac_a));
+ osmo_hexparse(tcase->out.f1star, tset->out.mac_s, sizeof(tset->out.mac_s));
+ OSMO_ASSERT(strlen(tcase->out.f1) == strlen(tcase->out.f1star));
+ tset->out.mac_len_bytes = strlen(tcase->out.f1)/2;
+ OSMO_ASSERT(tset->out.mac_len_bytes == 8 || tset->out.mac_len_bytes == 16 ||
+ tset->out.mac_len_bytes == 32);
+
+ osmo_hexparse(tcase->out.f2, tset->out.res, sizeof(tset->out.res));
+ tset->out.res_len_bytes = strlen(tcase->out.f2)/2;
+ OSMO_ASSERT(tset->out.res_len_bytes == 4 || tset->out.res_len_bytes == 8 ||
+ tset->out.res_len_bytes == 16 || tset->out.res_len_bytes == 32);
+
+ osmo_hexparse(tcase->out.f3, tset->out.ck, sizeof(tset->out.ck));
+ tset->out.ck_len_bytes = strlen(tcase->out.f3)/2;
+ OSMO_ASSERT(tset->out.ck_len_bytes == 16 || tset->out.ck_len_bytes == 32);
+
+ osmo_hexparse(tcase->out.f4, tset->out.ik, sizeof(tset->out.ik));
+ tset->out.ik_len_bytes = strlen(tcase->out.f4)/2;
+ OSMO_ASSERT(tset->out.ik_len_bytes == 16 || tset->out.ik_len_bytes == 32);
+
+ osmo_hexparse(tcase->out.f5, tset->out.ak, sizeof(tset->out.ak));
+ OSMO_ASSERT(strlen(tcase->out.f5)/2 == 6);
+
+ osmo_hexparse(tcase->out.f5star, tset->out.f5star, sizeof(tset->out.f5star));
+ OSMO_ASSERT(strlen(tcase->out.f5star)/2 == 6);
+
+ execute_testset(tset);
+}
+
+int main(int argc, char **argv)
+{
+#if 0
+ for (unsigned int i = 0; i < ARRAY_SIZE(testsets); i++)
+ execute_testset(&testsets[i]);
+#endif
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(testspecs); i++)
+ execute_testspec(&testspecs[i]);
+
+}
diff --git a/tests/auth/tuak_test.ok b/tests/auth/tuak_test.ok
new file mode 100644
index 00000000..976fb59e
--- /dev/null
+++ b/tests/auth/tuak_test.ok
@@ -0,0 +1,48 @@
+==> TS 35.233 Section 6.3 Test Set 1
+ TOPc: bd04d9530e87513c5d837ac2ad954623a8e2330c115305a73eb45d1f40cccbff
+ MAC_A: f9a54e6aeaa8618d
+ MAC_S: e94b4dc6c7297df3
+ RES: 657acd64
+ CK: d71a1e5c6caffe986a26f783e5c78be1
+ IK: be849fa2564f869aecee6f62d4337e72
+ AK: 719f1e9b9054
+==> TS 35.233 Section 6.4 Test Set 2
+ TOPc: 305425427e18c503c8a4b294ea72c95d0c36c6c6b29d0c65de5974d5977f8524
+ MAC_A: c0b8c2d4148ec7aa5f1d78a97e4d1d58
+ MAC_S: ef81af7290f7842c6ceafa537fa0745b
+ RES: e9d749dc4eea0035
+ CK: a4cb6f6529ab17f8337f27baa8234d47
+ IK: 2274155ccf4199d5e2abcbf621907f90
+ AK: 480a9345cc1e
+==> TS 35.233 Section 6.5 Test Set 3
+ TOPc: 305425427e18c503c8a4b294ea72c95d0c36c6c6b29d0c65de5974d5977f8524
+ MAC_A: d97b75a1776065271b1e212bc3b1bf173f438b21e6c64a55a96c372e085e5cc5
+ MAC_S: 427bbf07c6e3a86c54f8c5216499f3909a6fd4a164c9fe235b1550258111b821
+ RES: 07021c73e7635c7d
+ CK: 4d59ac796834eb85d11fa148a5058c3c
+ IK: 126d47500136fdc5ddfd14f19ebf16749ce4b6435323fbb5715a3a796a6082bd
+ AK: 1d6622c4e59a
+==> TS 35.233 Section 6.6 Test Set 4
+ TOPc: 2bc16eb657a68e1f446f08f57c0efb1d493527a2e652ce281eb6ca0e4487760a
+ MAC_A: 749214087958dd8f58bfcdf869d8ae3f
+ MAC_S: 619e865afe80e382aee13063f9dfb56d
+ RES: 4041ce438e3e38e8aa96562eed83ac43
+ CK: 3e3bc01bea0cd914c4c2c83ce2d92757
+ IK: 666a8e6f577b1aa77b7fd53cebb8a3d6
+ AK: 1f880d005119
+==> TS 35.233 Section 6.7 Test Set 5
+ TOPc: 3c6052e41532a28a47aa3cbb89f223e8f3aaa976aecd48bc3e7d6165a55eff62
+ MAC_A: d7340dad02b4cb01
+ MAC_S: c6021e2e66accb15
+ RES: 84d89b41db1867ffd4c7ba1d82163f4d526a20fbae5418fbb526940b1eeb905c
+ CK: d419676afe5ab58c1d8bee0d43523a4d2f52ef0b31a4676a0c334427a988fe65
+ IK: 205533e505661b61d05cc0eac87818f4
+ AK: d7b3d2d4980a
+==> TS 35.233 Section 6.8 Test Set 6
+ TOPc: b04a66f26c62fcd6c82de22a179ab65506ecf47f56245cd149966cfa9cec7a51
+ MAC_A: 90d2289ed1ca1c3dbc2247bb480d431ac71d2e4a7677f6e997cfddb0cbad88b7
+ MAC_S: 427355dbac30e825063aba61b556e87583abac638e3ab01c4c884ad9d458dc2f
+ RES: d67e6e64590d22eecba7324afa4af4460c93f01b24506d6e12047d789a94c867
+ CK: ede57edfc57cdffe1aae75066a1b7479bbc3837438e88d37a801cccc9f972b89
+ IK: 48ed9299126e5057402fe01f9201cf25249f9c5c0ed2afcf084755daff1d3999
+ AK: 6aae8d18c448
diff --git a/tests/auth/xor2g_test.c b/tests/auth/xor2g_test.c
new file mode 100644
index 00000000..82ab25ac
--- /dev/null
+++ b/tests/auth/xor2g_test.c
@@ -0,0 +1,77 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <osmocom/crypt/auth.h>
+#include <osmocom/core/utils.h>
+
+static void dump_auth_vec(struct osmo_auth_vector *vec)
+{
+ printf("RAND:\t%s\n", osmo_hexdump(vec->rand, sizeof(vec->rand)));
+
+ if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) {
+ printf("AUTN:\t%s\n", osmo_hexdump(vec->autn, sizeof(vec->autn)));
+ printf("IK:\t%s\n", osmo_hexdump(vec->ik, sizeof(vec->ik)));
+ printf("CK:\t%s\n", osmo_hexdump(vec->ck, sizeof(vec->ck)));
+ printf("RES:\t%s\n", osmo_hexdump(vec->res, vec->res_len));
+ }
+
+ if (vec->auth_types & OSMO_AUTH_TYPE_GSM) {
+ printf("SRES:\t%s\n", osmo_hexdump(vec->sres, sizeof(vec->sres)));
+ /* According to 3GPP TS 55.205 Sec. 4 the GSM-MILENAGE output is limited to 64 bits.
+ According to 3GPP TS 33.102 Annex. B5 in UMTS security context Kc can be 128 bits.
+ Here we test the former, so make sure we only print interesting Kc bits. */
+ printf("Kc:\t%s\n", osmo_hexdump(vec->kc, OSMO_A5_MAX_KEY_LEN_BYTES/2));
+ }
+}
+
+static struct osmo_sub_auth_data test_aud = {
+ .type = OSMO_AUTH_TYPE_GSM,
+ .algo = OSMO_AUTH_ALG_XOR_2G,
+ .u.gsm = {
+ .ki = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+ },
+};
+
+int main(int argc, char **argv)
+{
+ struct osmo_auth_vector _vec;
+ struct osmo_auth_vector *vec = &_vec;
+ uint8_t _rand[16];
+ int rc;
+
+#if 0
+ srand(time(NULL));
+ *(uint32_t *)&_rand[0] = rand();
+ *(uint32_t *)(&_rand[4]) = rand();
+ *(uint32_t *)(&_rand[8]) = rand();
+ *(uint32_t *)(&_rand[12]) = rand();
+#else
+ memset(_rand, 0, sizeof(_rand));
+#endif
+ memset(vec, 0, sizeof(*vec));
+
+ rc = osmo_auth_gen_vec(vec, &test_aud, _rand);
+ if (rc < 0) {
+ fprintf(stderr, "error generating auth vector\n");
+ exit(1);
+ }
+ dump_auth_vec(vec);
+
+ /* test once more with non-zero RAND to see it show in result */
+ for (int i = 0; i < sizeof(_rand); i++)
+ _rand[i] = i << 4;
+
+ rc = osmo_auth_gen_vec(vec, &test_aud, _rand);
+ if (rc < 0) {
+ fprintf(stderr, "error generating auth vector\n");
+ exit(1);
+ }
+ dump_auth_vec(vec);
+
+ exit(0);
+}
diff --git a/tests/auth/xor2g_test.ok b/tests/auth/xor2g_test.ok
new file mode 100644
index 00000000..58becf65
--- /dev/null
+++ b/tests/auth/xor2g_test.ok
@@ -0,0 +1,6 @@
+RAND: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+SRES: 00 01 02 03
+Kc: 04 05 06 07 08 09 0a 0b
+RAND: 00 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0
+SRES: 00 11 22 33
+Kc: 44 55 66 77 88 99 aa bb
diff --git a/tests/base64/base64_test.c b/tests/base64/base64_test.c
new file mode 100644
index 00000000..79ec212a
--- /dev/null
+++ b/tests/base64/base64_test.c
@@ -0,0 +1,55 @@
+#include <osmocom/core/base64.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+static const unsigned char base64_test_dec[64] = {
+ 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
+ 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
+ 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
+ 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
+ 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
+ 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
+ 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
+ 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
+};
+
+static const unsigned char base64_test_enc[] =
+ "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
+ "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
+
+/*
+ * Checkup routine
+ */
+int main(int argc, char **argv)
+{
+ size_t len;
+ const unsigned char *src;
+ unsigned char buffer[128];
+
+ printf(" Base64 encoding test: ");
+
+ src = base64_test_dec;
+
+ if (osmo_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 ||
+ memcmp(base64_test_enc, buffer, 88) != 0) {
+ printf("failed\n");
+
+ exit(1);
+ }
+
+ printf("passed\n Base64 decoding test: ");
+
+ src = base64_test_enc;
+
+ if (osmo_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 ||
+ memcmp(base64_test_dec, buffer, 64) != 0) {
+ printf("failed\n");
+
+ exit(1);
+ }
+
+ printf("passed\n\n");
+
+ exit(0);
+}
diff --git a/tests/base64/base64_test.ok b/tests/base64/base64_test.ok
new file mode 100644
index 00000000..ff187d9c
--- /dev/null
+++ b/tests/base64/base64_test.ok
@@ -0,0 +1,3 @@
+ Base64 encoding test: passed
+ Base64 decoding test: passed
+
diff --git a/tests/bitgen/bitgen_test.c b/tests/bitgen/bitgen_test.c
new file mode 100644
index 00000000..bd472916
--- /dev/null
+++ b/tests/bitgen/bitgen_test.c
@@ -0,0 +1,85 @@
+#include <inttypes.h>
+#include <osmocom/core/bit16gen.h>
+#include <osmocom/core/bit32gen.h>
+#include <osmocom/core/bit64gen.h>
+
+#define DO_TEST(BE_LE, SIZE) do { \
+ int8_t len; \
+ printf("--- " #SIZE " " #BE_LE "\n"); \
+ for (len = SIZE / 8; len > 0; len--) { \
+ uint8_t buf[len * 2]; \
+ uint8_t at_idx; \
+ uint##SIZE##_t val = (uint##SIZE##_t)0x8877665544332211; \
+ \
+ for (at_idx = 0; at_idx < len; at_idx++) { \
+ uint##SIZE##_t read_val = 0; \
+ memset(buf, 0, sizeof(buf)); \
+ osmo_store##SIZE##BE_LE##_ext(val, &buf[at_idx], len); \
+ printf("osmo_store" #SIZE #BE_LE "_ext(0x%" PRIx##SIZE ", &buf[%d], %d) = %s\n", \
+ val, \
+ at_idx, len, osmo_hexdump(buf, sizeof(buf))); \
+ \
+ read_val = osmo_load##SIZE##BE_LE##_ext(&buf[at_idx], len); \
+ printf("osmo_load" #SIZE #BE_LE "_ext(&buf[%d], %d) = 0x%" PRIx##SIZE "\n", \
+ at_idx, len, read_val); \
+ \
+ if (!strcmp(#BE_LE, "be")) { \
+ read_val = osmo_load##SIZE##BE_LE##_ext_2(&buf[at_idx], len); \
+ printf("osmo_load" #SIZE #BE_LE "_ext_2(&buf[%d], %d) = 0x%" PRIx##SIZE "\n", \
+ at_idx, len, read_val); \
+ } \
+ } \
+ } \
+ } while (0)
+
+/* Shims to allow compiling, the *le_ext_2 are not actually invoked because of the strcmp() condition above. */
+#define osmo_load16le_ext_2 dummy
+#define osmo_load32le_ext_2 dummy
+#define osmo_load64le_ext_2 dummy
+
+static inline uint64_t dummy(const void *p, uint8_t n)
+{
+ OSMO_ASSERT(false);
+}
+
+int main(int argc, char **argv)
+{
+ DO_TEST(be, 16);
+ DO_TEST(le, 16);
+ DO_TEST(be, 32);
+ DO_TEST(le, 32);
+ DO_TEST(be, 64);
+ DO_TEST(le, 64);
+
+ {
+ printf("--- store/load 0x112233 as 24bit big-endian, legacy\n");
+ uint8_t buf[4];
+ memset(buf, 0, sizeof(buf));
+ osmo_store32be_ext(0x00112233, buf, 3); // stores 11 22 33
+ printf("%s\n", osmo_hexdump(buf, 4));
+ uint32_t r = osmo_load32be_ext(buf, 3); // returns 0x11223300, not 0x00112233
+ printf("0x%x\n", r);
+ }
+
+ {
+ printf("--- store/load 0x112233 as 24bit big-endian\n");
+ uint8_t buf[4];
+ memset(buf, 0, sizeof(buf));
+ osmo_store32be_ext(0x00112233, buf, 3); // stores 11 22 33
+ printf("%s\n", osmo_hexdump(buf, 4));
+ uint32_t r = osmo_load32be_ext_2(buf, 3); // returns 0x00112233
+ printf("0x%x\n", r);
+ }
+
+ {
+ printf("--- store/load 0x112233 as 24bit little-endian\n");
+ uint8_t buf[4];
+ memset(buf, 0, sizeof(buf));
+ osmo_store32le_ext(0x00112233, buf, 3); // stores 33 22 11
+ printf("%s\n", osmo_hexdump(buf, 4));
+ uint32_t r = osmo_load32le_ext(buf, 3); // returns 0x00112233
+ printf("0x%x\n", r);
+ }
+
+ return 0;
+}
diff --git a/tests/bitgen/bitgen_test.ok b/tests/bitgen/bitgen_test.ok
new file mode 100644
index 00000000..b8ce9bc8
--- /dev/null
+++ b/tests/bitgen/bitgen_test.ok
@@ -0,0 +1,260 @@
+--- 16 be
+osmo_store16be_ext(0x2211, &buf[0], 2) = 22 11 00 00
+osmo_load16be_ext(&buf[0], 2) = 0x2211
+osmo_load16be_ext_2(&buf[0], 2) = 0x2211
+osmo_store16be_ext(0x2211, &buf[1], 2) = 00 22 11 00
+osmo_load16be_ext(&buf[1], 2) = 0x2211
+osmo_load16be_ext_2(&buf[1], 2) = 0x2211
+osmo_store16be_ext(0x2211, &buf[0], 1) = 11 00
+osmo_load16be_ext(&buf[0], 1) = 0x1100
+osmo_load16be_ext_2(&buf[0], 1) = 0x11
+--- 16 le
+osmo_store16le_ext(0x2211, &buf[0], 2) = 11 22 00 00
+osmo_load16le_ext(&buf[0], 2) = 0x2211
+osmo_store16le_ext(0x2211, &buf[1], 2) = 00 11 22 00
+osmo_load16le_ext(&buf[1], 2) = 0x2211
+osmo_store16le_ext(0x2211, &buf[0], 1) = 11 00
+osmo_load16le_ext(&buf[0], 1) = 0x11
+--- 32 be
+osmo_store32be_ext(0x44332211, &buf[0], 4) = 44 33 22 11 00 00 00 00
+osmo_load32be_ext(&buf[0], 4) = 0x44332211
+osmo_load32be_ext_2(&buf[0], 4) = 0x44332211
+osmo_store32be_ext(0x44332211, &buf[1], 4) = 00 44 33 22 11 00 00 00
+osmo_load32be_ext(&buf[1], 4) = 0x44332211
+osmo_load32be_ext_2(&buf[1], 4) = 0x44332211
+osmo_store32be_ext(0x44332211, &buf[2], 4) = 00 00 44 33 22 11 00 00
+osmo_load32be_ext(&buf[2], 4) = 0x44332211
+osmo_load32be_ext_2(&buf[2], 4) = 0x44332211
+osmo_store32be_ext(0x44332211, &buf[3], 4) = 00 00 00 44 33 22 11 00
+osmo_load32be_ext(&buf[3], 4) = 0x44332211
+osmo_load32be_ext_2(&buf[3], 4) = 0x44332211
+osmo_store32be_ext(0x44332211, &buf[0], 3) = 33 22 11 00 00 00
+osmo_load32be_ext(&buf[0], 3) = 0x33221100
+osmo_load32be_ext_2(&buf[0], 3) = 0x332211
+osmo_store32be_ext(0x44332211, &buf[1], 3) = 00 33 22 11 00 00
+osmo_load32be_ext(&buf[1], 3) = 0x33221100
+osmo_load32be_ext_2(&buf[1], 3) = 0x332211
+osmo_store32be_ext(0x44332211, &buf[2], 3) = 00 00 33 22 11 00
+osmo_load32be_ext(&buf[2], 3) = 0x33221100
+osmo_load32be_ext_2(&buf[2], 3) = 0x332211
+osmo_store32be_ext(0x44332211, &buf[0], 2) = 22 11 00 00
+osmo_load32be_ext(&buf[0], 2) = 0x22110000
+osmo_load32be_ext_2(&buf[0], 2) = 0x2211
+osmo_store32be_ext(0x44332211, &buf[1], 2) = 00 22 11 00
+osmo_load32be_ext(&buf[1], 2) = 0x22110000
+osmo_load32be_ext_2(&buf[1], 2) = 0x2211
+osmo_store32be_ext(0x44332211, &buf[0], 1) = 11 00
+osmo_load32be_ext(&buf[0], 1) = 0x11000000
+osmo_load32be_ext_2(&buf[0], 1) = 0x11
+--- 32 le
+osmo_store32le_ext(0x44332211, &buf[0], 4) = 11 22 33 44 00 00 00 00
+osmo_load32le_ext(&buf[0], 4) = 0x44332211
+osmo_store32le_ext(0x44332211, &buf[1], 4) = 00 11 22 33 44 00 00 00
+osmo_load32le_ext(&buf[1], 4) = 0x44332211
+osmo_store32le_ext(0x44332211, &buf[2], 4) = 00 00 11 22 33 44 00 00
+osmo_load32le_ext(&buf[2], 4) = 0x44332211
+osmo_store32le_ext(0x44332211, &buf[3], 4) = 00 00 00 11 22 33 44 00
+osmo_load32le_ext(&buf[3], 4) = 0x44332211
+osmo_store32le_ext(0x44332211, &buf[0], 3) = 11 22 33 00 00 00
+osmo_load32le_ext(&buf[0], 3) = 0x332211
+osmo_store32le_ext(0x44332211, &buf[1], 3) = 00 11 22 33 00 00
+osmo_load32le_ext(&buf[1], 3) = 0x332211
+osmo_store32le_ext(0x44332211, &buf[2], 3) = 00 00 11 22 33 00
+osmo_load32le_ext(&buf[2], 3) = 0x332211
+osmo_store32le_ext(0x44332211, &buf[0], 2) = 11 22 00 00
+osmo_load32le_ext(&buf[0], 2) = 0x2211
+osmo_store32le_ext(0x44332211, &buf[1], 2) = 00 11 22 00
+osmo_load32le_ext(&buf[1], 2) = 0x2211
+osmo_store32le_ext(0x44332211, &buf[0], 1) = 11 00
+osmo_load32le_ext(&buf[0], 1) = 0x11
+--- 64 be
+osmo_store64be_ext(0x8877665544332211, &buf[0], 8) = 88 77 66 55 44 33 22 11 00 00 00 00 00 00 00 00
+osmo_load64be_ext(&buf[0], 8) = 0x8877665544332211
+osmo_load64be_ext_2(&buf[0], 8) = 0x8877665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[1], 8) = 00 88 77 66 55 44 33 22 11 00 00 00 00 00 00 00
+osmo_load64be_ext(&buf[1], 8) = 0x8877665544332211
+osmo_load64be_ext_2(&buf[1], 8) = 0x8877665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[2], 8) = 00 00 88 77 66 55 44 33 22 11 00 00 00 00 00 00
+osmo_load64be_ext(&buf[2], 8) = 0x8877665544332211
+osmo_load64be_ext_2(&buf[2], 8) = 0x8877665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[3], 8) = 00 00 00 88 77 66 55 44 33 22 11 00 00 00 00 00
+osmo_load64be_ext(&buf[3], 8) = 0x8877665544332211
+osmo_load64be_ext_2(&buf[3], 8) = 0x8877665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[4], 8) = 00 00 00 00 88 77 66 55 44 33 22 11 00 00 00 00
+osmo_load64be_ext(&buf[4], 8) = 0x8877665544332211
+osmo_load64be_ext_2(&buf[4], 8) = 0x8877665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[5], 8) = 00 00 00 00 00 88 77 66 55 44 33 22 11 00 00 00
+osmo_load64be_ext(&buf[5], 8) = 0x8877665544332211
+osmo_load64be_ext_2(&buf[5], 8) = 0x8877665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[6], 8) = 00 00 00 00 00 00 88 77 66 55 44 33 22 11 00 00
+osmo_load64be_ext(&buf[6], 8) = 0x8877665544332211
+osmo_load64be_ext_2(&buf[6], 8) = 0x8877665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[7], 8) = 00 00 00 00 00 00 00 88 77 66 55 44 33 22 11 00
+osmo_load64be_ext(&buf[7], 8) = 0x8877665544332211
+osmo_load64be_ext_2(&buf[7], 8) = 0x8877665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[0], 7) = 77 66 55 44 33 22 11 00 00 00 00 00 00 00
+osmo_load64be_ext(&buf[0], 7) = 0x7766554433221100
+osmo_load64be_ext_2(&buf[0], 7) = 0x77665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[1], 7) = 00 77 66 55 44 33 22 11 00 00 00 00 00 00
+osmo_load64be_ext(&buf[1], 7) = 0x7766554433221100
+osmo_load64be_ext_2(&buf[1], 7) = 0x77665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[2], 7) = 00 00 77 66 55 44 33 22 11 00 00 00 00 00
+osmo_load64be_ext(&buf[2], 7) = 0x7766554433221100
+osmo_load64be_ext_2(&buf[2], 7) = 0x77665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[3], 7) = 00 00 00 77 66 55 44 33 22 11 00 00 00 00
+osmo_load64be_ext(&buf[3], 7) = 0x7766554433221100
+osmo_load64be_ext_2(&buf[3], 7) = 0x77665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[4], 7) = 00 00 00 00 77 66 55 44 33 22 11 00 00 00
+osmo_load64be_ext(&buf[4], 7) = 0x7766554433221100
+osmo_load64be_ext_2(&buf[4], 7) = 0x77665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[5], 7) = 00 00 00 00 00 77 66 55 44 33 22 11 00 00
+osmo_load64be_ext(&buf[5], 7) = 0x7766554433221100
+osmo_load64be_ext_2(&buf[5], 7) = 0x77665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[6], 7) = 00 00 00 00 00 00 77 66 55 44 33 22 11 00
+osmo_load64be_ext(&buf[6], 7) = 0x7766554433221100
+osmo_load64be_ext_2(&buf[6], 7) = 0x77665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[0], 6) = 66 55 44 33 22 11 00 00 00 00 00 00
+osmo_load64be_ext(&buf[0], 6) = 0x6655443322110000
+osmo_load64be_ext_2(&buf[0], 6) = 0x665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[1], 6) = 00 66 55 44 33 22 11 00 00 00 00 00
+osmo_load64be_ext(&buf[1], 6) = 0x6655443322110000
+osmo_load64be_ext_2(&buf[1], 6) = 0x665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[2], 6) = 00 00 66 55 44 33 22 11 00 00 00 00
+osmo_load64be_ext(&buf[2], 6) = 0x6655443322110000
+osmo_load64be_ext_2(&buf[2], 6) = 0x665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[3], 6) = 00 00 00 66 55 44 33 22 11 00 00 00
+osmo_load64be_ext(&buf[3], 6) = 0x6655443322110000
+osmo_load64be_ext_2(&buf[3], 6) = 0x665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[4], 6) = 00 00 00 00 66 55 44 33 22 11 00 00
+osmo_load64be_ext(&buf[4], 6) = 0x6655443322110000
+osmo_load64be_ext_2(&buf[4], 6) = 0x665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[5], 6) = 00 00 00 00 00 66 55 44 33 22 11 00
+osmo_load64be_ext(&buf[5], 6) = 0x6655443322110000
+osmo_load64be_ext_2(&buf[5], 6) = 0x665544332211
+osmo_store64be_ext(0x8877665544332211, &buf[0], 5) = 55 44 33 22 11 00 00 00 00 00
+osmo_load64be_ext(&buf[0], 5) = 0x5544332211000000
+osmo_load64be_ext_2(&buf[0], 5) = 0x5544332211
+osmo_store64be_ext(0x8877665544332211, &buf[1], 5) = 00 55 44 33 22 11 00 00 00 00
+osmo_load64be_ext(&buf[1], 5) = 0x5544332211000000
+osmo_load64be_ext_2(&buf[1], 5) = 0x5544332211
+osmo_store64be_ext(0x8877665544332211, &buf[2], 5) = 00 00 55 44 33 22 11 00 00 00
+osmo_load64be_ext(&buf[2], 5) = 0x5544332211000000
+osmo_load64be_ext_2(&buf[2], 5) = 0x5544332211
+osmo_store64be_ext(0x8877665544332211, &buf[3], 5) = 00 00 00 55 44 33 22 11 00 00
+osmo_load64be_ext(&buf[3], 5) = 0x5544332211000000
+osmo_load64be_ext_2(&buf[3], 5) = 0x5544332211
+osmo_store64be_ext(0x8877665544332211, &buf[4], 5) = 00 00 00 00 55 44 33 22 11 00
+osmo_load64be_ext(&buf[4], 5) = 0x5544332211000000
+osmo_load64be_ext_2(&buf[4], 5) = 0x5544332211
+osmo_store64be_ext(0x8877665544332211, &buf[0], 4) = 44 33 22 11 00 00 00 00
+osmo_load64be_ext(&buf[0], 4) = 0x4433221100000000
+osmo_load64be_ext_2(&buf[0], 4) = 0x44332211
+osmo_store64be_ext(0x8877665544332211, &buf[1], 4) = 00 44 33 22 11 00 00 00
+osmo_load64be_ext(&buf[1], 4) = 0x4433221100000000
+osmo_load64be_ext_2(&buf[1], 4) = 0x44332211
+osmo_store64be_ext(0x8877665544332211, &buf[2], 4) = 00 00 44 33 22 11 00 00
+osmo_load64be_ext(&buf[2], 4) = 0x4433221100000000
+osmo_load64be_ext_2(&buf[2], 4) = 0x44332211
+osmo_store64be_ext(0x8877665544332211, &buf[3], 4) = 00 00 00 44 33 22 11 00
+osmo_load64be_ext(&buf[3], 4) = 0x4433221100000000
+osmo_load64be_ext_2(&buf[3], 4) = 0x44332211
+osmo_store64be_ext(0x8877665544332211, &buf[0], 3) = 33 22 11 00 00 00
+osmo_load64be_ext(&buf[0], 3) = 0x3322110000000000
+osmo_load64be_ext_2(&buf[0], 3) = 0x332211
+osmo_store64be_ext(0x8877665544332211, &buf[1], 3) = 00 33 22 11 00 00
+osmo_load64be_ext(&buf[1], 3) = 0x3322110000000000
+osmo_load64be_ext_2(&buf[1], 3) = 0x332211
+osmo_store64be_ext(0x8877665544332211, &buf[2], 3) = 00 00 33 22 11 00
+osmo_load64be_ext(&buf[2], 3) = 0x3322110000000000
+osmo_load64be_ext_2(&buf[2], 3) = 0x332211
+osmo_store64be_ext(0x8877665544332211, &buf[0], 2) = 22 11 00 00
+osmo_load64be_ext(&buf[0], 2) = 0x2211000000000000
+osmo_load64be_ext_2(&buf[0], 2) = 0x2211
+osmo_store64be_ext(0x8877665544332211, &buf[1], 2) = 00 22 11 00
+osmo_load64be_ext(&buf[1], 2) = 0x2211000000000000
+osmo_load64be_ext_2(&buf[1], 2) = 0x2211
+osmo_store64be_ext(0x8877665544332211, &buf[0], 1) = 11 00
+osmo_load64be_ext(&buf[0], 1) = 0x1100000000000000
+osmo_load64be_ext_2(&buf[0], 1) = 0x11
+--- 64 le
+osmo_store64le_ext(0x8877665544332211, &buf[0], 8) = 11 22 33 44 55 66 77 88 00 00 00 00 00 00 00 00
+osmo_load64le_ext(&buf[0], 8) = 0x8877665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[1], 8) = 00 11 22 33 44 55 66 77 88 00 00 00 00 00 00 00
+osmo_load64le_ext(&buf[1], 8) = 0x8877665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[2], 8) = 00 00 11 22 33 44 55 66 77 88 00 00 00 00 00 00
+osmo_load64le_ext(&buf[2], 8) = 0x8877665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[3], 8) = 00 00 00 11 22 33 44 55 66 77 88 00 00 00 00 00
+osmo_load64le_ext(&buf[3], 8) = 0x8877665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[4], 8) = 00 00 00 00 11 22 33 44 55 66 77 88 00 00 00 00
+osmo_load64le_ext(&buf[4], 8) = 0x8877665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[5], 8) = 00 00 00 00 00 11 22 33 44 55 66 77 88 00 00 00
+osmo_load64le_ext(&buf[5], 8) = 0x8877665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[6], 8) = 00 00 00 00 00 00 11 22 33 44 55 66 77 88 00 00
+osmo_load64le_ext(&buf[6], 8) = 0x8877665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[7], 8) = 00 00 00 00 00 00 00 11 22 33 44 55 66 77 88 00
+osmo_load64le_ext(&buf[7], 8) = 0x8877665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[0], 7) = 11 22 33 44 55 66 77 00 00 00 00 00 00 00
+osmo_load64le_ext(&buf[0], 7) = 0x77665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[1], 7) = 00 11 22 33 44 55 66 77 00 00 00 00 00 00
+osmo_load64le_ext(&buf[1], 7) = 0x77665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[2], 7) = 00 00 11 22 33 44 55 66 77 00 00 00 00 00
+osmo_load64le_ext(&buf[2], 7) = 0x77665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[3], 7) = 00 00 00 11 22 33 44 55 66 77 00 00 00 00
+osmo_load64le_ext(&buf[3], 7) = 0x77665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[4], 7) = 00 00 00 00 11 22 33 44 55 66 77 00 00 00
+osmo_load64le_ext(&buf[4], 7) = 0x77665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[5], 7) = 00 00 00 00 00 11 22 33 44 55 66 77 00 00
+osmo_load64le_ext(&buf[5], 7) = 0x77665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[6], 7) = 00 00 00 00 00 00 11 22 33 44 55 66 77 00
+osmo_load64le_ext(&buf[6], 7) = 0x77665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[0], 6) = 11 22 33 44 55 66 00 00 00 00 00 00
+osmo_load64le_ext(&buf[0], 6) = 0x665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[1], 6) = 00 11 22 33 44 55 66 00 00 00 00 00
+osmo_load64le_ext(&buf[1], 6) = 0x665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[2], 6) = 00 00 11 22 33 44 55 66 00 00 00 00
+osmo_load64le_ext(&buf[2], 6) = 0x665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[3], 6) = 00 00 00 11 22 33 44 55 66 00 00 00
+osmo_load64le_ext(&buf[3], 6) = 0x665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[4], 6) = 00 00 00 00 11 22 33 44 55 66 00 00
+osmo_load64le_ext(&buf[4], 6) = 0x665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[5], 6) = 00 00 00 00 00 11 22 33 44 55 66 00
+osmo_load64le_ext(&buf[5], 6) = 0x665544332211
+osmo_store64le_ext(0x8877665544332211, &buf[0], 5) = 11 22 33 44 55 00 00 00 00 00
+osmo_load64le_ext(&buf[0], 5) = 0x5544332211
+osmo_store64le_ext(0x8877665544332211, &buf[1], 5) = 00 11 22 33 44 55 00 00 00 00
+osmo_load64le_ext(&buf[1], 5) = 0x5544332211
+osmo_store64le_ext(0x8877665544332211, &buf[2], 5) = 00 00 11 22 33 44 55 00 00 00
+osmo_load64le_ext(&buf[2], 5) = 0x5544332211
+osmo_store64le_ext(0x8877665544332211, &buf[3], 5) = 00 00 00 11 22 33 44 55 00 00
+osmo_load64le_ext(&buf[3], 5) = 0x5544332211
+osmo_store64le_ext(0x8877665544332211, &buf[4], 5) = 00 00 00 00 11 22 33 44 55 00
+osmo_load64le_ext(&buf[4], 5) = 0x5544332211
+osmo_store64le_ext(0x8877665544332211, &buf[0], 4) = 11 22 33 44 00 00 00 00
+osmo_load64le_ext(&buf[0], 4) = 0x44332211
+osmo_store64le_ext(0x8877665544332211, &buf[1], 4) = 00 11 22 33 44 00 00 00
+osmo_load64le_ext(&buf[1], 4) = 0x44332211
+osmo_store64le_ext(0x8877665544332211, &buf[2], 4) = 00 00 11 22 33 44 00 00
+osmo_load64le_ext(&buf[2], 4) = 0x44332211
+osmo_store64le_ext(0x8877665544332211, &buf[3], 4) = 00 00 00 11 22 33 44 00
+osmo_load64le_ext(&buf[3], 4) = 0x44332211
+osmo_store64le_ext(0x8877665544332211, &buf[0], 3) = 11 22 33 00 00 00
+osmo_load64le_ext(&buf[0], 3) = 0x332211
+osmo_store64le_ext(0x8877665544332211, &buf[1], 3) = 00 11 22 33 00 00
+osmo_load64le_ext(&buf[1], 3) = 0x332211
+osmo_store64le_ext(0x8877665544332211, &buf[2], 3) = 00 00 11 22 33 00
+osmo_load64le_ext(&buf[2], 3) = 0x332211
+osmo_store64le_ext(0x8877665544332211, &buf[0], 2) = 11 22 00 00
+osmo_load64le_ext(&buf[0], 2) = 0x2211
+osmo_store64le_ext(0x8877665544332211, &buf[1], 2) = 00 11 22 00
+osmo_load64le_ext(&buf[1], 2) = 0x2211
+osmo_store64le_ext(0x8877665544332211, &buf[0], 1) = 11 00
+osmo_load64le_ext(&buf[0], 1) = 0x11
+--- store/load 0x112233 as 24bit big-endian, legacy
+11 22 33 00
+0x11223300
+--- store/load 0x112233 as 24bit big-endian
+11 22 33 00
+0x112233
+--- store/load 0x112233 as 24bit little-endian
+33 22 11 00
+0x112233
diff --git a/tests/bitvec/bitvec_test.c b/tests/bitvec/bitvec_test.c
index fbf5c5dd..29c3e33d 100644
--- a/tests/bitvec/bitvec_test.c
+++ b/tests/bitvec/bitvec_test.c
@@ -69,7 +69,7 @@ static inline void test_set(struct bitvec *bv, enum bit_value bit)
printf(" %s [%d]\n\n", lol, bv->cur_bit);
}
-static void test_byte_ops()
+static void test_byte_ops(void)
{
struct bitvec bv;
const uint8_t *in = (const uint8_t *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -162,7 +162,7 @@ static inline void test_bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits
OSMO_ASSERT(num == result);
}
-static void test_array()
+static void test_array(void)
{
struct bitvec b;
uint8_t d[4096];
@@ -181,7 +181,7 @@ static void test_array()
test_array_item(17, &b, n, array, n * 3);
}
-static void test_used_bytes()
+static void test_used_bytes(void)
{
struct bitvec b;
uint8_t d[32];
@@ -204,7 +204,7 @@ static void test_used_bytes()
}
}
-static void test_tailroom()
+static void test_tailroom(void)
{
struct bitvec b;
uint8_t d[32];
@@ -222,6 +222,46 @@ static void test_tailroom()
}
}
+static void test_bitvec_read_field(void)
+{
+ uint8_t data[8] = { 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xeb, 0xda, 0xed };
+ struct bitvec bv = {
+ .data_len = sizeof(data),
+ .data = data,
+ .cur_bit = 0,
+ };
+
+ unsigned int readIndex;
+ uint64_t field;
+
+#define _bitvec_read_field(idx, len) \
+ readIndex = idx; \
+ field = bitvec_read_field(&bv, &readIndex, len); \
+ printf("bitvec_read_field(idx=%u, len=%u) => %" PRIx64 " (%s)\n", \
+ idx, len, field, errno == 0 ? "success" : "error");
+
+ _bitvec_read_field(0, 64);
+ _bitvec_read_field(0, 32);
+ _bitvec_read_field(0, 16);
+ _bitvec_read_field(0, 8);
+ _bitvec_read_field(0, 0);
+
+ _bitvec_read_field(8, 8);
+ _bitvec_read_field(8, 4);
+ _bitvec_read_field(8, 0);
+
+ _bitvec_read_field(10, 9);
+ _bitvec_read_field(10, 7);
+ _bitvec_read_field(10, 5);
+ _bitvec_read_field(10, 3);
+ _bitvec_read_field(10, 1);
+
+ /* Out of bounds (see OS#4388) */
+ _bitvec_read_field(8 * 8 * 8, 16); /* index too far */
+ _bitvec_read_field(0, 8 * 8 + 1); /* too many bits */
+ _bitvec_read_field(8 * 8, 16); /* 16 bits past */
+}
+
int main(int argc, char **argv)
{
struct bitvec bv;
@@ -331,6 +371,9 @@ int main(int argc, char **argv)
test_used_bytes();
test_tailroom();
+ printf("\ntest bitvec_read_field():\n");
+ test_bitvec_read_field();
+
printf("\nbitvec ok.\n");
return 0;
}
diff --git a/tests/bitvec/bitvec_test.ok b/tests/bitvec/bitvec_test.ok
index a48912d5..d87ac7e0 100644
--- a/tests/bitvec/bitvec_test.ok
+++ b/tests/bitvec/bitvec_test.ok
@@ -119,19 +119,19 @@ out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58
bitvec: 00 00 00 00 fc 82 84 86 88 8a 8c 8e 90 92 94 96 98 9a 9c 9e a0 a2 a4 a6 a8 aa ac ae b0 b2 b4 fc 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
out: ff 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a ff
=== end test_byte_ops ===
-1 -=> cur_bit=184
+0 -=> cur_bit=184
48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b0000000000000000000000000000000000000000000000000000000000000000000000000000000000
48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b
-1 -=> cur_bit=184
+0 -=> cur_bit=184
47240c00400000000000000079eb2ac9402b2b2b2b2b2b0000000000000000000000000000000000000000000000000000000000000000000000000000000000
47240c00400000000000000079eb2ac9402b2b2b2b2b2b
-1 -=> cur_bit=184
+0 -=> cur_bit=184
47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b0000000000000000000000000000000000000000000000000000000000000000000000000000000000
47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b
-1 -=> cur_bit=184
+0 -=> cur_bit=184
deadface000000000000000000000000000000beeffeed0000000000000000000000000000000000000000000000000000000000000000000000000000000000
DEADFACE000000000000000000000000000000BEEFFEED
-0 -=> cur_bit=512
+1 -=> cur_bit=0
fffffaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
FFFFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
arrr...
@@ -170,4 +170,22 @@ bitvec_runlength....
bitvec bytes used.
+test bitvec_read_field():
+bitvec_read_field(idx=0, len=64) => deadbeeffeebdaed (success)
+bitvec_read_field(idx=0, len=32) => deadbeef (success)
+bitvec_read_field(idx=0, len=16) => dead (success)
+bitvec_read_field(idx=0, len=8) => de (success)
+bitvec_read_field(idx=0, len=0) => 0 (success)
+bitvec_read_field(idx=8, len=8) => ad (success)
+bitvec_read_field(idx=8, len=4) => a (success)
+bitvec_read_field(idx=8, len=0) => 0 (success)
+bitvec_read_field(idx=10, len=9) => 16d (success)
+bitvec_read_field(idx=10, len=7) => 5b (success)
+bitvec_read_field(idx=10, len=5) => 16 (success)
+bitvec_read_field(idx=10, len=3) => 5 (success)
+bitvec_read_field(idx=10, len=1) => 1 (success)
+bitvec_read_field(idx=512, len=16) => 0 (error)
+bitvec_read_field(idx=0, len=65) => 0 (error)
+bitvec_read_field(idx=64, len=16) => 0 (error)
+
bitvec ok.
diff --git a/tests/bsslap/bsslap_test.c b/tests/bsslap/bsslap_test.c
new file mode 100644
index 00000000..fc5ce754
--- /dev/null
+++ b/tests/bsslap/bsslap_test.c
@@ -0,0 +1,103 @@
+#include <stdio.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/bsslap.h>
+
+struct bsslap_pdu bsslap_test_pdus[] = {
+ {
+ .msg_type = BSSLAP_MSGT_TA_REQUEST,
+ },
+ {
+ .msg_type = BSSLAP_MSGT_TA_RESPONSE,
+ .ta_response = {
+ .cell_id = 23,
+ .ta = 42,
+ },
+ },
+ {
+ .msg_type = BSSLAP_MSGT_REJECT,
+ .reject = BSSLAP_CAUSE_OTHER_RADIO_EVT_FAIL,
+ },
+ {
+ .msg_type = BSSLAP_MSGT_RESET,
+ .reset = {
+ .cell_id = 23,
+ .ta = 42,
+ .chan_desc = {
+ .chan_nr = 23,
+ .h0 = {
+ .tsc = 5,
+ .h = 1,
+ .arfcn_high = 2,
+ .arfcn_low = 3,
+ },
+ },
+ .cause = BSSLAP_CAUSE_INTRA_BSS_HO,
+ },
+ },
+ {
+ .msg_type = BSSLAP_MSGT_ABORT,
+ .abort = BSSLAP_CAUSE_LOSS_SIG_CONN_MS,
+ },
+ {
+ .msg_type = BSSLAP_MSGT_TA_LAYER3,
+ .ta_layer3 = {
+ .ta = 23,
+ },
+ },
+};
+
+void test_bsslap_enc_dec(void)
+{
+ struct bsslap_pdu *pdu;
+ printf("--- %s\n", __func__);
+
+ for (pdu = bsslap_test_pdus; (pdu - bsslap_test_pdus) < ARRAY_SIZE(bsslap_test_pdus); pdu++) {
+ struct msgb *msg = msgb_alloc(1024, __func__);
+ struct bsslap_pdu dec_pdu;
+ struct osmo_bsslap_err *err;
+ int rc;
+ void *loop_ctx = msg;
+ rc = osmo_bsslap_enc(msg, pdu);
+ if (rc <= 0) {
+ printf("[%td] %s: ERROR: failed to encode pdu\n", (pdu - bsslap_test_pdus),
+ osmo_bsslap_msgt_name(pdu->msg_type));
+ goto loop_end;
+ }
+ if (rc != msg->len) {
+ printf("[%td] %s: ERROR: osmo_bsslap_enc() returned length %d but msgb has %d bytes\n",
+ (pdu - bsslap_test_pdus), osmo_bsslap_msgt_name(pdu->msg_type),
+ rc, msg->len);
+ goto loop_end;
+ }
+
+ memset(&dec_pdu, 0xff, sizeof(dec_pdu));
+ rc = osmo_bsslap_dec(&dec_pdu, &err, loop_ctx, msg->data, msg->len);
+ if (rc) {
+ printf("[%td] %s: ERROR: failed to decode pdu: %s\n", (pdu - bsslap_test_pdus),
+ osmo_bsslap_msgt_name(pdu->msg_type), err->logmsg);
+ printf(" encoded data: %s\n", osmo_hexdump(msg->data, msg->len));
+ goto loop_end;
+ }
+
+ if (memcmp(pdu, &dec_pdu, sizeof(dec_pdu))) {
+ printf("[%td] %s: ERROR: decoded PDU != encoded PDU\n", (pdu - bsslap_test_pdus),
+ osmo_bsslap_msgt_name(pdu->msg_type));
+ printf(" original struct: %s\n", osmo_hexdump((void*)pdu, sizeof(*pdu)));
+ printf(" decoded struct: %s\n", osmo_hexdump((void*)&dec_pdu, sizeof(dec_pdu)));
+ goto loop_end;
+ }
+
+ printf("[%td] %s: ok\n", (pdu - bsslap_test_pdus), osmo_bsslap_msgt_name(pdu->msg_type));
+
+loop_end:
+ msgb_free(msg);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ test_bsslap_enc_dec();
+ return 0;
+}
diff --git a/tests/bsslap/bsslap_test.ok b/tests/bsslap/bsslap_test.ok
new file mode 100644
index 00000000..f3199e11
--- /dev/null
+++ b/tests/bsslap/bsslap_test.ok
@@ -0,0 +1,7 @@
+--- test_bsslap_enc_dec
+[0] TA Request: ok
+[1] TA Response: ok
+[2] Reject: ok
+[3] Reset: ok
+[4] Abort: ok
+[5] TA Layer3: ok
diff --git a/tests/bssmap_le/bssmap_le_test.c b/tests/bssmap_le/bssmap_le_test.c
new file mode 100644
index 00000000..a14c38a3
--- /dev/null
+++ b/tests/bssmap_le/bssmap_le_test.c
@@ -0,0 +1,207 @@
+#include <stdio.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/bssmap_le.h>
+
+struct bssmap_le_pdu bssmap_le_test_pdus[] = {
+ {
+ .msg_type = BSSMAP_LE_MSGT_RESET,
+ .reset = GSM0808_CAUSE_EQUIPMENT_FAILURE,
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_RESET_ACK,
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_REQ,
+ .perform_loc_req = {
+ .location_type = {
+ .location_information = BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC,
+ },
+
+ .cell_id = {
+ .id_discr = CELL_IDENT_LAC_AND_CI,
+ .id.lac_and_ci = {
+ .lac = 23,
+ .ci = 42,
+ },
+ },
+
+ .lcs_client_type_present = true,
+ .lcs_client_type = BSSMAP_LE_LCS_CTYPE_VALUE_ADDED_UNSPECIFIED,
+
+ .imsi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "1234567890",
+ },
+
+ .imei = {
+ .type = GSM_MI_TYPE_IMEI,
+ .imei = "123456789012345",
+ },
+
+ .apdu_present = true,
+ .apdu = {
+ .msg_type = BSSLAP_MSGT_TA_LAYER3,
+ .ta_layer3 = {
+ .ta = 23,
+ },
+ },
+ },
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,
+ .perform_loc_resp = {
+ .location_estimate_present = true,
+ .location_estimate = {
+ .ell_point_unc_circle = {
+ .h = { .type = GAD_TYPE_ELL_POINT_UNC_CIRCLE },
+ .lat = { 1, 2, 3 },
+ .lon = { 4, 5, 6 },
+ .unc = 123,
+ },
+ },
+ },
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,
+ .perform_loc_resp = {
+ .lcs_cause = {
+ .present = true,
+ .cause_val = LCS_CAUSE_REQUEST_ABORTED,
+ },
+ },
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,
+ .perform_loc_resp = {
+ .lcs_cause = {
+ .present = true,
+ .cause_val = LCS_CAUSE_POS_METH_FAILURE,
+ .diag_val_present = true,
+ .diag_val = 23,
+ },
+ },
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_ABORT,
+ .perform_loc_abort = {
+ .present = true,
+ .cause_val = LCS_CAUSE_REQUEST_ABORTED,
+ },
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
+ .conn_oriented_info = {
+ .apdu = {
+ .msg_type = BSSLAP_MSGT_TA_REQUEST,
+ },
+ },
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
+ .conn_oriented_info = {
+ .apdu = {
+ .msg_type = BSSLAP_MSGT_TA_RESPONSE,
+ .ta_response = {
+ .cell_id = 23,
+ .ta = 42,
+ },
+ },
+ },
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
+ .conn_oriented_info = {
+ .apdu = {
+ .msg_type = BSSLAP_MSGT_REJECT,
+ .reject = BSSLAP_CAUSE_CONGESTION,
+ },
+ },
+ },
+ {
+ .msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_REQ,
+ .perform_loc_req = {
+ .location_type = {
+ .location_information = BSSMAP_LE_LOC_INFO_CURRENT_GEOGRAPHIC,
+ },
+
+ .cell_id = {
+ .id_discr = CELL_IDENT_LAC_AND_CI,
+ .id.lac_and_ci = {
+ .lac = 23,
+ .ci = 42,
+ },
+ },
+
+ .lcs_client_type_present = true,
+ .lcs_client_type = BSSMAP_LE_LCS_CTYPE_EMERG_SVC_UNSPECIFIED,
+
+ .more_items = true,
+
+ .lcs_priority_present = true,
+ .lcs_priority = 0x00, /* highest */
+
+ .lcs_qos_present = true,
+ .lcs_qos = {
+ .ha_ind = 1,
+ .ha_val = 0x12,
+ },
+ },
+ },
+};
+
+void test_bssmap_le_enc_dec(void)
+{
+ struct bssmap_le_pdu *pdu;
+ printf("--- %s\n", __func__);
+
+ for (pdu = bssmap_le_test_pdus; (pdu - bssmap_le_test_pdus) < ARRAY_SIZE(bssmap_le_test_pdus); pdu++) {
+ struct msgb *msg;
+ struct bssap_le_pdu enc_pdu = {
+ .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
+ .bssmap_le = *pdu,
+ };
+ struct bssap_le_pdu dec_pdu;
+ struct osmo_bssap_le_err *err = NULL;
+ void *loop_ctx;
+ int rc;
+
+ msg = osmo_bssap_le_enc(&enc_pdu);
+ if (!msg) {
+ printf("[%td] %s: ERROR: failed to encode pdu\n", (pdu - bssmap_le_test_pdus),
+ osmo_bssmap_le_msgt_name(pdu->msg_type));
+ goto loop_end;
+ }
+ loop_ctx = msg;
+
+ memset(&dec_pdu, 0xff, sizeof(dec_pdu));
+ rc = osmo_bssap_le_dec(&dec_pdu, &err, loop_ctx, msg);
+ if (rc) {
+ printf("[%td] %s: ERROR: failed to decode pdu: %s\n", (pdu - bssmap_le_test_pdus),
+ osmo_bssmap_le_msgt_name(pdu->msg_type), err->logmsg);
+ printf(" encoded data: %s\n", osmo_hexdump(msg->data, msg->len));
+ goto loop_end;
+ }
+
+ if (memcmp(&enc_pdu, &dec_pdu, sizeof(dec_pdu))) {
+ printf("[%td] %s: ERROR: decoded PDU != encoded PDU\n", (pdu - bssmap_le_test_pdus),
+ osmo_bssmap_le_msgt_name(pdu->msg_type));
+ printf(" original struct: %s\n", osmo_hexdump((void*)&enc_pdu, sizeof(enc_pdu)));
+ printf(" decoded struct: %s\n", osmo_hexdump((void*)&dec_pdu, sizeof(dec_pdu)));
+ printf(" encoded data: %s\n", osmo_hexdump(msg->data, msg->len));
+ goto loop_end;
+ }
+
+ printf("[%td] %s: ok (encoded len = %d)\n", (pdu - bssmap_le_test_pdus),
+ osmo_bssmap_le_msgt_name(pdu->msg_type), msg->len);
+
+loop_end:
+ msgb_free(msg);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ test_bssmap_le_enc_dec();
+ return 0;
+}
diff --git a/tests/bssmap_le/bssmap_le_test.ok b/tests/bssmap_le/bssmap_le_test.ok
new file mode 100644
index 00000000..8cc77039
--- /dev/null
+++ b/tests/bssmap_le/bssmap_le_test.ok
@@ -0,0 +1,12 @@
+--- test_bssmap_le_enc_dec
+[0] RESET: ok (encoded len = 6)
+[1] RESET ACKNOWLEDGE: ok (encoded len = 3)
+[2] PERFORM LOCATION REQUEST: ok (encoded len = 41)
+[3] PERFORM LOCATION RESPONSE: ok (encoded len = 13)
+[4] PERFORM LOCATION RESPONSE: ok (encoded len = 6)
+[5] PERFORM LOCATION RESPONSE: ok (encoded len = 7)
+[6] PERFORM LOCATION ABORT: ok (encoded len = 6)
+[7] CONNECTION ORIENTED INFORMATON: ok (encoded len = 8)
+[8] CONNECTION ORIENTED INFORMATON: ok (encoded len = 13)
+[9] CONNECTION ORIENTED INFORMATON: ok (encoded len = 10)
+[10] PERFORM LOCATION REQUEST: ok (encoded len = 25)
diff --git a/tests/codec/codec_ecu_fr_test.c b/tests/codec/codec_ecu_fr_test.c
index 4040ce94..4e5b71d7 100644
--- a/tests/codec/codec_ecu_fr_test.c
+++ b/tests/codec/codec_ecu_fr_test.c
@@ -151,7 +151,7 @@ void test_fr_concealment_core(void)
int i, rc;
int j = 0;
- printf("=> Testing FR concealment (simple, consecutive bad frames)\n");
+ printf("=> Testing FR concealment (simple, using ECU abstraction)\n");
while (sample_frame_hex[j] != NULL) {
/* Parse frame from string to hex */
@@ -183,7 +183,7 @@ void test_fr_concealment_core(void)
}
/* Simulate a real life situation: voice frames with a few dropouts */
-void test_fr_concealment_realistic()
+void test_fr_concealment_realistic(void)
{
struct osmo_ecu_fr_state state;
uint8_t frame[GSM_FR_BYTES];
@@ -219,7 +219,7 @@ void test_fr_concealment_realistic()
}
/* Simulate a real life situation: voice frames with a few dropouts, using generic core */
-void test_fr_concealment_realistic_core()
+void test_fr_concealment_realistic_core(void)
{
struct osmo_ecu_state *state = osmo_ecu_init(NULL, OSMO_ECU_CODEC_FR);
uint8_t frame[GSM_FR_BYTES];
diff --git a/tests/codec/codec_ecu_fr_test.ok b/tests/codec/codec_ecu_fr_test.ok
index d925e285..ed5eb9d8 100644
--- a/tests/codec/codec_ecu_fr_test.ok
+++ b/tests/codec/codec_ecu_fr_test.ok
@@ -41,49 +41,49 @@ conceal: 16, result: d0000000000000000000000000000000000000000000000000000000000
conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-=> Testing FR concealment (simple, consecutive bad frames)
+=> Testing FR concealment (simple, using ECU abstraction)
Start with: d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da, XMAXC: [3f, 3f, 3f, 3f]
conceal: 00, result: d9ec9be212901f802335598c501f805bad3d4ba01f809b69df5a501f809cd1b4da XMAXC: [3f, 3f, 3f, 3f]
-conceal: 01, result: d9ec9be212901d802335598c501d805bad3d4ba01d809b69df5a501d809cd1b4da XMAXC: [3b, 3b, 3b, 3b]
-conceal: 02, result: d9ec9be212901b802335598c501b805bad3d4ba01b809b69df5a501b809cd1b4da XMAXC: [37, 37, 37, 37]
+conceal: 01, result: d9ec9be212905d802335598c501d805bad3d4ba01d809b69df5a501d809cd1b4da XMAXC: [3b, 3b, 3b, 3b]
+conceal: 02, result: d9ec9be212901b802335598c501b805bad3d4ba01b809b69df5a507b809cd1b4da XMAXC: [37, 37, 37, 37]
conceal: 03, result: d9ec9be2129019802335598c5019805bad3d4ba019809b69df5a5019809cd1b4da XMAXC: [33, 33, 33, 33]
-conceal: 04, result: d9ec9be2129017802335598c5017805bad3d4ba017809b69df5a5017809cd1b4da XMAXC: [2f, 2f, 2f, 2f]
+conceal: 04, result: d9ec9be2129017802335598c5017805bad3d4ba057809b69df5a5057809cd1b4da XMAXC: [2f, 2f, 2f, 2f]
conceal: 05, result: d9ec9be2129015802335598c5015805bad3d4ba015809b69df5a5015809cd1b4da XMAXC: [2b, 2b, 2b, 2b]
-conceal: 06, result: d9ec9be2129013802335598c5013805bad3d4ba013809b69df5a5013809cd1b4da XMAXC: [27, 27, 27, 27]
+conceal: 06, result: d9ec9be2129013802335598c5073805bad3d4ba073809b69df5a5013809cd1b4da XMAXC: [27, 27, 27, 27]
conceal: 07, result: d9ec9be2129011802335598c5011805bad3d4ba011809b69df5a5011809cd1b4da XMAXC: [23, 23, 23, 23]
-conceal: 08, result: d9ec9be212900f802335598c500f805bad3d4ba00f809b69df5a500f809cd1b4da XMAXC: [1f, 1f, 1f, 1f]
-conceal: 09, result: d9ec9be212900d802335598c500d805bad3d4ba00d809b69df5a500d809cd1b4da XMAXC: [1b, 1b, 1b, 1b]
-conceal: 10, result: d9ec9be212900b802335598c500b805bad3d4ba00b809b69df5a500b809cd1b4da XMAXC: [17, 17, 17, 17]
-conceal: 11, result: d9ec9be2129009802335598c5009805bad3d4ba009809b69df5a5009809cd1b4da XMAXC: [13, 13, 13, 13]
-conceal: 12, result: d9ec9be2129007802335598c5007805bad3d4ba007809b69df5a5007809cd1b4da XMAXC: [f, f, f, f]
-conceal: 13, result: d9ec9be2129005802335598c5005805bad3d4ba005809b69df5a5005809cd1b4da XMAXC: [b, b, b, b]
-conceal: 14, result: d9ec9be2129003802335598c5003805bad3d4ba003809b69df5a5003809cd1b4da XMAXC: [7, 7, 7, 7]
-conceal: 15, result: d9ec9be2129001802335598c5001805bad3d4ba001809b69df5a5001809cd1b4da XMAXC: [3, 3, 3, 3]
-conceal: 16, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
+conceal: 08, result: d9ec9be212904f802335598c500f805bad3d4ba04f809b69df5a500f809cd1b4da XMAXC: [1f, 1f, 1f, 1f]
+conceal: 09, result: d9ec9be212900d802335598c500d805bad3d4ba00d809b69df5a506d809cd1b4da XMAXC: [1b, 1b, 1b, 1b]
+conceal: 10, result: d9ec9be212900b802335598c506b805bad3d4ba00b809b69df5a500b809cd1b4da XMAXC: [17, 17, 17, 17]
+conceal: 11, result: d9ec9be2129009802335598c5009805bad3d4ba049809b69df5a5049809cd1b4da XMAXC: [13, 13, 13, 13]
+conceal: 12, result: d9ec9be2129047802335598c5047805bad3d4ba007809b69df5a5007809cd1b4da XMAXC: [f, f, f, f]
+conceal: 13, result: d9ec9be2129005802335598c5065805bad3d4ba065809b69df5a5065809cd1b4da XMAXC: [b, b, b, b]
+conceal: 14, result: d9ec9be2129063802335598c5003805bad3d4ba003809b69df5a5003809cd1b4da XMAXC: [7, 7, 7, 7]
+conceal: 15, result: d9ec9be2129041802335598c5001805bad3d4ba001809b69df5a5001809cd1b4da XMAXC: [3, 3, 3, 3]
+conceal: 16, result: d9ec9be2129040002335598c5000005bad3d4ba000009b69df5a5060009cd1b4da XMAXC: [0, 0, 0, 0]
+conceal: 17, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
+conceal: 18, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
+conceal: 19, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
Start with: d9ec9be212901d802335598c5013805bad3d4ba01f809b69df5a5019809cd1b4da, XMAXC: [3b, 27, 3f, 33]
conceal: 00, result: d9ec9be212901d802335598c5013805bad3d4ba01f809b69df5a5019809cd1b4da XMAXC: [3b, 27, 3f, 33]
-conceal: 01, result: d9ec9be212901b802335598c5011805bad3d4ba01d809b69df5a5017809cd1b4da XMAXC: [37, 23, 3b, 2f]
-conceal: 02, result: d9ec9be2129019802335598c500f805bad3d4ba01b809b69df5a5015809cd1b4da XMAXC: [33, 1f, 37, 2b]
-conceal: 03, result: d9ec9be2129017802335598c500d805bad3d4ba019809b69df5a5013809cd1b4da XMAXC: [2f, 1b, 33, 27]
-conceal: 04, result: d9ec9be2129015802335598c500b805bad3d4ba017809b69df5a5011809cd1b4da XMAXC: [2b, 17, 2f, 23]
-conceal: 05, result: d9ec9be2129013802335598c5009805bad3d4ba015809b69df5a500f809cd1b4da XMAXC: [27, 13, 2b, 1f]
-conceal: 06, result: d9ec9be2129011802335598c5007805bad3d4ba013809b69df5a500d809cd1b4da XMAXC: [23, f, 27, 1b]
-conceal: 07, result: d9ec9be212900f802335598c5005805bad3d4ba011809b69df5a500b809cd1b4da XMAXC: [1f, b, 23, 17]
-conceal: 08, result: d9ec9be212900d802335598c5003805bad3d4ba00f809b69df5a5009809cd1b4da XMAXC: [1b, 7, 1f, 13]
-conceal: 09, result: d9ec9be212900b802335598c5001805bad3d4ba00d809b69df5a5007809cd1b4da XMAXC: [17, 3, 1b, f]
-conceal: 10, result: d9ec9be2129009802335598c5000005bad3d4ba00b809b69df5a5005809cd1b4da XMAXC: [13, 0, 17, b]
-conceal: 11, result: d9ec9be2129007802335598c5000005bad3d4ba009809b69df5a5003809cd1b4da XMAXC: [f, 0, 13, 7]
-conceal: 12, result: d9ec9be2129005802335598c5000005bad3d4ba007809b69df5a5001809cd1b4da XMAXC: [b, 0, f, 3]
-conceal: 13, result: d9ec9be2129003802335598c5000005bad3d4ba005809b69df5a5000009cd1b4da XMAXC: [7, 0, b, 0]
-conceal: 14, result: d9ec9be2129001802335598c5000005bad3d4ba003809b69df5a5000009cd1b4da XMAXC: [3, 0, 7, 0]
-conceal: 15, result: d9ec9be2129000002335598c5000005bad3d4ba001809b69df5a5000009cd1b4da XMAXC: [0, 0, 3, 0]
-conceal: 16, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 17, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 18, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
-conceal: 19, result: d00000000000000000000000000000000000000000000000000000000000000000 XMAXC: [0, 0, 0, 0]
+conceal: 01, result: d9ec9be212901b802335598c5011805bad3d4ba01d809b69df5a5077809cd1b4da XMAXC: [37, 23, 3b, 2f]
+conceal: 02, result: d9ec9be2129019802335598c500f805bad3d4ba05b809b69df5a5055809cd1b4da XMAXC: [33, 1f, 37, 2b]
+conceal: 03, result: d9ec9be2129017802335598c500d805bad3d4ba059809b69df5a5053809cd1b4da XMAXC: [2f, 1b, 33, 27]
+conceal: 04, result: d9ec9be2129015802335598c506b805bad3d4ba077809b69df5a5011809cd1b4da XMAXC: [2b, 17, 2f, 23]
+conceal: 05, result: d9ec9be2129013802335598c5069805bad3d4ba075809b69df5a500f809cd1b4da XMAXC: [27, 13, 2b, 1f]
+conceal: 06, result: d9ec9be2129051802335598c5007805bad3d4ba053809b69df5a500d809cd1b4da XMAXC: [23, f, 27, 1b]
+conceal: 07, result: d9ec9be212904f802335598c5005805bad3d4ba051809b69df5a506b809cd1b4da XMAXC: [1f, b, 23, 17]
+conceal: 08, result: d9ec9be212900d802335598c5063805bad3d4ba00f809b69df5a5069809cd1b4da XMAXC: [1b, 7, 1f, 13]
+conceal: 09, result: d9ec9be212900b802335598c5061805bad3d4ba04d809b69df5a5047809cd1b4da XMAXC: [17, 3, 1b, f]
+conceal: 10, result: d9ec9be2129049802335598c5040005bad3d4ba04b809b69df5a5045809cd1b4da XMAXC: [13, 0, 17, b]
+conceal: 11, result: d9ec9be2129047802335598c5020005bad3d4ba069809b69df5a5063809cd1b4da XMAXC: [f, 0, 13, 7]
+conceal: 12, result: d9ec9be2129065802335598c5060005bad3d4ba067809b69df5a5061809cd1b4da XMAXC: [b, 0, f, 3]
+conceal: 13, result: d9ec9be2129023802335598c5000005bad3d4ba005809b69df5a5000009cd1b4da XMAXC: [7, 0, b, 0]
+conceal: 14, result: d9ec9be2129001802335598c5000005bad3d4ba003809b69df5a5060009cd1b4da XMAXC: [3, 0, 7, 0]
+conceal: 15, result: d9ec9be2129040002335598c5000005bad3d4ba001809b69df5a5000009cd1b4da XMAXC: [0, 0, 3, 0]
+conceal: 16, result: d9ec9be2129000002335598c5000005bad3d4ba040009b69df5a5020009cd1b4da XMAXC: [0, 0, 0, 0]
+conceal: 17, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
+conceal: 18, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
+conceal: 19, result: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b XMAXC: [0, 0, 0, 0]
=> Testing FR concealment (realistic, various bad frames)
Frame No. 000:
@@ -318,7 +318,7 @@ Frame No. 018:
* output: d9689ba5e3d260491b516adb5e4027256e27227ee0351c8e549a5c60492471971b
Frame No. 019:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: d9689ba5e3d240491b516adb5e0027256e27227e80351c8e549a5c00492471971b
Frame No. 020:
* input: d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab
* output: d8e6a2e1d3d2605b1376c8d35280392451391cbc80392a71b6db8aa049238dc8ab
@@ -357,19 +357,19 @@ Frame No. 031:
* output: d9a99361a276403b1a6ad6dcd40026e489c8e3bc40371c4dc564e2c036e28eb963
Frame No. 032:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: d9a99361a276003b1a6ad6dcd40026e489c8e3bc00371c4dc564e2e036e28eb963
Frame No. 033:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 034:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 035:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 036:
* input: (bad)
- * output: d00000000000000000000000000000000000000000000000000000000000000000
+ * output: daa7aaa51a502038e46db91b502038e46db91b502038e46db91b502038e46db91b
Frame No. 037:
* input: d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25
* output: d92c8b6d5aee4034ebb22724862047145634a5c0a038e371b8e4a880485c89dd25
diff --git a/tests/codec/codec_test.c b/tests/codec/codec_test.c
index 7a10fc57..5579e996 100644
--- a/tests/codec/codec_test.c
+++ b/tests/codec/codec_test.c
@@ -190,6 +190,42 @@ static void test_sid_fr(void)
}
}
+
+
+static void test_amr_s_d(void)
+{
+ ubit_t in[244];
+ ubit_t mid[244];
+ ubit_t out[244];
+ int i, j;
+
+ for (j = AMR_4_75; j <= AMR_12_2; j++) {
+ unsigned int n_bits = gsm690_bitlength[j];
+
+ printf("=> AMR Mode %d (%d bits)\n", j, n_bits);
+ /* set a single bit in the input buffer */
+ for (i = 0; i < n_bits; i++) {
+
+ memset(in, 0, sizeof(in));
+ in[i] = 1;
+
+ /* re-order from s to d */
+ osmo_amr_s_to_d(mid, in, n_bits, j);
+
+ /* and back to d */
+ osmo_amr_d_to_s(out, mid, n_bits, j);
+
+ if (memcmp(in, out, n_bits)) {
+ printf("Error in bit %d of mode %d!\n", i, j);
+ printf("inp s-bits: %s\n", osmo_ubit_dump(in, n_bits));
+ printf("mid d-bits: %s\n", osmo_ubit_dump(mid, n_bits));
+ printf("out s-bits: %s\n", osmo_ubit_dump(out, n_bits));
+ //OSMO_ASSERT(0);
+ }
+ }
+ }
+}
+
int main(int argc, char **argv)
{
printf("AMR RTP payload decoder test:\n");
@@ -213,6 +249,9 @@ int main(int argc, char **argv)
printf("FR RTP payload SID test:\n");
test_sid_fr();
+ printf("AMR s/d bit re-ordering test:\n");
+ test_amr_s_d();
+
return 0;
}
diff --git a/tests/codec/codec_test.ok b/tests/codec/codec_test.ok
index 53070233..7d8609ba 100644
--- a/tests/codec/codec_test.ok
+++ b/tests/codec/codec_test.ok
@@ -1,6 +1,6 @@
AMR RTP payload decoder test:
-[9] decode RTP 20 44 00 00 00 00 04 OK: FT AMR SID, CMR AMR 5,90 kbit/s, CMI is 2, SID type FIRST [9] encode [0]
-[9] decode RTP 20 44 29 c2 92 91 f4 OK: FT AMR SID, CMR AMR 5,90 kbit/s, CMI is 2, SID type UPDATE [9] encode [0]
+[7] decode RTP 20 44 00 00 00 00 04 OK: FT AMR SID, CMR AMR 5,90 kbit/s, CMI is 2, SID type FIRST [7] encode [0]
+[7] decode RTP 20 44 29 c2 92 91 f4 OK: FT AMR SID, CMR AMR 5,90 kbit/s, CMI is 2, SID type UPDATE [7] encode [0]
[2/2] No Data/NA, CMR: OK, FT: OK, BFI: OK, CMI: -1, STI: -1
[2/2] No Data/NA, CMR: OK, FT: OK, BFI: OK, CMI: -1, STI: -1
[33/33] AMR 12,2 kbit/s (GSM-EFR), CMR: OK, FT: OK, BFI: OK, CMI: -1, STI: -1
@@ -30,3 +30,12 @@ FR SID d9 23 ba e5 e2 00 00 80 41 20 00 01 00 00 10 00 04 00 00 00 00 00 48 00 0
FR SID d8 62 a2 61 60 00 00 10 00 00 92 00 00 00 00 40 00 00 08 00 00 00 01 00 00 01 00 00 80 00 40 02 40 : 1
FR SID d9 e4 c3 6d 12 00 00 80 00 20 00 40 00 00 00 00 00 10 00 00 00 10 48 00 10 48 00 00 00 00 2d 04 00 : 1
FR SID d9 a4 c3 29 59 00 00 10 00 00 12 00 00 00 00 41 00 00 01 00 00 00 01 00 80 00 00 00 00 42 00 12 02 : 1
+AMR s/d bit re-ordering test:
+=> AMR Mode 0 (95 bits)
+=> AMR Mode 1 (103 bits)
+=> AMR Mode 2 (118 bits)
+=> AMR Mode 3 (134 bits)
+=> AMR Mode 4 (148 bits)
+=> AMR Mode 5 (159 bits)
+=> AMR Mode 6 (204 bits)
+=> AMR Mode 7 (244 bits)
diff --git a/tests/coding/coding_test.c b/tests/coding/coding_test.c
index 2b0830f2..d536e443 100644
--- a/tests/coding/coding_test.c
+++ b/tests/coding/coding_test.c
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -28,6 +24,7 @@
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/coding/gsm0503_coding.h>
#define DUMP_U_AT(b, x, u) do { \
@@ -44,6 +41,19 @@
return; \
} while(0)
+/* Similar to OSMO_ASSERT, but does not panic() */
+#define CHECK_RC_OR_RET(exp, action) \
+ if (!(exp)) { \
+ printf("%s(%s): assert %s failed\n", __func__, action, #exp); \
+ return; \
+ }
+
+#ifdef DEBUG
+#define printd(fmt, args...) printf(fmt, ##args)
+#else
+#define printd(fmt, args...)
+#endif
+
static inline void dump_ubits(ubit_t *bursts_u, unsigned until)
{
printf("U-Bits:\n");
@@ -76,10 +86,12 @@ static void test_xcch(uint8_t *l2)
ubit_t bursts_u[116 * 4];
sbit_t bursts_s[116 * 4];
int n_errors, n_bits_total;
+ int rc;
/* Encode L2 message */
printf("Encoding: %s\n", osmo_hexdump(l2, 23));
- gsm0503_xcch_encode(bursts_u, l2);
+ rc = gsm0503_xcch_encode(bursts_u, l2);
+ CHECK_RC_OR_RET(rc == 0, "encoding");
/* Prepare soft-bits */
osmo_ubit2sbit(bursts_s, bursts_u, 116 * 4);
@@ -91,7 +103,9 @@ static void test_xcch(uint8_t *l2)
memset(bursts_s + 116, 0, 30);
/* Decode, correcting errors */
- gsm0503_xcch_decode(result, bursts_s, &n_errors, &n_bits_total);
+ rc = gsm0503_xcch_decode(result, bursts_s, &n_errors, &n_bits_total);
+ CHECK_RC_OR_RET(rc == 0, "decoding");
+
printf("Decoded: %s\n", osmo_hexdump(result, 23));
printf("xcch_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
n_errors, n_bits_total, (float) n_errors / n_bits_total);
@@ -110,27 +124,29 @@ static void test_rach(uint8_t bsic, uint8_t ra)
sbit_t bursts_s[36];
/* Encode L2 message */
+ printd("Encoding: %02x\n", ra);
rc = gsm0503_rach_ext_encode(bursts_u, ra, bsic, false);
- printf("Encoding: %02x%s\n", ra, (rc != 0) ? " FAIL" : "");
+ CHECK_RC_OR_RET(rc == 0, "encoding");
/* Prepare soft-bits */
osmo_ubit2sbit(bursts_s, bursts_u, 36);
- printf("U-Bits: %s\n", osmo_ubit_dump(bursts_u, 36));
+ printd("U-Bits: %s\n", osmo_ubit_dump(bursts_u, 36));
- printf("S-Bits: %s\n", osmo_hexdump((uint8_t *)bursts_s, 36));
+ printd("S-Bits: %s\n", osmo_hexdump((uint8_t *)bursts_s, 36));
/* Destroy some bits */
memset(bursts_s + 6, 0, 8);
/* Decode, correcting errors */
rc = gsm0503_rach_decode_ber(&result, bursts_s, bsic, NULL, NULL);
- printf("Decoded: %02x%s\n", result, (rc != 0) ? " FAIL" : "");
+ CHECK_RC_OR_RET(rc == 0, "decoding");
+ printd("Decoded: %02x\n", result);
if (ra != result)
printf("FAIL [RACH]: encoded %u != %u decoded\n", ra, result);
- printf("\n");
+ printd("\n");
}
static void test_rach_ext(uint8_t bsic, uint16_t ra)
@@ -141,27 +157,45 @@ static void test_rach_ext(uint8_t bsic, uint16_t ra)
sbit_t bursts_s[36];
/* Encode L2 message */
+ printd("Encoding: %02x\n", ra);
rc = gsm0503_rach_ext_encode(bursts_u, ra, bsic, true);
- printf("Encoding: %02x%s\n", ra, (rc != 0) ? " FAIL" : "");
+ CHECK_RC_OR_RET(rc == 0, "encoding");
/* Prepare soft-bits */
osmo_ubit2sbit(bursts_s, bursts_u, 36);
- printf("U-Bits: %s\n", osmo_ubit_dump(bursts_u, 36));
+ printd("U-Bits: %s\n", osmo_ubit_dump(bursts_u, 36));
- printf("S-Bits: %s\n", osmo_hexdump((uint8_t *)bursts_s, 36));
+ printd("S-Bits: %s\n", osmo_hexdump((uint8_t *)bursts_s, 36));
/* Destroy some bits */
memset(bursts_s + 9, 0, 8);
/* Decode, correcting errors */
rc = gsm0503_rach_ext_decode_ber(&result, bursts_s, bsic, NULL, NULL);
- printf("Decoded: %02x%s\n", result, (rc != 0) ? " FAIL" : "");
+ CHECK_RC_OR_RET(rc == 0, "decoding");
+ printd("Decoded: %02x\n", result);
if (ra != result)
printf("FAIL [RACH ext]: encoded %u != %u decoded\n", ra, result);
- printf("\n");
+ printd("\n");
+}
+
+static void test_rach_11bit_sample(uint8_t bsic, const sbit_t *payload)
+{
+ int n_errors, n_bits_total;
+ uint16_t ra11;
+ int rc;
+
+ /* Decode, correcting errors */
+ rc = gsm0503_rach_ext_decode_ber(&ra11, payload, bsic, &n_errors, &n_bits_total);
+ if (rc) {
+ printf("%s(): decoding failed (rc=%d)\n", __func__, rc);
+ return;
+ }
+
+ printf("Decoded RA11: 0x%03x\n", ra11);
}
static void test_sch(uint8_t *info)
@@ -169,6 +203,7 @@ static void test_sch(uint8_t *info)
uint8_t result[4];
ubit_t bursts_u[78];
sbit_t bursts_s[78];
+ int rc;
/* Zero bits 25 and above */
info[3] &= 1;
@@ -176,7 +211,8 @@ static void test_sch(uint8_t *info)
/* Encode L2 message */
printf("Encoding: %s\n", osmo_hexdump(info, 4));
- gsm0503_sch_encode(bursts_u, info);
+ rc = gsm0503_sch_encode(bursts_u, info);
+ CHECK_RC_OR_RET(rc == 0, "encoding");
/* Prepare soft-bits */
osmo_ubit2sbit(bursts_s, bursts_u, 78);
@@ -189,7 +225,9 @@ static void test_sch(uint8_t *info)
memset(bursts_s + 6, 0, 10);
/* Decode, correcting errors */
- gsm0503_sch_decode(result, bursts_s);
+ rc = gsm0503_sch_decode(result, bursts_s);
+ CHECK_RC_OR_RET(rc == 0, "decoding");
+
printf("Decoded: %s\n", osmo_hexdump(result, 4));
OSMO_ASSERT(!memcmp(info, result, 4));
@@ -210,7 +248,8 @@ static void test_fr(uint8_t *speech, int len)
/* Encode L2 message */
printf("Encoding: %s\n", osmo_hexdump(speech, len));
- gsm0503_tch_fr_encode(bursts_u, speech, len, 1);
+ rc = gsm0503_tch_fr_encode(bursts_u, speech, len, 1);
+ CHECK_RC_OR_RET(rc == 0, "encoding");
/* Prepare soft-bits */
osmo_ubit2sbit(bursts_s, bursts_u, 116 * 8);
@@ -224,11 +263,12 @@ static void test_fr(uint8_t *speech, int len)
/* Decode, correcting errors */
rc = gsm0503_tch_fr_decode(result, bursts_s, 1, len == 31,
&n_errors, &n_bits_total);
+ CHECK_RC_OR_RET(rc == len, "decoding");
+
printf("Decoded: %s\n", osmo_hexdump(result, len));
printf("tch_fr_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
n_errors, n_bits_total, (float)n_errors/n_bits_total);
- OSMO_ASSERT(rc == len);
OSMO_ASSERT(!memcmp(speech, result, len));
printf("\n");
@@ -247,7 +287,8 @@ static void test_hr(uint8_t *speech, int len)
/* Encode L2 message */
printf("Encoding: %s\n", osmo_hexdump(speech, len));
- gsm0503_tch_hr_encode(bursts_u, speech, len);
+ rc = gsm0503_tch_hr_encode(bursts_u, speech, len);
+ CHECK_RC_OR_RET(rc == 0, "encoding");
/* Prepare soft-bits */
osmo_ubit2sbit(bursts_s, bursts_u, 116 * 6);
@@ -259,42 +300,162 @@ static void test_hr(uint8_t *speech, int len)
memset(bursts_s + 6, 0, 20);
/* Decode, correcting errors */
- rc = gsm0503_tch_hr_decode(result, bursts_s, 0,
+ rc = gsm0503_tch_hr_decode2(result, bursts_s, 0,
&n_errors, &n_bits_total);
+ CHECK_RC_OR_RET(rc == len, "decoding");
+
printf("Decoded: %s\n", osmo_hexdump(result, len));
printf("tch_hr_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
n_errors, n_bits_total, (float)n_errors/n_bits_total);
- OSMO_ASSERT(rc == len);
OSMO_ASSERT(!memcmp(speech, result, len));
printf("\n");
}
-static void test_pdtch(uint8_t *l2, int len)
+static void test_facch(const uint8_t *data, bool half_rate)
{
- uint8_t result[len];
+ ubit_t bursts_u[116 * 8 * 2] = { 0 };
+ sbit_t bursts_s[116 * 8 * 2] = { 0 };
+ int rc;
+
+ /* Encode the given FACCH message three times (at different offsets) */
+ printf("%s(FACCH/%c): encoding: %s\n",
+ __func__, half_rate ? 'H' : 'F',
+ osmo_hexdump(&data[0], GSM_MACBLOCK_LEN));
+ for (unsigned int i = 0; i < 3; i++) {
+ ubit_t *pos = &bursts_u[116 * 4 * i];
+
+ if (half_rate)
+ rc = gsm0503_tch_hr_facch_encode(pos, &data[0]);
+ else
+ rc = gsm0503_tch_fr_facch_encode(pos, &data[0]);
+ CHECK_RC_OR_RET(rc == 0, "encoding");
+ }
+
+ /* Prepare soft-bits */
+ osmo_ubit2sbit(bursts_s, bursts_u, sizeof(bursts_s));
+
+ /* Decode three FACCH messages (at different offsets) */
+ for (unsigned int i = 0; i < 3; i++) {
+ const sbit_t *pos = &bursts_s[116 * 4 * i];
+ uint8_t result[GSM_MACBLOCK_LEN];
+ int n_errors, n_bits_total;
+
+ if (half_rate)
+ rc = gsm0503_tch_hr_facch_decode(&result[0], pos,
+ &n_errors, &n_bits_total);
+ else
+ rc = gsm0503_tch_fr_facch_decode(&result[0], pos,
+ &n_errors, &n_bits_total);
+ CHECK_RC_OR_RET(rc == GSM_MACBLOCK_LEN, "decoding");
+
+ printf("%s(FACCH/%c): decoded (BER=%d/%d): %s\n",
+ __func__, half_rate ? 'H' : 'F', n_errors, n_bits_total,
+ osmo_hexdump(result, GSM_MACBLOCK_LEN));
+ }
+
+ printf("\n");
+}
+
+struct test_macblock {
+ bool is_egprs;
+ uint16_t exp_burst_bits;
+ uint16_t l2_len;
+ uint8_t l2[54];
+};
+
+static const struct test_macblock test_macblock[] = {
+ /* Random frame */
+ { false,
+ GSM0503_GPRS_BURSTS_NBITS,
+ 54,
+ { 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
+ 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
+ 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8, 0x42,
+ 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
+ 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
+ 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
+ 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0xa8 }
+ },
+ /* jolly frame */
+ { false,
+ GSM0503_GPRS_BURSTS_NBITS,
+ 23,
+ { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }
+ },
+/*
+GSM RLC/MAC: EGPRS DL HEADER
+ 0... .... .... 0000 = DL TFI: 0
+ 0... .... crumb 1 of DL TFI (decoded above)
+ .00. .... = RRBP: Reserved Block: (N+13) mod 2715648 (0)
+ ...0 0... = ES/P: RRBP field is not valid (no Polling) (0)
+ .... .111 = USF: 7
+ 01.. .... 0000 0000 .... ...0 = BSN: 1
+ 01.. .... crumb 2 of BSN (decoded above)
+ ..00 .... = PR: 0 dB (included) to 3 dB (excluded) less than BCCH level - P0 (0)
+ .... 0000 crumb 0 of DL TFI (decoded above)
+ 0000 0000 crumb 1 of BSN (decoded above)
+ .00. .... = SPB (DL): No retransmission (0)
+ ...1 011. = CPS: MCS-1/P1 (0x0b)
+ .... ...0 crumb 0 of BSN (decoded above)
+GSM RLC/MAC: EGPRS DL DATA BLOCK 1 (BSN 1)
+ .... ..0. = FBI: Current Block is not last RLC data block in TBF
+ .... ...0 = Extension: Extension octet follows immediately
+ 0000 100. = Length Indicator: 4
+ .... ...0 = Extension: Extension octet follows immediately
+ 0010 000. = Length Indicator: 16
+ .... ...1 = Extension: No extension octet follows
+ data segment: LI[0]=4 indicates: (Last segment of) LLC frame (4 octets)
+ Data (4 bytes)
+ Data: 012b2b2b
+ [Length: 4]
+ data segment: LI[1]=16 indicates: (Last segment of) LLC frame (16 octets)
+ Data (16 bytes)
+ Data: 43c0012b2b2b2b2b2b2b2b2b2b2b2b2b
+ [Length: 16]
+*/
+ { true,
+ GSM0503_GPRS_BURSTS_NBITS,
+ 27,
+ { 0x07, 0x40, 0x00, 0x16, 0x10, 0x42, 0x02, 0x56,
+ 0x56, 0x56, 0x86, 0x80, 0x03, 0x56, 0x56, 0x56,
+ 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56,
+ 0x56, 0x56, 0x00 }
+ },
+};
+
+static void test_pdtch(const struct test_macblock *tmb, int len)
+{
+ uint8_t l2[len], result[len];
ubit_t bursts_u[116 * 4];
sbit_t bursts_s[116 * 4];
int n_errors, n_bits_total;
int rc;
/* Zero the not coded tail bits */
+ memcpy(l2, tmb->l2, len);
switch (len) {
case 34:
case 54:
l2[len - 1] &= 0x7f;
- result[len - 1] &= 0x7f;
+ result[len - 1] = 0x00;
break;
case 40:
l2[len - 1] &= 0x07;
- result[len - 1] &= 0x07;
+ result[len - 1] = 0x00;
break;
}
/* Encode L2 message */
printf("Encoding: %s\n", osmo_hexdump(l2, len));
- gsm0503_pdtch_encode(bursts_u, l2, len);
+ if (tmb->is_egprs)
+ rc = gsm0503_pdtch_egprs_encode(bursts_u, l2, len);
+ else
+ rc = gsm0503_pdtch_encode(bursts_u, l2, len);
+ CHECK_RC_OR_RET(rc == (int)tmb->exp_burst_bits, "encoding");
/* Prepare soft-bits */
osmo_ubit2sbit(bursts_s, bursts_u, 116 * 4);
@@ -303,13 +464,20 @@ static void test_pdtch(uint8_t *l2, int len)
dump_sbits((uint8_t *)bursts_s, 348);
/* Decode */
- rc = gsm0503_pdtch_decode(result, bursts_s, NULL,
- &n_errors, &n_bits_total);
+ if (tmb->is_egprs) {
+ /* gsm0503_pdtch_egprs_decode() is meant to decode EGPRS UL frames, so we cannot use it here */
+ rc = gsm0503_pdtch_egprs_decode(result, bursts_s, rc, NULL, &n_errors, &n_bits_total);
+ OSMO_ASSERT(rc == -EIO);
+ return;
+ } else {
+ rc = gsm0503_pdtch_decode(result, bursts_s, NULL, &n_errors, &n_bits_total);
+ }
+ CHECK_RC_OR_RET(rc == len, "decoding");
+
printf("Decoded: %s\n", osmo_hexdump(result, len));
printf("pdtch_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
n_errors, n_bits_total, (float)n_errors/n_bits_total);
- OSMO_ASSERT(rc == len);
OSMO_ASSERT(!memcmp(l2, result, len));
printf("\n");
@@ -330,31 +498,170 @@ uint8_t test_l2[][23] = {
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
};
-uint8_t test_macblock[][54] = {
- /* Random frame */
- { 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
- 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
- 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8, 0x42,
- 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
- 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
- 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
- 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0xa8 },
- /* jolly frame */
- { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
- 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
+/* 11-bit Access Burst soft-bits (payload only) from an EGPRS capable phone (BSIC 63) */
+static const sbit_t test_rach_11bit[6][36] = {
+ { 103, 109, -108, -110, 107, 108, -106, -120, -121,
+ -120, -105, 122, -104, -109, 108, 109, -109, -111,
+ 107, 111, -105, -119, -121, -104, 122, -120, 121,
+ -99, -121, -120, -122, -106, 109, 109, -108, -111 },
+
+ { 103, 109, -109, -109, 106, 107, -106, -121, -121,
+ -120, -106, 121, -120, 117, -122, 101, 109, -122,
+ 120, -120, 101, 118, 120, 102, -125, 101, 110,
+ -120, 121, -101, -121, -118, -121, -106, 108, 121 },
+
+ { -121, -122, -104, 123, -104, -108, 122, -104, -121,
+ -121, -102, 124, -105, -110, 107, 109, -108, -109,
+ 121, -122, 101, 107, -121, 105, 108, -110, -107,
+ 124, -104, -109, 120, -122, 100, 122, 104, -123 },
+
+ { -122, -123, -103, 123, -105, -109, 122, -105, -121,
+ -120, -104, 122, -120, 121, -101, -122, -120, -120,
+ -119, -105, 120, -106, -108, 123, -104, -113, 105,
+ 122, 101, -122, 119, -122, 117, -121, 119, -122 },
+
+ { 105, 110, -109, -109, 107, 108, -108, -120, -120,
+ -121, -106, 121, -104, -107, 106, 108, -108, -108,
+ 108, 107, -105, -120, -122, -104, 122, -119, 121,
+ -103, -122, -118, -120, -106, 108, 108, -110, -111 },
+
+ { 120, -103, -123, -104, 119, -121, 100, 123, 106,
+ -109, -107, 121, -122, 118, -121, 103, 108, -122,
+ 120, -119, 121, -103, -121, -119, -121, -103, 124,
+ -106, -108, 122, -103, -106, 121, -120, 119, -121 },
};
uint8_t test_speech_fr[33];
uint8_t test_speech_efr[31];
-uint8_t test_speech_hr[15];
+uint8_t test_speech_hr[14];
+
+struct csd_test_case {
+ const char *name;
+ unsigned int num_bits;
+ int (*enc_fn)(ubit_t *out, const ubit_t *in);
+ int (*dec_fn)(ubit_t *out, const sbit_t *in, int *ne, int *nb);
+ bool half_rate;
+};
+
+static const struct csd_test_case csd_tests[] = {
+ {
+ .name = "TCH/F9.6",
+ .num_bits = 4 * 60,
+ .enc_fn = &gsm0503_tch_fr96_encode,
+ .dec_fn = &gsm0503_tch_fr96_decode,
+ },
+ {
+ .name = "TCH/F4.8",
+ .num_bits = 2 * 60,
+ .enc_fn = &gsm0503_tch_fr48_encode,
+ .dec_fn = &gsm0503_tch_fr48_decode,
+ },
+ {
+ .name = "TCH/H4.8",
+ .num_bits = 4 * 60,
+ .enc_fn = &gsm0503_tch_hr48_encode,
+ .dec_fn = &gsm0503_tch_hr48_decode,
+ .half_rate = true,
+ },
+ {
+ .name = "TCH/F2.4",
+ .num_bits = 2 * 36,
+ .enc_fn = &gsm0503_tch_fr24_encode,
+ .dec_fn = &gsm0503_tch_fr24_decode,
+ },
+ {
+ .name = "TCH/H2.4",
+ .num_bits = 4 * 36,
+ .enc_fn = &gsm0503_tch_hr24_encode,
+ .dec_fn = &gsm0503_tch_hr24_decode,
+ .half_rate = true,
+ },
+ {
+ .name = "TCH/F14.4",
+ .num_bits = 290,
+ .enc_fn = &gsm0503_tch_fr144_encode,
+ .dec_fn = &gsm0503_tch_fr144_decode,
+ },
+};
+
+static void test_csd(const struct csd_test_case *tc, bool facch)
+{
+ const uint8_t patterns[] = { 0x00, 0xaa, 0xff };
+ ubit_t bursts_u[116 * (22 + 8)] = { 0 };
+ sbit_t bursts_s[116 * (22 + 8)] = { 0 };
+ ubit_t data[512];
+ int rc;
+
+ /* Encode three data blocks, each block filled-in with a pattern */
+ for (unsigned int i = 0; i < ARRAY_SIZE(patterns); i++) {
+ for (unsigned int j = 0; j < tc->num_bits; j++)
+ data[j] = (patterns[i] & (1 << (j % 8))) != 0;
+
+ rc = tc->enc_fn(&bursts_u[i * 4 * 116], &data[0]);
+ CHECK_RC_OR_RET(rc == 0, "encoding");
+
+ /* Test FACCH bitstealing */
+ if (facch && i == 1) {
+ memset(&data, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+ if (tc->half_rate)
+ rc = gsm0503_tch_hr_facch_encode(&bursts_u[116 * 4], &data[0]);
+ else
+ rc = gsm0503_tch_fr_facch_encode(&bursts_u[116 * 4], &data[0]);
+ CHECK_RC_OR_RET(rc == 0, "encoding FACCH");
+ }
+ }
+
+ /* Prepare soft-bits */
+ osmo_ubit2sbit(&bursts_s[0], &bursts_u[0], sizeof(bursts_s));
+
+ /* Decode the soft-bits, print decoded blocks */
+ for (unsigned int i = 0; i < ARRAY_SIZE(patterns); i++) {
+ int n_errors, n_bits_total;
+
+ rc = tc->dec_fn(&data[0], &bursts_s[i * 4 * 116],
+ &n_errors, &n_bits_total);
+ CHECK_RC_OR_RET(rc == tc->num_bits, "decoding");
+
+ printf("%s(%s): block #%u (pattern 0x%02x): n_errors=%d / n_bits_total=%d\n",
+ __func__, tc->name, i, patterns[i], n_errors, n_bits_total);
+
+ for (unsigned int j = 0; j < tc->num_bits; j++) {
+ if (j && j % 64 == 0)
+ printf("\n");
+ else if (j && j % 8 == 0)
+ printf(" ");
+ printf("%c", data[j] ? '1' : '0');
+ }
+ printf("\n");
+ }
+
+ /* Test FACCH bitstealing if requested */
+ if (facch) {
+ int n_errors = 0, n_bits_total = 0;
+
+ if (tc->half_rate) {
+ rc = gsm0503_tch_hr_facch_decode(&data[0], &bursts_s[116 * 4],
+ &n_errors, &n_bits_total);
+ } else {
+ rc = gsm0503_tch_fr_facch_decode(&data[0], &bursts_s[116 * 4],
+ &n_errors, &n_bits_total);
+ }
+ CHECK_RC_OR_RET(rc == GSM_MACBLOCK_LEN, "decoding FACCH");
+
+ printf("%s(%s): FACCH/%c (pattern 0x2b): n_errors=%d / n_bits_total=%d\n",
+ __func__, tc->name, tc->half_rate ? 'H' : 'F', n_errors, n_bits_total);
+ printf("%s\n", osmo_hexdump(&data[0], GSM_MACBLOCK_LEN));
+ }
+
+ printf("\n");
+}
int main(int argc, char **argv)
{
int i, len_l2, len_mb;
- len_l2 = sizeof(test_l2) / sizeof(test_l2[0]);
- len_mb = sizeof(test_macblock) / sizeof(test_macblock[0]);
+ len_l2 = ARRAY_SIZE(test_l2);
+ len_mb = ARRAY_SIZE(test_macblock);
for (i = 0; i < len_l2; i++)
test_xcch(test_l2[i]);
@@ -371,6 +678,10 @@ int main(int argc, char **argv)
test_rach_ext(0x1a, i);
}
+ for (i = 0; i < ARRAY_SIZE(test_rach_11bit); i++)
+ test_rach_11bit_sample(0x3f, test_rach_11bit[i]);
+ printf("\n");
+
for (i = 0; i < len_l2; i++)
test_sch(test_l2[i]);
@@ -389,19 +700,36 @@ int main(int argc, char **argv)
for (i = 0; i < sizeof(test_speech_hr); i++)
test_speech_hr[i] = i * 17;
- test_speech_hr[0] = 0x00;
test_hr(test_speech_hr, sizeof(test_speech_hr));
for (i = 0; i < len_l2; i++)
test_hr(test_l2[i], sizeof(test_l2[0]));
for (i = 0; i < len_mb; i++) {
- test_pdtch(test_macblock[i], 23);
- test_pdtch(test_macblock[i], 34);
- test_pdtch(test_macblock[i], 40);
- test_pdtch(test_macblock[i], 54);
+ if (test_macblock[i].is_egprs) {
+ test_pdtch(&test_macblock[i], test_macblock[i].l2_len);
+ } else {
+ test_pdtch(&test_macblock[i], 23);
+ test_pdtch(&test_macblock[i], 34);
+ test_pdtch(&test_macblock[i], 40);
+ test_pdtch(&test_macblock[i], 54);
+ }
}
+ printf("\nTesting FACCH/F codec:\n");
+ for (i = 0; i < ARRAY_SIZE(test_l2); i++)
+ test_facch(test_l2[i], false);
+ printf("\nTesting FACCH/H codec:\n");
+ for (i = 0; i < ARRAY_SIZE(test_l2); i++)
+ test_facch(test_l2[i], true);
+
+ printf("\nTesting CSD functions (no FACCH):\n");
+ for (i = 0; i < ARRAY_SIZE(csd_tests); i++)
+ test_csd(&csd_tests[i], false);
+ printf("\nTesting CSD functions (with FACCH):\n");
+ for (i = 0; i < ARRAY_SIZE(csd_tests); i++)
+ test_csd(&csd_tests[i], true);
+
printf("Success\n");
return 0;
diff --git a/tests/coding/coding_test.ok b/tests/coding/coding_test.ok
index 8813af20..6767aeaf 100644
--- a/tests/coding/coding_test.ok
+++ b/tests/coding/coding_test.ok
@@ -40,34565 +40,12 @@ S-Bits:
Decoded: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
xcch_decode: n_errors=60 n_bits_total=456 ber=0.13
-Encoding: 00
-U-Bits: 000000000000000000000000000000000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 00
-
-Encoding: 00
-U-Bits: 000000000000000011101001101001000011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 00
-
-Encoding: 00
-U-Bits: 000000000000000011010000101110111111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: 00
-
-Encoding: 01
-U-Bits: 110100111100000000111010100000110000
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 01
-
-Encoding: 01
-U-Bits: 110100111100000011010011001001110011
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 01
-
-Encoding: 01
-U-Bits: 110100111100000011101010001110001111
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 01
-
-Encoding: 02
-U-Bits: 001101001111000000001110101000001100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 02
-
-Encoding: 02
-U-Bits: 001101001111000011100111000001001111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 02
-
-Encoding: 02
-U-Bits: 001101001111000011011110000110110011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 02
-
-Encoding: 03
-U-Bits: 111001110011000000110100001000111100
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 03
-
-Encoding: 03
-U-Bits: 111001110011000011011101100001111111
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 03
-
-Encoding: 03
-U-Bits: 111001110011000011100100100110000011
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 03
-
-Encoding: 04
-U-Bits: 000011010011110000000011101010000011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 04
-
-Encoding: 04
-U-Bits: 000011010011110011101010000011000000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 04
-
-Encoding: 04
-U-Bits: 000011010011110011010011000100111100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 04
-
-Encoding: 05
-U-Bits: 110111101111110000111001001010110011
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 05
-
-Encoding: 05
-U-Bits: 110111101111110011010000100011110000
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 05
-
-Encoding: 05
-U-Bits: 110111101111110011101001100100001100
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 05
-
-Encoding: 06
-U-Bits: 001110011100110000001101000010001111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 06
-
-Encoding: 06
-U-Bits: 001110011100110011100100101011001100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 06
-
-Encoding: 06
-U-Bits: 001110011100110011011101101100110000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 06
-
-Encoding: 07
-U-Bits: 111010100000110000110111100010111111
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 07
-
-Encoding: 07
-U-Bits: 111010100000110011011110001011111100
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 07
-
-Encoding: 07
-U-Bits: 111010100000110011100111001100000000
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 07
-
-Encoding: 08
-U-Bits: 000000110100111111100100011111110000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 08
-
-Encoding: 08
-U-Bits: 000000110100111100001101110110110011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 08
-
-Encoding: 08
-U-Bits: 000000110100111100110100110001001111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 08
-
-Encoding: 09
-U-Bits: 110100001000111111011110111111000000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 09
-
-Encoding: 09
-U-Bits: 110100001000111100110111010110000011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 09
-
-Encoding: 09
-U-Bits: 110100001000111100001110010001111111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 09
-
-Encoding: 0a
-U-Bits: 001101111011111111101010110111111100
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 0a
-
-Encoding: 0a
-U-Bits: 001101111011111100000011011110111111
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: 0a
-
-Encoding: 0a
-U-Bits: 001101111011111100111010011001000011
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 0a
-
-Encoding: 0b
-U-Bits: 111001000111111111010000010111001100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 0b
-
-Encoding: 0b
-U-Bits: 111001000111111100111001111110001111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 0b
-
-Encoding: 0b
-U-Bits: 111001000111111100000000111001110011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 0b
-
-Encoding: 0c
-U-Bits: 000011100111001111100111110101110011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 0c
-
-Encoding: 0c
-U-Bits: 000011100111001100001110011100110000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 0c
-
-Encoding: 0c
-U-Bits: 000011100111001100110111011011001100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 0c
-
-Encoding: 0d
-U-Bits: 110111011011001111011101010101000011
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 0d
-
-Encoding: 0d
-U-Bits: 110111011011001100110100111100000000
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 0d
-
-Encoding: 0d
-U-Bits: 110111011011001100001101111011111100
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 0d
-
-Encoding: 0e
-U-Bits: 001110101000001111101001011101111111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: 0e
-
-Encoding: 0e
-U-Bits: 001110101000001100000000110100111100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 0e
-
-Encoding: 0e
-U-Bits: 001110101000001100111001110011000000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 0e
-
-Encoding: 0f
-U-Bits: 111010010100001111010011111101001111
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 0f
-
-Encoding: 0f
-U-Bits: 111010010100001100111010010100001100
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 0f
-
-Encoding: 0f
-U-Bits: 111010010100001100000011010011110000
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 0f
-
-Encoding: 10
-U-Bits: 000000001101001111111001000111111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 10
-
-Encoding: 10
-U-Bits: 000000001101001100010000101110111111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: 10
-
-Encoding: 10
-U-Bits: 000000001101001100101001101001000011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 10
-
-Encoding: 11
-U-Bits: 110100110001001111000011100111001100
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 11
-
-Encoding: 11
-U-Bits: 110100110001001100101010001110001111
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 11
-
-Encoding: 11
-U-Bits: 110100110001001100010011001001110011
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 11
-
-Encoding: 12
-U-Bits: 001101000010001111110111101111110000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 12
-
-Encoding: 12
-U-Bits: 001101000010001100011110000110110011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 12
-
-Encoding: 12
-U-Bits: 001101000010001100100111000001001111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 12
-
-Encoding: 13
-U-Bits: 111001111110001111001101001111000000
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 13
-
-Encoding: 13
-U-Bits: 111001111110001100100100100110000011
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 13
-
-Encoding: 13
-U-Bits: 111001111110001100011101100001111111
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 13
-
-Encoding: 14
-U-Bits: 000011011110111111111010101101111111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: 14
-
-Encoding: 14
-U-Bits: 000011011110111100010011000100111100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 14
-
-Encoding: 14
-U-Bits: 000011011110111100101010000011000000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 14
-
-Encoding: 15
-U-Bits: 110111100010111111000000001101001111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 15
-
-Encoding: 15
-U-Bits: 110111100010111100101001100100001100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 15
-
-Encoding: 15
-U-Bits: 110111100010111100010000100011110000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 15
-
-Encoding: 16
-U-Bits: 001110010001111111110100000101110011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 16
-
-Encoding: 16
-U-Bits: 001110010001111100011101101100110000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 16
-
-Encoding: 16
-U-Bits: 001110010001111100100100101011001100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 16
-
-Encoding: 17
-U-Bits: 111010101101111111001110100101000011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 17
-
-Encoding: 17
-U-Bits: 111010101101111100100111001100000000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 17
-
-Encoding: 17
-U-Bits: 111010101101111100011110001011111100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 17
-
-Encoding: 18
-U-Bits: 000000111001110000011101011000001100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 18
-
-Encoding: 18
-U-Bits: 000000111001110011110100110001001111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 18
-
-Encoding: 18
-U-Bits: 000000111001110011001101110110110011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 18
-
-Encoding: 19
-U-Bits: 110100000101110000100111111000111100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 19
-
-Encoding: 19
-U-Bits: 110100000101110011001110010001111111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 19
-
-Encoding: 19
-U-Bits: 110100000101110011110111010110000011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 19
-
-Encoding: 1a
-U-Bits: 001101110110110000010011110000000000
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1a
-
-Encoding: 1a
-U-Bits: 001101110110110011111010011001000011
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 1a
-
-Encoding: 1a
-U-Bits: 001101110110110011000011011110111111
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: 1a
-
-Encoding: 1b
-U-Bits: 111001001010110000101001010000110000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 1b
-
-Encoding: 1b
-U-Bits: 111001001010110011000000111001110011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 1b
-
-Encoding: 1b
-U-Bits: 111001001010110011111001111110001111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 1b
-
-Encoding: 1c
-U-Bits: 000011101010000000011110110010001111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 1c
-
-Encoding: 1c
-U-Bits: 000011101010000011110111011011001100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 1c
-
-Encoding: 1c
-U-Bits: 000011101010000011001110011100110000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 1c
-
-Encoding: 1d
-U-Bits: 110111010110000000100100010010111111
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 1d
-
-Encoding: 1d
-U-Bits: 110111010110000011001101111011111100
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 1d
-
-Encoding: 1d
-U-Bits: 110111010110000011110100111100000000
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1d
-
-Encoding: 1e
-U-Bits: 001110100101000000010000011010000011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 1e
-
-Encoding: 1e
-U-Bits: 001110100101000011111001110011000000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 1e
-
-Encoding: 1e
-U-Bits: 001110100101000011000000110100111100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 1e
-
-Encoding: 1f
-U-Bits: 111010011001000000101010111010110011
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 1f
-
-Encoding: 1f
-U-Bits: 111010011001000011000011010011110000
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 1f
-
-Encoding: 1f
-U-Bits: 111010011001000011111010010100001100
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 1f
-
-Encoding: 20
-U-Bits: 000000000011010011111110010001111111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 20
-
-Encoding: 20
-U-Bits: 000000000011010000010111111000111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 20
-
-Encoding: 20
-U-Bits: 000000000011010000101110111111000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 20
-
-Encoding: 21
-U-Bits: 110100111111010011000100110001001111
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 21
-
-Encoding: 21
-U-Bits: 110100111111010000101101011000001100
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 21
-
-Encoding: 21
-U-Bits: 110100111111010000010100011111110000
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 21
-
-Encoding: 22
-U-Bits: 001101001100010011110000111001110011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 22
-
-Encoding: 22
-U-Bits: 001101001100010000011001010000110000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 22
-
-Encoding: 22
-U-Bits: 001101001100010000100000010111001100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 22
-
-Encoding: 23
-U-Bits: 111001110000010011001010011001000011
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 23
-
-Encoding: 23
-U-Bits: 111001110000010000100011110000000000
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 23
-
-Encoding: 23
-U-Bits: 111001110000010000011010110111111100
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 23
-
-Encoding: 24
-U-Bits: 000011010000100011111101111011111100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 24
-
-Encoding: 24
-U-Bits: 000011010000100000010100010010111111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 24
-
-Encoding: 24
-U-Bits: 000011010000100000101101010101000011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 24
-
-Encoding: 25
-U-Bits: 110111101100100011000111011011001100
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 25
-
-Encoding: 25
-U-Bits: 110111101100100000101110110010001111
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 25
-
-Encoding: 25
-U-Bits: 110111101100100000010111110101110011
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 25
-
-Encoding: 26
-U-Bits: 001110011111100011110011010011110000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 26
-
-Encoding: 26
-U-Bits: 001110011111100000011010111010110011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 26
-
-Encoding: 26
-U-Bits: 001110011111100000100011111101001111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 26
-
-Encoding: 27
-U-Bits: 111010100011100011001001110011000000
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 27
-
-Encoding: 27
-U-Bits: 111010100011100000100000011010000011
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 27
-
-Encoding: 27
-U-Bits: 111010100011100000011001011101111111
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: 27
-
-Encoding: 28
-U-Bits: 000000110111101100011010001110001111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 28
-
-Encoding: 28
-U-Bits: 000000110111101111110011100111001100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 28
-
-Encoding: 28
-U-Bits: 000000110111101111001010100000110000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 28
-
-Encoding: 29
-U-Bits: 110100001011101100100000101110111111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: 29
-
-Encoding: 29
-U-Bits: 110100001011101111001001000111111100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 29
-
-Encoding: 29
-U-Bits: 110100001011101111110000000000000000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 29
-
-Encoding: 2a
-U-Bits: 001101111000101100010100100110000011
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 2a
-
-Encoding: 2a
-U-Bits: 001101111000101111111101001111000000
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 2a
-
-Encoding: 2a
-U-Bits: 001101111000101111000100001000111100
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 2a
-
-Encoding: 2b
-U-Bits: 111001000100101100101110000110110011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 2b
-
-Encoding: 2b
-U-Bits: 111001000100101111000111101111110000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 2b
-
-Encoding: 2b
-U-Bits: 111001000100101111111110101000001100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 2b
-
-Encoding: 2c
-U-Bits: 000011100100011100011001100100001100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 2c
-
-Encoding: 2c
-U-Bits: 000011100100011111110000001101001111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 2c
-
-Encoding: 2c
-U-Bits: 000011100100011111001001001010110011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 2c
-
-Encoding: 2d
-U-Bits: 110111011000011100100011000100111100
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 2d
-
-Encoding: 2d
-U-Bits: 110111011000011111001010101101111111
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: 2d
-
-Encoding: 2d
-U-Bits: 110111011000011111110011101010000011
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 2d
-
-Encoding: 2e
-U-Bits: 001110101011011100010111001100000000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2e
-
-Encoding: 2e
-U-Bits: 001110101011011111111110100101000011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 2e
-
-Encoding: 2e
-U-Bits: 001110101011011111000111100010111111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 2e
-
-Encoding: 2f
-U-Bits: 111010010111011100101101101100110000
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 2f
-
-Encoding: 2f
-U-Bits: 111010010111011111000100000101110011
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 2f
-
-Encoding: 2f
-U-Bits: 111010010111011111111101000010001111
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 2f
-
-Encoding: 30
-U-Bits: 000000001110011100000111010110000011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 30
-
-Encoding: 30
-U-Bits: 000000001110011111101110111111000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 30
-
-Encoding: 30
-U-Bits: 000000001110011111010111111000111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 30
-
-Encoding: 31
-U-Bits: 110100110010011100111101110110110011
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 31
-
-Encoding: 31
-U-Bits: 110100110010011111010100011111110000
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 31
-
-Encoding: 31
-U-Bits: 110100110010011111101101011000001100
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 31
-
-Encoding: 32
-U-Bits: 001101000001011100001001111110001111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 32
-
-Encoding: 32
-U-Bits: 001101000001011111100000010111001100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 32
-
-Encoding: 32
-U-Bits: 001101000001011111011001010000110000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 32
-
-Encoding: 33
-U-Bits: 111001111101011100110011011110111111
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: 33
-
-Encoding: 33
-U-Bits: 111001111101011111011010110111111100
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 33
-
-Encoding: 33
-U-Bits: 111001111101011111100011110000000000
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 33
-
-Encoding: 34
-U-Bits: 000011011101101100000100111100000000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 34
-
-Encoding: 34
-U-Bits: 000011011101101111101101010101000011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 34
-
-Encoding: 34
-U-Bits: 000011011101101111010100010010111111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 34
-
-Encoding: 35
-U-Bits: 110111100001101100111110011100110000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 35
-
-Encoding: 35
-U-Bits: 110111100001101111010111110101110011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 35
-
-Encoding: 35
-U-Bits: 110111100001101111101110110010001111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 35
-
-Encoding: 36
-U-Bits: 001110010010101100001010010100001100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 36
-
-Encoding: 36
-U-Bits: 001110010010101111100011111101001111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 36
-
-Encoding: 36
-U-Bits: 001110010010101111011010111010110011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 36
-
-Encoding: 37
-U-Bits: 111010101110101100110000110100111100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 37
-
-Encoding: 37
-U-Bits: 111010101110101111011001011101111111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: 37
-
-Encoding: 37
-U-Bits: 111010101110101111100000011010000011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 37
-
-Encoding: 38
-U-Bits: 000000111010100011100011001001110011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 38
-
-Encoding: 38
-U-Bits: 000000111010100000001010100000110000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 38
-
-Encoding: 38
-U-Bits: 000000111010100000110011100111001100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 38
-
-Encoding: 39
-U-Bits: 110100000110100011011001101001000011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 39
-
-Encoding: 39
-U-Bits: 110100000110100000110000000000000000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 39
-
-Encoding: 39
-U-Bits: 110100000110100000001001000111111100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 39
-
-Encoding: 3a
-U-Bits: 001101110101100011101101100001111111
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 3a
-
-Encoding: 3a
-U-Bits: 001101110101100000000100001000111100
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 3a
-
-Encoding: 3a
-U-Bits: 001101110101100000111101001111000000
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 3a
-
-Encoding: 3b
-U-Bits: 111001001001100011010111000001001111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 3b
-
-Encoding: 3b
-U-Bits: 111001001001100000111110101000001100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 3b
-
-Encoding: 3b
-U-Bits: 111001001001100000000111101111110000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 3b
-
-Encoding: 3c
-U-Bits: 000011101001010011100000100011110000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 3c
-
-Encoding: 3c
-U-Bits: 000011101001010000001001001010110011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 3c
-
-Encoding: 3c
-U-Bits: 000011101001010000110000001101001111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 3c
-
-Encoding: 3d
-U-Bits: 110111010101010011011010000011000000
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 3d
-
-Encoding: 3d
-U-Bits: 110111010101010000110011101010000011
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 3d
-
-Encoding: 3d
-U-Bits: 110111010101010000001010101101111111
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: 3d
-
-Encoding: 3e
-U-Bits: 001110100110010011101110001011111100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 3e
-
-Encoding: 3e
-U-Bits: 001110100110010000000111100010111111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 3e
-
-Encoding: 3e
-U-Bits: 001110100110010000111110100101000011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 3e
-
-Encoding: 3f
-U-Bits: 111010011010010011010100101011001100
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 3f
-
-Encoding: 3f
-U-Bits: 111010011010010000111101000010001111
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 3f
-
-Encoding: 3f
-U-Bits: 111010011010010000000100000101110011
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 3f
-
-Encoding: 40
-U-Bits: 000000000000110111011011000001001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 40
-
-Encoding: 40
-U-Bits: 000000000000110100110010101000001100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 40
-
-Encoding: 40
-U-Bits: 000000000000110100001011101111110000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 40
-
-Encoding: 41
-U-Bits: 110100111100110111100001100001111111
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 41
-
-Encoding: 41
-U-Bits: 110100111100110100001000001000111100
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 41
-
-Encoding: 41
-U-Bits: 110100111100110100110001001111000000
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 41
-
-Encoding: 42
-U-Bits: 001101001111110111010101101001000011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 42
-
-Encoding: 42
-U-Bits: 001101001111110100111100000000000000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 42
-
-Encoding: 42
-U-Bits: 001101001111110100000101000111111100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 42
-
-Encoding: 43
-U-Bits: 111001110011110111101111001001110011
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 43
-
-Encoding: 43
-U-Bits: 111001110011110100000110100000110000
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 43
-
-Encoding: 43
-U-Bits: 111001110011110100111111100111001100
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 43
-
-Encoding: 44
-U-Bits: 000011010011000111011000101011001100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 44
-
-Encoding: 44
-U-Bits: 000011010011000100110001000010001111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 44
-
-Encoding: 44
-U-Bits: 000011010011000100001000000101110011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 44
-
-Encoding: 45
-U-Bits: 110111101111000111100010001011111100
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 45
-
-Encoding: 45
-U-Bits: 110111101111000100001011100010111111
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 45
-
-Encoding: 45
-U-Bits: 110111101111000100110010100101000011
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 45
-
-Encoding: 46
-U-Bits: 001110011100000111010110000011000000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 46
-
-Encoding: 46
-U-Bits: 001110011100000100111111101010000011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 46
-
-Encoding: 46
-U-Bits: 001110011100000100000110101101111111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: 46
-
-Encoding: 47
-U-Bits: 111010100000000111101100100011110000
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 47
-
-Encoding: 47
-U-Bits: 111010100000000100000101001010110011
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 47
-
-Encoding: 47
-U-Bits: 111010100000000100111100001101001111
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 47
-
-Encoding: 48
-U-Bits: 000000110100001000111111011110111111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: 48
-
-Encoding: 48
-U-Bits: 000000110100001011010110110111111100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 48
-
-Encoding: 48
-U-Bits: 000000110100001011101111110000000000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 48
-
-Encoding: 49
-U-Bits: 110100001000001000000101111110001111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 49
-
-Encoding: 49
-U-Bits: 110100001000001011101100010111001100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 49
-
-Encoding: 49
-U-Bits: 110100001000001011010101010000110000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 49
-
-Encoding: 4a
-U-Bits: 001101111011001000110001110110110011
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 4a
-
-Encoding: 4a
-U-Bits: 001101111011001011011000011111110000
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 4a
-
-Encoding: 4a
-U-Bits: 001101111011001011100001011000001100
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 4a
-
-Encoding: 4b
-U-Bits: 111001000111001000001011010110000011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 4b
-
-Encoding: 4b
-U-Bits: 111001000111001011100010111111000000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 4b
-
-Encoding: 4b
-U-Bits: 111001000111001011011011111000111100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 4b
-
-Encoding: 4c
-U-Bits: 000011100111111000111100110100111100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 4c
-
-Encoding: 4c
-U-Bits: 000011100111111011010101011101111111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: 4c
-
-Encoding: 4c
-U-Bits: 000011100111111011101100011010000011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 4c
-
-Encoding: 4d
-U-Bits: 110111011011111000000110010100001100
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 4d
-
-Encoding: 4d
-U-Bits: 110111011011111011101111111101001111
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 4d
-
-Encoding: 4d
-U-Bits: 110111011011111011010110111010110011
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 4d
-
-Encoding: 4e
-U-Bits: 001110101000111000110010011100110000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 4e
-
-Encoding: 4e
-U-Bits: 001110101000111011011011110101110011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 4e
-
-Encoding: 4e
-U-Bits: 001110101000111011100010110010001111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 4e
-
-Encoding: 4f
-U-Bits: 111010010100111000001000111100000000
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4f
-
-Encoding: 4f
-U-Bits: 111010010100111011100001010101000011
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 4f
-
-Encoding: 4f
-U-Bits: 111010010100111011011000010010111111
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 4f
-
-Encoding: 50
-U-Bits: 000000001101111000100010000110110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 50
-
-Encoding: 50
-U-Bits: 000000001101111011001011101111110000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 50
-
-Encoding: 50
-U-Bits: 000000001101111011110010101000001100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 50
-
-Encoding: 51
-U-Bits: 110100110001111000011000100110000011
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 51
-
-Encoding: 51
-U-Bits: 110100110001111011110001001111000000
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 51
-
-Encoding: 51
-U-Bits: 110100110001111011001000001000111100
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 51
-
-Encoding: 52
-U-Bits: 001101000010111000101100101110111111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: 52
-
-Encoding: 52
-U-Bits: 001101000010111011000101000111111100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 52
-
-Encoding: 52
-U-Bits: 001101000010111011111100000000000000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 52
-
-Encoding: 53
-U-Bits: 111001111110111000010110001110001111
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 53
-
-Encoding: 53
-U-Bits: 111001111110111011111111100111001100
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 53
-
-Encoding: 53
-U-Bits: 111001111110111011000110100000110000
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 53
-
-Encoding: 54
-U-Bits: 000011011110001000100001101100110000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 54
-
-Encoding: 54
-U-Bits: 000011011110001011001000000101110011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 54
-
-Encoding: 54
-U-Bits: 000011011110001011110001000010001111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 54
-
-Encoding: 55
-U-Bits: 110111100010001000011011001100000000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 55
-
-Encoding: 55
-U-Bits: 110111100010001011110010100101000011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 55
-
-Encoding: 55
-U-Bits: 110111100010001011001011100010111111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 55
-
-Encoding: 56
-U-Bits: 001110010001001000101111000100111100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 56
-
-Encoding: 56
-U-Bits: 001110010001001011000110101101111111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: 56
-
-Encoding: 56
-U-Bits: 001110010001001011111111101010000011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 56
-
-Encoding: 57
-U-Bits: 111010101101001000010101100100001100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 57
-
-Encoding: 57
-U-Bits: 111010101101001011111100001101001111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 57
-
-Encoding: 57
-U-Bits: 111010101101001011000101001010110011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 57
-
-Encoding: 58
-U-Bits: 000000111001000111000110011001000011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 58
-
-Encoding: 58
-U-Bits: 000000111001000100101111110000000000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 58
-
-Encoding: 58
-U-Bits: 000000111001000100010110110111111100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 58
-
-Encoding: 59
-U-Bits: 110100000101000111111100111001110011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 59
-
-Encoding: 59
-U-Bits: 110100000101000100010101010000110000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 59
-
-Encoding: 59
-U-Bits: 110100000101000100101100010111001100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 59
-
-Encoding: 5a
-U-Bits: 001101110110000111001000110001001111
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 5a
-
-Encoding: 5a
-U-Bits: 001101110110000100100001011000001100
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 5a
-
-Encoding: 5a
-U-Bits: 001101110110000100011000011111110000
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 5a
-
-Encoding: 5b
-U-Bits: 111001001010000111110010010001111111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 5b
-
-Encoding: 5b
-U-Bits: 111001001010000100011011111000111100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 5b
-
-Encoding: 5b
-U-Bits: 111001001010000100100010111111000000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 5b
-
-Encoding: 5c
-U-Bits: 000011101010110111000101110011000000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 5c
-
-Encoding: 5c
-U-Bits: 000011101010110100101100011010000011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 5c
-
-Encoding: 5c
-U-Bits: 000011101010110100010101011101111111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: 5c
-
-Encoding: 5d
-U-Bits: 110111010110110111111111010011110000
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 5d
-
-Encoding: 5d
-U-Bits: 110111010110110100010110111010110011
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 5d
-
-Encoding: 5d
-U-Bits: 110111010110110100101111111101001111
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 5d
-
-Encoding: 5e
-U-Bits: 001110100101110111001011011011001100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 5e
-
-Encoding: 5e
-U-Bits: 001110100101110100100010110010001111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 5e
-
-Encoding: 5e
-U-Bits: 001110100101110100011011110101110011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 5e
-
-Encoding: 5f
-U-Bits: 111010011001110111110001111011111100
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 5f
-
-Encoding: 5f
-U-Bits: 111010011001110100011000010010111111
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 5f
-
-Encoding: 5f
-U-Bits: 111010011001110100100001010101000011
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 5f
-
-Encoding: 60
-U-Bits: 000000000011100100100101010000110000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 60
-
-Encoding: 60
-U-Bits: 000000000011100111001100111001110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 60
-
-Encoding: 60
-U-Bits: 000000000011100111110101111110001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 60
-
-Encoding: 61
-U-Bits: 110100111111100100011111110000000000
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 61
-
-Encoding: 61
-U-Bits: 110100111111100111110110011001000011
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 61
-
-Encoding: 61
-U-Bits: 110100111111100111001111011110111111
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: 61
-
-Encoding: 62
-U-Bits: 001101001100100100101011111000111100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 62
-
-Encoding: 62
-U-Bits: 001101001100100111000010010001111111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 62
-
-Encoding: 62
-U-Bits: 001101001100100111111011010110000011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 62
-
-Encoding: 63
-U-Bits: 111001110000100100010001011000001100
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 63
-
-Encoding: 63
-U-Bits: 111001110000100111111000110001001111
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 63
-
-Encoding: 63
-U-Bits: 111001110000100111000001110110110011
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 63
-
-Encoding: 64
-U-Bits: 000011010000010100100110111010110011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 64
-
-Encoding: 64
-U-Bits: 000011010000010111001111010011110000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 64
-
-Encoding: 64
-U-Bits: 000011010000010111110110010100001100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 64
-
-Encoding: 65
-U-Bits: 110111101100010100011100011010000011
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 65
-
-Encoding: 65
-U-Bits: 110111101100010111110101110011000000
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 65
-
-Encoding: 65
-U-Bits: 110111101100010111001100110100111100
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 65
-
-Encoding: 66
-U-Bits: 001110011111010100101000010010111111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 66
-
-Encoding: 66
-U-Bits: 001110011111010111000001111011111100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 66
-
-Encoding: 66
-U-Bits: 001110011111010111111000111100000000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 66
-
-Encoding: 67
-U-Bits: 111010100011010100010010110010001111
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 67
-
-Encoding: 67
-U-Bits: 111010100011010111111011011011001100
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 67
-
-Encoding: 67
-U-Bits: 111010100011010111000010011100110000
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 67
-
-Encoding: 68
-U-Bits: 000000110111011011000001001111000000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 68
-
-Encoding: 68
-U-Bits: 000000110111011000101000100110000011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 68
-
-Encoding: 68
-U-Bits: 000000110111011000010001100001111111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 68
-
-Encoding: 69
-U-Bits: 110100001011011011111011101111110000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 69
-
-Encoding: 69
-U-Bits: 110100001011011000010010000110110011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 69
-
-Encoding: 69
-U-Bits: 110100001011011000101011000001001111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 69
-
-Encoding: 6a
-U-Bits: 001101111000011011001111100111001100
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 6a
-
-Encoding: 6a
-U-Bits: 001101111000011000100110001110001111
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 6a
-
-Encoding: 6a
-U-Bits: 001101111000011000011111001001110011
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 6a
-
-Encoding: 6b
-U-Bits: 111001000100011011110101000111111100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 6b
-
-Encoding: 6b
-U-Bits: 111001000100011000011100101110111111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: 6b
-
-Encoding: 6b
-U-Bits: 111001000100011000100101101001000011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 6b
-
-Encoding: 6c
-U-Bits: 000011100100101011000010100101000011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 6c
-
-Encoding: 6c
-U-Bits: 000011100100101000101011001100000000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6c
-
-Encoding: 6c
-U-Bits: 000011100100101000010010001011111100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 6c
-
-Encoding: 6d
-U-Bits: 110111011000101011111000000101110011
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 6d
-
-Encoding: 6d
-U-Bits: 110111011000101000010001101100110000
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 6d
-
-Encoding: 6d
-U-Bits: 110111011000101000101000101011001100
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 6d
-
-Encoding: 6e
-U-Bits: 001110101011101011001100001101001111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 6e
-
-Encoding: 6e
-U-Bits: 001110101011101000100101100100001100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 6e
-
-Encoding: 6e
-U-Bits: 001110101011101000011100100011110000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 6e
-
-Encoding: 6f
-U-Bits: 111010010111101011110110101101111111
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: 6f
-
-Encoding: 6f
-U-Bits: 111010010111101000011111000100111100
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 6f
-
-Encoding: 6f
-U-Bits: 111010010111101000100110000011000000
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 6f
-
-Encoding: 70
-U-Bits: 000000001110101011011100010111001100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 70
-
-Encoding: 70
-U-Bits: 000000001110101000110101111110001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 70
-
-Encoding: 70
-U-Bits: 000000001110101000001100111001110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 70
-
-Encoding: 71
-U-Bits: 110100110010101011100110110111111100
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 71
-
-Encoding: 71
-U-Bits: 110100110010101000001111011110111111
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: 71
-
-Encoding: 71
-U-Bits: 110100110010101000110110011001000011
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 71
-
-Encoding: 72
-U-Bits: 001101000001101011010010111111000000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 72
-
-Encoding: 72
-U-Bits: 001101000001101000111011010110000011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 72
-
-Encoding: 72
-U-Bits: 001101000001101000000010010001111111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 72
-
-Encoding: 73
-U-Bits: 111001111101101011101000011111110000
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 73
-
-Encoding: 73
-U-Bits: 111001111101101000000001110110110011
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 73
-
-Encoding: 73
-U-Bits: 111001111101101000111000110001001111
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 73
-
-Encoding: 74
-U-Bits: 000011011101011011011111111101001111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 74
-
-Encoding: 74
-U-Bits: 000011011101011000110110010100001100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 74
-
-Encoding: 74
-U-Bits: 000011011101011000001111010011110000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 74
-
-Encoding: 75
-U-Bits: 110111100001011011100101011101111111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: 75
-
-Encoding: 75
-U-Bits: 110111100001011000001100110100111100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 75
-
-Encoding: 75
-U-Bits: 110111100001011000110101110011000000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 75
-
-Encoding: 76
-U-Bits: 001110010010011011010001010101000011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 76
-
-Encoding: 76
-U-Bits: 001110010010011000111000111100000000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 76
-
-Encoding: 76
-U-Bits: 001110010010011000000001111011111100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 76
-
-Encoding: 77
-U-Bits: 111010101110011011101011110101110011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 77
-
-Encoding: 77
-U-Bits: 111010101110011000000010011100110000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 77
-
-Encoding: 77
-U-Bits: 111010101110011000111011011011001100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 77
-
-Encoding: 78
-U-Bits: 000000111010010100111000001000111100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 78
-
-Encoding: 78
-U-Bits: 000000111010010111010001100001111111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 78
-
-Encoding: 78
-U-Bits: 000000111010010111101000100110000011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 78
-
-Encoding: 79
-U-Bits: 110100000110010100000010101000001100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 79
-
-Encoding: 79
-U-Bits: 110100000110010111101011000001001111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 79
-
-Encoding: 79
-U-Bits: 110100000110010111010010000110110011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 79
-
-Encoding: 7a
-U-Bits: 001101110101010100110110100000110000
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 7a
-
-Encoding: 7a
-U-Bits: 001101110101010111011111001001110011
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 7a
-
-Encoding: 7a
-U-Bits: 001101110101010111100110001110001111
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 7a
-
-Encoding: 7b
-U-Bits: 111001001001010100001100000000000000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7b
-
-Encoding: 7b
-U-Bits: 111001001001010111100101101001000011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 7b
-
-Encoding: 7b
-U-Bits: 111001001001010111011100101110111111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: 7b
-
-Encoding: 7c
-U-Bits: 000011101001100100111011100010111111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 7c
-
-Encoding: 7c
-U-Bits: 000011101001100111010010001011111100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 7c
-
-Encoding: 7c
-U-Bits: 000011101001100111101011001100000000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7c
-
-Encoding: 7d
-U-Bits: 110111010101100100000001000010001111
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 7d
-
-Encoding: 7d
-U-Bits: 110111010101100111101000101011001100
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 7d
-
-Encoding: 7d
-U-Bits: 110111010101100111010001101100110000
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 7d
-
-Encoding: 7e
-U-Bits: 001110100110100100110101001010110011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 7e
-
-Encoding: 7e
-U-Bits: 001110100110100111011100100011110000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 7e
-
-Encoding: 7e
-U-Bits: 001110100110100111100101100100001100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 7e
-
-Encoding: 7f
-U-Bits: 111010011010100100001111101010000011
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 7f
-
-Encoding: 7f
-U-Bits: 111010011010100111100110000011000000
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 7f
-
-Encoding: 7f
-U-Bits: 111010011010100111011111000100111100
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 7f
-
-Encoding: 80
-U-Bits: 000000000000001110010010010101000011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 80
-
-Encoding: 80
-U-Bits: 000000000000001101111011111100000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 80
-
-Encoding: 80
-U-Bits: 000000000000001101000010111011111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 80
-
-Encoding: 81
-U-Bits: 110100111100001110101000110101110011
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 81
-
-Encoding: 81
-U-Bits: 110100111100001101000001011100110000
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 81
-
-Encoding: 81
-U-Bits: 110100111100001101111000011011001100
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 81
-
-Encoding: 82
-U-Bits: 001101001111001110011100111101001111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 82
-
-Encoding: 82
-U-Bits: 001101001111001101110101010100001100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 82
-
-Encoding: 82
-U-Bits: 001101001111001101001100010011110000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 82
-
-Encoding: 83
-U-Bits: 111001110011001110100110011101111111
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: 83
-
-Encoding: 83
-U-Bits: 111001110011001101001111110100111100
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 83
-
-Encoding: 83
-U-Bits: 111001110011001101110110110011000000
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 83
-
-Encoding: 84
-U-Bits: 000011010011111110010001111111000000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 84
-
-Encoding: 84
-U-Bits: 000011010011111101111000010110000011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 84
-
-Encoding: 84
-U-Bits: 000011010011111101000001010001111111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 84
-
-Encoding: 85
-U-Bits: 110111101111111110101011011111110000
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 85
-
-Encoding: 85
-U-Bits: 110111101111111101000010110110110011
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 85
-
-Encoding: 85
-U-Bits: 110111101111111101111011110001001111
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 85
-
-Encoding: 86
-U-Bits: 001110011100111110011111010111001100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 86
-
-Encoding: 86
-U-Bits: 001110011100111101110110111110001111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 86
-
-Encoding: 86
-U-Bits: 001110011100111101001111111001110011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 86
-
-Encoding: 87
-U-Bits: 111010100000111110100101110111111100
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 87
-
-Encoding: 87
-U-Bits: 111010100000111101001100011110111111
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: 87
-
-Encoding: 87
-U-Bits: 111010100000111101110101011001000011
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 87
-
-Encoding: 88
-U-Bits: 000000110100110001110110001010110011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 88
-
-Encoding: 88
-U-Bits: 000000110100110010011111100011110000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 88
-
-Encoding: 88
-U-Bits: 000000110100110010100110100100001100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 88
-
-Encoding: 89
-U-Bits: 110100001000110001001100101010000011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 89
-
-Encoding: 89
-U-Bits: 110100001000110010100101000011000000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 89
-
-Encoding: 89
-U-Bits: 110100001000110010011100000100111100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 89
-
-Encoding: 8a
-U-Bits: 001101111011110001111000100010111111
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 8a
-
-Encoding: 8a
-U-Bits: 001101111011110010010001001011111100
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 8a
-
-Encoding: 8a
-U-Bits: 001101111011110010101000001100000000
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 8a
-
-Encoding: 8b
-U-Bits: 111001000111110001000010000010001111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 8b
-
-Encoding: 8b
-U-Bits: 111001000111110010101011101011001100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 8b
-
-Encoding: 8b
-U-Bits: 111001000111110010010010101100110000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 8b
-
-Encoding: 8c
-U-Bits: 000011100111000001110101100000110000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 8c
-
-Encoding: 8c
-U-Bits: 000011100111000010011100001001110011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 8c
-
-Encoding: 8c
-U-Bits: 000011100111000010100101001110001111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 8c
-
-Encoding: 8d
-U-Bits: 110111011011000001001111000000000000
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 8d
-
-Encoding: 8d
-U-Bits: 110111011011000010100110101001000011
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 8d
-
-Encoding: 8d
-U-Bits: 110111011011000010011111101110111111
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: 8d
-
-Encoding: 8e
-U-Bits: 001110101000000001111011001000111100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 8e
-
-Encoding: 8e
-U-Bits: 001110101000000010010010100001111111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 8e
-
-Encoding: 8e
-U-Bits: 001110101000000010101011100110000011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 8e
-
-Encoding: 8f
-U-Bits: 111010010100000001000001101000001100
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 8f
-
-Encoding: 8f
-U-Bits: 111010010100000010101000000001001111
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 8f
-
-Encoding: 8f
-U-Bits: 111010010100000010010001000110110011
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 8f
-
-Encoding: 90
-U-Bits: 000000001101000001101011010010111111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: 90
-
-Encoding: 90
-U-Bits: 000000001101000010000010111011111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 90
-
-Encoding: 90
-U-Bits: 000000001101000010111011111100000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 90
-
-Encoding: 91
-U-Bits: 110100110001000001010001110010001111
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 91
-
-Encoding: 91
-U-Bits: 110100110001000010111000011011001100
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 91
-
-Encoding: 91
-U-Bits: 110100110001000010000001011100110000
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 91
-
-Encoding: 92
-U-Bits: 001101000010000001100101111010110011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: 92
-
-Encoding: 92
-U-Bits: 001101000010000010001100010011110000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 92
-
-Encoding: 92
-U-Bits: 001101000010000010110101010100001100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 92
-
-Encoding: 93
-U-Bits: 111001111110000001011111011010000011
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: 93
-
-Encoding: 93
-U-Bits: 111001111110000010110110110011000000
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 93
-
-Encoding: 93
-U-Bits: 111001111110000010001111110100111100
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 93
-
-Encoding: 94
-U-Bits: 000011011110110001101000111000111100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: 94
-
-Encoding: 94
-U-Bits: 000011011110110010000001010001111111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 94
-
-Encoding: 94
-U-Bits: 000011011110110010111000010110000011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 94
-
-Encoding: 95
-U-Bits: 110111100010110001010010011000001100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 95
-
-Encoding: 95
-U-Bits: 110111100010110010111011110001001111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 95
-
-Encoding: 95
-U-Bits: 110111100010110010000010110110110011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 95
-
-Encoding: 96
-U-Bits: 001110010001110001100110010000110000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 96
-
-Encoding: 96
-U-Bits: 001110010001110010001111111001110011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 96
-
-Encoding: 96
-U-Bits: 001110010001110010110110111110001111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 96
-
-Encoding: 97
-U-Bits: 111010101101110001011100110000000000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 97
-
-Encoding: 97
-U-Bits: 111010101101110010110101011001000011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 97
-
-Encoding: 97
-U-Bits: 111010101101110010001100011110111111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: 97
-
-Encoding: 98
-U-Bits: 000000111001111110001111001101001111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: 98
-
-Encoding: 98
-U-Bits: 000000111001111101100110100100001100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: 98
-
-Encoding: 98
-U-Bits: 000000111001111101011111100011110000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: 98
-
-Encoding: 99
-U-Bits: 110100000101111110110101101101111111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: 99
-
-Encoding: 99
-U-Bits: 110100000101111101011100000100111100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: 99
-
-Encoding: 99
-U-Bits: 110100000101111101100101000011000000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 99
-
-Encoding: 9a
-U-Bits: 001101110110111110000001100101000011
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: 9a
-
-Encoding: 9a
-U-Bits: 001101110110111101101000001100000000
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 9a
-
-Encoding: 9a
-U-Bits: 001101110110111101010001001011111100
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 9a
-
-Encoding: 9b
-U-Bits: 111001001010111110111011000101110011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: 9b
-
-Encoding: 9b
-U-Bits: 111001001010111101010010101100110000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: 9b
-
-Encoding: 9b
-U-Bits: 111001001010111101101011101011001100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: 9b
-
-Encoding: 9c
-U-Bits: 000011101010001110001100100111001100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: 9c
-
-Encoding: 9c
-U-Bits: 000011101010001101100101001110001111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: 9c
-
-Encoding: 9c
-U-Bits: 000011101010001101011100001001110011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 9c
-
-Encoding: 9d
-U-Bits: 110111010110001110110110000111111100
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: 9d
-
-Encoding: 9d
-U-Bits: 110111010110001101011111101110111111
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: 9d
-
-Encoding: 9d
-U-Bits: 110111010110001101100110101001000011
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: 9d
-
-Encoding: 9e
-U-Bits: 001110100101001110000010001111000000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: 9e
-
-Encoding: 9e
-U-Bits: 001110100101001101101011100110000011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: 9e
-
-Encoding: 9e
-U-Bits: 001110100101001101010010100001111111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: 9e
-
-Encoding: 9f
-U-Bits: 111010011001001110111000101111110000
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: 9f
-
-Encoding: 9f
-U-Bits: 111010011001001101010001000110110011
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: 9f
-
-Encoding: 9f
-U-Bits: 111010011001001101101000000001001111
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: 9f
-
-Encoding: a0
-U-Bits: 000000000011011101101100000100111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: a0
-
-Encoding: a0
-U-Bits: 000000000011011110000101101101111111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: a0
-
-Encoding: a0
-U-Bits: 000000000011011110111100101010000011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: a0
-
-Encoding: a1
-U-Bits: 110100111111011101010110100100001100
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: a1
-
-Encoding: a1
-U-Bits: 110100111111011110111111001101001111
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: a1
-
-Encoding: a1
-U-Bits: 110100111111011110000110001010110011
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: a1
-
-Encoding: a2
-U-Bits: 001101001100011101100010101100110000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: a2
-
-Encoding: a2
-U-Bits: 001101001100011110001011000101110011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: a2
-
-Encoding: a2
-U-Bits: 001101001100011110110010000010001111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: a2
-
-Encoding: a3
-U-Bits: 111001110000011101011000001100000000
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: a3
-
-Encoding: a3
-U-Bits: 111001110000011110110001100101000011
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: a3
-
-Encoding: a3
-U-Bits: 111001110000011110001000100010111111
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: a3
-
-Encoding: a4
-U-Bits: 000011010000101101101111101110111111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: a4
-
-Encoding: a4
-U-Bits: 000011010000101110000110000111111100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: a4
-
-Encoding: a4
-U-Bits: 000011010000101110111111000000000000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: a4
-
-Encoding: a5
-U-Bits: 110111101100101101010101001110001111
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: a5
-
-Encoding: a5
-U-Bits: 110111101100101110111100100111001100
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: a5
-
-Encoding: a5
-U-Bits: 110111101100101110000101100000110000
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: a5
-
-Encoding: a6
-U-Bits: 001110011111101101100001000110110011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: a6
-
-Encoding: a6
-U-Bits: 001110011111101110001000101111110000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: a6
-
-Encoding: a6
-U-Bits: 001110011111101110110001101000001100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: a6
-
-Encoding: a7
-U-Bits: 111010100011101101011011100110000011
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: a7
-
-Encoding: a7
-U-Bits: 111010100011101110110010001111000000
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: a7
-
-Encoding: a7
-U-Bits: 111010100011101110001011001000111100
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: a7
-
-Encoding: a8
-U-Bits: 000000110111100010001000011011001100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: a8
-
-Encoding: a8
-U-Bits: 000000110111100001100001110010001111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: a8
-
-Encoding: a8
-U-Bits: 000000110111100001011000110101110011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: a8
-
-Encoding: a9
-U-Bits: 110100001011100010110010111011111100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: a9
-
-Encoding: a9
-U-Bits: 110100001011100001011011010010111111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: a9
-
-Encoding: a9
-U-Bits: 110100001011100001100010010101000011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: a9
-
-Encoding: aa
-U-Bits: 001101111000100010000110110011000000
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: aa
-
-Encoding: aa
-U-Bits: 001101111000100001101111011010000011
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: aa
-
-Encoding: aa
-U-Bits: 001101111000100001010110011101111111
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: aa
-
-Encoding: ab
-U-Bits: 111001000100100010111100010011110000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: ab
-
-Encoding: ab
-U-Bits: 111001000100100001010101111010110011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: ab
-
-Encoding: ab
-U-Bits: 111001000100100001101100111101001111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: ab
-
-Encoding: ac
-U-Bits: 000011100100010010001011110001001111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: ac
-
-Encoding: ac
-U-Bits: 000011100100010001100010011000001100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: ac
-
-Encoding: ac
-U-Bits: 000011100100010001011011011111110000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: ac
-
-Encoding: ad
-U-Bits: 110111011000010010110001010001111111
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: ad
-
-Encoding: ad
-U-Bits: 110111011000010001011000111000111100
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: ad
-
-Encoding: ad
-U-Bits: 110111011000010001100001111111000000
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: ad
-
-Encoding: ae
-U-Bits: 001110101011010010000101011001000011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: ae
-
-Encoding: ae
-U-Bits: 001110101011010001101100110000000000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: ae
-
-Encoding: ae
-U-Bits: 001110101011010001010101110111111100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: ae
-
-Encoding: af
-U-Bits: 111010010111010010111111111001110011
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: af
-
-Encoding: af
-U-Bits: 111010010111010001010110010000110000
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: af
-
-Encoding: af
-U-Bits: 111010010111010001101111010111001100
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: af
-
-Encoding: b0
-U-Bits: 000000001110010010010101000011000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: b0
-
-Encoding: b0
-U-Bits: 000000001110010001111100101010000011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: b0
-
-Encoding: b0
-U-Bits: 000000001110010001000101101101111111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: b0
-
-Encoding: b1
-U-Bits: 110100110010010010101111100011110000
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: b1
-
-Encoding: b1
-U-Bits: 110100110010010001000110001010110011
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: b1
-
-Encoding: b1
-U-Bits: 110100110010010001111111001101001111
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: b1
-
-Encoding: b2
-U-Bits: 001101000001010010011011101011001100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: b2
-
-Encoding: b2
-U-Bits: 001101000001010001110010000010001111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: b2
-
-Encoding: b2
-U-Bits: 001101000001010001001011000101110011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: b2
-
-Encoding: b3
-U-Bits: 111001111101010010100001001011111100
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: b3
-
-Encoding: b3
-U-Bits: 111001111101010001001000100010111111
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: b3
-
-Encoding: b3
-U-Bits: 111001111101010001110001100101000011
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: b3
-
-Encoding: b4
-U-Bits: 000011011101100010010110101001000011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: b4
-
-Encoding: b4
-U-Bits: 000011011101100001111111000000000000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: b4
-
-Encoding: b4
-U-Bits: 000011011101100001000110000111111100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: b4
-
-Encoding: b5
-U-Bits: 110111100001100010101100001001110011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: b5
-
-Encoding: b5
-U-Bits: 110111100001100001000101100000110000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: b5
-
-Encoding: b5
-U-Bits: 110111100001100001111100100111001100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: b5
-
-Encoding: b6
-U-Bits: 001110010010100010011000000001001111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: b6
-
-Encoding: b6
-U-Bits: 001110010010100001110001101000001100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: b6
-
-Encoding: b6
-U-Bits: 001110010010100001001000101111110000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: b6
-
-Encoding: b7
-U-Bits: 111010101110100010100010100001111111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: b7
-
-Encoding: b7
-U-Bits: 111010101110100001001011001000111100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: b7
-
-Encoding: b7
-U-Bits: 111010101110100001110010001111000000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: b7
-
-Encoding: b8
-U-Bits: 000000111010101101110001011100110000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: b8
-
-Encoding: b8
-U-Bits: 000000111010101110011000110101110011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: b8
-
-Encoding: b8
-U-Bits: 000000111010101110100001110010001111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: b8
-
-Encoding: b9
-U-Bits: 110100000110101101001011111100000000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: b9
-
-Encoding: b9
-U-Bits: 110100000110101110100010010101000011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: b9
-
-Encoding: b9
-U-Bits: 110100000110101110011011010010111111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: b9
-
-Encoding: ba
-U-Bits: 001101110101101101111111110100111100
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: ba
-
-Encoding: ba
-U-Bits: 001101110101101110010110011101111111
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: ba
-
-Encoding: ba
-U-Bits: 001101110101101110101111011010000011
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: ba
-
-Encoding: bb
-U-Bits: 111001001001101101000101010100001100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: bb
-
-Encoding: bb
-U-Bits: 111001001001101110101100111101001111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: bb
-
-Encoding: bb
-U-Bits: 111001001001101110010101111010110011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: bb
-
-Encoding: bc
-U-Bits: 000011101001011101110010110110110011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: bc
-
-Encoding: bc
-U-Bits: 000011101001011110011011011111110000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: bc
-
-Encoding: bc
-U-Bits: 000011101001011110100010011000001100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: bc
-
-Encoding: bd
-U-Bits: 110111010101011101001000010110000011
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: bd
-
-Encoding: bd
-U-Bits: 110111010101011110100001111111000000
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: bd
-
-Encoding: bd
-U-Bits: 110111010101011110011000111000111100
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: bd
-
-Encoding: be
-U-Bits: 001110100110011101111100011110111111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: be
-
-Encoding: be
-U-Bits: 001110100110011110010101110111111100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: be
-
-Encoding: be
-U-Bits: 001110100110011110101100110000000000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: be
-
-Encoding: bf
-U-Bits: 111010011010011101000110111110001111
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: bf
-
-Encoding: bf
-U-Bits: 111010011010011110101111010111001100
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: bf
-
-Encoding: bf
-U-Bits: 111010011010011110010110010000110000
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: bf
-
-Encoding: c0
-U-Bits: 000000000000111001001001010100001100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: c0
-
-Encoding: c0
-U-Bits: 000000000000111010100000111101001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: c0
-
-Encoding: c0
-U-Bits: 000000000000111010011001111010110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: c0
-
-Encoding: c1
-U-Bits: 110100111100111001110011110100111100
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: c1
-
-Encoding: c1
-U-Bits: 110100111100111010011010011101111111
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: c1
-
-Encoding: c1
-U-Bits: 110100111100111010100011011010000011
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: c1
-
-Encoding: c2
-U-Bits: 001101001111111001000111111100000000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: c2
-
-Encoding: c2
-U-Bits: 001101001111111010101110010101000011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: c2
-
-Encoding: c2
-U-Bits: 001101001111111010010111010010111111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: c2
-
-Encoding: c3
-U-Bits: 111001110011111001111101011100110000
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: c3
-
-Encoding: c3
-U-Bits: 111001110011111010010100110101110011
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: c3
-
-Encoding: c3
-U-Bits: 111001110011111010101101110010001111
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: c3
-
-Encoding: c4
-U-Bits: 000011010011001001001010111110001111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: c4
-
-Encoding: c4
-U-Bits: 000011010011001010100011010111001100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: c4
-
-Encoding: c4
-U-Bits: 000011010011001010011010010000110000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: c4
-
-Encoding: c5
-U-Bits: 110111101111001001110000011110111111
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: c5
-
-Encoding: c5
-U-Bits: 110111101111001010011001110111111100
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: c5
-
-Encoding: c5
-U-Bits: 110111101111001010100000110000000000
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: c5
-
-Encoding: c6
-U-Bits: 001110011100001001000100010110000011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: c6
-
-Encoding: c6
-U-Bits: 001110011100001010101101111111000000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: c6
-
-Encoding: c6
-U-Bits: 001110011100001010010100111000111100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: c6
-
-Encoding: c7
-U-Bits: 111010100000001001111110110110110011
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: c7
-
-Encoding: c7
-U-Bits: 111010100000001010010111011111110000
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: c7
-
-Encoding: c7
-U-Bits: 111010100000001010101110011000001100
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: c7
-
-Encoding: c8
-U-Bits: 000000110100000110101101001011111100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: c8
-
-Encoding: c8
-U-Bits: 000000110100000101000100100010111111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: c8
-
-Encoding: c8
-U-Bits: 000000110100000101111101100101000011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: c8
-
-Encoding: c9
-U-Bits: 110100001000000110010111101011001100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: c9
-
-Encoding: c9
-U-Bits: 110100001000000101111110000010001111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: c9
-
-Encoding: c9
-U-Bits: 110100001000000101000111000101110011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: c9
-
-Encoding: ca
-U-Bits: 001101111011000110100011100011110000
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: ca
-
-Encoding: ca
-U-Bits: 001101111011000101001010001010110011
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: ca
-
-Encoding: ca
-U-Bits: 001101111011000101110011001101001111
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: ca
-
-Encoding: cb
-U-Bits: 111001000111000110011001000011000000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: cb
-
-Encoding: cb
-U-Bits: 111001000111000101110000101010000011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: cb
-
-Encoding: cb
-U-Bits: 111001000111000101001001101101111111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: cb
-
-Encoding: cc
-U-Bits: 000011100111110110101110100001111111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: cc
-
-Encoding: cc
-U-Bits: 000011100111110101000111001000111100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: cc
-
-Encoding: cc
-U-Bits: 000011100111110101111110001111000000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: cc
-
-Encoding: cd
-U-Bits: 110111011011110110010100000001001111
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: cd
-
-Encoding: cd
-U-Bits: 110111011011110101111101101000001100
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: cd
-
-Encoding: cd
-U-Bits: 110111011011110101000100101111110000
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: cd
-
-Encoding: ce
-U-Bits: 001110101000110110100000001001110011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: ce
-
-Encoding: ce
-U-Bits: 001110101000110101001001100000110000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: ce
-
-Encoding: ce
-U-Bits: 001110101000110101110000100111001100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: ce
-
-Encoding: cf
-U-Bits: 111010010100110110011010101001000011
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: cf
-
-Encoding: cf
-U-Bits: 111010010100110101110011000000000000
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: cf
-
-Encoding: cf
-U-Bits: 111010010100110101001010000111111100
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: cf
-
-Encoding: d0
-U-Bits: 000000001101110110110000010011110000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: d0
-
-Encoding: d0
-U-Bits: 000000001101110101011001111010110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: d0
-
-Encoding: d0
-U-Bits: 000000001101110101100000111101001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: d0
-
-Encoding: d1
-U-Bits: 110100110001110110001010110011000000
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: d1
-
-Encoding: d1
-U-Bits: 110100110001110101100011011010000011
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: d1
-
-Encoding: d1
-U-Bits: 110100110001110101011010011101111111
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: d1
-
-Encoding: d2
-U-Bits: 001101000010110110111110111011111100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: d2
-
-Encoding: d2
-U-Bits: 001101000010110101010111010010111111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: d2
-
-Encoding: d2
-U-Bits: 001101000010110101101110010101000011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: d2
-
-Encoding: d3
-U-Bits: 111001111110110110000100011011001100
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: d3
-
-Encoding: d3
-U-Bits: 111001111110110101101101110010001111
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: d3
-
-Encoding: d3
-U-Bits: 111001111110110101010100110101110011
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: d3
-
-Encoding: d4
-U-Bits: 000011011110000110110011111001110011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: d4
-
-Encoding: d4
-U-Bits: 000011011110000101011010010000110000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: d4
-
-Encoding: d4
-U-Bits: 000011011110000101100011010111001100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: d4
-
-Encoding: d5
-U-Bits: 110111100010000110001001011001000011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: d5
-
-Encoding: d5
-U-Bits: 110111100010000101100000110000000000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: d5
-
-Encoding: d5
-U-Bits: 110111100010000101011001110111111100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: d5
-
-Encoding: d6
-U-Bits: 001110010001000110111101010001111111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: d6
-
-Encoding: d6
-U-Bits: 001110010001000101010100111000111100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: d6
-
-Encoding: d6
-U-Bits: 001110010001000101101101111111000000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: d6
-
-Encoding: d7
-U-Bits: 111010101101000110000111110001001111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: d7
-
-Encoding: d7
-U-Bits: 111010101101000101101110011000001100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: d7
-
-Encoding: d7
-U-Bits: 111010101101000101010111011111110000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: d7
-
-Encoding: d8
-U-Bits: 000000111001001001010100001100000000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: d8
-
-Encoding: d8
-U-Bits: 000000111001001010111101100101000011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: d8
-
-Encoding: d8
-U-Bits: 000000111001001010000100100010111111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: d8
-
-Encoding: d9
-U-Bits: 110100000101001001101110101100110000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: d9
-
-Encoding: d9
-U-Bits: 110100000101001010000111000101110011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: d9
-
-Encoding: d9
-U-Bits: 110100000101001010111110000010001111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: d9
-
-Encoding: da
-U-Bits: 001101110110001001011010100100001100
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: da
-
-Encoding: da
-U-Bits: 001101110110001010110011001101001111
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: da
-
-Encoding: da
-U-Bits: 001101110110001010001010001010110011
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: da
-
-Encoding: db
-U-Bits: 111001001010001001100000000100111100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: db
-
-Encoding: db
-U-Bits: 111001001010001010001001101101111111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: db
-
-Encoding: db
-U-Bits: 111001001010001010110000101010000011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: db
-
-Encoding: dc
-U-Bits: 000011101010111001010111100110000011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: dc
-
-Encoding: dc
-U-Bits: 000011101010111010111110001111000000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: dc
-
-Encoding: dc
-U-Bits: 000011101010111010000111001000111100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: dc
-
-Encoding: dd
-U-Bits: 110111010110111001101101000110110011
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: dd
-
-Encoding: dd
-U-Bits: 110111010110111010000100101111110000
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: dd
-
-Encoding: dd
-U-Bits: 110111010110111010111101101000001100
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: dd
-
-Encoding: de
-U-Bits: 001110100101111001011001001110001111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: de
-
-Encoding: de
-U-Bits: 001110100101111010110000100111001100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: de
-
-Encoding: de
-U-Bits: 001110100101111010001001100000110000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: de
-
-Encoding: df
-U-Bits: 111010011001111001100011101110111111
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: df
-
-Encoding: df
-U-Bits: 111010011001111010001010000111111100
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: df
-
-Encoding: df
-U-Bits: 111010011001111010110011000000000000
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: df
-
-Encoding: e0
-U-Bits: 000000000011101010110111000101110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: e0
-
-Encoding: e0
-U-Bits: 000000000011101001011110101100110000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: e0
-
-Encoding: e0
-U-Bits: 000000000011101001100111101011001100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: e0
-
-Encoding: e1
-U-Bits: 110100111111101010001101100101000011
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: e1
-
-Encoding: e1
-U-Bits: 110100111111101001100100001100000000
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: e1
-
-Encoding: e1
-U-Bits: 110100111111101001011101001011111100
-S-Bits: 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: e1
-
-Encoding: e2
-U-Bits: 001101001100101010111001101101111111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81
-Decoded: e2
-
-Encoding: e2
-U-Bits: 001101001100101001010000000100111100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: e2
-
-Encoding: e2
-U-Bits: 001101001100101001101001000011000000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: e2
-
-Encoding: e3
-U-Bits: 111001110000101010000011001101001111
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: e3
-
-Encoding: e3
-U-Bits: 111001110000101001101010100100001100
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: e3
-
-Encoding: e3
-U-Bits: 111001110000101001010011100011110000
-S-Bits: 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: e3
-
-Encoding: e4
-U-Bits: 000011010000011010110100101111110000
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: e4
-
-Encoding: e4
-U-Bits: 000011010000011001011101000110110011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: e4
-
-Encoding: e4
-U-Bits: 000011010000011001100100000001001111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: e4
-
-Encoding: e5
-U-Bits: 110111101100011010001110001111000000
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: e5
-
-Encoding: e5
-U-Bits: 110111101100011001100111100110000011
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: e5
-
-Encoding: e5
-U-Bits: 110111101100011001011110100001111111
-S-Bits: 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: e5
-
-Encoding: e6
-U-Bits: 001110011111011010111010000111111100
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: e6
-
-Encoding: e6
-U-Bits: 001110011111011001010011101110111111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: e6
-
-Encoding: e6
-U-Bits: 001110011111011001101010101001000011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: e6
-
-Encoding: e7
-U-Bits: 111010100011011010000000100111001100
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: e7
-
-Encoding: e7
-U-Bits: 111010100011011001101001001110001111
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: e7
-
-Encoding: e7
-U-Bits: 111010100011011001010000001001110011
-S-Bits: 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: e7
-
-Encoding: e8
-U-Bits: 000000110111010101010011011010000011
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: e8
-
-Encoding: e8
-U-Bits: 000000110111010110111010110011000000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: e8
-
-Encoding: e8
-U-Bits: 000000110111010110000011110100111100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: e8
-
-Encoding: e9
-U-Bits: 110100001011010101101001111010110011
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: e9
-
-Encoding: e9
-U-Bits: 110100001011010110000000010011110000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: e9
-
-Encoding: e9
-U-Bits: 110100001011010110111001010100001100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: e9
-
-Encoding: ea
-U-Bits: 001101111000010101011101110010001111
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: ea
-
-Encoding: ea
-U-Bits: 001101111000010110110100011011001100
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: ea
-
-Encoding: ea
-U-Bits: 001101111000010110001101011100110000
-S-Bits: 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: ea
-
-Encoding: eb
-U-Bits: 111001000100010101100111010010111111
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: eb
-
-Encoding: eb
-U-Bits: 111001000100010110001110111011111100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: eb
-
-Encoding: eb
-U-Bits: 111001000100010110110111111100000000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: eb
-
-Encoding: ec
-U-Bits: 000011100100100101010000110000000000
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: ec
-
-Encoding: ec
-U-Bits: 000011100100100110111001011001000011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: ec
-
-Encoding: ec
-U-Bits: 000011100100100110000000011110111111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: ec
-
-Encoding: ed
-U-Bits: 110111011000100101101010010000110000
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: ed
-
-Encoding: ed
-U-Bits: 110111011000100110000011111001110011
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: ed
-
-Encoding: ed
-U-Bits: 110111011000100110111010111110001111
-S-Bits: 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: ed
-
-Encoding: ee
-U-Bits: 001110101011100101011110011000001100
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: ee
-
-Encoding: ee
-U-Bits: 001110101011100110110111110001001111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: ee
-
-Encoding: ee
-U-Bits: 001110101011100110001110110110110011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: ee
-
-Encoding: ef
-U-Bits: 111010010111100101100100111000111100
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: ef
-
-Encoding: ef
-U-Bits: 111010010111100110001101010001111111
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: ef
-
-Encoding: ef
-U-Bits: 111010010111100110110100010110000011
-S-Bits: 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: ef
-
-Encoding: f0
-U-Bits: 000000001110100101001110000010001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: f0
-
-Encoding: f0
-U-Bits: 000000001110100110100111101011001100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: f0
-
-Encoding: f0
-U-Bits: 000000001110100110011110101100110000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: f0
-
-Encoding: f1
-U-Bits: 110100110010100101110100100010111111
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81
-Decoded: f1
-
-Encoding: f1
-U-Bits: 110100110010100110011101001011111100
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: f1
-
-Encoding: f1
-U-Bits: 110100110010100110100100001100000000
-S-Bits: 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: f1
-
-Encoding: f2
-U-Bits: 001101000001100101000000101010000011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81
-Decoded: f2
-
-Encoding: f2
-U-Bits: 001101000001100110101001000011000000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: f2
-
-Encoding: f2
-U-Bits: 001101000001100110010000000100111100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: f2
-
-Encoding: f3
-U-Bits: 111001111101100101111010001010110011
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81
-Decoded: f3
-
-Encoding: f3
-U-Bits: 111001111101100110010011100011110000
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: f3
-
-Encoding: f3
-U-Bits: 111001111101100110101010100100001100
-S-Bits: 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: f3
-
-Encoding: f4
-U-Bits: 000011011101010101001101101000001100
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f
-Decoded: f4
-
-Encoding: f4
-U-Bits: 000011011101010110100100000001001111
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: f4
-
-Encoding: f4
-U-Bits: 000011011101010110011101000110110011
-S-Bits: 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: f4
-
-Encoding: f5
-U-Bits: 110111100001010101110111001000111100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f
-Decoded: f5
-
-Encoding: f5
-U-Bits: 110111100001010110011110100001111111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: f5
-
-Encoding: f5
-U-Bits: 110111100001010110100111100110000011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: f5
-
-Encoding: f6
-U-Bits: 001110010010010101000011000000000000
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: f6
-
-Encoding: f6
-U-Bits: 001110010010010110101010101001000011
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: f6
-
-Encoding: f6
-U-Bits: 001110010010010110010011101110111111
-S-Bits: 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81
-Decoded: f6
-
-Encoding: f7
-U-Bits: 111010101110010101111001100000110000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f
-Decoded: f7
-
-Encoding: f7
-U-Bits: 111010101110010110010000001001110011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: f7
-
-Encoding: f7
-U-Bits: 111010101110010110101001001110001111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: f7
-
-Encoding: f8
-U-Bits: 000000111010011010101010011101111111
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81
-Decoded: f8
-
-Encoding: f8
-U-Bits: 000000111010011001000011110100111100
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f
-Decoded: f8
-
-Encoding: f8
-U-Bits: 000000111010011001111010110011000000
-S-Bits: 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f
-Decoded: f8
-
-Encoding: f9
-U-Bits: 110100000110011010010000111101001111
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81
-Decoded: f9
-
-Encoding: f9
-U-Bits: 110100000110011001111001010100001100
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f
-Decoded: f9
-
-Encoding: f9
-U-Bits: 110100000110011001000000010011110000
-S-Bits: 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f
-Decoded: f9
-
-Encoding: fa
-U-Bits: 001101110101011010100100110101110011
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81
-Decoded: fa
-
-Encoding: fa
-U-Bits: 001101110101011001001101011100110000
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f
-Decoded: fa
-
-Encoding: fa
-U-Bits: 001101110101011001110100011011001100
-S-Bits: 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f
-Decoded: fa
-
-Encoding: fb
-U-Bits: 111001001001011010011110010101000011
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81
-Decoded: fb
-
-Encoding: fb
-U-Bits: 111001001001011001110111111100000000
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: fb
-
-Encoding: fb
-U-Bits: 111001001001011001001110111011111100
-S-Bits: 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: fb
-
-Encoding: fc
-U-Bits: 000011101001101010101001110111111100
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f
-Decoded: fc
-
-Encoding: fc
-U-Bits: 000011101001101001000000011110111111
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81
-Decoded: fc
-
-Encoding: fc
-U-Bits: 000011101001101001111001011001000011
-S-Bits: 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81
-Decoded: fc
-
-Encoding: fd
-U-Bits: 110111010101101010010011010111001100
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f
-Decoded: fd
-
-Encoding: fd
-U-Bits: 110111010101101001111010111110001111
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81
-Decoded: fd
-
-Encoding: fd
-U-Bits: 110111010101101001000011111001110011
-S-Bits: 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: fd
-
-Encoding: fe
-U-Bits: 001110100110101010100111011111110000
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f
-Decoded: fe
-
-Encoding: fe
-U-Bits: 001110100110101001001110110110110011
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81
-Decoded: fe
-
-Encoding: fe
-U-Bits: 001110100110101001110111110001001111
-S-Bits: 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81
-Decoded: fe
-
-Encoding: ff
-U-Bits: 111010011010101010011101111111000000
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f
-Decoded: ff
-
-Encoding: ff
-U-Bits: 111010011010101001110100010110000011
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81
-Decoded: ff
-
-Encoding: ff
-U-Bits: 111010011010101001001101010001111111
-S-Bits: 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81
-Decoded: ff
-
-Encoding: 00
-U-Bits: 000000000000000000000000000000000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 00
-
-Encoding: 00
-U-Bits: 000000000000000000011101001101001001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 00
-
-Encoding: 00
-U-Bits: 000000000000000000011010000101110111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 00
-
-Encoding: 01
-U-Bits: 110111100000000000011101001100100010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 01
-
-Encoding: 01
-U-Bits: 110111100000000000000000000001101011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 01
-
-Encoding: 01
-U-Bits: 110111100000000000000111001001010101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 01
-
-Encoding: 02
-U-Bits: 010001111000000000000111010011001001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 02
-
-Encoding: 02
-U-Bits: 010001111000000000011010011110000000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 02
-
-Encoding: 02
-U-Bits: 010001111000000000011101010110111110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 02
-
-Encoding: 03
-U-Bits: 100110011000000000011010011111101011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 03
-
-Encoding: 03
-U-Bits: 100110011000000000000111010010100010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 03
-
-Encoding: 03
-U-Bits: 100110011000000000000000011010011100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 03
-
-Encoding: 04
-U-Bits: 001010011110000000011101010000011000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 04
-
-Encoding: 04
-U-Bits: 001010011110000000000000011101010001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 04
-
-Encoding: 04
-U-Bits: 001010011110000000000111010101101111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 04
-
-Encoding: 05
-U-Bits: 111101111110000000000000011100111010
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 05
-
-Encoding: 05
-U-Bits: 111101111110000000011101010001110011
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 05
-
-Encoding: 05
-U-Bits: 111101111110000000011010011001001101
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 05
-
-Encoding: 06
-U-Bits: 011011100110000000011010000011010001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 06
-
-Encoding: 06
-U-Bits: 011011100110000000000111001110011000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 06
-
-Encoding: 06
-U-Bits: 011011100110000000000000000110100110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 06
-
-Encoding: 07
-U-Bits: 101100000110000000000111001111110011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 07
-
-Encoding: 07
-U-Bits: 101100000110000000011010000010111010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 07
-
-Encoding: 07
-U-Bits: 101100000110000000011101001010000100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 07
-
-Encoding: 08
-U-Bits: 000110100111100000000111010100000100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 08
-
-Encoding: 08
-U-Bits: 000110100111100000011010011001001101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 08
-
-Encoding: 08
-U-Bits: 000110100111100000011101010001110011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 08
-
-Encoding: 09
-U-Bits: 110001000111100000011010011000100110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 09
-
-Encoding: 09
-U-Bits: 110001000111100000000111010101101111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 09
-
-Encoding: 09
-U-Bits: 110001000111100000000000011101010001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 09
-
-Encoding: 0a
-U-Bits: 010111011111100000000000000111001101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 0a
-
-Encoding: 0a
-U-Bits: 010111011111100000011101001010000100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 0a
-
-Encoding: 0a
-U-Bits: 010111011111100000011010000010111010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 0a
-
-Encoding: 0b
-U-Bits: 100000111111100000011101001011101111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 0b
-
-Encoding: 0b
-U-Bits: 100000111111100000000000000110100110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 0b
-
-Encoding: 0b
-U-Bits: 100000111111100000000111001110011000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 0b
-
-Encoding: 0c
-U-Bits: 001100111001100000011010000100011100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 0c
-
-Encoding: 0c
-U-Bits: 001100111001100000000111001001010101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 0c
-
-Encoding: 0c
-U-Bits: 001100111001100000000000000001101011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 0c
-
-Encoding: 0d
-U-Bits: 111011011001100000000111001000111110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 0d
-
-Encoding: 0d
-U-Bits: 111011011001100000011010000101110111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 0d
-
-Encoding: 0d
-U-Bits: 111011011001100000011101001101001001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 0d
-
-Encoding: 0e
-U-Bits: 011101000001100000011101010111010101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 0e
-
-Encoding: 0e
-U-Bits: 011101000001100000000000011010011100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 0e
-
-Encoding: 0e
-U-Bits: 011101000001100000000111010010100010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 0e
-
-Encoding: 0f
-U-Bits: 101010100001100000000000011011110111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 0f
-
-Encoding: 0f
-U-Bits: 101010100001100000011101010110111110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 0f
-
-Encoding: 0f
-U-Bits: 101010100001100000011010011110000000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 0f
-
-Encoding: 10
-U-Bits: 000001101001111000000001110101000010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 10
-
-Encoding: 10
-U-Bits: 000001101001111000011100111000001011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 10
-
-Encoding: 10
-U-Bits: 000001101001111000011011110000110101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 10
-
-Encoding: 11
-U-Bits: 110110001001111000011100111001100000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 11
-
-Encoding: 11
-U-Bits: 110110001001111000000001110100101001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 11
-
-Encoding: 11
-U-Bits: 110110001001111000000110111100010111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 11
-
-Encoding: 12
-U-Bits: 010000010001111000000110100110001011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 12
-
-Encoding: 12
-U-Bits: 010000010001111000011011101011000010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 12
-
-Encoding: 12
-U-Bits: 010000010001111000011100100011111100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 12
-
-Encoding: 13
-U-Bits: 100111110001111000011011101010101001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 13
-
-Encoding: 13
-U-Bits: 100111110001111000000110100111100000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 13
-
-Encoding: 13
-U-Bits: 100111110001111000000001101111011110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 13
-
-Encoding: 14
-U-Bits: 001011110111111000011100100101011010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 14
-
-Encoding: 14
-U-Bits: 001011110111111000000001101000010011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 14
-
-Encoding: 14
-U-Bits: 001011110111111000000110100000101101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 14
-
-Encoding: 15
-U-Bits: 111100010111111000000001101001111000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 15
-
-Encoding: 15
-U-Bits: 111100010111111000011100100100110001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 15
-
-Encoding: 15
-U-Bits: 111100010111111000011011101100001111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 15
-
-Encoding: 16
-U-Bits: 011010001111111000011011110110010011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 16
-
-Encoding: 16
-U-Bits: 011010001111111000000110111011011010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 16
-
-Encoding: 16
-U-Bits: 011010001111111000000001110011100100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 16
-
-Encoding: 17
-U-Bits: 101101101111111000000110111010110001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 17
-
-Encoding: 17
-U-Bits: 101101101111111000011011110111111000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 17
-
-Encoding: 17
-U-Bits: 101101101111111000011100111111000110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 17
-
-Encoding: 18
-U-Bits: 000111001110011000000110100001000110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 18
-
-Encoding: 18
-U-Bits: 000111001110011000011011101100001111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 18
-
-Encoding: 18
-U-Bits: 000111001110011000011100100100110001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 18
-
-Encoding: 19
-U-Bits: 110000101110011000011011101101100100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 19
-
-Encoding: 19
-U-Bits: 110000101110011000000110100000101101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 19
-
-Encoding: 19
-U-Bits: 110000101110011000000001101000010011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 19
-
-Encoding: 1a
-U-Bits: 010110110110011000000001110010001111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 1a
-
-Encoding: 1a
-U-Bits: 010110110110011000011100111111000110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 1a
-
-Encoding: 1a
-U-Bits: 010110110110011000011011110111111000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 1a
-
-Encoding: 1b
-U-Bits: 100001010110011000011100111110101101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 1b
-
-Encoding: 1b
-U-Bits: 100001010110011000000001110011100100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 1b
-
-Encoding: 1b
-U-Bits: 100001010110011000000110111011011010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 1b
-
-Encoding: 1c
-U-Bits: 001101010000011000011011110001011110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 1c
-
-Encoding: 1c
-U-Bits: 001101010000011000000110111100010111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 1c
-
-Encoding: 1c
-U-Bits: 001101010000011000000001110100101001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 1c
-
-Encoding: 1d
-U-Bits: 111010110000011000000110111101111100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 1d
-
-Encoding: 1d
-U-Bits: 111010110000011000011011110000110101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 1d
-
-Encoding: 1d
-U-Bits: 111010110000011000011100111000001011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 1d
-
-Encoding: 1e
-U-Bits: 011100101000011000011100100010010111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 1e
-
-Encoding: 1e
-U-Bits: 011100101000011000000001101111011110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 1e
-
-Encoding: 1e
-U-Bits: 011100101000011000000110100111100000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 1e
-
-Encoding: 1f
-U-Bits: 101011001000011000000001101110110101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 1f
-
-Encoding: 1f
-U-Bits: 101011001000011000011100100011111100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 1f
-
-Encoding: 1f
-U-Bits: 101011001000011000011011101011000010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 1f
-
-Encoding: 20
-U-Bits: 000000011010011110000000011101010001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 20
-
-Encoding: 20
-U-Bits: 000000011010011110011101010000011000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 20
-
-Encoding: 20
-U-Bits: 000000011010011110011010011000100110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 20
-
-Encoding: 21
-U-Bits: 110111111010011110011101010001110011
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 21
-
-Encoding: 21
-U-Bits: 110111111010011110000000011100111010
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 21
-
-Encoding: 21
-U-Bits: 110111111010011110000111010100000100
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 21
-
-Encoding: 22
-U-Bits: 010001100010011110000111001110011000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 22
-
-Encoding: 22
-U-Bits: 010001100010011110011010000011010001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 22
-
-Encoding: 22
-U-Bits: 010001100010011110011101001011101111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 22
-
-Encoding: 23
-U-Bits: 100110000010011110011010000010111010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 23
-
-Encoding: 23
-U-Bits: 100110000010011110000111001111110011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 23
-
-Encoding: 23
-U-Bits: 100110000010011110000000000111001101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 23
-
-Encoding: 24
-U-Bits: 001010000100011110011101001101001001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 24
-
-Encoding: 24
-U-Bits: 001010000100011110000000000000000000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 24
-
-Encoding: 24
-U-Bits: 001010000100011110000111001000111110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 24
-
-Encoding: 25
-U-Bits: 111101100100011110000000000001101011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 25
-
-Encoding: 25
-U-Bits: 111101100100011110011101001100100010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 25
-
-Encoding: 25
-U-Bits: 111101100100011110011010000100011100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 25
-
-Encoding: 26
-U-Bits: 011011111100011110011010011110000000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 26
-
-Encoding: 26
-U-Bits: 011011111100011110000111010011001001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 26
-
-Encoding: 26
-U-Bits: 011011111100011110000000011011110111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 26
-
-Encoding: 27
-U-Bits: 101100011100011110000111010010100010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 27
-
-Encoding: 27
-U-Bits: 101100011100011110011010011111101011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 27
-
-Encoding: 27
-U-Bits: 101100011100011110011101010111010101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 27
-
-Encoding: 28
-U-Bits: 000110111101111110000111001001010101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 28
-
-Encoding: 28
-U-Bits: 000110111101111110011010000100011100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 28
-
-Encoding: 28
-U-Bits: 000110111101111110011101001100100010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 28
-
-Encoding: 29
-U-Bits: 110001011101111110011010000101110111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 29
-
-Encoding: 29
-U-Bits: 110001011101111110000111001000111110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 29
-
-Encoding: 29
-U-Bits: 110001011101111110000000000000000000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 29
-
-Encoding: 2a
-U-Bits: 010111000101111110000000011010011100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 2a
-
-Encoding: 2a
-U-Bits: 010111000101111110011101010111010101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 2a
-
-Encoding: 2a
-U-Bits: 010111000101111110011010011111101011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 2a
-
-Encoding: 2b
-U-Bits: 100000100101111110011101010110111110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 2b
-
-Encoding: 2b
-U-Bits: 100000100101111110000000011011110111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 2b
-
-Encoding: 2b
-U-Bits: 100000100101111110000111010011001001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 2b
-
-Encoding: 2c
-U-Bits: 001100100011111110011010011001001101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 2c
-
-Encoding: 2c
-U-Bits: 001100100011111110000111010100000100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 2c
-
-Encoding: 2c
-U-Bits: 001100100011111110000000011100111010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 2c
-
-Encoding: 2d
-U-Bits: 111011000011111110000111010101101111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 2d
-
-Encoding: 2d
-U-Bits: 111011000011111110011010011000100110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 2d
-
-Encoding: 2d
-U-Bits: 111011000011111110011101010000011000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 2d
-
-Encoding: 2e
-U-Bits: 011101011011111110011101001010000100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 2e
-
-Encoding: 2e
-U-Bits: 011101011011111110000000000111001101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 2e
-
-Encoding: 2e
-U-Bits: 011101011011111110000111001111110011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 2e
-
-Encoding: 2f
-U-Bits: 101010111011111110000000000110100110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 2f
-
-Encoding: 2f
-U-Bits: 101010111011111110011101001011101111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 2f
-
-Encoding: 2f
-U-Bits: 101010111011111110011010000011010001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 2f
-
-Encoding: 30
-U-Bits: 000001110011100110000001101000010011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 30
-
-Encoding: 30
-U-Bits: 000001110011100110011100100101011010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 30
-
-Encoding: 30
-U-Bits: 000001110011100110011011101101100100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 30
-
-Encoding: 31
-U-Bits: 110110010011100110011100100100110001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 31
-
-Encoding: 31
-U-Bits: 110110010011100110000001101001111000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 31
-
-Encoding: 31
-U-Bits: 110110010011100110000110100001000110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 31
-
-Encoding: 32
-U-Bits: 010000001011100110000110111011011010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 32
-
-Encoding: 32
-U-Bits: 010000001011100110011011110110010011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 32
-
-Encoding: 32
-U-Bits: 010000001011100110011100111110101101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 32
-
-Encoding: 33
-U-Bits: 100111101011100110011011110111111000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 33
-
-Encoding: 33
-U-Bits: 100111101011100110000110111010110001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 33
-
-Encoding: 33
-U-Bits: 100111101011100110000001110010001111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 33
-
-Encoding: 34
-U-Bits: 001011101101100110011100111000001011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 34
-
-Encoding: 34
-U-Bits: 001011101101100110000001110101000010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 34
-
-Encoding: 34
-U-Bits: 001011101101100110000110111101111100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 34
-
-Encoding: 35
-U-Bits: 111100001101100110000001110100101001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 35
-
-Encoding: 35
-U-Bits: 111100001101100110011100111001100000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 35
-
-Encoding: 35
-U-Bits: 111100001101100110011011110001011110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 35
-
-Encoding: 36
-U-Bits: 011010010101100110011011101011000010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 36
-
-Encoding: 36
-U-Bits: 011010010101100110000110100110001011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 36
-
-Encoding: 36
-U-Bits: 011010010101100110000001101110110101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 36
-
-Encoding: 37
-U-Bits: 101101110101100110000110100111100000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 37
-
-Encoding: 37
-U-Bits: 101101110101100110011011101010101001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 37
-
-Encoding: 37
-U-Bits: 101101110101100110011100100010010111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 37
-
-Encoding: 38
-U-Bits: 000111010100000110000110111100010111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 38
-
-Encoding: 38
-U-Bits: 000111010100000110011011110001011110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 38
-
-Encoding: 38
-U-Bits: 000111010100000110011100111001100000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 38
-
-Encoding: 39
-U-Bits: 110000110100000110011011110000110101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 39
-
-Encoding: 39
-U-Bits: 110000110100000110000110111101111100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 39
-
-Encoding: 39
-U-Bits: 110000110100000110000001110101000010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 39
-
-Encoding: 3a
-U-Bits: 010110101100000110000001101111011110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 3a
-
-Encoding: 3a
-U-Bits: 010110101100000110011100100010010111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 3a
-
-Encoding: 3a
-U-Bits: 010110101100000110011011101010101001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 3a
-
-Encoding: 3b
-U-Bits: 100001001100000110011100100011111100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 3b
-
-Encoding: 3b
-U-Bits: 100001001100000110000001101110110101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 3b
-
-Encoding: 3b
-U-Bits: 100001001100000110000110100110001011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 3b
-
-Encoding: 3c
-U-Bits: 001101001010000110011011101100001111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 3c
-
-Encoding: 3c
-U-Bits: 001101001010000110000110100001000110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 3c
-
-Encoding: 3c
-U-Bits: 001101001010000110000001101001111000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 3c
-
-Encoding: 3d
-U-Bits: 111010101010000110000110100000101101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 3d
-
-Encoding: 3d
-U-Bits: 111010101010000110011011101101100100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 3d
-
-Encoding: 3d
-U-Bits: 111010101010000110011100100101011010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 3d
-
-Encoding: 3e
-U-Bits: 011100110010000110011100111111000110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 3e
-
-Encoding: 3e
-U-Bits: 011100110010000110000001110010001111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 3e
-
-Encoding: 3e
-U-Bits: 011100110010000110000110111010110001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 3e
-
-Encoding: 3f
-U-Bits: 101011010010000110000001110011100100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 3f
-
-Encoding: 3f
-U-Bits: 101011010010000110011100111110101101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 3f
-
-Encoding: 3f
-U-Bits: 101011010010000110011011110110010011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 3f
-
-Encoding: 40
-U-Bits: 000000000110100111111100100011111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 40
-
-Encoding: 40
-U-Bits: 000000000110100111100001101110110101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 40
-
-Encoding: 40
-U-Bits: 000000000110100111100110100110001011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 40
-
-Encoding: 41
-U-Bits: 110111100110100111100001101111011110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 41
-
-Encoding: 41
-U-Bits: 110111100110100111111100100010010111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 41
-
-Encoding: 41
-U-Bits: 110111100110100111111011101010101001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 41
-
-Encoding: 42
-U-Bits: 010001111110100111111011110000110101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 42
-
-Encoding: 42
-U-Bits: 010001111110100111100110111101111100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 42
-
-Encoding: 42
-U-Bits: 010001111110100111100001110101000010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 42
-
-Encoding: 43
-U-Bits: 100110011110100111100110111100010111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 43
-
-Encoding: 43
-U-Bits: 100110011110100111111011110001011110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 43
-
-Encoding: 43
-U-Bits: 100110011110100111111100111001100000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 43
-
-Encoding: 44
-U-Bits: 001010011000100111100001110011100100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 44
-
-Encoding: 44
-U-Bits: 001010011000100111111100111110101101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 44
-
-Encoding: 44
-U-Bits: 001010011000100111111011110110010011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 44
-
-Encoding: 45
-U-Bits: 111101111000100111111100111111000110
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 45
-
-Encoding: 45
-U-Bits: 111101111000100111100001110010001111
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 45
-
-Encoding: 45
-U-Bits: 111101111000100111100110111010110001
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 45
-
-Encoding: 46
-U-Bits: 011011100000100111100110100000101101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 46
-
-Encoding: 46
-U-Bits: 011011100000100111111011101101100100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 46
-
-Encoding: 46
-U-Bits: 011011100000100111111100100101011010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 46
-
-Encoding: 47
-U-Bits: 101100000000100111111011101100001111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 47
-
-Encoding: 47
-U-Bits: 101100000000100111100110100001000110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 47
-
-Encoding: 47
-U-Bits: 101100000000100111100001101001111000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 47
-
-Encoding: 48
-U-Bits: 000110100001000111111011110111111000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 48
-
-Encoding: 48
-U-Bits: 000110100001000111100110111010110001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 48
-
-Encoding: 48
-U-Bits: 000110100001000111100001110010001111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 48
-
-Encoding: 49
-U-Bits: 110001000001000111100110111011011010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 49
-
-Encoding: 49
-U-Bits: 110001000001000111111011110110010011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 49
-
-Encoding: 49
-U-Bits: 110001000001000111111100111110101101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 49
-
-Encoding: 4a
-U-Bits: 010111011001000111111100100100110001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 4a
-
-Encoding: 4a
-U-Bits: 010111011001000111100001101001111000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 4a
-
-Encoding: 4a
-U-Bits: 010111011001000111100110100001000110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 4a
-
-Encoding: 4b
-U-Bits: 100000111001000111100001101000010011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 4b
-
-Encoding: 4b
-U-Bits: 100000111001000111111100100101011010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 4b
-
-Encoding: 4b
-U-Bits: 100000111001000111111011101101100100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 4b
-
-Encoding: 4c
-U-Bits: 001100111111000111100110100111100000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 4c
-
-Encoding: 4c
-U-Bits: 001100111111000111111011101010101001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 4c
-
-Encoding: 4c
-U-Bits: 001100111111000111111100100010010111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 4c
-
-Encoding: 4d
-U-Bits: 111011011111000111111011101011000010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 4d
-
-Encoding: 4d
-U-Bits: 111011011111000111100110100110001011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 4d
-
-Encoding: 4d
-U-Bits: 111011011111000111100001101110110101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 4d
-
-Encoding: 4e
-U-Bits: 011101000111000111100001110100101001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 4e
-
-Encoding: 4e
-U-Bits: 011101000111000111111100111001100000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 4e
-
-Encoding: 4e
-U-Bits: 011101000111000111111011110001011110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 4e
-
-Encoding: 4f
-U-Bits: 101010100111000111111100111000001011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 4f
-
-Encoding: 4f
-U-Bits: 101010100111000111100001110101000010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 4f
-
-Encoding: 4f
-U-Bits: 101010100111000111100110111101111100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 4f
-
-Encoding: 50
-U-Bits: 000001101111011111111101010110111110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 50
-
-Encoding: 50
-U-Bits: 000001101111011111100000011011110111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 50
-
-Encoding: 50
-U-Bits: 000001101111011111100111010011001001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 50
-
-Encoding: 51
-U-Bits: 110110001111011111100000011010011100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 51
-
-Encoding: 51
-U-Bits: 110110001111011111111101010111010101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 51
-
-Encoding: 51
-U-Bits: 110110001111011111111010011111101011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 51
-
-Encoding: 52
-U-Bits: 010000010111011111111010000101110111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 52
-
-Encoding: 52
-U-Bits: 010000010111011111100111001000111110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 52
-
-Encoding: 52
-U-Bits: 010000010111011111100000000000000000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 52
-
-Encoding: 53
-U-Bits: 100111110111011111100111001001010101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 53
-
-Encoding: 53
-U-Bits: 100111110111011111111010000100011100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 53
-
-Encoding: 53
-U-Bits: 100111110111011111111101001100100010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 53
-
-Encoding: 54
-U-Bits: 001011110001011111100000000110100110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 54
-
-Encoding: 54
-U-Bits: 001011110001011111111101001011101111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 54
-
-Encoding: 54
-U-Bits: 001011110001011111111010000011010001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 54
-
-Encoding: 55
-U-Bits: 111100010001011111111101001010000100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 55
-
-Encoding: 55
-U-Bits: 111100010001011111100000000111001101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 55
-
-Encoding: 55
-U-Bits: 111100010001011111100111001111110011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 55
-
-Encoding: 56
-U-Bits: 011010001001011111100111010101101111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 56
-
-Encoding: 56
-U-Bits: 011010001001011111111010011000100110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 56
-
-Encoding: 56
-U-Bits: 011010001001011111111101010000011000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 56
-
-Encoding: 57
-U-Bits: 101101101001011111111010011001001101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 57
-
-Encoding: 57
-U-Bits: 101101101001011111100111010100000100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 57
-
-Encoding: 57
-U-Bits: 101101101001011111100000011100111010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 57
-
-Encoding: 58
-U-Bits: 000111001000111111111010000010111010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 58
-
-Encoding: 58
-U-Bits: 000111001000111111100111001111110011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 58
-
-Encoding: 58
-U-Bits: 000111001000111111100000000111001101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 58
-
-Encoding: 59
-U-Bits: 110000101000111111100111001110011000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 59
-
-Encoding: 59
-U-Bits: 110000101000111111111010000011010001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 59
-
-Encoding: 59
-U-Bits: 110000101000111111111101001011101111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 59
-
-Encoding: 5a
-U-Bits: 010110110000111111111101010001110011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 5a
-
-Encoding: 5a
-U-Bits: 010110110000111111100000011100111010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 5a
-
-Encoding: 5a
-U-Bits: 010110110000111111100111010100000100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 5a
-
-Encoding: 5b
-U-Bits: 100001010000111111100000011101010001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 5b
-
-Encoding: 5b
-U-Bits: 100001010000111111111101010000011000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 5b
-
-Encoding: 5b
-U-Bits: 100001010000111111111010011000100110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 5b
-
-Encoding: 5c
-U-Bits: 001101010110111111100111010010100010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 5c
-
-Encoding: 5c
-U-Bits: 001101010110111111111010011111101011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 5c
-
-Encoding: 5c
-U-Bits: 001101010110111111111101010111010101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 5c
-
-Encoding: 5d
-U-Bits: 111010110110111111111010011110000000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5d
-
-Encoding: 5d
-U-Bits: 111010110110111111100111010011001001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 5d
-
-Encoding: 5d
-U-Bits: 111010110110111111100000011011110111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 5d
-
-Encoding: 5e
-U-Bits: 011100101110111111100000000001101011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 5e
-
-Encoding: 5e
-U-Bits: 011100101110111111111101001100100010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 5e
-
-Encoding: 5e
-U-Bits: 011100101110111111111010000100011100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 5e
-
-Encoding: 5f
-U-Bits: 101011001110111111111101001101001001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 5f
-
-Encoding: 5f
-U-Bits: 101011001110111111100000000000000000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5f
-
-Encoding: 5f
-U-Bits: 101011001110111111100111001000111110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 5f
-
-Encoding: 60
-U-Bits: 000000011100111001111100111110101101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 60
-
-Encoding: 60
-U-Bits: 000000011100111001100001110011100100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 60
-
-Encoding: 60
-U-Bits: 000000011100111001100110111011011010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 60
-
-Encoding: 61
-U-Bits: 110111111100111001100001110010001111
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 61
-
-Encoding: 61
-U-Bits: 110111111100111001111100111111000110
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 61
-
-Encoding: 61
-U-Bits: 110111111100111001111011110111111000
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 61
-
-Encoding: 62
-U-Bits: 010001100100111001111011101101100100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 62
-
-Encoding: 62
-U-Bits: 010001100100111001100110100000101101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 62
-
-Encoding: 62
-U-Bits: 010001100100111001100001101000010011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 62
-
-Encoding: 63
-U-Bits: 100110000100111001100110100001000110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 63
-
-Encoding: 63
-U-Bits: 100110000100111001111011101100001111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 63
-
-Encoding: 63
-U-Bits: 100110000100111001111100100100110001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 63
-
-Encoding: 64
-U-Bits: 001010000010111001100001101110110101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 64
-
-Encoding: 64
-U-Bits: 001010000010111001111100100011111100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 64
-
-Encoding: 64
-U-Bits: 001010000010111001111011101011000010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 64
-
-Encoding: 65
-U-Bits: 111101100010111001111100100010010111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 65
-
-Encoding: 65
-U-Bits: 111101100010111001100001101111011110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 65
-
-Encoding: 65
-U-Bits: 111101100010111001100110100111100000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 65
-
-Encoding: 66
-U-Bits: 011011111010111001100110111101111100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 66
-
-Encoding: 66
-U-Bits: 011011111010111001111011110000110101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 66
-
-Encoding: 66
-U-Bits: 011011111010111001111100111000001011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 66
-
-Encoding: 67
-U-Bits: 101100011010111001111011110001011110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 67
-
-Encoding: 67
-U-Bits: 101100011010111001100110111100010111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 67
-
-Encoding: 67
-U-Bits: 101100011010111001100001110100101001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 67
-
-Encoding: 68
-U-Bits: 000110111011011001111011101010101001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 68
-
-Encoding: 68
-U-Bits: 000110111011011001100110100111100000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 68
-
-Encoding: 68
-U-Bits: 000110111011011001100001101111011110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 68
-
-Encoding: 69
-U-Bits: 110001011011011001100110100110001011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 69
-
-Encoding: 69
-U-Bits: 110001011011011001111011101011000010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 69
-
-Encoding: 69
-U-Bits: 110001011011011001111100100011111100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 69
-
-Encoding: 6a
-U-Bits: 010111000011011001111100111001100000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 6a
-
-Encoding: 6a
-U-Bits: 010111000011011001100001110100101001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 6a
-
-Encoding: 6a
-U-Bits: 010111000011011001100110111100010111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 6a
-
-Encoding: 6b
-U-Bits: 100000100011011001100001110101000010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 6b
-
-Encoding: 6b
-U-Bits: 100000100011011001111100111000001011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 6b
-
-Encoding: 6b
-U-Bits: 100000100011011001111011110000110101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 6b
-
-Encoding: 6c
-U-Bits: 001100100101011001100110111010110001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 6c
-
-Encoding: 6c
-U-Bits: 001100100101011001111011110111111000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 6c
-
-Encoding: 6c
-U-Bits: 001100100101011001111100111111000110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 6c
-
-Encoding: 6d
-U-Bits: 111011000101011001111011110110010011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 6d
-
-Encoding: 6d
-U-Bits: 111011000101011001100110111011011010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 6d
-
-Encoding: 6d
-U-Bits: 111011000101011001100001110011100100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 6d
-
-Encoding: 6e
-U-Bits: 011101011101011001100001101001111000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 6e
-
-Encoding: 6e
-U-Bits: 011101011101011001111100100100110001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 6e
-
-Encoding: 6e
-U-Bits: 011101011101011001111011101100001111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 6e
-
-Encoding: 6f
-U-Bits: 101010111101011001111100100101011010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 6f
-
-Encoding: 6f
-U-Bits: 101010111101011001100001101000010011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 6f
-
-Encoding: 6f
-U-Bits: 101010111101011001100110100000101101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 6f
-
-Encoding: 70
-U-Bits: 000001110101000001111101001011101111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 70
-
-Encoding: 70
-U-Bits: 000001110101000001100000000110100110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 70
-
-Encoding: 70
-U-Bits: 000001110101000001100111001110011000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 70
-
-Encoding: 71
-U-Bits: 110110010101000001100000000111001101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 71
-
-Encoding: 71
-U-Bits: 110110010101000001111101001010000100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 71
-
-Encoding: 71
-U-Bits: 110110010101000001111010000010111010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 71
-
-Encoding: 72
-U-Bits: 010000001101000001111010011000100110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 72
-
-Encoding: 72
-U-Bits: 010000001101000001100111010101101111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 72
-
-Encoding: 72
-U-Bits: 010000001101000001100000011101010001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 72
-
-Encoding: 73
-U-Bits: 100111101101000001100111010100000100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 73
-
-Encoding: 73
-U-Bits: 100111101101000001111010011001001101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 73
-
-Encoding: 73
-U-Bits: 100111101101000001111101010001110011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 73
-
-Encoding: 74
-U-Bits: 001011101011000001100000011011110111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 74
-
-Encoding: 74
-U-Bits: 001011101011000001111101010110111110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 74
-
-Encoding: 74
-U-Bits: 001011101011000001111010011110000000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 74
-
-Encoding: 75
-U-Bits: 111100001011000001111101010111010101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 75
-
-Encoding: 75
-U-Bits: 111100001011000001100000011010011100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 75
-
-Encoding: 75
-U-Bits: 111100001011000001100111010010100010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 75
-
-Encoding: 76
-U-Bits: 011010010011000001100111001000111110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 76
-
-Encoding: 76
-U-Bits: 011010010011000001111010000101110111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 76
-
-Encoding: 76
-U-Bits: 011010010011000001111101001101001001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 76
-
-Encoding: 77
-U-Bits: 101101110011000001111010000100011100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 77
-
-Encoding: 77
-U-Bits: 101101110011000001100111001001010101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 77
-
-Encoding: 77
-U-Bits: 101101110011000001100000000001101011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 77
-
-Encoding: 78
-U-Bits: 000111010010100001111010011111101011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 78
-
-Encoding: 78
-U-Bits: 000111010010100001100111010010100010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 78
-
-Encoding: 78
-U-Bits: 000111010010100001100000011010011100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 78
-
-Encoding: 79
-U-Bits: 110000110010100001100111010011001001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 79
-
-Encoding: 79
-U-Bits: 110000110010100001111010011110000000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 79
-
-Encoding: 79
-U-Bits: 110000110010100001111101010110111110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 79
-
-Encoding: 7a
-U-Bits: 010110101010100001111101001100100010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 7a
-
-Encoding: 7a
-U-Bits: 010110101010100001100000000001101011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 7a
-
-Encoding: 7a
-U-Bits: 010110101010100001100111001001010101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 7a
-
-Encoding: 7b
-U-Bits: 100001001010100001100000000000000000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7b
-
-Encoding: 7b
-U-Bits: 100001001010100001111101001101001001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 7b
-
-Encoding: 7b
-U-Bits: 100001001010100001111010000101110111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 7b
-
-Encoding: 7c
-U-Bits: 001101001100100001100111001111110011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 7c
-
-Encoding: 7c
-U-Bits: 001101001100100001111010000010111010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 7c
-
-Encoding: 7c
-U-Bits: 001101001100100001111101001010000100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 7c
-
-Encoding: 7d
-U-Bits: 111010101100100001111010000011010001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 7d
-
-Encoding: 7d
-U-Bits: 111010101100100001100111001110011000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 7d
-
-Encoding: 7d
-U-Bits: 111010101100100001100000000110100110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 7d
-
-Encoding: 7e
-U-Bits: 011100110100100001100000011100111010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 7e
-
-Encoding: 7e
-U-Bits: 011100110100100001111101010001110011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 7e
-
-Encoding: 7e
-U-Bits: 011100110100100001111010011001001101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 7e
-
-Encoding: 7f
-U-Bits: 101011010100100001111101010000011000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 7f
-
-Encoding: 7f
-U-Bits: 101011010100100001100000011101010001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 7f
-
-Encoding: 7f
-U-Bits: 101011010100100001100111010101101111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 7f
-
-Encoding: 80
-U-Bits: 000000000001101001111111001000111110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 80
-
-Encoding: 80
-U-Bits: 000000000001101001100010000101110111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 80
-
-Encoding: 80
-U-Bits: 000000000001101001100101001101001001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 80
-
-Encoding: 81
-U-Bits: 110111100001101001100010000100011100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 81
-
-Encoding: 81
-U-Bits: 110111100001101001111111001001010101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 81
-
-Encoding: 81
-U-Bits: 110111100001101001111000000001101011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 81
-
-Encoding: 82
-U-Bits: 010001111001101001111000011011110111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 82
-
-Encoding: 82
-U-Bits: 010001111001101001100101010110111110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 82
-
-Encoding: 82
-U-Bits: 010001111001101001100010011110000000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 82
-
-Encoding: 83
-U-Bits: 100110011001101001100101010111010101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 83
-
-Encoding: 83
-U-Bits: 100110011001101001111000011010011100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 83
-
-Encoding: 83
-U-Bits: 100110011001101001111111010010100010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 83
-
-Encoding: 84
-U-Bits: 001010011111101001100010011000100110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 84
-
-Encoding: 84
-U-Bits: 001010011111101001111111010101101111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 84
-
-Encoding: 84
-U-Bits: 001010011111101001111000011101010001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 84
-
-Encoding: 85
-U-Bits: 111101111111101001111111010100000100
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 85
-
-Encoding: 85
-U-Bits: 111101111111101001100010011001001101
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 85
-
-Encoding: 85
-U-Bits: 111101111111101001100101010001110011
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 85
-
-Encoding: 86
-U-Bits: 011011100111101001100101001011101111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 86
-
-Encoding: 86
-U-Bits: 011011100111101001111000000110100110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 86
-
-Encoding: 86
-U-Bits: 011011100111101001111111001110011000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 86
-
-Encoding: 87
-U-Bits: 101100000111101001111000000111001101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 87
-
-Encoding: 87
-U-Bits: 101100000111101001100101001010000100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 87
-
-Encoding: 87
-U-Bits: 101100000111101001100010000010111010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 87
-
-Encoding: 88
-U-Bits: 000110100110001001111000011100111010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 88
-
-Encoding: 88
-U-Bits: 000110100110001001100101010001110011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 88
-
-Encoding: 88
-U-Bits: 000110100110001001100010011001001101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 88
-
-Encoding: 89
-U-Bits: 110001000110001001100101010000011000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 89
-
-Encoding: 89
-U-Bits: 110001000110001001111000011101010001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 89
-
-Encoding: 89
-U-Bits: 110001000110001001111111010101101111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 89
-
-Encoding: 8a
-U-Bits: 010111011110001001111111001111110011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 8a
-
-Encoding: 8a
-U-Bits: 010111011110001001100010000010111010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 8a
-
-Encoding: 8a
-U-Bits: 010111011110001001100101001010000100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 8a
-
-Encoding: 8b
-U-Bits: 100000111110001001100010000011010001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 8b
-
-Encoding: 8b
-U-Bits: 100000111110001001111111001110011000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 8b
-
-Encoding: 8b
-U-Bits: 100000111110001001111000000110100110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 8b
-
-Encoding: 8c
-U-Bits: 001100111000001001100101001100100010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 8c
-
-Encoding: 8c
-U-Bits: 001100111000001001111000000001101011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 8c
-
-Encoding: 8c
-U-Bits: 001100111000001001111111001001010101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 8c
-
-Encoding: 8d
-U-Bits: 111011011000001001111000000000000000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 8d
-
-Encoding: 8d
-U-Bits: 111011011000001001100101001101001001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 8d
-
-Encoding: 8d
-U-Bits: 111011011000001001100010000101110111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 8d
-
-Encoding: 8e
-U-Bits: 011101000000001001100010011111101011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 8e
-
-Encoding: 8e
-U-Bits: 011101000000001001111111010010100010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 8e
-
-Encoding: 8e
-U-Bits: 011101000000001001111000011010011100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 8e
-
-Encoding: 8f
-U-Bits: 101010100000001001111111010011001001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 8f
-
-Encoding: 8f
-U-Bits: 101010100000001001100010011110000000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 8f
-
-Encoding: 8f
-U-Bits: 101010100000001001100101010110111110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 8f
-
-Encoding: 90
-U-Bits: 000001101000010001111110111101111100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 90
-
-Encoding: 90
-U-Bits: 000001101000010001100011110000110101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 90
-
-Encoding: 90
-U-Bits: 000001101000010001100100111000001011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 90
-
-Encoding: 91
-U-Bits: 110110001000010001100011110001011110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 91
-
-Encoding: 91
-U-Bits: 110110001000010001111110111100010111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 91
-
-Encoding: 91
-U-Bits: 110110001000010001111001110100101001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 91
-
-Encoding: 92
-U-Bits: 010000010000010001111001101110110101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 92
-
-Encoding: 92
-U-Bits: 010000010000010001100100100011111100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 92
-
-Encoding: 92
-U-Bits: 010000010000010001100011101011000010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 92
-
-Encoding: 93
-U-Bits: 100111110000010001100100100010010111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 93
-
-Encoding: 93
-U-Bits: 100111110000010001111001101111011110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 93
-
-Encoding: 93
-U-Bits: 100111110000010001111110100111100000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 93
-
-Encoding: 94
-U-Bits: 001011110110010001100011101101100100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 94
-
-Encoding: 94
-U-Bits: 001011110110010001111110100000101101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 94
-
-Encoding: 94
-U-Bits: 001011110110010001111001101000010011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 94
-
-Encoding: 95
-U-Bits: 111100010110010001111110100001000110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 95
-
-Encoding: 95
-U-Bits: 111100010110010001100011101100001111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 95
-
-Encoding: 95
-U-Bits: 111100010110010001100100100100110001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 95
-
-Encoding: 96
-U-Bits: 011010001110010001100100111110101101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 96
-
-Encoding: 96
-U-Bits: 011010001110010001111001110011100100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 96
-
-Encoding: 96
-U-Bits: 011010001110010001111110111011011010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 96
-
-Encoding: 97
-U-Bits: 101101101110010001111001110010001111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 97
-
-Encoding: 97
-U-Bits: 101101101110010001100100111111000110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 97
-
-Encoding: 97
-U-Bits: 101101101110010001100011110111111000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 97
-
-Encoding: 98
-U-Bits: 000111001111110001111001101001111000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 98
-
-Encoding: 98
-U-Bits: 000111001111110001100100100100110001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 98
-
-Encoding: 98
-U-Bits: 000111001111110001100011101100001111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 98
-
-Encoding: 99
-U-Bits: 110000101111110001100100100101011010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 99
-
-Encoding: 99
-U-Bits: 110000101111110001111001101000010011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 99
-
-Encoding: 99
-U-Bits: 110000101111110001111110100000101101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 99
-
-Encoding: 9a
-U-Bits: 010110110111110001111110111010110001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 9a
-
-Encoding: 9a
-U-Bits: 010110110111110001100011110111111000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 9a
-
-Encoding: 9a
-U-Bits: 010110110111110001100100111111000110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 9a
-
-Encoding: 9b
-U-Bits: 100001010111110001100011110110010011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 9b
-
-Encoding: 9b
-U-Bits: 100001010111110001111110111011011010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 9b
-
-Encoding: 9b
-U-Bits: 100001010111110001111001110011100100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 9b
-
-Encoding: 9c
-U-Bits: 001101010001110001100100111001100000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 9c
-
-Encoding: 9c
-U-Bits: 001101010001110001111001110100101001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 9c
-
-Encoding: 9c
-U-Bits: 001101010001110001111110111100010111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 9c
-
-Encoding: 9d
-U-Bits: 111010110001110001111001110101000010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 9d
-
-Encoding: 9d
-U-Bits: 111010110001110001100100111000001011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 9d
-
-Encoding: 9d
-U-Bits: 111010110001110001100011110000110101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 9d
-
-Encoding: 9e
-U-Bits: 011100101001110001100011101010101001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 9e
-
-Encoding: 9e
-U-Bits: 011100101001110001111110100111100000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 9e
-
-Encoding: 9e
-U-Bits: 011100101001110001111001101111011110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 9e
-
-Encoding: 9f
-U-Bits: 101011001001110001111110100110001011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 9f
-
-Encoding: 9f
-U-Bits: 101011001001110001100011101011000010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 9f
-
-Encoding: 9f
-U-Bits: 101011001001110001100100100011111100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 9f
-
-Encoding: a0
-U-Bits: 000000011011110111111111010101101111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: a0
-
-Encoding: a0
-U-Bits: 000000011011110111100010011000100110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: a0
-
-Encoding: a0
-U-Bits: 000000011011110111100101010000011000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: a0
-
-Encoding: a1
-U-Bits: 110111111011110111100010011001001101
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: a1
-
-Encoding: a1
-U-Bits: 110111111011110111111111010100000100
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: a1
-
-Encoding: a1
-U-Bits: 110111111011110111111000011100111010
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: a1
-
-Encoding: a2
-U-Bits: 010001100011110111111000000110100110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: a2
-
-Encoding: a2
-U-Bits: 010001100011110111100101001011101111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: a2
-
-Encoding: a2
-U-Bits: 010001100011110111100010000011010001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: a2
-
-Encoding: a3
-U-Bits: 100110000011110111100101001010000100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: a3
-
-Encoding: a3
-U-Bits: 100110000011110111111000000111001101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: a3
-
-Encoding: a3
-U-Bits: 100110000011110111111111001111110011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: a3
-
-Encoding: a4
-U-Bits: 001010000101110111100010000101110111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: a4
-
-Encoding: a4
-U-Bits: 001010000101110111111111001000111110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: a4
-
-Encoding: a4
-U-Bits: 001010000101110111111000000000000000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: a4
-
-Encoding: a5
-U-Bits: 111101100101110111111111001001010101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: a5
-
-Encoding: a5
-U-Bits: 111101100101110111100010000100011100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: a5
-
-Encoding: a5
-U-Bits: 111101100101110111100101001100100010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: a5
-
-Encoding: a6
-U-Bits: 011011111101110111100101010110111110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: a6
-
-Encoding: a6
-U-Bits: 011011111101110111111000011011110111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: a6
-
-Encoding: a6
-U-Bits: 011011111101110111111111010011001001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: a6
-
-Encoding: a7
-U-Bits: 101100011101110111111000011010011100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: a7
-
-Encoding: a7
-U-Bits: 101100011101110111100101010111010101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: a7
-
-Encoding: a7
-U-Bits: 101100011101110111100010011111101011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: a7
-
-Encoding: a8
-U-Bits: 000110111100010111111000000001101011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: a8
-
-Encoding: a8
-U-Bits: 000110111100010111100101001100100010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: a8
-
-Encoding: a8
-U-Bits: 000110111100010111100010000100011100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: a8
-
-Encoding: a9
-U-Bits: 110001011100010111100101001101001001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: a9
-
-Encoding: a9
-U-Bits: 110001011100010111111000000000000000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: a9
-
-Encoding: a9
-U-Bits: 110001011100010111111111001000111110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: a9
-
-Encoding: aa
-U-Bits: 010111000100010111111111010010100010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: aa
-
-Encoding: aa
-U-Bits: 010111000100010111100010011111101011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: aa
-
-Encoding: aa
-U-Bits: 010111000100010111100101010111010101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: aa
-
-Encoding: ab
-U-Bits: 100000100100010111100010011110000000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: ab
-
-Encoding: ab
-U-Bits: 100000100100010111111111010011001001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: ab
-
-Encoding: ab
-U-Bits: 100000100100010111111000011011110111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: ab
-
-Encoding: ac
-U-Bits: 001100100010010111100101010001110011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: ac
-
-Encoding: ac
-U-Bits: 001100100010010111111000011100111010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: ac
-
-Encoding: ac
-U-Bits: 001100100010010111111111010100000100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: ac
-
-Encoding: ad
-U-Bits: 111011000010010111111000011101010001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: ad
-
-Encoding: ad
-U-Bits: 111011000010010111100101010000011000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: ad
-
-Encoding: ad
-U-Bits: 111011000010010111100010011000100110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: ad
-
-Encoding: ae
-U-Bits: 011101011010010111100010000010111010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: ae
-
-Encoding: ae
-U-Bits: 011101011010010111111111001111110011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: ae
-
-Encoding: ae
-U-Bits: 011101011010010111111000000111001101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: ae
-
-Encoding: af
-U-Bits: 101010111010010111111111001110011000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: af
-
-Encoding: af
-U-Bits: 101010111010010111100010000011010001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: af
-
-Encoding: af
-U-Bits: 101010111010010111100101001011101111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: af
-
-Encoding: b0
-U-Bits: 000001110010001111111110100000101101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: b0
-
-Encoding: b0
-U-Bits: 000001110010001111100011101101100100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: b0
-
-Encoding: b0
-U-Bits: 000001110010001111100100100101011010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: b0
-
-Encoding: b1
-U-Bits: 110110010010001111100011101100001111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: b1
-
-Encoding: b1
-U-Bits: 110110010010001111111110100001000110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: b1
-
-Encoding: b1
-U-Bits: 110110010010001111111001101001111000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: b1
-
-Encoding: b2
-U-Bits: 010000001010001111111001110011100100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: b2
-
-Encoding: b2
-U-Bits: 010000001010001111100100111110101101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: b2
-
-Encoding: b2
-U-Bits: 010000001010001111100011110110010011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: b2
-
-Encoding: b3
-U-Bits: 100111101010001111100100111111000110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: b3
-
-Encoding: b3
-U-Bits: 100111101010001111111001110010001111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: b3
-
-Encoding: b3
-U-Bits: 100111101010001111111110111010110001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: b3
-
-Encoding: b4
-U-Bits: 001011101100001111100011110000110101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: b4
-
-Encoding: b4
-U-Bits: 001011101100001111111110111101111100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: b4
-
-Encoding: b4
-U-Bits: 001011101100001111111001110101000010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: b4
-
-Encoding: b5
-U-Bits: 111100001100001111111110111100010111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: b5
-
-Encoding: b5
-U-Bits: 111100001100001111100011110001011110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: b5
-
-Encoding: b5
-U-Bits: 111100001100001111100100111001100000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: b5
-
-Encoding: b6
-U-Bits: 011010010100001111100100100011111100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: b6
-
-Encoding: b6
-U-Bits: 011010010100001111111001101110110101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: b6
-
-Encoding: b6
-U-Bits: 011010010100001111111110100110001011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: b6
-
-Encoding: b7
-U-Bits: 101101110100001111111001101111011110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: b7
-
-Encoding: b7
-U-Bits: 101101110100001111100100100010010111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: b7
-
-Encoding: b7
-U-Bits: 101101110100001111100011101010101001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: b7
-
-Encoding: b8
-U-Bits: 000111010101101111111001110100101001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: b8
-
-Encoding: b8
-U-Bits: 000111010101101111100100111001100000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: b8
-
-Encoding: b8
-U-Bits: 000111010101101111100011110001011110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: b8
-
-Encoding: b9
-U-Bits: 110000110101101111100100111000001011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: b9
-
-Encoding: b9
-U-Bits: 110000110101101111111001110101000010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: b9
-
-Encoding: b9
-U-Bits: 110000110101101111111110111101111100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: b9
-
-Encoding: ba
-U-Bits: 010110101101101111111110100111100000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: ba
-
-Encoding: ba
-U-Bits: 010110101101101111100011101010101001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: ba
-
-Encoding: ba
-U-Bits: 010110101101101111100100100010010111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: ba
-
-Encoding: bb
-U-Bits: 100001001101101111100011101011000010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: bb
-
-Encoding: bb
-U-Bits: 100001001101101111111110100110001011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: bb
-
-Encoding: bb
-U-Bits: 100001001101101111111001101110110101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: bb
-
-Encoding: bc
-U-Bits: 001101001011101111100100100100110001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: bc
-
-Encoding: bc
-U-Bits: 001101001011101111111001101001111000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: bc
-
-Encoding: bc
-U-Bits: 001101001011101111111110100001000110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: bc
-
-Encoding: bd
-U-Bits: 111010101011101111111001101000010011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: bd
-
-Encoding: bd
-U-Bits: 111010101011101111100100100101011010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: bd
-
-Encoding: bd
-U-Bits: 111010101011101111100011101101100100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: bd
-
-Encoding: be
-U-Bits: 011100110011101111100011110111111000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: be
-
-Encoding: be
-U-Bits: 011100110011101111111110111010110001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: be
-
-Encoding: be
-U-Bits: 011100110011101111111001110010001111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: be
-
-Encoding: bf
-U-Bits: 101011010011101111111110111011011010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: bf
-
-Encoding: bf
-U-Bits: 101011010011101111100011110110010011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: bf
-
-Encoding: bf
-U-Bits: 101011010011101111100100111110101101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: bf
-
-Encoding: c0
-U-Bits: 000000000111001110000011101011000010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: c0
-
-Encoding: c0
-U-Bits: 000000000111001110011110100110001011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: c0
-
-Encoding: c0
-U-Bits: 000000000111001110011001101110110101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: c0
-
-Encoding: c1
-U-Bits: 110111100111001110011110100111100000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: c1
-
-Encoding: c1
-U-Bits: 110111100111001110000011101010101001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: c1
-
-Encoding: c1
-U-Bits: 110111100111001110000100100010010111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: c1
-
-Encoding: c2
-U-Bits: 010001111111001110000100111000001011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: c2
-
-Encoding: c2
-U-Bits: 010001111111001110011001110101000010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: c2
-
-Encoding: c2
-U-Bits: 010001111111001110011110111101111100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: c2
-
-Encoding: c3
-U-Bits: 100110011111001110011001110100101001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: c3
-
-Encoding: c3
-U-Bits: 100110011111001110000100111001100000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: c3
-
-Encoding: c3
-U-Bits: 100110011111001110000011110001011110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: c3
-
-Encoding: c4
-U-Bits: 001010011001001110011110111011011010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: c4
-
-Encoding: c4
-U-Bits: 001010011001001110000011110110010011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: c4
-
-Encoding: c4
-U-Bits: 001010011001001110000100111110101101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: c4
-
-Encoding: c5
-U-Bits: 111101111001001110000011110111111000
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: c5
-
-Encoding: c5
-U-Bits: 111101111001001110011110111010110001
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: c5
-
-Encoding: c5
-U-Bits: 111101111001001110011001110010001111
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: c5
-
-Encoding: c6
-U-Bits: 011011100001001110011001101000010011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: c6
-
-Encoding: c6
-U-Bits: 011011100001001110000100100101011010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: c6
-
-Encoding: c6
-U-Bits: 011011100001001110000011101101100100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: c6
-
-Encoding: c7
-U-Bits: 101100000001001110000100100100110001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: c7
-
-Encoding: c7
-U-Bits: 101100000001001110011001101001111000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: c7
-
-Encoding: c7
-U-Bits: 101100000001001110011110100001000110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: c7
-
-Encoding: c8
-U-Bits: 000110100000101110000100111111000110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: c8
-
-Encoding: c8
-U-Bits: 000110100000101110011001110010001111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: c8
-
-Encoding: c8
-U-Bits: 000110100000101110011110111010110001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: c8
-
-Encoding: c9
-U-Bits: 110001000000101110011001110011100100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: c9
-
-Encoding: c9
-U-Bits: 110001000000101110000100111110101101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: c9
-
-Encoding: c9
-U-Bits: 110001000000101110000011110110010011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: c9
-
-Encoding: ca
-U-Bits: 010111011000101110000011101100001111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: ca
-
-Encoding: ca
-U-Bits: 010111011000101110011110100001000110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: ca
-
-Encoding: ca
-U-Bits: 010111011000101110011001101001111000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: ca
-
-Encoding: cb
-U-Bits: 100000111000101110011110100000101101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: cb
-
-Encoding: cb
-U-Bits: 100000111000101110000011101101100100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: cb
-
-Encoding: cb
-U-Bits: 100000111000101110000100100101011010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: cb
-
-Encoding: cc
-U-Bits: 001100111110101110011001101111011110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: cc
-
-Encoding: cc
-U-Bits: 001100111110101110000100100010010111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: cc
-
-Encoding: cc
-U-Bits: 001100111110101110000011101010101001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: cc
-
-Encoding: cd
-U-Bits: 111011011110101110000100100011111100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: cd
-
-Encoding: cd
-U-Bits: 111011011110101110011001101110110101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: cd
-
-Encoding: cd
-U-Bits: 111011011110101110011110100110001011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: cd
-
-Encoding: ce
-U-Bits: 011101000110101110011110111100010111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: ce
-
-Encoding: ce
-U-Bits: 011101000110101110000011110001011110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: ce
-
-Encoding: ce
-U-Bits: 011101000110101110000100111001100000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: ce
-
-Encoding: cf
-U-Bits: 101010100110101110000011110000110101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: cf
-
-Encoding: cf
-U-Bits: 101010100110101110011110111101111100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: cf
-
-Encoding: cf
-U-Bits: 101010100110101110011001110101000010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: cf
-
-Encoding: d0
-U-Bits: 000001101110110110000010011110000000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: d0
-
-Encoding: d0
-U-Bits: 000001101110110110011111010011001001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: d0
-
-Encoding: d0
-U-Bits: 000001101110110110011000011011110111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: d0
-
-Encoding: d1
-U-Bits: 110110001110110110011111010010100010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: d1
-
-Encoding: d1
-U-Bits: 110110001110110110000010011111101011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: d1
-
-Encoding: d1
-U-Bits: 110110001110110110000101010111010101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: d1
-
-Encoding: d2
-U-Bits: 010000010110110110000101001101001001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: d2
-
-Encoding: d2
-U-Bits: 010000010110110110011000000000000000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: d2
-
-Encoding: d2
-U-Bits: 010000010110110110011111001000111110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: d2
-
-Encoding: d3
-U-Bits: 100111110110110110011000000001101011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: d3
-
-Encoding: d3
-U-Bits: 100111110110110110000101001100100010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: d3
-
-Encoding: d3
-U-Bits: 100111110110110110000010000100011100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: d3
-
-Encoding: d4
-U-Bits: 001011110000110110011111001110011000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: d4
-
-Encoding: d4
-U-Bits: 001011110000110110000010000011010001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: d4
-
-Encoding: d4
-U-Bits: 001011110000110110000101001011101111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: d4
-
-Encoding: d5
-U-Bits: 111100010000110110000010000010111010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: d5
-
-Encoding: d5
-U-Bits: 111100010000110110011111001111110011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: d5
-
-Encoding: d5
-U-Bits: 111100010000110110011000000111001101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: d5
-
-Encoding: d6
-U-Bits: 011010001000110110011000011101010001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: d6
-
-Encoding: d6
-U-Bits: 011010001000110110000101010000011000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: d6
-
-Encoding: d6
-U-Bits: 011010001000110110000010011000100110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: d6
-
-Encoding: d7
-U-Bits: 101101101000110110000101010001110011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: d7
-
-Encoding: d7
-U-Bits: 101101101000110110011000011100111010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: d7
-
-Encoding: d7
-U-Bits: 101101101000110110011111010100000100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: d7
-
-Encoding: d8
-U-Bits: 000111001001010110000101001010000100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: d8
-
-Encoding: d8
-U-Bits: 000111001001010110011000000111001101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: d8
-
-Encoding: d8
-U-Bits: 000111001001010110011111001111110011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: d8
-
-Encoding: d9
-U-Bits: 110000101001010110011000000110100110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: d9
-
-Encoding: d9
-U-Bits: 110000101001010110000101001011101111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: d9
-
-Encoding: d9
-U-Bits: 110000101001010110000010000011010001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: d9
-
-Encoding: da
-U-Bits: 010110110001010110000010011001001101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: da
-
-Encoding: da
-U-Bits: 010110110001010110011111010100000100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: da
-
-Encoding: da
-U-Bits: 010110110001010110011000011100111010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: da
-
-Encoding: db
-U-Bits: 100001010001010110011111010101101111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: db
-
-Encoding: db
-U-Bits: 100001010001010110000010011000100110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: db
-
-Encoding: db
-U-Bits: 100001010001010110000101010000011000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: db
-
-Encoding: dc
-U-Bits: 001101010111010110011000011010011100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: dc
-
-Encoding: dc
-U-Bits: 001101010111010110000101010111010101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: dc
-
-Encoding: dc
-U-Bits: 001101010111010110000010011111101011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: dc
-
-Encoding: dd
-U-Bits: 111010110111010110000101010110111110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: dd
-
-Encoding: dd
-U-Bits: 111010110111010110011000011011110111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: dd
-
-Encoding: dd
-U-Bits: 111010110111010110011111010011001001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: dd
-
-Encoding: de
-U-Bits: 011100101111010110011111001001010101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: de
-
-Encoding: de
-U-Bits: 011100101111010110000010000100011100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: de
-
-Encoding: de
-U-Bits: 011100101111010110000101001100100010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: de
-
-Encoding: df
-U-Bits: 101011001111010110000010000101110111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: df
-
-Encoding: df
-U-Bits: 101011001111010110011111001000111110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: df
-
-Encoding: df
-U-Bits: 101011001111010110011000000000000000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: df
-
-Encoding: e0
-U-Bits: 000000011101010000000011110110010011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: e0
-
-Encoding: e0
-U-Bits: 000000011101010000011110111011011010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: e0
-
-Encoding: e0
-U-Bits: 000000011101010000011001110011100100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: e0
-
-Encoding: e1
-U-Bits: 110111111101010000011110111010110001
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: e1
-
-Encoding: e1
-U-Bits: 110111111101010000000011110111111000
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: e1
-
-Encoding: e1
-U-Bits: 110111111101010000000100111111000110
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: e1
-
-Encoding: e2
-U-Bits: 010001100101010000000100100101011010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: e2
-
-Encoding: e2
-U-Bits: 010001100101010000011001101000010011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: e2
-
-Encoding: e2
-U-Bits: 010001100101010000011110100000101101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: e2
-
-Encoding: e3
-U-Bits: 100110000101010000011001101001111000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: e3
-
-Encoding: e3
-U-Bits: 100110000101010000000100100100110001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: e3
-
-Encoding: e3
-U-Bits: 100110000101010000000011101100001111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: e3
-
-Encoding: e4
-U-Bits: 001010000011010000011110100110001011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: e4
-
-Encoding: e4
-U-Bits: 001010000011010000000011101011000010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: e4
-
-Encoding: e4
-U-Bits: 001010000011010000000100100011111100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: e4
-
-Encoding: e5
-U-Bits: 111101100011010000000011101010101001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: e5
-
-Encoding: e5
-U-Bits: 111101100011010000011110100111100000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: e5
-
-Encoding: e5
-U-Bits: 111101100011010000011001101111011110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: e5
-
-Encoding: e6
-U-Bits: 011011111011010000011001110101000010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: e6
-
-Encoding: e6
-U-Bits: 011011111011010000000100111000001011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: e6
-
-Encoding: e6
-U-Bits: 011011111011010000000011110000110101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: e6
-
-Encoding: e7
-U-Bits: 101100011011010000000100111001100000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: e7
-
-Encoding: e7
-U-Bits: 101100011011010000011001110100101001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: e7
-
-Encoding: e7
-U-Bits: 101100011011010000011110111100010111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: e7
-
-Encoding: e8
-U-Bits: 000110111010110000000100100010010111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: e8
-
-Encoding: e8
-U-Bits: 000110111010110000011001101111011110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: e8
-
-Encoding: e8
-U-Bits: 000110111010110000011110100111100000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: e8
-
-Encoding: e9
-U-Bits: 110001011010110000011001101110110101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: e9
-
-Encoding: e9
-U-Bits: 110001011010110000000100100011111100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: e9
-
-Encoding: e9
-U-Bits: 110001011010110000000011101011000010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: e9
-
-Encoding: ea
-U-Bits: 010111000010110000000011110001011110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: ea
-
-Encoding: ea
-U-Bits: 010111000010110000011110111100010111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: ea
-
-Encoding: ea
-U-Bits: 010111000010110000011001110100101001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: ea
-
-Encoding: eb
-U-Bits: 100000100010110000011110111101111100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: eb
-
-Encoding: eb
-U-Bits: 100000100010110000000011110000110101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: eb
-
-Encoding: eb
-U-Bits: 100000100010110000000100111000001011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: eb
-
-Encoding: ec
-U-Bits: 001100100100110000011001110010001111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: ec
-
-Encoding: ec
-U-Bits: 001100100100110000000100111111000110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: ec
-
-Encoding: ec
-U-Bits: 001100100100110000000011110111111000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: ec
-
-Encoding: ed
-U-Bits: 111011000100110000000100111110101101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: ed
-
-Encoding: ed
-U-Bits: 111011000100110000011001110011100100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: ed
-
-Encoding: ed
-U-Bits: 111011000100110000011110111011011010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: ed
-
-Encoding: ee
-U-Bits: 011101011100110000011110100001000110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: ee
-
-Encoding: ee
-U-Bits: 011101011100110000000011101100001111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: ee
-
-Encoding: ee
-U-Bits: 011101011100110000000100100100110001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: ee
-
-Encoding: ef
-U-Bits: 101010111100110000000011101101100100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: ef
-
-Encoding: ef
-U-Bits: 101010111100110000011110100000101101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: ef
-
-Encoding: ef
-U-Bits: 101010111100110000011001101000010011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: ef
-
-Encoding: f0
-U-Bits: 000001110100101000000010000011010001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: f0
-
-Encoding: f0
-U-Bits: 000001110100101000011111001110011000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: f0
-
-Encoding: f0
-U-Bits: 000001110100101000011000000110100110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: f0
-
-Encoding: f1
-U-Bits: 110110010100101000011111001111110011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: f1
-
-Encoding: f1
-U-Bits: 110110010100101000000010000010111010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: f1
-
-Encoding: f1
-U-Bits: 110110010100101000000101001010000100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: f1
-
-Encoding: f2
-U-Bits: 010000001100101000000101010000011000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: f2
-
-Encoding: f2
-U-Bits: 010000001100101000011000011101010001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: f2
-
-Encoding: f2
-U-Bits: 010000001100101000011111010101101111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: f2
-
-Encoding: f3
-U-Bits: 100111101100101000011000011100111010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: f3
-
-Encoding: f3
-U-Bits: 100111101100101000000101010001110011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: f3
-
-Encoding: f3
-U-Bits: 100111101100101000000010011001001101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: f3
-
-Encoding: f4
-U-Bits: 001011101010101000011111010011001001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: f4
-
-Encoding: f4
-U-Bits: 001011101010101000000010011110000000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: f4
-
-Encoding: f4
-U-Bits: 001011101010101000000101010110111110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: f4
-
-Encoding: f5
-U-Bits: 111100001010101000000010011111101011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: f5
-
-Encoding: f5
-U-Bits: 111100001010101000011111010010100010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: f5
-
-Encoding: f5
-U-Bits: 111100001010101000011000011010011100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: f5
-
-Encoding: f6
-U-Bits: 011010010010101000011000000000000000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: f6
-
-Encoding: f6
-U-Bits: 011010010010101000000101001101001001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: f6
-
-Encoding: f6
-U-Bits: 011010010010101000000010000101110111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: f6
-
-Encoding: f7
-U-Bits: 101101110010101000000101001100100010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: f7
-
-Encoding: f7
-U-Bits: 101101110010101000011000000001101011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: f7
-
-Encoding: f7
-U-Bits: 101101110010101000011111001001010101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: f7
-
-Encoding: f8
-U-Bits: 000111010011001000000101010111010101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: f8
-
-Encoding: f8
-U-Bits: 000111010011001000011000011010011100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: f8
-
-Encoding: f8
-U-Bits: 000111010011001000011111010010100010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: f8
-
-Encoding: f9
-U-Bits: 110000110011001000011000011011110111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: f9
-
-Encoding: f9
-U-Bits: 110000110011001000000101010110111110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: f9
-
-Encoding: f9
-U-Bits: 110000110011001000000010011110000000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: f9
-
-Encoding: fa
-U-Bits: 010110101011001000000010000100011100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: fa
-
-Encoding: fa
-U-Bits: 010110101011001000011111001001010101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: fa
-
-Encoding: fa
-U-Bits: 010110101011001000011000000001101011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: fa
-
-Encoding: fb
-U-Bits: 100001001011001000011111001000111110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: fb
-
-Encoding: fb
-U-Bits: 100001001011001000000010000101110111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: fb
-
-Encoding: fb
-U-Bits: 100001001011001000000101001101001001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: fb
-
-Encoding: fc
-U-Bits: 001101001101001000011000000111001101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: fc
-
-Encoding: fc
-U-Bits: 001101001101001000000101001010000100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: fc
-
-Encoding: fc
-U-Bits: 001101001101001000000010000010111010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: fc
-
-Encoding: fd
-U-Bits: 111010101101001000000101001011101111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: fd
-
-Encoding: fd
-U-Bits: 111010101101001000011000000110100110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: fd
-
-Encoding: fd
-U-Bits: 111010101101001000011111001110011000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: fd
-
-Encoding: fe
-U-Bits: 011100110101001000011111010100000100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: fe
-
-Encoding: fe
-U-Bits: 011100110101001000000010011001001101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: fe
-
-Encoding: fe
-U-Bits: 011100110101001000000101010001110011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: fe
-
-Encoding: ff
-U-Bits: 101011010101001000000010011000100110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: ff
-
-Encoding: ff
-U-Bits: 101011010101001000011111010101101111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: ff
-
-Encoding: ff
-U-Bits: 101011010101001000011000011101010001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: ff
-
-Encoding: 100
-U-Bits: 000000000000011010011111110010001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 100
-
-Encoding: 100
-U-Bits: 000000000000011010000010111111000110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 100
-
-Encoding: 100
-U-Bits: 000000000000011010000101110111111000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 100
-
-Encoding: 101
-U-Bits: 110111100000011010000010111110101101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 101
-
-Encoding: 101
-U-Bits: 110111100000011010011111110011100100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 101
-
-Encoding: 101
-U-Bits: 110111100000011010011000111011011010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 101
-
-Encoding: 102
-U-Bits: 010001111000011010011000100001000110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 102
-
-Encoding: 102
-U-Bits: 010001111000011010000101101100001111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 102
-
-Encoding: 102
-U-Bits: 010001111000011010000010100100110001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 102
-
-Encoding: 103
-U-Bits: 100110011000011010000101101101100100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 103
-
-Encoding: 103
-U-Bits: 100110011000011010011000100000101101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 103
-
-Encoding: 103
-U-Bits: 100110011000011010011111101000010011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 103
-
-Encoding: 104
-U-Bits: 001010011110011010000010100010010111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 104
-
-Encoding: 104
-U-Bits: 001010011110011010011111101111011110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 104
-
-Encoding: 104
-U-Bits: 001010011110011010011000100111100000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 104
-
-Encoding: 105
-U-Bits: 111101111110011010011111101110110101
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 105
-
-Encoding: 105
-U-Bits: 111101111110011010000010100011111100
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 105
-
-Encoding: 105
-U-Bits: 111101111110011010000101101011000010
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 105
-
-Encoding: 106
-U-Bits: 011011100110011010000101110001011110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 106
-
-Encoding: 106
-U-Bits: 011011100110011010011000111100010111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 106
-
-Encoding: 106
-U-Bits: 011011100110011010011111110100101001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 106
-
-Encoding: 107
-U-Bits: 101100000110011010011000111101111100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 107
-
-Encoding: 107
-U-Bits: 101100000110011010000101110000110101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 107
-
-Encoding: 107
-U-Bits: 101100000110011010000010111000001011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 107
-
-Encoding: 108
-U-Bits: 000110100111111010011000100110001011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 108
-
-Encoding: 108
-U-Bits: 000110100111111010000101101011000010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 108
-
-Encoding: 108
-U-Bits: 000110100111111010000010100011111100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 108
-
-Encoding: 109
-U-Bits: 110001000111111010000101101010101001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 109
-
-Encoding: 109
-U-Bits: 110001000111111010011000100111100000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 109
-
-Encoding: 109
-U-Bits: 110001000111111010011111101111011110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 109
-
-Encoding: 10a
-U-Bits: 010111011111111010011111110101000010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 10a
-
-Encoding: 10a
-U-Bits: 010111011111111010000010111000001011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 10a
-
-Encoding: 10a
-U-Bits: 010111011111111010000101110000110101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 10a
-
-Encoding: 10b
-U-Bits: 100000111111111010000010111001100000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 10b
-
-Encoding: 10b
-U-Bits: 100000111111111010011111110100101001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 10b
-
-Encoding: 10b
-U-Bits: 100000111111111010011000111100010111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 10b
-
-Encoding: 10c
-U-Bits: 001100111001111010000101110110010011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 10c
-
-Encoding: 10c
-U-Bits: 001100111001111010011000111011011010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 10c
-
-Encoding: 10c
-U-Bits: 001100111001111010011111110011100100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 10c
-
-Encoding: 10d
-U-Bits: 111011011001111010011000111010110001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 10d
-
-Encoding: 10d
-U-Bits: 111011011001111010000101110111111000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 10d
-
-Encoding: 10d
-U-Bits: 111011011001111010000010111111000110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 10d
-
-Encoding: 10e
-U-Bits: 011101000001111010000010100101011010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 10e
-
-Encoding: 10e
-U-Bits: 011101000001111010011111101000010011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 10e
-
-Encoding: 10e
-U-Bits: 011101000001111010011000100000101101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 10e
-
-Encoding: 10f
-U-Bits: 101010100001111010011111101001111000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 10f
-
-Encoding: 10f
-U-Bits: 101010100001111010000010100100110001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 10f
-
-Encoding: 10f
-U-Bits: 101010100001111010000101101100001111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 10f
-
-Encoding: 110
-U-Bits: 000001101001100010011110000111001101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 110
-
-Encoding: 110
-U-Bits: 000001101001100010000011001010000100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 110
-
-Encoding: 110
-U-Bits: 000001101001100010000100000010111010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 110
-
-Encoding: 111
-U-Bits: 110110001001100010000011001011101111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 111
-
-Encoding: 111
-U-Bits: 110110001001100010011110000110100110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 111
-
-Encoding: 111
-U-Bits: 110110001001100010011001001110011000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 111
-
-Encoding: 112
-U-Bits: 010000010001100010011001010100000100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 112
-
-Encoding: 112
-U-Bits: 010000010001100010000100011001001101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 112
-
-Encoding: 112
-U-Bits: 010000010001100010000011010001110011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 112
-
-Encoding: 113
-U-Bits: 100111110001100010000100011000100110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 113
-
-Encoding: 113
-U-Bits: 100111110001100010011001010101101111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 113
-
-Encoding: 113
-U-Bits: 100111110001100010011110011101010001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 113
-
-Encoding: 114
-U-Bits: 001011110111100010000011010111010101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 114
-
-Encoding: 114
-U-Bits: 001011110111100010011110011010011100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 114
-
-Encoding: 114
-U-Bits: 001011110111100010011001010010100010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 114
-
-Encoding: 115
-U-Bits: 111100010111100010011110011011110111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 115
-
-Encoding: 115
-U-Bits: 111100010111100010000011010110111110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 115
-
-Encoding: 115
-U-Bits: 111100010111100010000100011110000000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 115
-
-Encoding: 116
-U-Bits: 011010001111100010000100000100011100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 116
-
-Encoding: 116
-U-Bits: 011010001111100010011001001001010101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 116
-
-Encoding: 116
-U-Bits: 011010001111100010011110000001101011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 116
-
-Encoding: 117
-U-Bits: 101101101111100010011001001000111110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 117
-
-Encoding: 117
-U-Bits: 101101101111100010000100000101110111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 117
-
-Encoding: 117
-U-Bits: 101101101111100010000011001101001001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 117
-
-Encoding: 118
-U-Bits: 000111001110000010011001010011001001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 118
-
-Encoding: 118
-U-Bits: 000111001110000010000100011110000000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 118
-
-Encoding: 118
-U-Bits: 000111001110000010000011010110111110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 118
-
-Encoding: 119
-U-Bits: 110000101110000010000100011111101011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 119
-
-Encoding: 119
-U-Bits: 110000101110000010011001010010100010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 119
-
-Encoding: 119
-U-Bits: 110000101110000010011110011010011100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 119
-
-Encoding: 11a
-U-Bits: 010110110110000010011110000000000000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 11a
-
-Encoding: 11a
-U-Bits: 010110110110000010000011001101001001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 11a
-
-Encoding: 11a
-U-Bits: 010110110110000010000100000101110111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 11a
-
-Encoding: 11b
-U-Bits: 100001010110000010000011001100100010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 11b
-
-Encoding: 11b
-U-Bits: 100001010110000010011110000001101011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 11b
-
-Encoding: 11b
-U-Bits: 100001010110000010011001001001010101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 11b
-
-Encoding: 11c
-U-Bits: 001101010000000010000100000011010001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 11c
-
-Encoding: 11c
-U-Bits: 001101010000000010011001001110011000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 11c
-
-Encoding: 11c
-U-Bits: 001101010000000010011110000110100110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 11c
-
-Encoding: 11d
-U-Bits: 111010110000000010011001001111110011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 11d
-
-Encoding: 11d
-U-Bits: 111010110000000010000100000010111010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 11d
-
-Encoding: 11d
-U-Bits: 111010110000000010000011001010000100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 11d
-
-Encoding: 11e
-U-Bits: 011100101000000010000011010000011000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 11e
-
-Encoding: 11e
-U-Bits: 011100101000000010011110011101010001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 11e
-
-Encoding: 11e
-U-Bits: 011100101000000010011001010101101111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 11e
-
-Encoding: 11f
-U-Bits: 101011001000000010011110011100111010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 11f
-
-Encoding: 11f
-U-Bits: 101011001000000010000011010001110011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 11f
-
-Encoding: 11f
-U-Bits: 101011001000000010000100011001001101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 11f
-
-Encoding: 120
-U-Bits: 000000011010000100011111101111011110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 120
-
-Encoding: 120
-U-Bits: 000000011010000100000010100010010111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 120
-
-Encoding: 120
-U-Bits: 000000011010000100000101101010101001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 120
-
-Encoding: 121
-U-Bits: 110111111010000100000010100011111100
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 121
-
-Encoding: 121
-U-Bits: 110111111010000100011111101110110101
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 121
-
-Encoding: 121
-U-Bits: 110111111010000100011000100110001011
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 121
-
-Encoding: 122
-U-Bits: 010001100010000100011000111100010111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 122
-
-Encoding: 122
-U-Bits: 010001100010000100000101110001011110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 122
-
-Encoding: 122
-U-Bits: 010001100010000100000010111001100000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 122
-
-Encoding: 123
-U-Bits: 100110000010000100000101110000110101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 123
-
-Encoding: 123
-U-Bits: 100110000010000100011000111101111100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 123
-
-Encoding: 123
-U-Bits: 100110000010000100011111110101000010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 123
-
-Encoding: 124
-U-Bits: 001010000100000100000010111111000110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 124
-
-Encoding: 124
-U-Bits: 001010000100000100011111110010001111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 124
-
-Encoding: 124
-U-Bits: 001010000100000100011000111010110001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 124
-
-Encoding: 125
-U-Bits: 111101100100000100011111110011100100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 125
-
-Encoding: 125
-U-Bits: 111101100100000100000010111110101101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 125
-
-Encoding: 125
-U-Bits: 111101100100000100000101110110010011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 125
-
-Encoding: 126
-U-Bits: 011011111100000100000101101100001111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 126
-
-Encoding: 126
-U-Bits: 011011111100000100011000100001000110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 126
-
-Encoding: 126
-U-Bits: 011011111100000100011111101001111000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 126
-
-Encoding: 127
-U-Bits: 101100011100000100011000100000101101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 127
-
-Encoding: 127
-U-Bits: 101100011100000100000101101101100100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 127
-
-Encoding: 127
-U-Bits: 101100011100000100000010100101011010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 127
-
-Encoding: 128
-U-Bits: 000110111101100100011000111011011010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 128
-
-Encoding: 128
-U-Bits: 000110111101100100000101110110010011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 128
-
-Encoding: 128
-U-Bits: 000110111101100100000010111110101101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 128
-
-Encoding: 129
-U-Bits: 110001011101100100000101110111111000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 129
-
-Encoding: 129
-U-Bits: 110001011101100100011000111010110001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 129
-
-Encoding: 129
-U-Bits: 110001011101100100011111110010001111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 129
-
-Encoding: 12a
-U-Bits: 010111000101100100011111101000010011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 12a
-
-Encoding: 12a
-U-Bits: 010111000101100100000010100101011010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 12a
-
-Encoding: 12a
-U-Bits: 010111000101100100000101101101100100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 12a
-
-Encoding: 12b
-U-Bits: 100000100101100100000010100100110001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 12b
-
-Encoding: 12b
-U-Bits: 100000100101100100011111101001111000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 12b
-
-Encoding: 12b
-U-Bits: 100000100101100100011000100001000110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 12b
-
-Encoding: 12c
-U-Bits: 001100100011100100000101101011000010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 12c
-
-Encoding: 12c
-U-Bits: 001100100011100100011000100110001011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 12c
-
-Encoding: 12c
-U-Bits: 001100100011100100011111101110110101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 12c
-
-Encoding: 12d
-U-Bits: 111011000011100100011000100111100000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 12d
-
-Encoding: 12d
-U-Bits: 111011000011100100000101101010101001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 12d
-
-Encoding: 12d
-U-Bits: 111011000011100100000010100010010111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 12d
-
-Encoding: 12e
-U-Bits: 011101011011100100000010111000001011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 12e
-
-Encoding: 12e
-U-Bits: 011101011011100100011111110101000010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 12e
-
-Encoding: 12e
-U-Bits: 011101011011100100011000111101111100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 12e
-
-Encoding: 12f
-U-Bits: 101010111011100100011111110100101001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 12f
-
-Encoding: 12f
-U-Bits: 101010111011100100000010111001100000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 12f
-
-Encoding: 12f
-U-Bits: 101010111011100100000101110001011110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 12f
-
-Encoding: 130
-U-Bits: 000001110011111100011110011010011100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 130
-
-Encoding: 130
-U-Bits: 000001110011111100000011010111010101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 130
-
-Encoding: 130
-U-Bits: 000001110011111100000100011111101011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 130
-
-Encoding: 131
-U-Bits: 110110010011111100000011010110111110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 131
-
-Encoding: 131
-U-Bits: 110110010011111100011110011011110111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 131
-
-Encoding: 131
-U-Bits: 110110010011111100011001010011001001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 131
-
-Encoding: 132
-U-Bits: 010000001011111100011001001001010101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 132
-
-Encoding: 132
-U-Bits: 010000001011111100000100000100011100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 132
-
-Encoding: 132
-U-Bits: 010000001011111100000011001100100010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 132
-
-Encoding: 133
-U-Bits: 100111101011111100000100000101110111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 133
-
-Encoding: 133
-U-Bits: 100111101011111100011001001000111110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 133
-
-Encoding: 133
-U-Bits: 100111101011111100011110000000000000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 133
-
-Encoding: 134
-U-Bits: 001011101101111100000011001010000100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 134
-
-Encoding: 134
-U-Bits: 001011101101111100011110000111001101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 134
-
-Encoding: 134
-U-Bits: 001011101101111100011001001111110011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 134
-
-Encoding: 135
-U-Bits: 111100001101111100011110000110100110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 135
-
-Encoding: 135
-U-Bits: 111100001101111100000011001011101111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 135
-
-Encoding: 135
-U-Bits: 111100001101111100000100000011010001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 135
-
-Encoding: 136
-U-Bits: 011010010101111100000100011001001101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 136
-
-Encoding: 136
-U-Bits: 011010010101111100011001010100000100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 136
-
-Encoding: 136
-U-Bits: 011010010101111100011110011100111010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 136
-
-Encoding: 137
-U-Bits: 101101110101111100011001010101101111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 137
-
-Encoding: 137
-U-Bits: 101101110101111100000100011000100110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 137
-
-Encoding: 137
-U-Bits: 101101110101111100000011010000011000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 137
-
-Encoding: 138
-U-Bits: 000111010100011100011001001110011000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 138
-
-Encoding: 138
-U-Bits: 000111010100011100000100000011010001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 138
-
-Encoding: 138
-U-Bits: 000111010100011100000011001011101111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 138
-
-Encoding: 139
-U-Bits: 110000110100011100000100000010111010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 139
-
-Encoding: 139
-U-Bits: 110000110100011100011001001111110011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 139
-
-Encoding: 139
-U-Bits: 110000110100011100011110000111001101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 139
-
-Encoding: 13a
-U-Bits: 010110101100011100011110011101010001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 13a
-
-Encoding: 13a
-U-Bits: 010110101100011100000011010000011000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 13a
-
-Encoding: 13a
-U-Bits: 010110101100011100000100011000100110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 13a
-
-Encoding: 13b
-U-Bits: 100001001100011100000011010001110011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 13b
-
-Encoding: 13b
-U-Bits: 100001001100011100011110011100111010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 13b
-
-Encoding: 13b
-U-Bits: 100001001100011100011001010100000100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 13b
-
-Encoding: 13c
-U-Bits: 001101001010011100000100011110000000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 13c
-
-Encoding: 13c
-U-Bits: 001101001010011100011001010011001001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 13c
-
-Encoding: 13c
-U-Bits: 001101001010011100011110011011110111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 13c
-
-Encoding: 13d
-U-Bits: 111010101010011100011001010010100010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 13d
-
-Encoding: 13d
-U-Bits: 111010101010011100000100011111101011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 13d
-
-Encoding: 13d
-U-Bits: 111010101010011100000011010111010101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 13d
-
-Encoding: 13e
-U-Bits: 011100110010011100000011001101001001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 13e
-
-Encoding: 13e
-U-Bits: 011100110010011100011110000000000000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 13e
-
-Encoding: 13e
-U-Bits: 011100110010011100011001001000111110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 13e
-
-Encoding: 13f
-U-Bits: 101011010010011100011110000001101011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 13f
-
-Encoding: 13f
-U-Bits: 101011010010011100000011001100100010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 13f
-
-Encoding: 13f
-U-Bits: 101011010010011100000100000100011100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 13f
-
-Encoding: 140
-U-Bits: 000000000110111101100011010001110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 140
-
-Encoding: 140
-U-Bits: 000000000110111101111110011100111010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 140
-
-Encoding: 140
-U-Bits: 000000000110111101111001010100000100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 140
-
-Encoding: 141
-U-Bits: 110111100110111101111110011101010001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 141
-
-Encoding: 141
-U-Bits: 110111100110111101100011010000011000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 141
-
-Encoding: 141
-U-Bits: 110111100110111101100100011000100110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 141
-
-Encoding: 142
-U-Bits: 010001111110111101100100000010111010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 142
-
-Encoding: 142
-U-Bits: 010001111110111101111001001111110011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 142
-
-Encoding: 142
-U-Bits: 010001111110111101111110000111001101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 142
-
-Encoding: 143
-U-Bits: 100110011110111101111001001110011000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 143
-
-Encoding: 143
-U-Bits: 100110011110111101100100000011010001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 143
-
-Encoding: 143
-U-Bits: 100110011110111101100011001011101111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 143
-
-Encoding: 144
-U-Bits: 001010011000111101111110000001101011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 144
-
-Encoding: 144
-U-Bits: 001010011000111101100011001100100010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 144
-
-Encoding: 144
-U-Bits: 001010011000111101100100000100011100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 144
-
-Encoding: 145
-U-Bits: 111101111000111101100011001101001001
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 145
-
-Encoding: 145
-U-Bits: 111101111000111101111110000000000000
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 145
-
-Encoding: 145
-U-Bits: 111101111000111101111001001000111110
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 145
-
-Encoding: 146
-U-Bits: 011011100000111101111001010010100010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 146
-
-Encoding: 146
-U-Bits: 011011100000111101100100011111101011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 146
-
-Encoding: 146
-U-Bits: 011011100000111101100011010111010101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 146
-
-Encoding: 147
-U-Bits: 101100000000111101100100011110000000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 147
-
-Encoding: 147
-U-Bits: 101100000000111101111001010011001001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 147
-
-Encoding: 147
-U-Bits: 101100000000111101111110011011110111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 147
-
-Encoding: 148
-U-Bits: 000110100001011101100100000101110111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 148
-
-Encoding: 148
-U-Bits: 000110100001011101111001001000111110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 148
-
-Encoding: 148
-U-Bits: 000110100001011101111110000000000000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 148
-
-Encoding: 149
-U-Bits: 110001000001011101111001001001010101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 149
-
-Encoding: 149
-U-Bits: 110001000001011101100100000100011100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 149
-
-Encoding: 149
-U-Bits: 110001000001011101100011001100100010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 149
-
-Encoding: 14a
-U-Bits: 010111011001011101100011010110111110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 14a
-
-Encoding: 14a
-U-Bits: 010111011001011101111110011011110111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 14a
-
-Encoding: 14a
-U-Bits: 010111011001011101111001010011001001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 14a
-
-Encoding: 14b
-U-Bits: 100000111001011101111110011010011100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 14b
-
-Encoding: 14b
-U-Bits: 100000111001011101100011010111010101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 14b
-
-Encoding: 14b
-U-Bits: 100000111001011101100100011111101011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 14b
-
-Encoding: 14c
-U-Bits: 001100111111011101111001010101101111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 14c
-
-Encoding: 14c
-U-Bits: 001100111111011101100100011000100110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 14c
-
-Encoding: 14c
-U-Bits: 001100111111011101100011010000011000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 14c
-
-Encoding: 14d
-U-Bits: 111011011111011101100100011001001101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 14d
-
-Encoding: 14d
-U-Bits: 111011011111011101111001010100000100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 14d
-
-Encoding: 14d
-U-Bits: 111011011111011101111110011100111010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 14d
-
-Encoding: 14e
-U-Bits: 011101000111011101111110000110100110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 14e
-
-Encoding: 14e
-U-Bits: 011101000111011101100011001011101111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 14e
-
-Encoding: 14e
-U-Bits: 011101000111011101100100000011010001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 14e
-
-Encoding: 14f
-U-Bits: 101010100111011101100011001010000100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 14f
-
-Encoding: 14f
-U-Bits: 101010100111011101111110000111001101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 14f
-
-Encoding: 14f
-U-Bits: 101010100111011101111001001111110011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 14f
-
-Encoding: 150
-U-Bits: 000001101111000101100010100100110001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 150
-
-Encoding: 150
-U-Bits: 000001101111000101111111101001111000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 150
-
-Encoding: 150
-U-Bits: 000001101111000101111000100001000110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 150
-
-Encoding: 151
-U-Bits: 110110001111000101111111101000010011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 151
-
-Encoding: 151
-U-Bits: 110110001111000101100010100101011010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 151
-
-Encoding: 151
-U-Bits: 110110001111000101100101101101100100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 151
-
-Encoding: 152
-U-Bits: 010000010111000101100101110111111000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 152
-
-Encoding: 152
-U-Bits: 010000010111000101111000111010110001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 152
-
-Encoding: 152
-U-Bits: 010000010111000101111111110010001111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 152
-
-Encoding: 153
-U-Bits: 100111110111000101111000111011011010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 153
-
-Encoding: 153
-U-Bits: 100111110111000101100101110110010011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 153
-
-Encoding: 153
-U-Bits: 100111110111000101100010111110101101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 153
-
-Encoding: 154
-U-Bits: 001011110001000101111111110100101001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 154
-
-Encoding: 154
-U-Bits: 001011110001000101100010111001100000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 154
-
-Encoding: 154
-U-Bits: 001011110001000101100101110001011110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 154
-
-Encoding: 155
-U-Bits: 111100010001000101100010111000001011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 155
-
-Encoding: 155
-U-Bits: 111100010001000101111111110101000010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 155
-
-Encoding: 155
-U-Bits: 111100010001000101111000111101111100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 155
-
-Encoding: 156
-U-Bits: 011010001001000101111000100111100000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 156
-
-Encoding: 156
-U-Bits: 011010001001000101100101101010101001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 156
-
-Encoding: 156
-U-Bits: 011010001001000101100010100010010111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 156
-
-Encoding: 157
-U-Bits: 101101101001000101100101101011000010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 157
-
-Encoding: 157
-U-Bits: 101101101001000101111000100110001011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 157
-
-Encoding: 157
-U-Bits: 101101101001000101111111101110110101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 157
-
-Encoding: 158
-U-Bits: 000111001000100101100101110000110101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 158
-
-Encoding: 158
-U-Bits: 000111001000100101111000111101111100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 158
-
-Encoding: 158
-U-Bits: 000111001000100101111111110101000010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 158
-
-Encoding: 159
-U-Bits: 110000101000100101111000111100010111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 159
-
-Encoding: 159
-U-Bits: 110000101000100101100101110001011110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 159
-
-Encoding: 159
-U-Bits: 110000101000100101100010111001100000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 159
-
-Encoding: 15a
-U-Bits: 010110110000100101100010100011111100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 15a
-
-Encoding: 15a
-U-Bits: 010110110000100101111111101110110101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 15a
-
-Encoding: 15a
-U-Bits: 010110110000100101111000100110001011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 15a
-
-Encoding: 15b
-U-Bits: 100001010000100101111111101111011110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 15b
-
-Encoding: 15b
-U-Bits: 100001010000100101100010100010010111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 15b
-
-Encoding: 15b
-U-Bits: 100001010000100101100101101010101001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 15b
-
-Encoding: 15c
-U-Bits: 001101010110100101111000100000101101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 15c
-
-Encoding: 15c
-U-Bits: 001101010110100101100101101101100100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 15c
-
-Encoding: 15c
-U-Bits: 001101010110100101100010100101011010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 15c
-
-Encoding: 15d
-U-Bits: 111010110110100101100101101100001111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 15d
-
-Encoding: 15d
-U-Bits: 111010110110100101111000100001000110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 15d
-
-Encoding: 15d
-U-Bits: 111010110110100101111111101001111000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 15d
-
-Encoding: 15e
-U-Bits: 011100101110100101111111110011100100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 15e
-
-Encoding: 15e
-U-Bits: 011100101110100101100010111110101101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 15e
-
-Encoding: 15e
-U-Bits: 011100101110100101100101110110010011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 15e
-
-Encoding: 15f
-U-Bits: 101011001110100101100010111111000110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 15f
-
-Encoding: 15f
-U-Bits: 101011001110100101111111110010001111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 15f
-
-Encoding: 15f
-U-Bits: 101011001110100101111000111010110001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 15f
-
-Encoding: 160
-U-Bits: 000000011100100011100011001100100010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 160
-
-Encoding: 160
-U-Bits: 000000011100100011111110000001101011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 160
-
-Encoding: 160
-U-Bits: 000000011100100011111001001001010101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 160
-
-Encoding: 161
-U-Bits: 110111111100100011111110000000000000
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 161
-
-Encoding: 161
-U-Bits: 110111111100100011100011001101001001
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 161
-
-Encoding: 161
-U-Bits: 110111111100100011100100000101110111
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 161
-
-Encoding: 162
-U-Bits: 010001100100100011100100011111101011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 162
-
-Encoding: 162
-U-Bits: 010001100100100011111001010010100010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 162
-
-Encoding: 162
-U-Bits: 010001100100100011111110011010011100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 162
-
-Encoding: 163
-U-Bits: 100110000100100011111001010011001001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 163
-
-Encoding: 163
-U-Bits: 100110000100100011100100011110000000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 163
-
-Encoding: 163
-U-Bits: 100110000100100011100011010110111110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 163
-
-Encoding: 164
-U-Bits: 001010000010100011111110011100111010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 164
-
-Encoding: 164
-U-Bits: 001010000010100011100011010001110011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 164
-
-Encoding: 164
-U-Bits: 001010000010100011100100011001001101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 164
-
-Encoding: 165
-U-Bits: 111101100010100011100011010000011000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 165
-
-Encoding: 165
-U-Bits: 111101100010100011111110011101010001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 165
-
-Encoding: 165
-U-Bits: 111101100010100011111001010101101111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 165
-
-Encoding: 166
-U-Bits: 011011111010100011111001001111110011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 166
-
-Encoding: 166
-U-Bits: 011011111010100011100100000010111010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 166
-
-Encoding: 166
-U-Bits: 011011111010100011100011001010000100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 166
-
-Encoding: 167
-U-Bits: 101100011010100011100100000011010001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 167
-
-Encoding: 167
-U-Bits: 101100011010100011111001001110011000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 167
-
-Encoding: 167
-U-Bits: 101100011010100011111110000110100110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 167
-
-Encoding: 168
-U-Bits: 000110111011000011100100011000100110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 168
-
-Encoding: 168
-U-Bits: 000110111011000011111001010101101111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 168
-
-Encoding: 168
-U-Bits: 000110111011000011111110011101010001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 168
-
-Encoding: 169
-U-Bits: 110001011011000011111001010100000100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 169
-
-Encoding: 169
-U-Bits: 110001011011000011100100011001001101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 169
-
-Encoding: 169
-U-Bits: 110001011011000011100011010001110011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 169
-
-Encoding: 16a
-U-Bits: 010111000011000011100011001011101111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 16a
-
-Encoding: 16a
-U-Bits: 010111000011000011111110000110100110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 16a
-
-Encoding: 16a
-U-Bits: 010111000011000011111001001110011000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 16a
-
-Encoding: 16b
-U-Bits: 100000100011000011111110000111001101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 16b
-
-Encoding: 16b
-U-Bits: 100000100011000011100011001010000100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 16b
-
-Encoding: 16b
-U-Bits: 100000100011000011100100000010111010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 16b
-
-Encoding: 16c
-U-Bits: 001100100101000011111001001000111110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 16c
-
-Encoding: 16c
-U-Bits: 001100100101000011100100000101110111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 16c
-
-Encoding: 16c
-U-Bits: 001100100101000011100011001101001001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 16c
-
-Encoding: 16d
-U-Bits: 111011000101000011100100000100011100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 16d
-
-Encoding: 16d
-U-Bits: 111011000101000011111001001001010101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 16d
-
-Encoding: 16d
-U-Bits: 111011000101000011111110000001101011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 16d
-
-Encoding: 16e
-U-Bits: 011101011101000011111110011011110111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 16e
-
-Encoding: 16e
-U-Bits: 011101011101000011100011010110111110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 16e
-
-Encoding: 16e
-U-Bits: 011101011101000011100100011110000000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 16e
-
-Encoding: 16f
-U-Bits: 101010111101000011100011010111010101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 16f
-
-Encoding: 16f
-U-Bits: 101010111101000011111110011010011100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 16f
-
-Encoding: 16f
-U-Bits: 101010111101000011111001010010100010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 16f
-
-Encoding: 170
-U-Bits: 000001110101011011100010111001100000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 170
-
-Encoding: 170
-U-Bits: 000001110101011011111111110100101001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 170
-
-Encoding: 170
-U-Bits: 000001110101011011111000111100010111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 170
-
-Encoding: 171
-U-Bits: 110110010101011011111111110101000010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 171
-
-Encoding: 171
-U-Bits: 110110010101011011100010111000001011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 171
-
-Encoding: 171
-U-Bits: 110110010101011011100101110000110101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 171
-
-Encoding: 172
-U-Bits: 010000001101011011100101101010101001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 172
-
-Encoding: 172
-U-Bits: 010000001101011011111000100111100000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 172
-
-Encoding: 172
-U-Bits: 010000001101011011111111101111011110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 172
-
-Encoding: 173
-U-Bits: 100111101101011011111000100110001011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 173
-
-Encoding: 173
-U-Bits: 100111101101011011100101101011000010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 173
-
-Encoding: 173
-U-Bits: 100111101101011011100010100011111100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 173
-
-Encoding: 174
-U-Bits: 001011101011011011111111101001111000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 174
-
-Encoding: 174
-U-Bits: 001011101011011011100010100100110001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 174
-
-Encoding: 174
-U-Bits: 001011101011011011100101101100001111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 174
-
-Encoding: 175
-U-Bits: 111100001011011011100010100101011010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 175
-
-Encoding: 175
-U-Bits: 111100001011011011111111101000010011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 175
-
-Encoding: 175
-U-Bits: 111100001011011011111000100000101101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 175
-
-Encoding: 176
-U-Bits: 011010010011011011111000111010110001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 176
-
-Encoding: 176
-U-Bits: 011010010011011011100101110111111000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 176
-
-Encoding: 176
-U-Bits: 011010010011011011100010111111000110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 176
-
-Encoding: 177
-U-Bits: 101101110011011011100101110110010011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 177
-
-Encoding: 177
-U-Bits: 101101110011011011111000111011011010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 177
-
-Encoding: 177
-U-Bits: 101101110011011011111111110011100100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 177
-
-Encoding: 178
-U-Bits: 000111010010111011100101101101100100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 178
-
-Encoding: 178
-U-Bits: 000111010010111011111000100000101101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 178
-
-Encoding: 178
-U-Bits: 000111010010111011111111101000010011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 178
-
-Encoding: 179
-U-Bits: 110000110010111011111000100001000110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 179
-
-Encoding: 179
-U-Bits: 110000110010111011100101101100001111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 179
-
-Encoding: 179
-U-Bits: 110000110010111011100010100100110001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 179
-
-Encoding: 17a
-U-Bits: 010110101010111011100010111110101101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 17a
-
-Encoding: 17a
-U-Bits: 010110101010111011111111110011100100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 17a
-
-Encoding: 17a
-U-Bits: 010110101010111011111000111011011010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 17a
-
-Encoding: 17b
-U-Bits: 100001001010111011111111110010001111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 17b
-
-Encoding: 17b
-U-Bits: 100001001010111011100010111111000110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 17b
-
-Encoding: 17b
-U-Bits: 100001001010111011100101110111111000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 17b
-
-Encoding: 17c
-U-Bits: 001101001100111011111000111101111100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 17c
-
-Encoding: 17c
-U-Bits: 001101001100111011100101110000110101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 17c
-
-Encoding: 17c
-U-Bits: 001101001100111011100010111000001011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 17c
-
-Encoding: 17d
-U-Bits: 111010101100111011100101110001011110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 17d
-
-Encoding: 17d
-U-Bits: 111010101100111011111000111100010111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 17d
-
-Encoding: 17d
-U-Bits: 111010101100111011111111110100101001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 17d
-
-Encoding: 17e
-U-Bits: 011100110100111011111111101110110101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 17e
-
-Encoding: 17e
-U-Bits: 011100110100111011100010100011111100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 17e
-
-Encoding: 17e
-U-Bits: 011100110100111011100101101011000010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 17e
-
-Encoding: 17f
-U-Bits: 101011010100111011100010100010010111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 17f
-
-Encoding: 17f
-U-Bits: 101011010100111011111111101111011110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 17f
-
-Encoding: 17f
-U-Bits: 101011010100111011111000100111100000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 17f
-
-Encoding: 180
-U-Bits: 000000000001110011100000111010110001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 180
-
-Encoding: 180
-U-Bits: 000000000001110011111101110111111000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 180
-
-Encoding: 180
-U-Bits: 000000000001110011111010111111000110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 180
-
-Encoding: 181
-U-Bits: 110111100001110011111101110110010011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 181
-
-Encoding: 181
-U-Bits: 110111100001110011100000111011011010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 181
-
-Encoding: 181
-U-Bits: 110111100001110011100111110011100100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 181
-
-Encoding: 182
-U-Bits: 010001111001110011100111101001111000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 182
-
-Encoding: 182
-U-Bits: 010001111001110011111010100100110001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 182
-
-Encoding: 182
-U-Bits: 010001111001110011111101101100001111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 182
-
-Encoding: 183
-U-Bits: 100110011001110011111010100101011010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 183
-
-Encoding: 183
-U-Bits: 100110011001110011100111101000010011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 183
-
-Encoding: 183
-U-Bits: 100110011001110011100000100000101101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 183
-
-Encoding: 184
-U-Bits: 001010011111110011111101101010101001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 184
-
-Encoding: 184
-U-Bits: 001010011111110011100000100111100000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 184
-
-Encoding: 184
-U-Bits: 001010011111110011100111101111011110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 184
-
-Encoding: 185
-U-Bits: 111101111111110011100000100110001011
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 185
-
-Encoding: 185
-U-Bits: 111101111111110011111101101011000010
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 185
-
-Encoding: 185
-U-Bits: 111101111111110011111010100011111100
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 185
-
-Encoding: 186
-U-Bits: 011011100111110011111010111001100000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 186
-
-Encoding: 186
-U-Bits: 011011100111110011100111110100101001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 186
-
-Encoding: 186
-U-Bits: 011011100111110011100000111100010111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 186
-
-Encoding: 187
-U-Bits: 101100000111110011100111110101000010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 187
-
-Encoding: 187
-U-Bits: 101100000111110011111010111000001011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 187
-
-Encoding: 187
-U-Bits: 101100000111110011111101110000110101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 187
-
-Encoding: 188
-U-Bits: 000110100110010011100111101110110101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 188
-
-Encoding: 188
-U-Bits: 000110100110010011111010100011111100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 188
-
-Encoding: 188
-U-Bits: 000110100110010011111101101011000010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 188
-
-Encoding: 189
-U-Bits: 110001000110010011111010100010010111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 189
-
-Encoding: 189
-U-Bits: 110001000110010011100111101111011110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 189
-
-Encoding: 189
-U-Bits: 110001000110010011100000100111100000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 189
-
-Encoding: 18a
-U-Bits: 010111011110010011100000111101111100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 18a
-
-Encoding: 18a
-U-Bits: 010111011110010011111101110000110101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 18a
-
-Encoding: 18a
-U-Bits: 010111011110010011111010111000001011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 18a
-
-Encoding: 18b
-U-Bits: 100000111110010011111101110001011110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 18b
-
-Encoding: 18b
-U-Bits: 100000111110010011100000111100010111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 18b
-
-Encoding: 18b
-U-Bits: 100000111110010011100111110100101001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 18b
-
-Encoding: 18c
-U-Bits: 001100111000010011111010111110101101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 18c
-
-Encoding: 18c
-U-Bits: 001100111000010011100111110011100100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 18c
-
-Encoding: 18c
-U-Bits: 001100111000010011100000111011011010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 18c
-
-Encoding: 18d
-U-Bits: 111011011000010011100111110010001111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 18d
-
-Encoding: 18d
-U-Bits: 111011011000010011111010111111000110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 18d
-
-Encoding: 18d
-U-Bits: 111011011000010011111101110111111000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 18d
-
-Encoding: 18e
-U-Bits: 011101000000010011111101101101100100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 18e
-
-Encoding: 18e
-U-Bits: 011101000000010011100000100000101101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 18e
-
-Encoding: 18e
-U-Bits: 011101000000010011100111101000010011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 18e
-
-Encoding: 18f
-U-Bits: 101010100000010011100000100001000110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 18f
-
-Encoding: 18f
-U-Bits: 101010100000010011111101101100001111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 18f
-
-Encoding: 18f
-U-Bits: 101010100000010011111010100100110001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 18f
-
-Encoding: 190
-U-Bits: 000001101000001011100001001111110011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 190
-
-Encoding: 190
-U-Bits: 000001101000001011111100000010111010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 190
-
-Encoding: 190
-U-Bits: 000001101000001011111011001010000100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 190
-
-Encoding: 191
-U-Bits: 110110001000001011111100000011010001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 191
-
-Encoding: 191
-U-Bits: 110110001000001011100001001110011000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 191
-
-Encoding: 191
-U-Bits: 110110001000001011100110000110100110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 191
-
-Encoding: 192
-U-Bits: 010000010000001011100110011100111010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 192
-
-Encoding: 192
-U-Bits: 010000010000001011111011010001110011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 192
-
-Encoding: 192
-U-Bits: 010000010000001011111100011001001101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 192
-
-Encoding: 193
-U-Bits: 100111110000001011111011010000011000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 193
-
-Encoding: 193
-U-Bits: 100111110000001011100110011101010001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 193
-
-Encoding: 193
-U-Bits: 100111110000001011100001010101101111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 193
-
-Encoding: 194
-U-Bits: 001011110110001011111100011111101011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 194
-
-Encoding: 194
-U-Bits: 001011110110001011100001010010100010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 194
-
-Encoding: 194
-U-Bits: 001011110110001011100110011010011100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 194
-
-Encoding: 195
-U-Bits: 111100010110001011100001010011001001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 195
-
-Encoding: 195
-U-Bits: 111100010110001011111100011110000000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 195
-
-Encoding: 195
-U-Bits: 111100010110001011111011010110111110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 195
-
-Encoding: 196
-U-Bits: 011010001110001011111011001100100010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 196
-
-Encoding: 196
-U-Bits: 011010001110001011100110000001101011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 196
-
-Encoding: 196
-U-Bits: 011010001110001011100001001001010101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 196
-
-Encoding: 197
-U-Bits: 101101101110001011100110000000000000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 197
-
-Encoding: 197
-U-Bits: 101101101110001011111011001101001001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 197
-
-Encoding: 197
-U-Bits: 101101101110001011111100000101110111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 197
-
-Encoding: 198
-U-Bits: 000111001111101011100110011011110111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 198
-
-Encoding: 198
-U-Bits: 000111001111101011111011010110111110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 198
-
-Encoding: 198
-U-Bits: 000111001111101011111100011110000000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 198
-
-Encoding: 199
-U-Bits: 110000101111101011111011010111010101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 199
-
-Encoding: 199
-U-Bits: 110000101111101011100110011010011100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 199
-
-Encoding: 199
-U-Bits: 110000101111101011100001010010100010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 199
-
-Encoding: 19a
-U-Bits: 010110110111101011100001001000111110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 19a
-
-Encoding: 19a
-U-Bits: 010110110111101011111100000101110111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 19a
-
-Encoding: 19a
-U-Bits: 010110110111101011111011001101001001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 19a
-
-Encoding: 19b
-U-Bits: 100001010111101011111100000100011100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 19b
-
-Encoding: 19b
-U-Bits: 100001010111101011100001001001010101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 19b
-
-Encoding: 19b
-U-Bits: 100001010111101011100110000001101011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 19b
-
-Encoding: 19c
-U-Bits: 001101010001101011111011001011101111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 19c
-
-Encoding: 19c
-U-Bits: 001101010001101011100110000110100110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 19c
-
-Encoding: 19c
-U-Bits: 001101010001101011100001001110011000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 19c
-
-Encoding: 19d
-U-Bits: 111010110001101011100110000111001101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 19d
-
-Encoding: 19d
-U-Bits: 111010110001101011111011001010000100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 19d
-
-Encoding: 19d
-U-Bits: 111010110001101011111100000010111010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 19d
-
-Encoding: 19e
-U-Bits: 011100101001101011111100011000100110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 19e
-
-Encoding: 19e
-U-Bits: 011100101001101011100001010101101111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 19e
-
-Encoding: 19e
-U-Bits: 011100101001101011100110011101010001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 19e
-
-Encoding: 19f
-U-Bits: 101011001001101011100001010100000100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 19f
-
-Encoding: 19f
-U-Bits: 101011001001101011111100011001001101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 19f
-
-Encoding: 19f
-U-Bits: 101011001001101011111011010001110011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 19f
-
-Encoding: 1a0
-U-Bits: 000000011011101101100000100111100000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 1a0
-
-Encoding: 1a0
-U-Bits: 000000011011101101111101101010101001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 1a0
-
-Encoding: 1a0
-U-Bits: 000000011011101101111010100010010111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 1a0
-
-Encoding: 1a1
-U-Bits: 110111111011101101111101101011000010
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 1a1
-
-Encoding: 1a1
-U-Bits: 110111111011101101100000100110001011
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 1a1
-
-Encoding: 1a1
-U-Bits: 110111111011101101100111101110110101
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 1a1
-
-Encoding: 1a2
-U-Bits: 010001100011101101100111110100101001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 1a2
-
-Encoding: 1a2
-U-Bits: 010001100011101101111010111001100000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 1a2
-
-Encoding: 1a2
-U-Bits: 010001100011101101111101110001011110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 1a2
-
-Encoding: 1a3
-U-Bits: 100110000011101101111010111000001011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 1a3
-
-Encoding: 1a3
-U-Bits: 100110000011101101100111110101000010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 1a3
-
-Encoding: 1a3
-U-Bits: 100110000011101101100000111101111100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 1a3
-
-Encoding: 1a4
-U-Bits: 001010000101101101111101110111111000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 1a4
-
-Encoding: 1a4
-U-Bits: 001010000101101101100000111010110001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 1a4
-
-Encoding: 1a4
-U-Bits: 001010000101101101100111110010001111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 1a4
-
-Encoding: 1a5
-U-Bits: 111101100101101101100000111011011010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 1a5
-
-Encoding: 1a5
-U-Bits: 111101100101101101111101110110010011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 1a5
-
-Encoding: 1a5
-U-Bits: 111101100101101101111010111110101101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 1a5
-
-Encoding: 1a6
-U-Bits: 011011111101101101111010100100110001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 1a6
-
-Encoding: 1a6
-U-Bits: 011011111101101101100111101001111000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 1a6
-
-Encoding: 1a6
-U-Bits: 011011111101101101100000100001000110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 1a6
-
-Encoding: 1a7
-U-Bits: 101100011101101101100111101000010011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 1a7
-
-Encoding: 1a7
-U-Bits: 101100011101101101111010100101011010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 1a7
-
-Encoding: 1a7
-U-Bits: 101100011101101101111101101101100100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 1a7
-
-Encoding: 1a8
-U-Bits: 000110111100001101100111110011100100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 1a8
-
-Encoding: 1a8
-U-Bits: 000110111100001101111010111110101101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 1a8
-
-Encoding: 1a8
-U-Bits: 000110111100001101111101110110010011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 1a8
-
-Encoding: 1a9
-U-Bits: 110001011100001101111010111111000110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 1a9
-
-Encoding: 1a9
-U-Bits: 110001011100001101100111110010001111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 1a9
-
-Encoding: 1a9
-U-Bits: 110001011100001101100000111010110001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 1a9
-
-Encoding: 1aa
-U-Bits: 010111000100001101100000100000101101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 1aa
-
-Encoding: 1aa
-U-Bits: 010111000100001101111101101101100100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 1aa
-
-Encoding: 1aa
-U-Bits: 010111000100001101111010100101011010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 1aa
-
-Encoding: 1ab
-U-Bits: 100000100100001101111101101100001111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 1ab
-
-Encoding: 1ab
-U-Bits: 100000100100001101100000100001000110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 1ab
-
-Encoding: 1ab
-U-Bits: 100000100100001101100111101001111000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 1ab
-
-Encoding: 1ac
-U-Bits: 001100100010001101111010100011111100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 1ac
-
-Encoding: 1ac
-U-Bits: 001100100010001101100111101110110101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 1ac
-
-Encoding: 1ac
-U-Bits: 001100100010001101100000100110001011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 1ac
-
-Encoding: 1ad
-U-Bits: 111011000010001101100111101111011110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 1ad
-
-Encoding: 1ad
-U-Bits: 111011000010001101111010100010010111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 1ad
-
-Encoding: 1ad
-U-Bits: 111011000010001101111101101010101001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 1ad
-
-Encoding: 1ae
-U-Bits: 011101011010001101111101110000110101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 1ae
-
-Encoding: 1ae
-U-Bits: 011101011010001101100000111101111100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 1ae
-
-Encoding: 1ae
-U-Bits: 011101011010001101100111110101000010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 1ae
-
-Encoding: 1af
-U-Bits: 101010111010001101100000111100010111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 1af
-
-Encoding: 1af
-U-Bits: 101010111010001101111101110001011110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 1af
-
-Encoding: 1af
-U-Bits: 101010111010001101111010111001100000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 1af
-
-Encoding: 1b0
-U-Bits: 000001110010010101100001010010100010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 1b0
-
-Encoding: 1b0
-U-Bits: 000001110010010101111100011111101011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 1b0
-
-Encoding: 1b0
-U-Bits: 000001110010010101111011010111010101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 1b0
-
-Encoding: 1b1
-U-Bits: 110110010010010101111100011110000000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1b1
-
-Encoding: 1b1
-U-Bits: 110110010010010101100001010011001001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 1b1
-
-Encoding: 1b1
-U-Bits: 110110010010010101100110011011110111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 1b1
-
-Encoding: 1b2
-U-Bits: 010000001010010101100110000001101011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 1b2
-
-Encoding: 1b2
-U-Bits: 010000001010010101111011001100100010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 1b2
-
-Encoding: 1b2
-U-Bits: 010000001010010101111100000100011100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 1b2
-
-Encoding: 1b3
-U-Bits: 100111101010010101111011001101001001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 1b3
-
-Encoding: 1b3
-U-Bits: 100111101010010101100110000000000000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1b3
-
-Encoding: 1b3
-U-Bits: 100111101010010101100001001000111110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 1b3
-
-Encoding: 1b4
-U-Bits: 001011101100010101111100000010111010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 1b4
-
-Encoding: 1b4
-U-Bits: 001011101100010101100001001111110011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 1b4
-
-Encoding: 1b4
-U-Bits: 001011101100010101100110000111001101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 1b4
-
-Encoding: 1b5
-U-Bits: 111100001100010101100001001110011000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 1b5
-
-Encoding: 1b5
-U-Bits: 111100001100010101111100000011010001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 1b5
-
-Encoding: 1b5
-U-Bits: 111100001100010101111011001011101111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 1b5
-
-Encoding: 1b6
-U-Bits: 011010010100010101111011010001110011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 1b6
-
-Encoding: 1b6
-U-Bits: 011010010100010101100110011100111010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 1b6
-
-Encoding: 1b6
-U-Bits: 011010010100010101100001010100000100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 1b6
-
-Encoding: 1b7
-U-Bits: 101101110100010101100110011101010001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 1b7
-
-Encoding: 1b7
-U-Bits: 101101110100010101111011010000011000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 1b7
-
-Encoding: 1b7
-U-Bits: 101101110100010101111100011000100110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 1b7
-
-Encoding: 1b8
-U-Bits: 000111010101110101100110000110100110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 1b8
-
-Encoding: 1b8
-U-Bits: 000111010101110101111011001011101111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 1b8
-
-Encoding: 1b8
-U-Bits: 000111010101110101111100000011010001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 1b8
-
-Encoding: 1b9
-U-Bits: 110000110101110101111011001010000100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 1b9
-
-Encoding: 1b9
-U-Bits: 110000110101110101100110000111001101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 1b9
-
-Encoding: 1b9
-U-Bits: 110000110101110101100001001111110011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 1b9
-
-Encoding: 1ba
-U-Bits: 010110101101110101100001010101101111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 1ba
-
-Encoding: 1ba
-U-Bits: 010110101101110101111100011000100110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 1ba
-
-Encoding: 1ba
-U-Bits: 010110101101110101111011010000011000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 1ba
-
-Encoding: 1bb
-U-Bits: 100001001101110101111100011001001101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 1bb
-
-Encoding: 1bb
-U-Bits: 100001001101110101100001010100000100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 1bb
-
-Encoding: 1bb
-U-Bits: 100001001101110101100110011100111010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 1bb
-
-Encoding: 1bc
-U-Bits: 001101001011110101111011010110111110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 1bc
-
-Encoding: 1bc
-U-Bits: 001101001011110101100110011011110111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 1bc
-
-Encoding: 1bc
-U-Bits: 001101001011110101100001010011001001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 1bc
-
-Encoding: 1bd
-U-Bits: 111010101011110101100110011010011100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 1bd
-
-Encoding: 1bd
-U-Bits: 111010101011110101111011010111010101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 1bd
-
-Encoding: 1bd
-U-Bits: 111010101011110101111100011111101011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 1bd
-
-Encoding: 1be
-U-Bits: 011100110011110101111100000101110111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 1be
-
-Encoding: 1be
-U-Bits: 011100110011110101100001001000111110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 1be
-
-Encoding: 1be
-U-Bits: 011100110011110101100110000000000000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1be
-
-Encoding: 1bf
-U-Bits: 101011010011110101100001001001010101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 1bf
-
-Encoding: 1bf
-U-Bits: 101011010011110101111100000100011100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 1bf
-
-Encoding: 1bf
-U-Bits: 101011010011110101111011001100100010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 1bf
-
-Encoding: 1c0
-U-Bits: 000000000111010100011100011001001101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 1c0
-
-Encoding: 1c0
-U-Bits: 000000000111010100000001010100000100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 1c0
-
-Encoding: 1c0
-U-Bits: 000000000111010100000110011100111010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 1c0
-
-Encoding: 1c1
-U-Bits: 110111100111010100000001010101101111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 1c1
-
-Encoding: 1c1
-U-Bits: 110111100111010100011100011000100110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 1c1
-
-Encoding: 1c1
-U-Bits: 110111100111010100011011010000011000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 1c1
-
-Encoding: 1c2
-U-Bits: 010001111111010100011011001010000100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 1c2
-
-Encoding: 1c2
-U-Bits: 010001111111010100000110000111001101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 1c2
-
-Encoding: 1c2
-U-Bits: 010001111111010100000001001111110011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 1c2
-
-Encoding: 1c3
-U-Bits: 100110011111010100000110000110100110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 1c3
-
-Encoding: 1c3
-U-Bits: 100110011111010100011011001011101111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 1c3
-
-Encoding: 1c3
-U-Bits: 100110011111010100011100000011010001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 1c3
-
-Encoding: 1c4
-U-Bits: 001010011001010100000001001001010101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 1c4
-
-Encoding: 1c4
-U-Bits: 001010011001010100011100000100011100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 1c4
-
-Encoding: 1c4
-U-Bits: 001010011001010100011011001100100010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 1c4
-
-Encoding: 1c5
-U-Bits: 111101111001010100011100000101110111
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 1c5
-
-Encoding: 1c5
-U-Bits: 111101111001010100000001001000111110
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 1c5
-
-Encoding: 1c5
-U-Bits: 111101111001010100000110000000000000
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1c5
-
-Encoding: 1c6
-U-Bits: 011011100001010100000110011010011100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 1c6
-
-Encoding: 1c6
-U-Bits: 011011100001010100011011010111010101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 1c6
-
-Encoding: 1c6
-U-Bits: 011011100001010100011100011111101011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 1c6
-
-Encoding: 1c7
-U-Bits: 101100000001010100011011010110111110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 1c7
-
-Encoding: 1c7
-U-Bits: 101100000001010100000110011011110111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 1c7
-
-Encoding: 1c7
-U-Bits: 101100000001010100000001010011001001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 1c7
-
-Encoding: 1c8
-U-Bits: 000110100000110100011011001101001001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 1c8
-
-Encoding: 1c8
-U-Bits: 000110100000110100000110000000000000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1c8
-
-Encoding: 1c8
-U-Bits: 000110100000110100000001001000111110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 1c8
-
-Encoding: 1c9
-U-Bits: 110001000000110100000110000001101011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 1c9
-
-Encoding: 1c9
-U-Bits: 110001000000110100011011001100100010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 1c9
-
-Encoding: 1c9
-U-Bits: 110001000000110100011100000100011100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 1c9
-
-Encoding: 1ca
-U-Bits: 010111011000110100011100011110000000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1ca
-
-Encoding: 1ca
-U-Bits: 010111011000110100000001010011001001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 1ca
-
-Encoding: 1ca
-U-Bits: 010111011000110100000110011011110111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 1ca
-
-Encoding: 1cb
-U-Bits: 100000111000110100000001010010100010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 1cb
-
-Encoding: 1cb
-U-Bits: 100000111000110100011100011111101011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 1cb
-
-Encoding: 1cb
-U-Bits: 100000111000110100011011010111010101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 1cb
-
-Encoding: 1cc
-U-Bits: 001100111110110100000110011101010001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 1cc
-
-Encoding: 1cc
-U-Bits: 001100111110110100011011010000011000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 1cc
-
-Encoding: 1cc
-U-Bits: 001100111110110100011100011000100110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 1cc
-
-Encoding: 1cd
-U-Bits: 111011011110110100011011010001110011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 1cd
-
-Encoding: 1cd
-U-Bits: 111011011110110100000110011100111010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 1cd
-
-Encoding: 1cd
-U-Bits: 111011011110110100000001010100000100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 1cd
-
-Encoding: 1ce
-U-Bits: 011101000110110100000001001110011000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 1ce
-
-Encoding: 1ce
-U-Bits: 011101000110110100011100000011010001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 1ce
-
-Encoding: 1ce
-U-Bits: 011101000110110100011011001011101111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 1ce
-
-Encoding: 1cf
-U-Bits: 101010100110110100011100000010111010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 1cf
-
-Encoding: 1cf
-U-Bits: 101010100110110100000001001111110011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 1cf
-
-Encoding: 1cf
-U-Bits: 101010100110110100000110000111001101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 1cf
-
-Encoding: 1d0
-U-Bits: 000001101110101100011101101100001111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 1d0
-
-Encoding: 1d0
-U-Bits: 000001101110101100000000100001000110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 1d0
-
-Encoding: 1d0
-U-Bits: 000001101110101100000111101001111000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 1d0
-
-Encoding: 1d1
-U-Bits: 110110001110101100000000100000101101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 1d1
-
-Encoding: 1d1
-U-Bits: 110110001110101100011101101101100100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 1d1
-
-Encoding: 1d1
-U-Bits: 110110001110101100011010100101011010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 1d1
-
-Encoding: 1d2
-U-Bits: 010000010110101100011010111111000110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 1d2
-
-Encoding: 1d2
-U-Bits: 010000010110101100000111110010001111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 1d2
-
-Encoding: 1d2
-U-Bits: 010000010110101100000000111010110001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 1d2
-
-Encoding: 1d3
-U-Bits: 100111110110101100000111110011100100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 1d3
-
-Encoding: 1d3
-U-Bits: 100111110110101100011010111110101101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 1d3
-
-Encoding: 1d3
-U-Bits: 100111110110101100011101110110010011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 1d3
-
-Encoding: 1d4
-U-Bits: 001011110000101100000000111100010111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 1d4
-
-Encoding: 1d4
-U-Bits: 001011110000101100011101110001011110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 1d4
-
-Encoding: 1d4
-U-Bits: 001011110000101100011010111001100000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 1d4
-
-Encoding: 1d5
-U-Bits: 111100010000101100011101110000110101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 1d5
-
-Encoding: 1d5
-U-Bits: 111100010000101100000000111101111100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 1d5
-
-Encoding: 1d5
-U-Bits: 111100010000101100000111110101000010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 1d5
-
-Encoding: 1d6
-U-Bits: 011010001000101100000111101111011110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 1d6
-
-Encoding: 1d6
-U-Bits: 011010001000101100011010100010010111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 1d6
-
-Encoding: 1d6
-U-Bits: 011010001000101100011101101010101001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 1d6
-
-Encoding: 1d7
-U-Bits: 101101101000101100011010100011111100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 1d7
-
-Encoding: 1d7
-U-Bits: 101101101000101100000111101110110101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 1d7
-
-Encoding: 1d7
-U-Bits: 101101101000101100000000100110001011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 1d7
-
-Encoding: 1d8
-U-Bits: 000111001001001100011010111000001011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 1d8
-
-Encoding: 1d8
-U-Bits: 000111001001001100000111110101000010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 1d8
-
-Encoding: 1d8
-U-Bits: 000111001001001100000000111101111100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 1d8
-
-Encoding: 1d9
-U-Bits: 110000101001001100000111110100101001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 1d9
-
-Encoding: 1d9
-U-Bits: 110000101001001100011010111001100000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 1d9
-
-Encoding: 1d9
-U-Bits: 110000101001001100011101110001011110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 1d9
-
-Encoding: 1da
-U-Bits: 010110110001001100011101101011000010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 1da
-
-Encoding: 1da
-U-Bits: 010110110001001100000000100110001011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 1da
-
-Encoding: 1da
-U-Bits: 010110110001001100000111101110110101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 1da
-
-Encoding: 1db
-U-Bits: 100001010001001100000000100111100000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 1db
-
-Encoding: 1db
-U-Bits: 100001010001001100011101101010101001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 1db
-
-Encoding: 1db
-U-Bits: 100001010001001100011010100010010111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 1db
-
-Encoding: 1dc
-U-Bits: 001101010111001100000111101000010011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 1dc
-
-Encoding: 1dc
-U-Bits: 001101010111001100011010100101011010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 1dc
-
-Encoding: 1dc
-U-Bits: 001101010111001100011101101101100100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 1dc
-
-Encoding: 1dd
-U-Bits: 111010110111001100011010100100110001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 1dd
-
-Encoding: 1dd
-U-Bits: 111010110111001100000111101001111000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 1dd
-
-Encoding: 1dd
-U-Bits: 111010110111001100000000100001000110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 1dd
-
-Encoding: 1de
-U-Bits: 011100101111001100000000111011011010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 1de
-
-Encoding: 1de
-U-Bits: 011100101111001100011101110110010011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 1de
-
-Encoding: 1de
-U-Bits: 011100101111001100011010111110101101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 1de
-
-Encoding: 1df
-U-Bits: 101011001111001100011101110111111000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 1df
-
-Encoding: 1df
-U-Bits: 101011001111001100000000111010110001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 1df
-
-Encoding: 1df
-U-Bits: 101011001111001100000111110010001111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 1df
-
-Encoding: 1e0
-U-Bits: 000000011101001010011100000100011100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 1e0
-
-Encoding: 1e0
-U-Bits: 000000011101001010000001001001010101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 1e0
-
-Encoding: 1e0
-U-Bits: 000000011101001010000110000001101011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 1e0
-
-Encoding: 1e1
-U-Bits: 110111111101001010000001001000111110
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 1e1
-
-Encoding: 1e1
-U-Bits: 110111111101001010011100000101110111
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 1e1
-
-Encoding: 1e1
-U-Bits: 110111111101001010011011001101001001
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 1e1
-
-Encoding: 1e2
-U-Bits: 010001100101001010011011010111010101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 1e2
-
-Encoding: 1e2
-U-Bits: 010001100101001010000110011010011100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 1e2
-
-Encoding: 1e2
-U-Bits: 010001100101001010000001010010100010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 1e2
-
-Encoding: 1e3
-U-Bits: 100110000101001010000110011011110111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 1e3
-
-Encoding: 1e3
-U-Bits: 100110000101001010011011010110111110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 1e3
-
-Encoding: 1e3
-U-Bits: 100110000101001010011100011110000000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1e3
-
-Encoding: 1e4
-U-Bits: 001010000011001010000001010100000100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 1e4
-
-Encoding: 1e4
-U-Bits: 001010000011001010011100011001001101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 1e4
-
-Encoding: 1e4
-U-Bits: 001010000011001010011011010001110011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 1e4
-
-Encoding: 1e5
-U-Bits: 111101100011001010011100011000100110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 1e5
-
-Encoding: 1e5
-U-Bits: 111101100011001010000001010101101111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 1e5
-
-Encoding: 1e5
-U-Bits: 111101100011001010000110011101010001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 1e5
-
-Encoding: 1e6
-U-Bits: 011011111011001010000110000111001101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 1e6
-
-Encoding: 1e6
-U-Bits: 011011111011001010011011001010000100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 1e6
-
-Encoding: 1e6
-U-Bits: 011011111011001010011100000010111010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 1e6
-
-Encoding: 1e7
-U-Bits: 101100011011001010011011001011101111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 1e7
-
-Encoding: 1e7
-U-Bits: 101100011011001010000110000110100110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 1e7
-
-Encoding: 1e7
-U-Bits: 101100011011001010000001001110011000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 1e7
-
-Encoding: 1e8
-U-Bits: 000110111010101010011011010000011000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 1e8
-
-Encoding: 1e8
-U-Bits: 000110111010101010000110011101010001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 1e8
-
-Encoding: 1e8
-U-Bits: 000110111010101010000001010101101111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 1e8
-
-Encoding: 1e9
-U-Bits: 110001011010101010000110011100111010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 1e9
-
-Encoding: 1e9
-U-Bits: 110001011010101010011011010001110011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 1e9
-
-Encoding: 1e9
-U-Bits: 110001011010101010011100011001001101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 1e9
-
-Encoding: 1ea
-U-Bits: 010111000010101010011100000011010001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 1ea
-
-Encoding: 1ea
-U-Bits: 010111000010101010000001001110011000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 1ea
-
-Encoding: 1ea
-U-Bits: 010111000010101010000110000110100110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 1ea
-
-Encoding: 1eb
-U-Bits: 100000100010101010000001001111110011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 1eb
-
-Encoding: 1eb
-U-Bits: 100000100010101010011100000010111010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 1eb
-
-Encoding: 1eb
-U-Bits: 100000100010101010011011001010000100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 1eb
-
-Encoding: 1ec
-U-Bits: 001100100100101010000110000000000000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1ec
-
-Encoding: 1ec
-U-Bits: 001100100100101010011011001101001001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 1ec
-
-Encoding: 1ec
-U-Bits: 001100100100101010011100000101110111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 1ec
-
-Encoding: 1ed
-U-Bits: 111011000100101010011011001100100010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 1ed
-
-Encoding: 1ed
-U-Bits: 111011000100101010000110000001101011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 1ed
-
-Encoding: 1ed
-U-Bits: 111011000100101010000001001001010101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 1ed
-
-Encoding: 1ee
-U-Bits: 011101011100101010000001010011001001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 1ee
-
-Encoding: 1ee
-U-Bits: 011101011100101010011100011110000000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 1ee
-
-Encoding: 1ee
-U-Bits: 011101011100101010011011010110111110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 1ee
-
-Encoding: 1ef
-U-Bits: 101010111100101010011100011111101011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 1ef
-
-Encoding: 1ef
-U-Bits: 101010111100101010000001010010100010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 1ef
-
-Encoding: 1ef
-U-Bits: 101010111100101010000110011010011100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 1ef
-
-Encoding: 1f0
-U-Bits: 000001110100110010011101110001011110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 1f0
-
-Encoding: 1f0
-U-Bits: 000001110100110010000000111100010111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 1f0
-
-Encoding: 1f0
-U-Bits: 000001110100110010000111110100101001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 1f0
-
-Encoding: 1f1
-U-Bits: 110110010100110010000000111101111100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 1f1
-
-Encoding: 1f1
-U-Bits: 110110010100110010011101110000110101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 1f1
-
-Encoding: 1f1
-U-Bits: 110110010100110010011010111000001011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 1f1
-
-Encoding: 1f2
-U-Bits: 010000001100110010011010100010010111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 1f2
-
-Encoding: 1f2
-U-Bits: 010000001100110010000111101111011110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 1f2
-
-Encoding: 1f2
-U-Bits: 010000001100110010000000100111100000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 1f2
-
-Encoding: 1f3
-U-Bits: 100111101100110010000111101110110101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 1f3
-
-Encoding: 1f3
-U-Bits: 100111101100110010011010100011111100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 1f3
-
-Encoding: 1f3
-U-Bits: 100111101100110010011101101011000010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 1f3
-
-Encoding: 1f4
-U-Bits: 001011101010110010000000100001000110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 1f4
-
-Encoding: 1f4
-U-Bits: 001011101010110010011101101100001111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 1f4
-
-Encoding: 1f4
-U-Bits: 001011101010110010011010100100110001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 1f4
-
-Encoding: 1f5
-U-Bits: 111100001010110010011101101101100100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 1f5
-
-Encoding: 1f5
-U-Bits: 111100001010110010000000100000101101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 1f5
-
-Encoding: 1f5
-U-Bits: 111100001010110010000111101000010011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 1f5
-
-Encoding: 1f6
-U-Bits: 011010010010110010000111110010001111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 1f6
-
-Encoding: 1f6
-U-Bits: 011010010010110010011010111111000110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 1f6
-
-Encoding: 1f6
-U-Bits: 011010010010110010011101110111111000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 1f6
-
-Encoding: 1f7
-U-Bits: 101101110010110010011010111110101101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 1f7
-
-Encoding: 1f7
-U-Bits: 101101110010110010000111110011100100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 1f7
-
-Encoding: 1f7
-U-Bits: 101101110010110010000000111011011010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 1f7
-
-Encoding: 1f8
-U-Bits: 000111010011010010011010100101011010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 1f8
-
-Encoding: 1f8
-U-Bits: 000111010011010010000111101000010011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 1f8
-
-Encoding: 1f8
-U-Bits: 000111010011010010000000100000101101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 1f8
-
-Encoding: 1f9
-U-Bits: 110000110011010010000111101001111000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 1f9
-
-Encoding: 1f9
-U-Bits: 110000110011010010011010100100110001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 1f9
-
-Encoding: 1f9
-U-Bits: 110000110011010010011101101100001111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 1f9
-
-Encoding: 1fa
-U-Bits: 010110101011010010011101110110010011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 1fa
-
-Encoding: 1fa
-U-Bits: 010110101011010010000000111011011010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 1fa
-
-Encoding: 1fa
-U-Bits: 010110101011010010000111110011100100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 1fa
-
-Encoding: 1fb
-U-Bits: 100001001011010010000000111010110001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 1fb
-
-Encoding: 1fb
-U-Bits: 100001001011010010011101110111111000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 1fb
-
-Encoding: 1fb
-U-Bits: 100001001011010010011010111111000110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 1fb
-
-Encoding: 1fc
-U-Bits: 001101001101010010000111110101000010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 1fc
-
-Encoding: 1fc
-U-Bits: 001101001101010010011010111000001011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 1fc
-
-Encoding: 1fc
-U-Bits: 001101001101010010011101110000110101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 1fc
-
-Encoding: 1fd
-U-Bits: 111010101101010010011010111001100000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 1fd
-
-Encoding: 1fd
-U-Bits: 111010101101010010000111110100101001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 1fd
-
-Encoding: 1fd
-U-Bits: 111010101101010010000000111100010111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 1fd
-
-Encoding: 1fe
-U-Bits: 011100110101010010000000100110001011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 1fe
-
-Encoding: 1fe
-U-Bits: 011100110101010010011101101011000010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 1fe
-
-Encoding: 1fe
-U-Bits: 011100110101010010011010100011111100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 1fe
-
-Encoding: 1ff
-U-Bits: 101011010101010010011101101010101001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 1ff
-
-Encoding: 1ff
-U-Bits: 101011010101010010000000100111100000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 1ff
-
-Encoding: 1ff
-U-Bits: 101011010101010010000111101111011110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 1ff
-
-Encoding: 200
-U-Bits: 000000000000000110111011011000001011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 200
-
-Encoding: 200
-U-Bits: 000000000000000110100110010101000010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 200
-
-Encoding: 200
-U-Bits: 000000000000000110100001011101111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 200
-
-Encoding: 201
-U-Bits: 110111100000000110100110010100101001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 201
-
-Encoding: 201
-U-Bits: 110111100000000110111011011001100000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 201
-
-Encoding: 201
-U-Bits: 110111100000000110111100010001011110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 201
-
-Encoding: 202
-U-Bits: 010001111000000110111100001011000010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 202
-
-Encoding: 202
-U-Bits: 010001111000000110100001000110001011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 202
-
-Encoding: 202
-U-Bits: 010001111000000110100110001110110101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 202
-
-Encoding: 203
-U-Bits: 100110011000000110100001000111100000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 203
-
-Encoding: 203
-U-Bits: 100110011000000110111100001010101001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 203
-
-Encoding: 203
-U-Bits: 100110011000000110111011000010010111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 203
-
-Encoding: 204
-U-Bits: 001010011110000110100110001000010011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 204
-
-Encoding: 204
-U-Bits: 001010011110000110111011000101011010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 204
-
-Encoding: 204
-U-Bits: 001010011110000110111100001101100100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 204
-
-Encoding: 205
-U-Bits: 111101111110000110111011000100110001
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 205
-
-Encoding: 205
-U-Bits: 111101111110000110100110001001111000
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 205
-
-Encoding: 205
-U-Bits: 111101111110000110100001000001000110
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 205
-
-Encoding: 206
-U-Bits: 011011100110000110100001011011011010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 206
-
-Encoding: 206
-U-Bits: 011011100110000110111100010110010011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 206
-
-Encoding: 206
-U-Bits: 011011100110000110111011011110101101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 206
-
-Encoding: 207
-U-Bits: 101100000110000110111100010111111000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 207
-
-Encoding: 207
-U-Bits: 101100000110000110100001011010110001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 207
-
-Encoding: 207
-U-Bits: 101100000110000110100110010010001111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 207
-
-Encoding: 208
-U-Bits: 000110100111100110111100001100001111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 208
-
-Encoding: 208
-U-Bits: 000110100111100110100001000001000110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 208
-
-Encoding: 208
-U-Bits: 000110100111100110100110001001111000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 208
-
-Encoding: 209
-U-Bits: 110001000111100110100001000000101101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 209
-
-Encoding: 209
-U-Bits: 110001000111100110111100001101100100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 209
-
-Encoding: 209
-U-Bits: 110001000111100110111011000101011010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 209
-
-Encoding: 20a
-U-Bits: 010111011111100110111011011111000110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 20a
-
-Encoding: 20a
-U-Bits: 010111011111100110100110010010001111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 20a
-
-Encoding: 20a
-U-Bits: 010111011111100110100001011010110001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 20a
-
-Encoding: 20b
-U-Bits: 100000111111100110100110010011100100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 20b
-
-Encoding: 20b
-U-Bits: 100000111111100110111011011110101101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 20b
-
-Encoding: 20b
-U-Bits: 100000111111100110111100010110010011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 20b
-
-Encoding: 20c
-U-Bits: 001100111001100110100001011100010111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 20c
-
-Encoding: 20c
-U-Bits: 001100111001100110111100010001011110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 20c
-
-Encoding: 20c
-U-Bits: 001100111001100110111011011001100000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 20c
-
-Encoding: 20d
-U-Bits: 111011011001100110111100010000110101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 20d
-
-Encoding: 20d
-U-Bits: 111011011001100110100001011101111100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 20d
-
-Encoding: 20d
-U-Bits: 111011011001100110100110010101000010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 20d
-
-Encoding: 20e
-U-Bits: 011101000001100110100110001111011110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 20e
-
-Encoding: 20e
-U-Bits: 011101000001100110111011000010010111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 20e
-
-Encoding: 20e
-U-Bits: 011101000001100110111100001010101001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 20e
-
-Encoding: 20f
-U-Bits: 101010100001100110111011000011111100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 20f
-
-Encoding: 20f
-U-Bits: 101010100001100110100110001110110101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 20f
-
-Encoding: 20f
-U-Bits: 101010100001100110100001000110001011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 20f
-
-Encoding: 210
-U-Bits: 000001101001111110111010101101001001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 210
-
-Encoding: 210
-U-Bits: 000001101001111110100111100000000000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 210
-
-Encoding: 210
-U-Bits: 000001101001111110100000101000111110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 210
-
-Encoding: 211
-U-Bits: 110110001001111110100111100001101011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 211
-
-Encoding: 211
-U-Bits: 110110001001111110111010101100100010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 211
-
-Encoding: 211
-U-Bits: 110110001001111110111101100100011100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 211
-
-Encoding: 212
-U-Bits: 010000010001111110111101111110000000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 212
-
-Encoding: 212
-U-Bits: 010000010001111110100000110011001001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 212
-
-Encoding: 212
-U-Bits: 010000010001111110100111111011110111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 212
-
-Encoding: 213
-U-Bits: 100111110001111110100000110010100010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 213
-
-Encoding: 213
-U-Bits: 100111110001111110111101111111101011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 213
-
-Encoding: 213
-U-Bits: 100111110001111110111010110111010101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 213
-
-Encoding: 214
-U-Bits: 001011110111111110100111111101010001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 214
-
-Encoding: 214
-U-Bits: 001011110111111110111010110000011000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 214
-
-Encoding: 214
-U-Bits: 001011110111111110111101111000100110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 214
-
-Encoding: 215
-U-Bits: 111100010111111110111010110001110011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 215
-
-Encoding: 215
-U-Bits: 111100010111111110100111111100111010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 215
-
-Encoding: 215
-U-Bits: 111100010111111110100000110100000100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 215
-
-Encoding: 216
-U-Bits: 011010001111111110100000101110011000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 216
-
-Encoding: 216
-U-Bits: 011010001111111110111101100011010001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 216
-
-Encoding: 216
-U-Bits: 011010001111111110111010101011101111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 216
-
-Encoding: 217
-U-Bits: 101101101111111110111101100010111010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 217
-
-Encoding: 217
-U-Bits: 101101101111111110100000101111110011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 217
-
-Encoding: 217
-U-Bits: 101101101111111110100111100111001101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 217
-
-Encoding: 218
-U-Bits: 000111001110011110111101111001001101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 218
-
-Encoding: 218
-U-Bits: 000111001110011110100000110100000100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 218
-
-Encoding: 218
-U-Bits: 000111001110011110100111111100111010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 218
-
-Encoding: 219
-U-Bits: 110000101110011110100000110101101111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 219
-
-Encoding: 219
-U-Bits: 110000101110011110111101111000100110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 219
-
-Encoding: 219
-U-Bits: 110000101110011110111010110000011000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 219
-
-Encoding: 21a
-U-Bits: 010110110110011110111010101010000100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 21a
-
-Encoding: 21a
-U-Bits: 010110110110011110100111100111001101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 21a
-
-Encoding: 21a
-U-Bits: 010110110110011110100000101111110011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 21a
-
-Encoding: 21b
-U-Bits: 100001010110011110100111100110100110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 21b
-
-Encoding: 21b
-U-Bits: 100001010110011110111010101011101111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 21b
-
-Encoding: 21b
-U-Bits: 100001010110011110111101100011010001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 21b
-
-Encoding: 21c
-U-Bits: 001101010000011110100000101001010101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 21c
-
-Encoding: 21c
-U-Bits: 001101010000011110111101100100011100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 21c
-
-Encoding: 21c
-U-Bits: 001101010000011110111010101100100010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 21c
-
-Encoding: 21d
-U-Bits: 111010110000011110111101100101110111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 21d
-
-Encoding: 21d
-U-Bits: 111010110000011110100000101000111110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 21d
-
-Encoding: 21d
-U-Bits: 111010110000011110100111100000000000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 21d
-
-Encoding: 21e
-U-Bits: 011100101000011110100111111010011100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 21e
-
-Encoding: 21e
-U-Bits: 011100101000011110111010110111010101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 21e
-
-Encoding: 21e
-U-Bits: 011100101000011110111101111111101011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 21e
-
-Encoding: 21f
-U-Bits: 101011001000011110111010110110111110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 21f
-
-Encoding: 21f
-U-Bits: 101011001000011110100111111011110111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 21f
-
-Encoding: 21f
-U-Bits: 101011001000011110100000110011001001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 21f
-
-Encoding: 220
-U-Bits: 000000011010011000111011000101011010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 220
-
-Encoding: 220
-U-Bits: 000000011010011000100110001000010011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 220
-
-Encoding: 220
-U-Bits: 000000011010011000100001000000101101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 220
-
-Encoding: 221
-U-Bits: 110111111010011000100110001001111000
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 221
-
-Encoding: 221
-U-Bits: 110111111010011000111011000100110001
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 221
-
-Encoding: 221
-U-Bits: 110111111010011000111100001100001111
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 221
-
-Encoding: 222
-U-Bits: 010001100010011000111100010110010011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 222
-
-Encoding: 222
-U-Bits: 010001100010011000100001011011011010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 222
-
-Encoding: 222
-U-Bits: 010001100010011000100110010011100100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 222
-
-Encoding: 223
-U-Bits: 100110000010011000100001011010110001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 223
-
-Encoding: 223
-U-Bits: 100110000010011000111100010111111000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 223
-
-Encoding: 223
-U-Bits: 100110000010011000111011011111000110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 223
-
-Encoding: 224
-U-Bits: 001010000100011000100110010101000010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 224
-
-Encoding: 224
-U-Bits: 001010000100011000111011011000001011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 224
-
-Encoding: 224
-U-Bits: 001010000100011000111100010000110101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 224
-
-Encoding: 225
-U-Bits: 111101100100011000111011011001100000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 225
-
-Encoding: 225
-U-Bits: 111101100100011000100110010100101001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 225
-
-Encoding: 225
-U-Bits: 111101100100011000100001011100010111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 225
-
-Encoding: 226
-U-Bits: 011011111100011000100001000110001011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 226
-
-Encoding: 226
-U-Bits: 011011111100011000111100001011000010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 226
-
-Encoding: 226
-U-Bits: 011011111100011000111011000011111100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 226
-
-Encoding: 227
-U-Bits: 101100011100011000111100001010101001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 227
-
-Encoding: 227
-U-Bits: 101100011100011000100001000111100000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 227
-
-Encoding: 227
-U-Bits: 101100011100011000100110001111011110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 227
-
-Encoding: 228
-U-Bits: 000110111101111000111100010001011110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 228
-
-Encoding: 228
-U-Bits: 000110111101111000100001011100010111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 228
-
-Encoding: 228
-U-Bits: 000110111101111000100110010100101001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 228
-
-Encoding: 229
-U-Bits: 110001011101111000100001011101111100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 229
-
-Encoding: 229
-U-Bits: 110001011101111000111100010000110101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 229
-
-Encoding: 229
-U-Bits: 110001011101111000111011011000001011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 229
-
-Encoding: 22a
-U-Bits: 010111000101111000111011000010010111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 22a
-
-Encoding: 22a
-U-Bits: 010111000101111000100110001111011110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 22a
-
-Encoding: 22a
-U-Bits: 010111000101111000100001000111100000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 22a
-
-Encoding: 22b
-U-Bits: 100000100101111000100110001110110101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 22b
-
-Encoding: 22b
-U-Bits: 100000100101111000111011000011111100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 22b
-
-Encoding: 22b
-U-Bits: 100000100101111000111100001011000010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 22b
-
-Encoding: 22c
-U-Bits: 001100100011111000100001000001000110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 22c
-
-Encoding: 22c
-U-Bits: 001100100011111000111100001100001111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 22c
-
-Encoding: 22c
-U-Bits: 001100100011111000111011000100110001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 22c
-
-Encoding: 22d
-U-Bits: 111011000011111000111100001101100100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 22d
-
-Encoding: 22d
-U-Bits: 111011000011111000100001000000101101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 22d
-
-Encoding: 22d
-U-Bits: 111011000011111000100110001000010011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 22d
-
-Encoding: 22e
-U-Bits: 011101011011111000100110010010001111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 22e
-
-Encoding: 22e
-U-Bits: 011101011011111000111011011111000110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 22e
-
-Encoding: 22e
-U-Bits: 011101011011111000111100010111111000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 22e
-
-Encoding: 22f
-U-Bits: 101010111011111000111011011110101101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 22f
-
-Encoding: 22f
-U-Bits: 101010111011111000100110010011100100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 22f
-
-Encoding: 22f
-U-Bits: 101010111011111000100001011011011010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 22f
-
-Encoding: 230
-U-Bits: 000001110011100000111010110000011000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 230
-
-Encoding: 230
-U-Bits: 000001110011100000100111111101010001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 230
-
-Encoding: 230
-U-Bits: 000001110011100000100000110101101111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 230
-
-Encoding: 231
-U-Bits: 110110010011100000100111111100111010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 231
-
-Encoding: 231
-U-Bits: 110110010011100000111010110001110011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 231
-
-Encoding: 231
-U-Bits: 110110010011100000111101111001001101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 231
-
-Encoding: 232
-U-Bits: 010000001011100000111101100011010001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 232
-
-Encoding: 232
-U-Bits: 010000001011100000100000101110011000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 232
-
-Encoding: 232
-U-Bits: 010000001011100000100111100110100110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 232
-
-Encoding: 233
-U-Bits: 100111101011100000100000101111110011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 233
-
-Encoding: 233
-U-Bits: 100111101011100000111101100010111010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 233
-
-Encoding: 233
-U-Bits: 100111101011100000111010101010000100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 233
-
-Encoding: 234
-U-Bits: 001011101101100000100111100000000000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 234
-
-Encoding: 234
-U-Bits: 001011101101100000111010101101001001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 234
-
-Encoding: 234
-U-Bits: 001011101101100000111101100101110111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 234
-
-Encoding: 235
-U-Bits: 111100001101100000111010101100100010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 235
-
-Encoding: 235
-U-Bits: 111100001101100000100111100001101011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 235
-
-Encoding: 235
-U-Bits: 111100001101100000100000101001010101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 235
-
-Encoding: 236
-U-Bits: 011010010101100000100000110011001001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 236
-
-Encoding: 236
-U-Bits: 011010010101100000111101111110000000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 236
-
-Encoding: 236
-U-Bits: 011010010101100000111010110110111110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 236
-
-Encoding: 237
-U-Bits: 101101110101100000111101111111101011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 237
-
-Encoding: 237
-U-Bits: 101101110101100000100000110010100010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 237
-
-Encoding: 237
-U-Bits: 101101110101100000100111111010011100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 237
-
-Encoding: 238
-U-Bits: 000111010100000000111101100100011100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 238
-
-Encoding: 238
-U-Bits: 000111010100000000100000101001010101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 238
-
-Encoding: 238
-U-Bits: 000111010100000000100111100001101011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 238
-
-Encoding: 239
-U-Bits: 110000110100000000100000101000111110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 239
-
-Encoding: 239
-U-Bits: 110000110100000000111101100101110111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 239
-
-Encoding: 239
-U-Bits: 110000110100000000111010101101001001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 239
-
-Encoding: 23a
-U-Bits: 010110101100000000111010110111010101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 23a
-
-Encoding: 23a
-U-Bits: 010110101100000000100111111010011100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 23a
-
-Encoding: 23a
-U-Bits: 010110101100000000100000110010100010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 23a
-
-Encoding: 23b
-U-Bits: 100001001100000000100111111011110111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 23b
-
-Encoding: 23b
-U-Bits: 100001001100000000111010110110111110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 23b
-
-Encoding: 23b
-U-Bits: 100001001100000000111101111110000000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 23b
-
-Encoding: 23c
-U-Bits: 001101001010000000100000110100000100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 23c
-
-Encoding: 23c
-U-Bits: 001101001010000000111101111001001101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 23c
-
-Encoding: 23c
-U-Bits: 001101001010000000111010110001110011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 23c
-
-Encoding: 23d
-U-Bits: 111010101010000000111101111000100110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 23d
-
-Encoding: 23d
-U-Bits: 111010101010000000100000110101101111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 23d
-
-Encoding: 23d
-U-Bits: 111010101010000000100111111101010001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 23d
-
-Encoding: 23e
-U-Bits: 011100110010000000100111100111001101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 23e
-
-Encoding: 23e
-U-Bits: 011100110010000000111010101010000100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 23e
-
-Encoding: 23e
-U-Bits: 011100110010000000111101100010111010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 23e
-
-Encoding: 23f
-U-Bits: 101011010010000000111010101011101111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 23f
-
-Encoding: 23f
-U-Bits: 101011010010000000100111100110100110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 23f
-
-Encoding: 23f
-U-Bits: 101011010010000000100000101110011000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 23f
-
-Encoding: 240
-U-Bits: 000000000110100001000111111011110111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 240
-
-Encoding: 240
-U-Bits: 000000000110100001011010110110111110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 240
-
-Encoding: 240
-U-Bits: 000000000110100001011101111110000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 240
-
-Encoding: 241
-U-Bits: 110111100110100001011010110111010101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 241
-
-Encoding: 241
-U-Bits: 110111100110100001000111111010011100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 241
-
-Encoding: 241
-U-Bits: 110111100110100001000000110010100010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 241
-
-Encoding: 242
-U-Bits: 010001111110100001000000101000111110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 242
-
-Encoding: 242
-U-Bits: 010001111110100001011101100101110111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 242
-
-Encoding: 242
-U-Bits: 010001111110100001011010101101001001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 242
-
-Encoding: 243
-U-Bits: 100110011110100001011101100100011100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 243
-
-Encoding: 243
-U-Bits: 100110011110100001000000101001010101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 243
-
-Encoding: 243
-U-Bits: 100110011110100001000111100001101011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 243
-
-Encoding: 244
-U-Bits: 001010011000100001011010101011101111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 244
-
-Encoding: 244
-U-Bits: 001010011000100001000111100110100110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 244
-
-Encoding: 244
-U-Bits: 001010011000100001000000101110011000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 244
-
-Encoding: 245
-U-Bits: 111101111000100001000111100111001101
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 245
-
-Encoding: 245
-U-Bits: 111101111000100001011010101010000100
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 245
-
-Encoding: 245
-U-Bits: 111101111000100001011101100010111010
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 245
-
-Encoding: 246
-U-Bits: 011011100000100001011101111000100110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 246
-
-Encoding: 246
-U-Bits: 011011100000100001000000110101101111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 246
-
-Encoding: 246
-U-Bits: 011011100000100001000111111101010001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 246
-
-Encoding: 247
-U-Bits: 101100000000100001000000110100000100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 247
-
-Encoding: 247
-U-Bits: 101100000000100001011101111001001101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 247
-
-Encoding: 247
-U-Bits: 101100000000100001011010110001110011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 247
-
-Encoding: 248
-U-Bits: 000110100001000001000000101111110011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 248
-
-Encoding: 248
-U-Bits: 000110100001000001011101100010111010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 248
-
-Encoding: 248
-U-Bits: 000110100001000001011010101010000100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 248
-
-Encoding: 249
-U-Bits: 110001000001000001011101100011010001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 249
-
-Encoding: 249
-U-Bits: 110001000001000001000000101110011000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 249
-
-Encoding: 249
-U-Bits: 110001000001000001000111100110100110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 249
-
-Encoding: 24a
-U-Bits: 010111011001000001000111111100111010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 24a
-
-Encoding: 24a
-U-Bits: 010111011001000001011010110001110011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 24a
-
-Encoding: 24a
-U-Bits: 010111011001000001011101111001001101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 24a
-
-Encoding: 24b
-U-Bits: 100000111001000001011010110000011000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 24b
-
-Encoding: 24b
-U-Bits: 100000111001000001000111111101010001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 24b
-
-Encoding: 24b
-U-Bits: 100000111001000001000000110101101111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 24b
-
-Encoding: 24c
-U-Bits: 001100111111000001011101111111101011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 24c
-
-Encoding: 24c
-U-Bits: 001100111111000001000000110010100010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 24c
-
-Encoding: 24c
-U-Bits: 001100111111000001000111111010011100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 24c
-
-Encoding: 24d
-U-Bits: 111011011111000001000000110011001001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 24d
-
-Encoding: 24d
-U-Bits: 111011011111000001011101111110000000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 24d
-
-Encoding: 24d
-U-Bits: 111011011111000001011010110110111110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 24d
-
-Encoding: 24e
-U-Bits: 011101000111000001011010101100100010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 24e
-
-Encoding: 24e
-U-Bits: 011101000111000001000111100001101011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 24e
-
-Encoding: 24e
-U-Bits: 011101000111000001000000101001010101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 24e
-
-Encoding: 24f
-U-Bits: 101010100111000001000111100000000000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 24f
-
-Encoding: 24f
-U-Bits: 101010100111000001011010101101001001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 24f
-
-Encoding: 24f
-U-Bits: 101010100111000001011101100101110111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 24f
-
-Encoding: 250
-U-Bits: 000001101111011001000110001110110101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 250
-
-Encoding: 250
-U-Bits: 000001101111011001011011000011111100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 250
-
-Encoding: 250
-U-Bits: 000001101111011001011100001011000010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 250
-
-Encoding: 251
-U-Bits: 110110001111011001011011000010010111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 251
-
-Encoding: 251
-U-Bits: 110110001111011001000110001111011110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 251
-
-Encoding: 251
-U-Bits: 110110001111011001000001000111100000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 251
-
-Encoding: 252
-U-Bits: 010000010111011001000001011101111100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 252
-
-Encoding: 252
-U-Bits: 010000010111011001011100010000110101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 252
-
-Encoding: 252
-U-Bits: 010000010111011001011011011000001011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 252
-
-Encoding: 253
-U-Bits: 100111110111011001011100010001011110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 253
-
-Encoding: 253
-U-Bits: 100111110111011001000001011100010111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 253
-
-Encoding: 253
-U-Bits: 100111110111011001000110010100101001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 253
-
-Encoding: 254
-U-Bits: 001011110001011001011011011110101101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 254
-
-Encoding: 254
-U-Bits: 001011110001011001000110010011100100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 254
-
-Encoding: 254
-U-Bits: 001011110001011001000001011011011010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 254
-
-Encoding: 255
-U-Bits: 111100010001011001000110010010001111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 255
-
-Encoding: 255
-U-Bits: 111100010001011001011011011111000110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 255
-
-Encoding: 255
-U-Bits: 111100010001011001011100010111111000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 255
-
-Encoding: 256
-U-Bits: 011010001001011001011100001101100100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 256
-
-Encoding: 256
-U-Bits: 011010001001011001000001000000101101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 256
-
-Encoding: 256
-U-Bits: 011010001001011001000110001000010011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 256
-
-Encoding: 257
-U-Bits: 101101101001011001000001000001000110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 257
-
-Encoding: 257
-U-Bits: 101101101001011001011100001100001111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 257
-
-Encoding: 257
-U-Bits: 101101101001011001011011000100110001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 257
-
-Encoding: 258
-U-Bits: 000111001000111001000001011010110001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 258
-
-Encoding: 258
-U-Bits: 000111001000111001011100010111111000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 258
-
-Encoding: 258
-U-Bits: 000111001000111001011011011111000110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 258
-
-Encoding: 259
-U-Bits: 110000101000111001011100010110010011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 259
-
-Encoding: 259
-U-Bits: 110000101000111001000001011011011010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 259
-
-Encoding: 259
-U-Bits: 110000101000111001000110010011100100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 259
-
-Encoding: 25a
-U-Bits: 010110110000111001000110001001111000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 25a
-
-Encoding: 25a
-U-Bits: 010110110000111001011011000100110001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 25a
-
-Encoding: 25a
-U-Bits: 010110110000111001011100001100001111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 25a
-
-Encoding: 25b
-U-Bits: 100001010000111001011011000101011010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 25b
-
-Encoding: 25b
-U-Bits: 100001010000111001000110001000010011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 25b
-
-Encoding: 25b
-U-Bits: 100001010000111001000001000000101101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 25b
-
-Encoding: 25c
-U-Bits: 001101010110111001011100001010101001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 25c
-
-Encoding: 25c
-U-Bits: 001101010110111001000001000111100000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 25c
-
-Encoding: 25c
-U-Bits: 001101010110111001000110001111011110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 25c
-
-Encoding: 25d
-U-Bits: 111010110110111001000001000110001011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 25d
-
-Encoding: 25d
-U-Bits: 111010110110111001011100001011000010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 25d
-
-Encoding: 25d
-U-Bits: 111010110110111001011011000011111100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 25d
-
-Encoding: 25e
-U-Bits: 011100101110111001011011011001100000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 25e
-
-Encoding: 25e
-U-Bits: 011100101110111001000110010100101001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 25e
-
-Encoding: 25e
-U-Bits: 011100101110111001000001011100010111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 25e
-
-Encoding: 25f
-U-Bits: 101011001110111001000110010101000010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 25f
-
-Encoding: 25f
-U-Bits: 101011001110111001011011011000001011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 25f
-
-Encoding: 25f
-U-Bits: 101011001110111001011100010000110101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 25f
-
-Encoding: 260
-U-Bits: 000000011100111111000111100110100110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 260
-
-Encoding: 260
-U-Bits: 000000011100111111011010101011101111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 260
-
-Encoding: 260
-U-Bits: 000000011100111111011101100011010001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 260
-
-Encoding: 261
-U-Bits: 110111111100111111011010101010000100
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 261
-
-Encoding: 261
-U-Bits: 110111111100111111000111100111001101
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 261
-
-Encoding: 261
-U-Bits: 110111111100111111000000101111110011
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 261
-
-Encoding: 262
-U-Bits: 010001100100111111000000110101101111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 262
-
-Encoding: 262
-U-Bits: 010001100100111111011101111000100110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 262
-
-Encoding: 262
-U-Bits: 010001100100111111011010110000011000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 262
-
-Encoding: 263
-U-Bits: 100110000100111111011101111001001101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 263
-
-Encoding: 263
-U-Bits: 100110000100111111000000110100000100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 263
-
-Encoding: 263
-U-Bits: 100110000100111111000111111100111010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 263
-
-Encoding: 264
-U-Bits: 001010000010111111011010110110111110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 264
-
-Encoding: 264
-U-Bits: 001010000010111111000111111011110111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 264
-
-Encoding: 264
-U-Bits: 001010000010111111000000110011001001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 264
-
-Encoding: 265
-U-Bits: 111101100010111111000111111010011100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 265
-
-Encoding: 265
-U-Bits: 111101100010111111011010110111010101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 265
-
-Encoding: 265
-U-Bits: 111101100010111111011101111111101011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 265
-
-Encoding: 266
-U-Bits: 011011111010111111011101100101110111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 266
-
-Encoding: 266
-U-Bits: 011011111010111111000000101000111110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 266
-
-Encoding: 266
-U-Bits: 011011111010111111000111100000000000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 266
-
-Encoding: 267
-U-Bits: 101100011010111111000000101001010101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 267
-
-Encoding: 267
-U-Bits: 101100011010111111011101100100011100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 267
-
-Encoding: 267
-U-Bits: 101100011010111111011010101100100010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 267
-
-Encoding: 268
-U-Bits: 000110111011011111000000110010100010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 268
-
-Encoding: 268
-U-Bits: 000110111011011111011101111111101011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 268
-
-Encoding: 268
-U-Bits: 000110111011011111011010110111010101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 268
-
-Encoding: 269
-U-Bits: 110001011011011111011101111110000000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 269
-
-Encoding: 269
-U-Bits: 110001011011011111000000110011001001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 269
-
-Encoding: 269
-U-Bits: 110001011011011111000111111011110111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 269
-
-Encoding: 26a
-U-Bits: 010111000011011111000111100001101011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 26a
-
-Encoding: 26a
-U-Bits: 010111000011011111011010101100100010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 26a
-
-Encoding: 26a
-U-Bits: 010111000011011111011101100100011100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 26a
-
-Encoding: 26b
-U-Bits: 100000100011011111011010101101001001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 26b
-
-Encoding: 26b
-U-Bits: 100000100011011111000111100000000000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 26b
-
-Encoding: 26b
-U-Bits: 100000100011011111000000101000111110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 26b
-
-Encoding: 26c
-U-Bits: 001100100101011111011101100010111010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 26c
-
-Encoding: 26c
-U-Bits: 001100100101011111000000101111110011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 26c
-
-Encoding: 26c
-U-Bits: 001100100101011111000111100111001101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 26c
-
-Encoding: 26d
-U-Bits: 111011000101011111000000101110011000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 26d
-
-Encoding: 26d
-U-Bits: 111011000101011111011101100011010001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 26d
-
-Encoding: 26d
-U-Bits: 111011000101011111011010101011101111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 26d
-
-Encoding: 26e
-U-Bits: 011101011101011111011010110001110011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 26e
-
-Encoding: 26e
-U-Bits: 011101011101011111000111111100111010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 26e
-
-Encoding: 26e
-U-Bits: 011101011101011111000000110100000100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 26e
-
-Encoding: 26f
-U-Bits: 101010111101011111000111111101010001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 26f
-
-Encoding: 26f
-U-Bits: 101010111101011111011010110000011000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 26f
-
-Encoding: 26f
-U-Bits: 101010111101011111011101111000100110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 26f
-
-Encoding: 270
-U-Bits: 000001110101000111000110010011100100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 270
-
-Encoding: 270
-U-Bits: 000001110101000111011011011110101101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 270
-
-Encoding: 270
-U-Bits: 000001110101000111011100010110010011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 270
-
-Encoding: 271
-U-Bits: 110110010101000111011011011111000110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 271
-
-Encoding: 271
-U-Bits: 110110010101000111000110010010001111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 271
-
-Encoding: 271
-U-Bits: 110110010101000111000001011010110001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 271
-
-Encoding: 272
-U-Bits: 010000001101000111000001000000101101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 272
-
-Encoding: 272
-U-Bits: 010000001101000111011100001101100100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 272
-
-Encoding: 272
-U-Bits: 010000001101000111011011000101011010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 272
-
-Encoding: 273
-U-Bits: 100111101101000111011100001100001111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 273
-
-Encoding: 273
-U-Bits: 100111101101000111000001000001000110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 273
-
-Encoding: 273
-U-Bits: 100111101101000111000110001001111000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 273
-
-Encoding: 274
-U-Bits: 001011101011000111011011000011111100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 274
-
-Encoding: 274
-U-Bits: 001011101011000111000110001110110101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 274
-
-Encoding: 274
-U-Bits: 001011101011000111000001000110001011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 274
-
-Encoding: 275
-U-Bits: 111100001011000111000110001111011110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 275
-
-Encoding: 275
-U-Bits: 111100001011000111011011000010010111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 275
-
-Encoding: 275
-U-Bits: 111100001011000111011100001010101001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 275
-
-Encoding: 276
-U-Bits: 011010010011000111011100010000110101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 276
-
-Encoding: 276
-U-Bits: 011010010011000111000001011101111100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 276
-
-Encoding: 276
-U-Bits: 011010010011000111000110010101000010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 276
-
-Encoding: 277
-U-Bits: 101101110011000111000001011100010111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 277
-
-Encoding: 277
-U-Bits: 101101110011000111011100010001011110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 277
-
-Encoding: 277
-U-Bits: 101101110011000111011011011001100000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 277
-
-Encoding: 278
-U-Bits: 000111010010100111000001000111100000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 278
-
-Encoding: 278
-U-Bits: 000111010010100111011100001010101001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 278
-
-Encoding: 278
-U-Bits: 000111010010100111011011000010010111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 278
-
-Encoding: 279
-U-Bits: 110000110010100111011100001011000010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 279
-
-Encoding: 279
-U-Bits: 110000110010100111000001000110001011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 279
-
-Encoding: 279
-U-Bits: 110000110010100111000110001110110101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 279
-
-Encoding: 27a
-U-Bits: 010110101010100111000110010100101001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 27a
-
-Encoding: 27a
-U-Bits: 010110101010100111011011011001100000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 27a
-
-Encoding: 27a
-U-Bits: 010110101010100111011100010001011110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 27a
-
-Encoding: 27b
-U-Bits: 100001001010100111011011011000001011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 27b
-
-Encoding: 27b
-U-Bits: 100001001010100111000110010101000010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 27b
-
-Encoding: 27b
-U-Bits: 100001001010100111000001011101111100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 27b
-
-Encoding: 27c
-U-Bits: 001101001100100111011100010111111000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 27c
-
-Encoding: 27c
-U-Bits: 001101001100100111000001011010110001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 27c
-
-Encoding: 27c
-U-Bits: 001101001100100111000110010010001111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 27c
-
-Encoding: 27d
-U-Bits: 111010101100100111000001011011011010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 27d
-
-Encoding: 27d
-U-Bits: 111010101100100111011100010110010011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 27d
-
-Encoding: 27d
-U-Bits: 111010101100100111011011011110101101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 27d
-
-Encoding: 27e
-U-Bits: 011100110100100111011011000100110001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 27e
-
-Encoding: 27e
-U-Bits: 011100110100100111000110001001111000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 27e
-
-Encoding: 27e
-U-Bits: 011100110100100111000001000001000110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 27e
-
-Encoding: 27f
-U-Bits: 101011010100100111000110001000010011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 27f
-
-Encoding: 27f
-U-Bits: 101011010100100111011011000101011010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 27f
-
-Encoding: 27f
-U-Bits: 101011010100100111011100001101100100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 27f
-
-Encoding: 280
-U-Bits: 000000000001101111000100010000110101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 280
-
-Encoding: 280
-U-Bits: 000000000001101111011001011101111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 280
-
-Encoding: 280
-U-Bits: 000000000001101111011110010101000010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 280
-
-Encoding: 281
-U-Bits: 110111100001101111011001011100010111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 281
-
-Encoding: 281
-U-Bits: 110111100001101111000100010001011110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 281
-
-Encoding: 281
-U-Bits: 110111100001101111000011011001100000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 281
-
-Encoding: 282
-U-Bits: 010001111001101111000011000011111100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 282
-
-Encoding: 282
-U-Bits: 010001111001101111011110001110110101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 282
-
-Encoding: 282
-U-Bits: 010001111001101111011001000110001011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 282
-
-Encoding: 283
-U-Bits: 100110011001101111011110001111011110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 283
-
-Encoding: 283
-U-Bits: 100110011001101111000011000010010111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 283
-
-Encoding: 283
-U-Bits: 100110011001101111000100001010101001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 283
-
-Encoding: 284
-U-Bits: 001010011111101111011001000000101101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 284
-
-Encoding: 284
-U-Bits: 001010011111101111000100001101100100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 284
-
-Encoding: 284
-U-Bits: 001010011111101111000011000101011010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 284
-
-Encoding: 285
-U-Bits: 111101111111101111000100001100001111
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 285
-
-Encoding: 285
-U-Bits: 111101111111101111011001000001000110
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 285
-
-Encoding: 285
-U-Bits: 111101111111101111011110001001111000
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 285
-
-Encoding: 286
-U-Bits: 011011100111101111011110010011100100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 286
-
-Encoding: 286
-U-Bits: 011011100111101111000011011110101101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 286
-
-Encoding: 286
-U-Bits: 011011100111101111000100010110010011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 286
-
-Encoding: 287
-U-Bits: 101100000111101111000011011111000110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 287
-
-Encoding: 287
-U-Bits: 101100000111101111011110010010001111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 287
-
-Encoding: 287
-U-Bits: 101100000111101111011001011010110001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 287
-
-Encoding: 288
-U-Bits: 000110100110001111000011000100110001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 288
-
-Encoding: 288
-U-Bits: 000110100110001111011110001001111000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 288
-
-Encoding: 288
-U-Bits: 000110100110001111011001000001000110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 288
-
-Encoding: 289
-U-Bits: 110001000110001111011110001000010011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 289
-
-Encoding: 289
-U-Bits: 110001000110001111000011000101011010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 289
-
-Encoding: 289
-U-Bits: 110001000110001111000100001101100100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 289
-
-Encoding: 28a
-U-Bits: 010111011110001111000100010111111000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 28a
-
-Encoding: 28a
-U-Bits: 010111011110001111011001011010110001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 28a
-
-Encoding: 28a
-U-Bits: 010111011110001111011110010010001111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 28a
-
-Encoding: 28b
-U-Bits: 100000111110001111011001011011011010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 28b
-
-Encoding: 28b
-U-Bits: 100000111110001111000100010110010011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 28b
-
-Encoding: 28b
-U-Bits: 100000111110001111000011011110101101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 28b
-
-Encoding: 28c
-U-Bits: 001100111000001111011110010100101001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 28c
-
-Encoding: 28c
-U-Bits: 001100111000001111000011011001100000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 28c
-
-Encoding: 28c
-U-Bits: 001100111000001111000100010001011110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 28c
-
-Encoding: 28d
-U-Bits: 111011011000001111000011011000001011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 28d
-
-Encoding: 28d
-U-Bits: 111011011000001111011110010101000010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 28d
-
-Encoding: 28d
-U-Bits: 111011011000001111011001011101111100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 28d
-
-Encoding: 28e
-U-Bits: 011101000000001111011001000111100000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 28e
-
-Encoding: 28e
-U-Bits: 011101000000001111000100001010101001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 28e
-
-Encoding: 28e
-U-Bits: 011101000000001111000011000010010111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 28e
-
-Encoding: 28f
-U-Bits: 101010100000001111000100001011000010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 28f
-
-Encoding: 28f
-U-Bits: 101010100000001111011001000110001011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 28f
-
-Encoding: 28f
-U-Bits: 101010100000001111011110001110110101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 28f
-
-Encoding: 290
-U-Bits: 000001101000010111000101100101110111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 290
-
-Encoding: 290
-U-Bits: 000001101000010111011000101000111110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 290
-
-Encoding: 290
-U-Bits: 000001101000010111011111100000000000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 290
-
-Encoding: 291
-U-Bits: 110110001000010111011000101001010101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 291
-
-Encoding: 291
-U-Bits: 110110001000010111000101100100011100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 291
-
-Encoding: 291
-U-Bits: 110110001000010111000010101100100010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 291
-
-Encoding: 292
-U-Bits: 010000010000010111000010110110111110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 292
-
-Encoding: 292
-U-Bits: 010000010000010111011111111011110111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 292
-
-Encoding: 292
-U-Bits: 010000010000010111011000110011001001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 292
-
-Encoding: 293
-U-Bits: 100111110000010111011111111010011100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 293
-
-Encoding: 293
-U-Bits: 100111110000010111000010110111010101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 293
-
-Encoding: 293
-U-Bits: 100111110000010111000101111111101011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 293
-
-Encoding: 294
-U-Bits: 001011110110010111011000110101101111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 294
-
-Encoding: 294
-U-Bits: 001011110110010111000101111000100110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 294
-
-Encoding: 294
-U-Bits: 001011110110010111000010110000011000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 294
-
-Encoding: 295
-U-Bits: 111100010110010111000101111001001101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 295
-
-Encoding: 295
-U-Bits: 111100010110010111011000110100000100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 295
-
-Encoding: 295
-U-Bits: 111100010110010111011111111100111010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 295
-
-Encoding: 296
-U-Bits: 011010001110010111011111100110100110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 296
-
-Encoding: 296
-U-Bits: 011010001110010111000010101011101111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 296
-
-Encoding: 296
-U-Bits: 011010001110010111000101100011010001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 296
-
-Encoding: 297
-U-Bits: 101101101110010111000010101010000100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 297
-
-Encoding: 297
-U-Bits: 101101101110010111011111100111001101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 297
-
-Encoding: 297
-U-Bits: 101101101110010111011000101111110011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 297
-
-Encoding: 298
-U-Bits: 000111001111110111000010110001110011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 298
-
-Encoding: 298
-U-Bits: 000111001111110111011111111100111010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 298
-
-Encoding: 298
-U-Bits: 000111001111110111011000110100000100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 298
-
-Encoding: 299
-U-Bits: 110000101111110111011111111101010001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 299
-
-Encoding: 299
-U-Bits: 110000101111110111000010110000011000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 299
-
-Encoding: 299
-U-Bits: 110000101111110111000101111000100110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 299
-
-Encoding: 29a
-U-Bits: 010110110111110111000101100010111010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 29a
-
-Encoding: 29a
-U-Bits: 010110110111110111011000101111110011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 29a
-
-Encoding: 29a
-U-Bits: 010110110111110111011111100111001101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 29a
-
-Encoding: 29b
-U-Bits: 100001010111110111011000101110011000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 29b
-
-Encoding: 29b
-U-Bits: 100001010111110111000101100011010001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 29b
-
-Encoding: 29b
-U-Bits: 100001010111110111000010101011101111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 29b
-
-Encoding: 29c
-U-Bits: 001101010001110111011111100001101011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 29c
-
-Encoding: 29c
-U-Bits: 001101010001110111000010101100100010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 29c
-
-Encoding: 29c
-U-Bits: 001101010001110111000101100100011100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 29c
-
-Encoding: 29d
-U-Bits: 111010110001110111000010101101001001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 29d
-
-Encoding: 29d
-U-Bits: 111010110001110111011111100000000000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 29d
-
-Encoding: 29d
-U-Bits: 111010110001110111011000101000111110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 29d
-
-Encoding: 29e
-U-Bits: 011100101001110111011000110010100010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 29e
-
-Encoding: 29e
-U-Bits: 011100101001110111000101111111101011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 29e
-
-Encoding: 29e
-U-Bits: 011100101001110111000010110111010101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 29e
-
-Encoding: 29f
-U-Bits: 101011001001110111000101111110000000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 29f
-
-Encoding: 29f
-U-Bits: 101011001001110111011000110011001001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 29f
-
-Encoding: 29f
-U-Bits: 101011001001110111011111111011110111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 29f
-
-Encoding: 2a0
-U-Bits: 000000011011110001000100001101100100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 2a0
-
-Encoding: 2a0
-U-Bits: 000000011011110001011001000000101101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 2a0
-
-Encoding: 2a0
-U-Bits: 000000011011110001011110001000010011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 2a0
-
-Encoding: 2a1
-U-Bits: 110111111011110001011001000001000110
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 2a1
-
-Encoding: 2a1
-U-Bits: 110111111011110001000100001100001111
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 2a1
-
-Encoding: 2a1
-U-Bits: 110111111011110001000011000100110001
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 2a1
-
-Encoding: 2a2
-U-Bits: 010001100011110001000011011110101101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 2a2
-
-Encoding: 2a2
-U-Bits: 010001100011110001011110010011100100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 2a2
-
-Encoding: 2a2
-U-Bits: 010001100011110001011001011011011010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 2a2
-
-Encoding: 2a3
-U-Bits: 100110000011110001011110010010001111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 2a3
-
-Encoding: 2a3
-U-Bits: 100110000011110001000011011111000110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 2a3
-
-Encoding: 2a3
-U-Bits: 100110000011110001000100010111111000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 2a3
-
-Encoding: 2a4
-U-Bits: 001010000101110001011001011101111100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 2a4
-
-Encoding: 2a4
-U-Bits: 001010000101110001000100010000110101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 2a4
-
-Encoding: 2a4
-U-Bits: 001010000101110001000011011000001011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 2a4
-
-Encoding: 2a5
-U-Bits: 111101100101110001000100010001011110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 2a5
-
-Encoding: 2a5
-U-Bits: 111101100101110001011001011100010111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 2a5
-
-Encoding: 2a5
-U-Bits: 111101100101110001011110010100101001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 2a5
-
-Encoding: 2a6
-U-Bits: 011011111101110001011110001110110101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 2a6
-
-Encoding: 2a6
-U-Bits: 011011111101110001000011000011111100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 2a6
-
-Encoding: 2a6
-U-Bits: 011011111101110001000100001011000010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 2a6
-
-Encoding: 2a7
-U-Bits: 101100011101110001000011000010010111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 2a7
-
-Encoding: 2a7
-U-Bits: 101100011101110001011110001111011110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 2a7
-
-Encoding: 2a7
-U-Bits: 101100011101110001011001000111100000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 2a7
-
-Encoding: 2a8
-U-Bits: 000110111100010001000011011001100000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 2a8
-
-Encoding: 2a8
-U-Bits: 000110111100010001011110010100101001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 2a8
-
-Encoding: 2a8
-U-Bits: 000110111100010001011001011100010111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 2a8
-
-Encoding: 2a9
-U-Bits: 110001011100010001011110010101000010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 2a9
-
-Encoding: 2a9
-U-Bits: 110001011100010001000011011000001011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 2a9
-
-Encoding: 2a9
-U-Bits: 110001011100010001000100010000110101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 2a9
-
-Encoding: 2aa
-U-Bits: 010111000100010001000100001010101001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 2aa
-
-Encoding: 2aa
-U-Bits: 010111000100010001011001000111100000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 2aa
-
-Encoding: 2aa
-U-Bits: 010111000100010001011110001111011110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 2aa
-
-Encoding: 2ab
-U-Bits: 100000100100010001011001000110001011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 2ab
-
-Encoding: 2ab
-U-Bits: 100000100100010001000100001011000010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 2ab
-
-Encoding: 2ab
-U-Bits: 100000100100010001000011000011111100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 2ab
-
-Encoding: 2ac
-U-Bits: 001100100010010001011110001001111000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 2ac
-
-Encoding: 2ac
-U-Bits: 001100100010010001000011000100110001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 2ac
-
-Encoding: 2ac
-U-Bits: 001100100010010001000100001100001111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 2ac
-
-Encoding: 2ad
-U-Bits: 111011000010010001000011000101011010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 2ad
-
-Encoding: 2ad
-U-Bits: 111011000010010001011110001000010011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 2ad
-
-Encoding: 2ad
-U-Bits: 111011000010010001011001000000101101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 2ad
-
-Encoding: 2ae
-U-Bits: 011101011010010001011001011010110001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 2ae
-
-Encoding: 2ae
-U-Bits: 011101011010010001000100010111111000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 2ae
-
-Encoding: 2ae
-U-Bits: 011101011010010001000011011111000110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 2ae
-
-Encoding: 2af
-U-Bits: 101010111010010001000100010110010011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 2af
-
-Encoding: 2af
-U-Bits: 101010111010010001011001011011011010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 2af
-
-Encoding: 2af
-U-Bits: 101010111010010001011110010011100100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 2af
-
-Encoding: 2b0
-U-Bits: 000001110010001001000101111000100110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 2b0
-
-Encoding: 2b0
-U-Bits: 000001110010001001011000110101101111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 2b0
-
-Encoding: 2b0
-U-Bits: 000001110010001001011111111101010001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 2b0
-
-Encoding: 2b1
-U-Bits: 110110010010001001011000110100000100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 2b1
-
-Encoding: 2b1
-U-Bits: 110110010010001001000101111001001101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 2b1
-
-Encoding: 2b1
-U-Bits: 110110010010001001000010110001110011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 2b1
-
-Encoding: 2b2
-U-Bits: 010000001010001001000010101011101111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 2b2
-
-Encoding: 2b2
-U-Bits: 010000001010001001011111100110100110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 2b2
-
-Encoding: 2b2
-U-Bits: 010000001010001001011000101110011000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 2b2
-
-Encoding: 2b3
-U-Bits: 100111101010001001011111100111001101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 2b3
-
-Encoding: 2b3
-U-Bits: 100111101010001001000010101010000100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 2b3
-
-Encoding: 2b3
-U-Bits: 100111101010001001000101100010111010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 2b3
-
-Encoding: 2b4
-U-Bits: 001011101100001001011000101000111110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 2b4
-
-Encoding: 2b4
-U-Bits: 001011101100001001000101100101110111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 2b4
-
-Encoding: 2b4
-U-Bits: 001011101100001001000010101101001001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 2b4
-
-Encoding: 2b5
-U-Bits: 111100001100001001000101100100011100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 2b5
-
-Encoding: 2b5
-U-Bits: 111100001100001001011000101001010101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 2b5
-
-Encoding: 2b5
-U-Bits: 111100001100001001011111100001101011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 2b5
-
-Encoding: 2b6
-U-Bits: 011010010100001001011111111011110111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 2b6
-
-Encoding: 2b6
-U-Bits: 011010010100001001000010110110111110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 2b6
-
-Encoding: 2b6
-U-Bits: 011010010100001001000101111110000000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2b6
-
-Encoding: 2b7
-U-Bits: 101101110100001001000010110111010101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 2b7
-
-Encoding: 2b7
-U-Bits: 101101110100001001011111111010011100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 2b7
-
-Encoding: 2b7
-U-Bits: 101101110100001001011000110010100010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 2b7
-
-Encoding: 2b8
-U-Bits: 000111010101101001000010101100100010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 2b8
-
-Encoding: 2b8
-U-Bits: 000111010101101001011111100001101011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 2b8
-
-Encoding: 2b8
-U-Bits: 000111010101101001011000101001010101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 2b8
-
-Encoding: 2b9
-U-Bits: 110000110101101001011111100000000000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2b9
-
-Encoding: 2b9
-U-Bits: 110000110101101001000010101101001001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 2b9
-
-Encoding: 2b9
-U-Bits: 110000110101101001000101100101110111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 2b9
-
-Encoding: 2ba
-U-Bits: 010110101101101001000101111111101011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 2ba
-
-Encoding: 2ba
-U-Bits: 010110101101101001011000110010100010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 2ba
-
-Encoding: 2ba
-U-Bits: 010110101101101001011111111010011100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 2ba
-
-Encoding: 2bb
-U-Bits: 100001001101101001011000110011001001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 2bb
-
-Encoding: 2bb
-U-Bits: 100001001101101001000101111110000000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2bb
-
-Encoding: 2bb
-U-Bits: 100001001101101001000010110110111110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 2bb
-
-Encoding: 2bc
-U-Bits: 001101001011101001011111111100111010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 2bc
-
-Encoding: 2bc
-U-Bits: 001101001011101001000010110001110011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 2bc
-
-Encoding: 2bc
-U-Bits: 001101001011101001000101111001001101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 2bc
-
-Encoding: 2bd
-U-Bits: 111010101011101001000010110000011000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 2bd
-
-Encoding: 2bd
-U-Bits: 111010101011101001011111111101010001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 2bd
-
-Encoding: 2bd
-U-Bits: 111010101011101001011000110101101111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 2bd
-
-Encoding: 2be
-U-Bits: 011100110011101001011000101111110011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 2be
-
-Encoding: 2be
-U-Bits: 011100110011101001000101100010111010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 2be
-
-Encoding: 2be
-U-Bits: 011100110011101001000010101010000100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 2be
-
-Encoding: 2bf
-U-Bits: 101011010011101001000101100011010001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 2bf
-
-Encoding: 2bf
-U-Bits: 101011010011101001011000101110011000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 2bf
-
-Encoding: 2bf
-U-Bits: 101011010011101001011111100110100110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 2bf
-
-Encoding: 2c0
-U-Bits: 000000000111001000111000110011001001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 2c0
-
-Encoding: 2c0
-U-Bits: 000000000111001000100101111110000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2c0
-
-Encoding: 2c0
-U-Bits: 000000000111001000100010110110111110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 2c0
-
-Encoding: 2c1
-U-Bits: 110111100111001000100101111111101011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 2c1
-
-Encoding: 2c1
-U-Bits: 110111100111001000111000110010100010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 2c1
-
-Encoding: 2c1
-U-Bits: 110111100111001000111111111010011100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 2c1
-
-Encoding: 2c2
-U-Bits: 010001111111001000111111100000000000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2c2
-
-Encoding: 2c2
-U-Bits: 010001111111001000100010101101001001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 2c2
-
-Encoding: 2c2
-U-Bits: 010001111111001000100101100101110111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 2c2
-
-Encoding: 2c3
-U-Bits: 100110011111001000100010101100100010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 2c3
-
-Encoding: 2c3
-U-Bits: 100110011111001000111111100001101011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 2c3
-
-Encoding: 2c3
-U-Bits: 100110011111001000111000101001010101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 2c3
-
-Encoding: 2c4
-U-Bits: 001010011001001000100101100011010001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 2c4
-
-Encoding: 2c4
-U-Bits: 001010011001001000111000101110011000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 2c4
-
-Encoding: 2c4
-U-Bits: 001010011001001000111111100110100110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 2c4
-
-Encoding: 2c5
-U-Bits: 111101111001001000111000101111110011
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 2c5
-
-Encoding: 2c5
-U-Bits: 111101111001001000100101100010111010
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 2c5
-
-Encoding: 2c5
-U-Bits: 111101111001001000100010101010000100
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 2c5
-
-Encoding: 2c6
-U-Bits: 011011100001001000100010110000011000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 2c6
-
-Encoding: 2c6
-U-Bits: 011011100001001000111111111101010001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 2c6
-
-Encoding: 2c6
-U-Bits: 011011100001001000111000110101101111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 2c6
-
-Encoding: 2c7
-U-Bits: 101100000001001000111111111100111010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 2c7
-
-Encoding: 2c7
-U-Bits: 101100000001001000100010110001110011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 2c7
-
-Encoding: 2c7
-U-Bits: 101100000001001000100101111001001101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 2c7
-
-Encoding: 2c8
-U-Bits: 000110100000101000111111100111001101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 2c8
-
-Encoding: 2c8
-U-Bits: 000110100000101000100010101010000100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 2c8
-
-Encoding: 2c8
-U-Bits: 000110100000101000100101100010111010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 2c8
-
-Encoding: 2c9
-U-Bits: 110001000000101000100010101011101111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 2c9
-
-Encoding: 2c9
-U-Bits: 110001000000101000111111100110100110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 2c9
-
-Encoding: 2c9
-U-Bits: 110001000000101000111000101110011000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 2c9
-
-Encoding: 2ca
-U-Bits: 010111011000101000111000110100000100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 2ca
-
-Encoding: 2ca
-U-Bits: 010111011000101000100101111001001101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 2ca
-
-Encoding: 2ca
-U-Bits: 010111011000101000100010110001110011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 2ca
-
-Encoding: 2cb
-U-Bits: 100000111000101000100101111000100110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 2cb
-
-Encoding: 2cb
-U-Bits: 100000111000101000111000110101101111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 2cb
-
-Encoding: 2cb
-U-Bits: 100000111000101000111111111101010001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 2cb
-
-Encoding: 2cc
-U-Bits: 001100111110101000100010110111010101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 2cc
-
-Encoding: 2cc
-U-Bits: 001100111110101000111111111010011100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 2cc
-
-Encoding: 2cc
-U-Bits: 001100111110101000111000110010100010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 2cc
-
-Encoding: 2cd
-U-Bits: 111011011110101000111111111011110111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 2cd
-
-Encoding: 2cd
-U-Bits: 111011011110101000100010110110111110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 2cd
-
-Encoding: 2cd
-U-Bits: 111011011110101000100101111110000000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2cd
-
-Encoding: 2ce
-U-Bits: 011101000110101000100101100100011100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 2ce
-
-Encoding: 2ce
-U-Bits: 011101000110101000111000101001010101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 2ce
-
-Encoding: 2ce
-U-Bits: 011101000110101000111111100001101011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 2ce
-
-Encoding: 2cf
-U-Bits: 101010100110101000111000101000111110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 2cf
-
-Encoding: 2cf
-U-Bits: 101010100110101000100101100101110111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 2cf
-
-Encoding: 2cf
-U-Bits: 101010100110101000100010101101001001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 2cf
-
-Encoding: 2d0
-U-Bits: 000001101110110000111001000110001011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 2d0
-
-Encoding: 2d0
-U-Bits: 000001101110110000100100001011000010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 2d0
-
-Encoding: 2d0
-U-Bits: 000001101110110000100011000011111100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 2d0
-
-Encoding: 2d1
-U-Bits: 110110001110110000100100001010101001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 2d1
-
-Encoding: 2d1
-U-Bits: 110110001110110000111001000111100000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 2d1
-
-Encoding: 2d1
-U-Bits: 110110001110110000111110001111011110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 2d1
-
-Encoding: 2d2
-U-Bits: 010000010110110000111110010101000010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 2d2
-
-Encoding: 2d2
-U-Bits: 010000010110110000100011011000001011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 2d2
-
-Encoding: 2d2
-U-Bits: 010000010110110000100100010000110101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 2d2
-
-Encoding: 2d3
-U-Bits: 100111110110110000100011011001100000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 2d3
-
-Encoding: 2d3
-U-Bits: 100111110110110000111110010100101001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 2d3
-
-Encoding: 2d3
-U-Bits: 100111110110110000111001011100010111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 2d3
-
-Encoding: 2d4
-U-Bits: 001011110000110000100100010110010011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 2d4
-
-Encoding: 2d4
-U-Bits: 001011110000110000111001011011011010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 2d4
-
-Encoding: 2d4
-U-Bits: 001011110000110000111110010011100100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 2d4
-
-Encoding: 2d5
-U-Bits: 111100010000110000111001011010110001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 2d5
-
-Encoding: 2d5
-U-Bits: 111100010000110000100100010111111000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 2d5
-
-Encoding: 2d5
-U-Bits: 111100010000110000100011011111000110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 2d5
-
-Encoding: 2d6
-U-Bits: 011010001000110000100011000101011010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 2d6
-
-Encoding: 2d6
-U-Bits: 011010001000110000111110001000010011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 2d6
-
-Encoding: 2d6
-U-Bits: 011010001000110000111001000000101101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 2d6
-
-Encoding: 2d7
-U-Bits: 101101101000110000111110001001111000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 2d7
-
-Encoding: 2d7
-U-Bits: 101101101000110000100011000100110001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 2d7
-
-Encoding: 2d7
-U-Bits: 101101101000110000100100001100001111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 2d7
-
-Encoding: 2d8
-U-Bits: 000111001001010000111110010010001111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 2d8
-
-Encoding: 2d8
-U-Bits: 000111001001010000100011011111000110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 2d8
-
-Encoding: 2d8
-U-Bits: 000111001001010000100100010111111000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 2d8
-
-Encoding: 2d9
-U-Bits: 110000101001010000100011011110101101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 2d9
-
-Encoding: 2d9
-U-Bits: 110000101001010000111110010011100100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 2d9
-
-Encoding: 2d9
-U-Bits: 110000101001010000111001011011011010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 2d9
-
-Encoding: 2da
-U-Bits: 010110110001010000111001000001000110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 2da
-
-Encoding: 2da
-U-Bits: 010110110001010000100100001100001111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 2da
-
-Encoding: 2da
-U-Bits: 010110110001010000100011000100110001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 2da
-
-Encoding: 2db
-U-Bits: 100001010001010000100100001101100100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 2db
-
-Encoding: 2db
-U-Bits: 100001010001010000111001000000101101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 2db
-
-Encoding: 2db
-U-Bits: 100001010001010000111110001000010011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 2db
-
-Encoding: 2dc
-U-Bits: 001101010111010000100011000010010111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 2dc
-
-Encoding: 2dc
-U-Bits: 001101010111010000111110001111011110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 2dc
-
-Encoding: 2dc
-U-Bits: 001101010111010000111001000111100000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 2dc
-
-Encoding: 2dd
-U-Bits: 111010110111010000111110001110110101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 2dd
-
-Encoding: 2dd
-U-Bits: 111010110111010000100011000011111100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 2dd
-
-Encoding: 2dd
-U-Bits: 111010110111010000100100001011000010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 2dd
-
-Encoding: 2de
-U-Bits: 011100101111010000100100010001011110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 2de
-
-Encoding: 2de
-U-Bits: 011100101111010000111001011100010111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 2de
-
-Encoding: 2de
-U-Bits: 011100101111010000111110010100101001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 2de
-
-Encoding: 2df
-U-Bits: 101011001111010000111001011101111100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 2df
-
-Encoding: 2df
-U-Bits: 101011001111010000100100010000110101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 2df
-
-Encoding: 2df
-U-Bits: 101011001111010000100011011000001011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 2df
-
-Encoding: 2e0
-U-Bits: 000000011101010110111000101110011000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 2e0
-
-Encoding: 2e0
-U-Bits: 000000011101010110100101100011010001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 2e0
-
-Encoding: 2e0
-U-Bits: 000000011101010110100010101011101111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 2e0
-
-Encoding: 2e1
-U-Bits: 110111111101010110100101100010111010
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 2e1
-
-Encoding: 2e1
-U-Bits: 110111111101010110111000101111110011
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 2e1
-
-Encoding: 2e1
-U-Bits: 110111111101010110111111100111001101
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 2e1
-
-Encoding: 2e2
-U-Bits: 010001100101010110111111111101010001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 2e2
-
-Encoding: 2e2
-U-Bits: 010001100101010110100010110000011000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 2e2
-
-Encoding: 2e2
-U-Bits: 010001100101010110100101111000100110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 2e2
-
-Encoding: 2e3
-U-Bits: 100110000101010110100010110001110011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 2e3
-
-Encoding: 2e3
-U-Bits: 100110000101010110111111111100111010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 2e3
-
-Encoding: 2e3
-U-Bits: 100110000101010110111000110100000100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 2e3
-
-Encoding: 2e4
-U-Bits: 001010000011010110100101111110000000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2e4
-
-Encoding: 2e4
-U-Bits: 001010000011010110111000110011001001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 2e4
-
-Encoding: 2e4
-U-Bits: 001010000011010110111111111011110111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 2e4
-
-Encoding: 2e5
-U-Bits: 111101100011010110111000110010100010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 2e5
-
-Encoding: 2e5
-U-Bits: 111101100011010110100101111111101011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 2e5
-
-Encoding: 2e5
-U-Bits: 111101100011010110100010110111010101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 2e5
-
-Encoding: 2e6
-U-Bits: 011011111011010110100010101101001001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 2e6
-
-Encoding: 2e6
-U-Bits: 011011111011010110111111100000000000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2e6
-
-Encoding: 2e6
-U-Bits: 011011111011010110111000101000111110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 2e6
-
-Encoding: 2e7
-U-Bits: 101100011011010110111111100001101011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 2e7
-
-Encoding: 2e7
-U-Bits: 101100011011010110100010101100100010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 2e7
-
-Encoding: 2e7
-U-Bits: 101100011011010110100101100100011100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 2e7
-
-Encoding: 2e8
-U-Bits: 000110111010110110111111111010011100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 2e8
-
-Encoding: 2e8
-U-Bits: 000110111010110110100010110111010101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 2e8
-
-Encoding: 2e8
-U-Bits: 000110111010110110100101111111101011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 2e8
-
-Encoding: 2e9
-U-Bits: 110001011010110110100010110110111110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 2e9
-
-Encoding: 2e9
-U-Bits: 110001011010110110111111111011110111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 2e9
-
-Encoding: 2e9
-U-Bits: 110001011010110110111000110011001001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 2e9
-
-Encoding: 2ea
-U-Bits: 010111000010110110111000101001010101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 2ea
-
-Encoding: 2ea
-U-Bits: 010111000010110110100101100100011100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 2ea
-
-Encoding: 2ea
-U-Bits: 010111000010110110100010101100100010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 2ea
-
-Encoding: 2eb
-U-Bits: 100000100010110110100101100101110111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 2eb
-
-Encoding: 2eb
-U-Bits: 100000100010110110111000101000111110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 2eb
-
-Encoding: 2eb
-U-Bits: 100000100010110110111111100000000000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 2eb
-
-Encoding: 2ec
-U-Bits: 001100100100110110100010101010000100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 2ec
-
-Encoding: 2ec
-U-Bits: 001100100100110110111111100111001101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 2ec
-
-Encoding: 2ec
-U-Bits: 001100100100110110111000101111110011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 2ec
-
-Encoding: 2ed
-U-Bits: 111011000100110110111111100110100110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 2ed
-
-Encoding: 2ed
-U-Bits: 111011000100110110100010101011101111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 2ed
-
-Encoding: 2ed
-U-Bits: 111011000100110110100101100011010001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 2ed
-
-Encoding: 2ee
-U-Bits: 011101011100110110100101111001001101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 2ee
-
-Encoding: 2ee
-U-Bits: 011101011100110110111000110100000100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 2ee
-
-Encoding: 2ee
-U-Bits: 011101011100110110111111111100111010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 2ee
-
-Encoding: 2ef
-U-Bits: 101010111100110110111000110101101111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 2ef
-
-Encoding: 2ef
-U-Bits: 101010111100110110100101111000100110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 2ef
-
-Encoding: 2ef
-U-Bits: 101010111100110110100010110000011000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 2ef
-
-Encoding: 2f0
-U-Bits: 000001110100101110111001011011011010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 2f0
-
-Encoding: 2f0
-U-Bits: 000001110100101110100100010110010011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 2f0
-
-Encoding: 2f0
-U-Bits: 000001110100101110100011011110101101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 2f0
-
-Encoding: 2f1
-U-Bits: 110110010100101110100100010111111000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 2f1
-
-Encoding: 2f1
-U-Bits: 110110010100101110111001011010110001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 2f1
-
-Encoding: 2f1
-U-Bits: 110110010100101110111110010010001111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 2f1
-
-Encoding: 2f2
-U-Bits: 010000001100101110111110001000010011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 2f2
-
-Encoding: 2f2
-U-Bits: 010000001100101110100011000101011010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 2f2
-
-Encoding: 2f2
-U-Bits: 010000001100101110100100001101100100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 2f2
-
-Encoding: 2f3
-U-Bits: 100111101100101110100011000100110001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 2f3
-
-Encoding: 2f3
-U-Bits: 100111101100101110111110001001111000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 2f3
-
-Encoding: 2f3
-U-Bits: 100111101100101110111001000001000110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 2f3
-
-Encoding: 2f4
-U-Bits: 001011101010101110100100001011000010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 2f4
-
-Encoding: 2f4
-U-Bits: 001011101010101110111001000110001011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 2f4
-
-Encoding: 2f4
-U-Bits: 001011101010101110111110001110110101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 2f4
-
-Encoding: 2f5
-U-Bits: 111100001010101110111001000111100000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 2f5
-
-Encoding: 2f5
-U-Bits: 111100001010101110100100001010101001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 2f5
-
-Encoding: 2f5
-U-Bits: 111100001010101110100011000010010111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 2f5
-
-Encoding: 2f6
-U-Bits: 011010010010101110100011011000001011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 2f6
-
-Encoding: 2f6
-U-Bits: 011010010010101110111110010101000010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 2f6
-
-Encoding: 2f6
-U-Bits: 011010010010101110111001011101111100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 2f6
-
-Encoding: 2f7
-U-Bits: 101101110010101110111110010100101001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 2f7
-
-Encoding: 2f7
-U-Bits: 101101110010101110100011011001100000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 2f7
-
-Encoding: 2f7
-U-Bits: 101101110010101110100100010001011110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 2f7
-
-Encoding: 2f8
-U-Bits: 000111010011001110111110001111011110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 2f8
-
-Encoding: 2f8
-U-Bits: 000111010011001110100011000010010111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 2f8
-
-Encoding: 2f8
-U-Bits: 000111010011001110100100001010101001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 2f8
-
-Encoding: 2f9
-U-Bits: 110000110011001110100011000011111100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 2f9
-
-Encoding: 2f9
-U-Bits: 110000110011001110111110001110110101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 2f9
-
-Encoding: 2f9
-U-Bits: 110000110011001110111001000110001011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 2f9
-
-Encoding: 2fa
-U-Bits: 010110101011001110111001011100010111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 2fa
-
-Encoding: 2fa
-U-Bits: 010110101011001110100100010001011110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 2fa
-
-Encoding: 2fa
-U-Bits: 010110101011001110100011011001100000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 2fa
-
-Encoding: 2fb
-U-Bits: 100001001011001110100100010000110101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 2fb
-
-Encoding: 2fb
-U-Bits: 100001001011001110111001011101111100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 2fb
-
-Encoding: 2fb
-U-Bits: 100001001011001110111110010101000010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 2fb
-
-Encoding: 2fc
-U-Bits: 001101001101001110100011011111000110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 2fc
-
-Encoding: 2fc
-U-Bits: 001101001101001110111110010010001111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 2fc
-
-Encoding: 2fc
-U-Bits: 001101001101001110111001011010110001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 2fc
-
-Encoding: 2fd
-U-Bits: 111010101101001110111110010011100100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 2fd
-
-Encoding: 2fd
-U-Bits: 111010101101001110100011011110101101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 2fd
-
-Encoding: 2fd
-U-Bits: 111010101101001110100100010110010011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 2fd
-
-Encoding: 2fe
-U-Bits: 011100110101001110100100001100001111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 2fe
-
-Encoding: 2fe
-U-Bits: 011100110101001110111001000001000110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 2fe
-
-Encoding: 2fe
-U-Bits: 011100110101001110111110001001111000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 2fe
-
-Encoding: 2ff
-U-Bits: 101011010101001110111001000000101101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 2ff
-
-Encoding: 2ff
-U-Bits: 101011010101001110100100001101100100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 2ff
-
-Encoding: 2ff
-U-Bits: 101011010101001110100011000101011010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 2ff
-
-Encoding: 300
-U-Bits: 000000000000011100100100101010000100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 300
-
-Encoding: 300
-U-Bits: 000000000000011100111001100111001101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 300
-
-Encoding: 300
-U-Bits: 000000000000011100111110101111110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 300
-
-Encoding: 301
-U-Bits: 110111100000011100111001100110100110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 301
-
-Encoding: 301
-U-Bits: 110111100000011100100100101011101111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 301
-
-Encoding: 301
-U-Bits: 110111100000011100100011100011010001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 301
-
-Encoding: 302
-U-Bits: 010001111000011100100011111001001101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 302
-
-Encoding: 302
-U-Bits: 010001111000011100111110110100000100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 302
-
-Encoding: 302
-U-Bits: 010001111000011100111001111100111010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 302
-
-Encoding: 303
-U-Bits: 100110011000011100111110110101101111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 303
-
-Encoding: 303
-U-Bits: 100110011000011100100011111000100110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 303
-
-Encoding: 303
-U-Bits: 100110011000011100100100110000011000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 303
-
-Encoding: 304
-U-Bits: 001010011110011100111001111010011100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 304
-
-Encoding: 304
-U-Bits: 001010011110011100100100110111010101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 304
-
-Encoding: 304
-U-Bits: 001010011110011100100011111111101011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 304
-
-Encoding: 305
-U-Bits: 111101111110011100100100110110111110
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 305
-
-Encoding: 305
-U-Bits: 111101111110011100111001111011110111
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 305
-
-Encoding: 305
-U-Bits: 111101111110011100111110110011001001
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 305
-
-Encoding: 306
-U-Bits: 011011100110011100111110101001010101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 306
-
-Encoding: 306
-U-Bits: 011011100110011100100011100100011100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 306
-
-Encoding: 306
-U-Bits: 011011100110011100100100101100100010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 306
-
-Encoding: 307
-U-Bits: 101100000110011100100011100101110111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 307
-
-Encoding: 307
-U-Bits: 101100000110011100111110101000111110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 307
-
-Encoding: 307
-U-Bits: 101100000110011100111001100000000000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 307
-
-Encoding: 308
-U-Bits: 000110100111111100100011111110000000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 308
-
-Encoding: 308
-U-Bits: 000110100111111100111110110011001001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 308
-
-Encoding: 308
-U-Bits: 000110100111111100111001111011110111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 308
-
-Encoding: 309
-U-Bits: 110001000111111100111110110010100010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 309
-
-Encoding: 309
-U-Bits: 110001000111111100100011111111101011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 309
-
-Encoding: 309
-U-Bits: 110001000111111100100100110111010101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 309
-
-Encoding: 30a
-U-Bits: 010111011111111100100100101101001001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 30a
-
-Encoding: 30a
-U-Bits: 010111011111111100111001100000000000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 30a
-
-Encoding: 30a
-U-Bits: 010111011111111100111110101000111110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 30a
-
-Encoding: 30b
-U-Bits: 100000111111111100111001100001101011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 30b
-
-Encoding: 30b
-U-Bits: 100000111111111100100100101100100010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 30b
-
-Encoding: 30b
-U-Bits: 100000111111111100100011100100011100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 30b
-
-Encoding: 30c
-U-Bits: 001100111001111100111110101110011000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 30c
-
-Encoding: 30c
-U-Bits: 001100111001111100100011100011010001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 30c
-
-Encoding: 30c
-U-Bits: 001100111001111100100100101011101111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 30c
-
-Encoding: 30d
-U-Bits: 111011011001111100100011100010111010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 30d
-
-Encoding: 30d
-U-Bits: 111011011001111100111110101111110011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 30d
-
-Encoding: 30d
-U-Bits: 111011011001111100111001100111001101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 30d
-
-Encoding: 30e
-U-Bits: 011101000001111100111001111101010001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 30e
-
-Encoding: 30e
-U-Bits: 011101000001111100100100110000011000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 30e
-
-Encoding: 30e
-U-Bits: 011101000001111100100011111000100110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 30e
-
-Encoding: 30f
-U-Bits: 101010100001111100100100110001110011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 30f
-
-Encoding: 30f
-U-Bits: 101010100001111100111001111100111010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 30f
-
-Encoding: 30f
-U-Bits: 101010100001111100111110110100000100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 30f
-
-Encoding: 310
-U-Bits: 000001101001100100100101011111000110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 310
-
-Encoding: 310
-U-Bits: 000001101001100100111000010010001111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 310
-
-Encoding: 310
-U-Bits: 000001101001100100111111011010110001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 310
-
-Encoding: 311
-U-Bits: 110110001001100100111000010011100100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 311
-
-Encoding: 311
-U-Bits: 110110001001100100100101011110101101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 311
-
-Encoding: 311
-U-Bits: 110110001001100100100010010110010011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 311
-
-Encoding: 312
-U-Bits: 010000010001100100100010001100001111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 312
-
-Encoding: 312
-U-Bits: 010000010001100100111111000001000110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 312
-
-Encoding: 312
-U-Bits: 010000010001100100111000001001111000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 312
-
-Encoding: 313
-U-Bits: 100111110001100100111111000000101101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 313
-
-Encoding: 313
-U-Bits: 100111110001100100100010001101100100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 313
-
-Encoding: 313
-U-Bits: 100111110001100100100101000101011010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 313
-
-Encoding: 314
-U-Bits: 001011110111100100111000001111011110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 314
-
-Encoding: 314
-U-Bits: 001011110111100100100101000010010111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 314
-
-Encoding: 314
-U-Bits: 001011110111100100100010001010101001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 314
-
-Encoding: 315
-U-Bits: 111100010111100100100101000011111100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 315
-
-Encoding: 315
-U-Bits: 111100010111100100111000001110110101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 315
-
-Encoding: 315
-U-Bits: 111100010111100100111111000110001011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 315
-
-Encoding: 316
-U-Bits: 011010001111100100111111011100010111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 316
-
-Encoding: 316
-U-Bits: 011010001111100100100010010001011110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 316
-
-Encoding: 316
-U-Bits: 011010001111100100100101011001100000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 316
-
-Encoding: 317
-U-Bits: 101101101111100100100010010000110101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 317
-
-Encoding: 317
-U-Bits: 101101101111100100111111011101111100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 317
-
-Encoding: 317
-U-Bits: 101101101111100100111000010101000010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 317
-
-Encoding: 318
-U-Bits: 000111001110000100100010001011000010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 318
-
-Encoding: 318
-U-Bits: 000111001110000100111111000110001011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 318
-
-Encoding: 318
-U-Bits: 000111001110000100111000001110110101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 318
-
-Encoding: 319
-U-Bits: 110000101110000100111111000111100000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 319
-
-Encoding: 319
-U-Bits: 110000101110000100100010001010101001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 319
-
-Encoding: 319
-U-Bits: 110000101110000100100101000010010111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 319
-
-Encoding: 31a
-U-Bits: 010110110110000100100101011000001011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 31a
-
-Encoding: 31a
-U-Bits: 010110110110000100111000010101000010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 31a
-
-Encoding: 31a
-U-Bits: 010110110110000100111111011101111100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 31a
-
-Encoding: 31b
-U-Bits: 100001010110000100111000010100101001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 31b
-
-Encoding: 31b
-U-Bits: 100001010110000100100101011001100000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 31b
-
-Encoding: 31b
-U-Bits: 100001010110000100100010010001011110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 31b
-
-Encoding: 31c
-U-Bits: 001101010000000100111111011011011010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 31c
-
-Encoding: 31c
-U-Bits: 001101010000000100100010010110010011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 31c
-
-Encoding: 31c
-U-Bits: 001101010000000100100101011110101101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 31c
-
-Encoding: 31d
-U-Bits: 111010110000000100100010010111111000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 31d
-
-Encoding: 31d
-U-Bits: 111010110000000100111111011010110001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 31d
-
-Encoding: 31d
-U-Bits: 111010110000000100111000010010001111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 31d
-
-Encoding: 31e
-U-Bits: 011100101000000100111000001000010011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 31e
-
-Encoding: 31e
-U-Bits: 011100101000000100100101000101011010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 31e
-
-Encoding: 31e
-U-Bits: 011100101000000100100010001101100100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 31e
-
-Encoding: 31f
-U-Bits: 101011001000000100100101000100110001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 31f
-
-Encoding: 31f
-U-Bits: 101011001000000100111000001001111000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 31f
-
-Encoding: 31f
-U-Bits: 101011001000000100111111000001000110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 31f
-
-Encoding: 320
-U-Bits: 000000011010000010100100110111010101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 320
-
-Encoding: 320
-U-Bits: 000000011010000010111001111010011100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 320
-
-Encoding: 320
-U-Bits: 000000011010000010111110110010100010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 320
-
-Encoding: 321
-U-Bits: 110111111010000010111001111011110111
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 321
-
-Encoding: 321
-U-Bits: 110111111010000010100100110110111110
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 321
-
-Encoding: 321
-U-Bits: 110111111010000010100011111110000000
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 321
-
-Encoding: 322
-U-Bits: 010001100010000010100011100100011100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 322
-
-Encoding: 322
-U-Bits: 010001100010000010111110101001010101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 322
-
-Encoding: 322
-U-Bits: 010001100010000010111001100001101011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 322
-
-Encoding: 323
-U-Bits: 100110000010000010111110101000111110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 323
-
-Encoding: 323
-U-Bits: 100110000010000010100011100101110111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 323
-
-Encoding: 323
-U-Bits: 100110000010000010100100101101001001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 323
-
-Encoding: 324
-U-Bits: 001010000100000010111001100111001101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 324
-
-Encoding: 324
-U-Bits: 001010000100000010100100101010000100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 324
-
-Encoding: 324
-U-Bits: 001010000100000010100011100010111010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 324
-
-Encoding: 325
-U-Bits: 111101100100000010100100101011101111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 325
-
-Encoding: 325
-U-Bits: 111101100100000010111001100110100110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 325
-
-Encoding: 325
-U-Bits: 111101100100000010111110101110011000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 325
-
-Encoding: 326
-U-Bits: 011011111100000010111110110100000100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 326
-
-Encoding: 326
-U-Bits: 011011111100000010100011111001001101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 326
-
-Encoding: 326
-U-Bits: 011011111100000010100100110001110011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 326
-
-Encoding: 327
-U-Bits: 101100011100000010100011111000100110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 327
-
-Encoding: 327
-U-Bits: 101100011100000010111110110101101111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 327
-
-Encoding: 327
-U-Bits: 101100011100000010111001111101010001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 327
-
-Encoding: 328
-U-Bits: 000110111101100010100011100011010001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 328
-
-Encoding: 328
-U-Bits: 000110111101100010111110101110011000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 328
-
-Encoding: 328
-U-Bits: 000110111101100010111001100110100110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 328
-
-Encoding: 329
-U-Bits: 110001011101100010111110101111110011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 329
-
-Encoding: 329
-U-Bits: 110001011101100010100011100010111010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 329
-
-Encoding: 329
-U-Bits: 110001011101100010100100101010000100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 329
-
-Encoding: 32a
-U-Bits: 010111000101100010100100110000011000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 32a
-
-Encoding: 32a
-U-Bits: 010111000101100010111001111101010001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 32a
-
-Encoding: 32a
-U-Bits: 010111000101100010111110110101101111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 32a
-
-Encoding: 32b
-U-Bits: 100000100101100010111001111100111010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 32b
-
-Encoding: 32b
-U-Bits: 100000100101100010100100110001110011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 32b
-
-Encoding: 32b
-U-Bits: 100000100101100010100011111001001101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 32b
-
-Encoding: 32c
-U-Bits: 001100100011100010111110110011001001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 32c
-
-Encoding: 32c
-U-Bits: 001100100011100010100011111110000000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 32c
-
-Encoding: 32c
-U-Bits: 001100100011100010100100110110111110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 32c
-
-Encoding: 32d
-U-Bits: 111011000011100010100011111111101011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 32d
-
-Encoding: 32d
-U-Bits: 111011000011100010111110110010100010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 32d
-
-Encoding: 32d
-U-Bits: 111011000011100010111001111010011100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 32d
-
-Encoding: 32e
-U-Bits: 011101011011100010111001100000000000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 32e
-
-Encoding: 32e
-U-Bits: 011101011011100010100100101101001001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 32e
-
-Encoding: 32e
-U-Bits: 011101011011100010100011100101110111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 32e
-
-Encoding: 32f
-U-Bits: 101010111011100010100100101100100010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 32f
-
-Encoding: 32f
-U-Bits: 101010111011100010111001100001101011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 32f
-
-Encoding: 32f
-U-Bits: 101010111011100010111110101001010101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 32f
-
-Encoding: 330
-U-Bits: 000001110011111010100101000010010111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 330
-
-Encoding: 330
-U-Bits: 000001110011111010111000001111011110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 330
-
-Encoding: 330
-U-Bits: 000001110011111010111111000111100000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 330
-
-Encoding: 331
-U-Bits: 110110010011111010111000001110110101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 331
-
-Encoding: 331
-U-Bits: 110110010011111010100101000011111100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 331
-
-Encoding: 331
-U-Bits: 110110010011111010100010001011000010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 331
-
-Encoding: 332
-U-Bits: 010000001011111010100010010001011110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 332
-
-Encoding: 332
-U-Bits: 010000001011111010111111011100010111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 332
-
-Encoding: 332
-U-Bits: 010000001011111010111000010100101001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 332
-
-Encoding: 333
-U-Bits: 100111101011111010111111011101111100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 333
-
-Encoding: 333
-U-Bits: 100111101011111010100010010000110101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 333
-
-Encoding: 333
-U-Bits: 100111101011111010100101011000001011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 333
-
-Encoding: 334
-U-Bits: 001011101101111010111000010010001111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 334
-
-Encoding: 334
-U-Bits: 001011101101111010100101011111000110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 334
-
-Encoding: 334
-U-Bits: 001011101101111010100010010111111000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 334
-
-Encoding: 335
-U-Bits: 111100001101111010100101011110101101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 335
-
-Encoding: 335
-U-Bits: 111100001101111010111000010011100100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 335
-
-Encoding: 335
-U-Bits: 111100001101111010111111011011011010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 335
-
-Encoding: 336
-U-Bits: 011010010101111010111111000001000110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 336
-
-Encoding: 336
-U-Bits: 011010010101111010100010001100001111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 336
-
-Encoding: 336
-U-Bits: 011010010101111010100101000100110001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 336
-
-Encoding: 337
-U-Bits: 101101110101111010100010001101100100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 337
-
-Encoding: 337
-U-Bits: 101101110101111010111111000000101101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 337
-
-Encoding: 337
-U-Bits: 101101110101111010111000001000010011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 337
-
-Encoding: 338
-U-Bits: 000111010100011010100010010110010011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 338
-
-Encoding: 338
-U-Bits: 000111010100011010111111011011011010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 338
-
-Encoding: 338
-U-Bits: 000111010100011010111000010011100100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 338
-
-Encoding: 339
-U-Bits: 110000110100011010111111011010110001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 339
-
-Encoding: 339
-U-Bits: 110000110100011010100010010111111000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 339
-
-Encoding: 339
-U-Bits: 110000110100011010100101011111000110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 339
-
-Encoding: 33a
-U-Bits: 010110101100011010100101000101011010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 33a
-
-Encoding: 33a
-U-Bits: 010110101100011010111000001000010011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 33a
-
-Encoding: 33a
-U-Bits: 010110101100011010111111000000101101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 33a
-
-Encoding: 33b
-U-Bits: 100001001100011010111000001001111000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 33b
-
-Encoding: 33b
-U-Bits: 100001001100011010100101000100110001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 33b
-
-Encoding: 33b
-U-Bits: 100001001100011010100010001100001111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 33b
-
-Encoding: 33c
-U-Bits: 001101001010011010111111000110001011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 33c
-
-Encoding: 33c
-U-Bits: 001101001010011010100010001011000010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 33c
-
-Encoding: 33c
-U-Bits: 001101001010011010100101000011111100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 33c
-
-Encoding: 33d
-U-Bits: 111010101010011010100010001010101001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 33d
-
-Encoding: 33d
-U-Bits: 111010101010011010111111000111100000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 33d
-
-Encoding: 33d
-U-Bits: 111010101010011010111000001111011110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 33d
-
-Encoding: 33e
-U-Bits: 011100110010011010111000010101000010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 33e
-
-Encoding: 33e
-U-Bits: 011100110010011010100101011000001011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 33e
-
-Encoding: 33e
-U-Bits: 011100110010011010100010010000110101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 33e
-
-Encoding: 33f
-U-Bits: 101011010010011010100101011001100000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 33f
-
-Encoding: 33f
-U-Bits: 101011010010011010111000010100101001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 33f
-
-Encoding: 33f
-U-Bits: 101011010010011010111111011100010111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 33f
-
-Encoding: 340
-U-Bits: 000000000110111011011000001001111000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 340
-
-Encoding: 340
-U-Bits: 000000000110111011000101000100110001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 340
-
-Encoding: 340
-U-Bits: 000000000110111011000010001100001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 340
-
-Encoding: 341
-U-Bits: 110111100110111011000101000101011010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 341
-
-Encoding: 341
-U-Bits: 110111100110111011011000001000010011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 341
-
-Encoding: 341
-U-Bits: 110111100110111011011111000000101101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 341
-
-Encoding: 342
-U-Bits: 010001111110111011011111011010110001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 342
-
-Encoding: 342
-U-Bits: 010001111110111011000010010111111000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 342
-
-Encoding: 342
-U-Bits: 010001111110111011000101011111000110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 342
-
-Encoding: 343
-U-Bits: 100110011110111011000010010110010011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 343
-
-Encoding: 343
-U-Bits: 100110011110111011011111011011011010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 343
-
-Encoding: 343
-U-Bits: 100110011110111011011000010011100100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 343
-
-Encoding: 344
-U-Bits: 001010011000111011000101011001100000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 344
-
-Encoding: 344
-U-Bits: 001010011000111011011000010100101001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 344
-
-Encoding: 344
-U-Bits: 001010011000111011011111011100010111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 344
-
-Encoding: 345
-U-Bits: 111101111000111011011000010101000010
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 345
-
-Encoding: 345
-U-Bits: 111101111000111011000101011000001011
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 345
-
-Encoding: 345
-U-Bits: 111101111000111011000010010000110101
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 345
-
-Encoding: 346
-U-Bits: 011011100000111011000010001010101001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 346
-
-Encoding: 346
-U-Bits: 011011100000111011011111000111100000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 346
-
-Encoding: 346
-U-Bits: 011011100000111011011000001111011110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 346
-
-Encoding: 347
-U-Bits: 101100000000111011011111000110001011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 347
-
-Encoding: 347
-U-Bits: 101100000000111011000010001011000010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 347
-
-Encoding: 347
-U-Bits: 101100000000111011000101000011111100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 347
-
-Encoding: 348
-U-Bits: 000110100001011011011111011101111100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 348
-
-Encoding: 348
-U-Bits: 000110100001011011000010010000110101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 348
-
-Encoding: 348
-U-Bits: 000110100001011011000101011000001011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 348
-
-Encoding: 349
-U-Bits: 110001000001011011000010010001011110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 349
-
-Encoding: 349
-U-Bits: 110001000001011011011111011100010111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 349
-
-Encoding: 349
-U-Bits: 110001000001011011011000010100101001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 349
-
-Encoding: 34a
-U-Bits: 010111011001011011011000001110110101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 34a
-
-Encoding: 34a
-U-Bits: 010111011001011011000101000011111100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 34a
-
-Encoding: 34a
-U-Bits: 010111011001011011000010001011000010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 34a
-
-Encoding: 34b
-U-Bits: 100000111001011011000101000010010111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 34b
-
-Encoding: 34b
-U-Bits: 100000111001011011011000001111011110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 34b
-
-Encoding: 34b
-U-Bits: 100000111001011011011111000111100000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 34b
-
-Encoding: 34c
-U-Bits: 001100111111011011000010001101100100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 34c
-
-Encoding: 34c
-U-Bits: 001100111111011011011111000000101101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 34c
-
-Encoding: 34c
-U-Bits: 001100111111011011011000001000010011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 34c
-
-Encoding: 34d
-U-Bits: 111011011111011011011111000001000110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 34d
-
-Encoding: 34d
-U-Bits: 111011011111011011000010001100001111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 34d
-
-Encoding: 34d
-U-Bits: 111011011111011011000101000100110001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 34d
-
-Encoding: 34e
-U-Bits: 011101000111011011000101011110101101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 34e
-
-Encoding: 34e
-U-Bits: 011101000111011011011000010011100100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 34e
-
-Encoding: 34e
-U-Bits: 011101000111011011011111011011011010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 34e
-
-Encoding: 34f
-U-Bits: 101010100111011011011000010010001111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 34f
-
-Encoding: 34f
-U-Bits: 101010100111011011000101011111000110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 34f
-
-Encoding: 34f
-U-Bits: 101010100111011011000010010111111000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 34f
-
-Encoding: 350
-U-Bits: 000001101111000011011001111100111010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 350
-
-Encoding: 350
-U-Bits: 000001101111000011000100110001110011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 350
-
-Encoding: 350
-U-Bits: 000001101111000011000011111001001101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 350
-
-Encoding: 351
-U-Bits: 110110001111000011000100110000011000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 351
-
-Encoding: 351
-U-Bits: 110110001111000011011001111101010001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 351
-
-Encoding: 351
-U-Bits: 110110001111000011011110110101101111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 351
-
-Encoding: 352
-U-Bits: 010000010111000011011110101111110011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 352
-
-Encoding: 352
-U-Bits: 010000010111000011000011100010111010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 352
-
-Encoding: 352
-U-Bits: 010000010111000011000100101010000100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 352
-
-Encoding: 353
-U-Bits: 100111110111000011000011100011010001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 353
-
-Encoding: 353
-U-Bits: 100111110111000011011110101110011000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 353
-
-Encoding: 353
-U-Bits: 100111110111000011011001100110100110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 353
-
-Encoding: 354
-U-Bits: 001011110001000011000100101100100010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 354
-
-Encoding: 354
-U-Bits: 001011110001000011011001100001101011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 354
-
-Encoding: 354
-U-Bits: 001011110001000011011110101001010101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 354
-
-Encoding: 355
-U-Bits: 111100010001000011011001100000000000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 355
-
-Encoding: 355
-U-Bits: 111100010001000011000100101101001001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 355
-
-Encoding: 355
-U-Bits: 111100010001000011000011100101110111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 355
-
-Encoding: 356
-U-Bits: 011010001001000011000011111111101011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 356
-
-Encoding: 356
-U-Bits: 011010001001000011011110110010100010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 356
-
-Encoding: 356
-U-Bits: 011010001001000011011001111010011100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 356
-
-Encoding: 357
-U-Bits: 101101101001000011011110110011001001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 357
-
-Encoding: 357
-U-Bits: 101101101001000011000011111110000000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 357
-
-Encoding: 357
-U-Bits: 101101101001000011000100110110111110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 357
-
-Encoding: 358
-U-Bits: 000111001000100011011110101000111110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 358
-
-Encoding: 358
-U-Bits: 000111001000100011000011100101110111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 358
-
-Encoding: 358
-U-Bits: 000111001000100011000100101101001001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 358
-
-Encoding: 359
-U-Bits: 110000101000100011000011100100011100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 359
-
-Encoding: 359
-U-Bits: 110000101000100011011110101001010101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 359
-
-Encoding: 359
-U-Bits: 110000101000100011011001100001101011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 359
-
-Encoding: 35a
-U-Bits: 010110110000100011011001111011110111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 35a
-
-Encoding: 35a
-U-Bits: 010110110000100011000100110110111110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 35a
-
-Encoding: 35a
-U-Bits: 010110110000100011000011111110000000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 35a
-
-Encoding: 35b
-U-Bits: 100001010000100011000100110111010101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 35b
-
-Encoding: 35b
-U-Bits: 100001010000100011011001111010011100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 35b
-
-Encoding: 35b
-U-Bits: 100001010000100011011110110010100010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 35b
-
-Encoding: 35c
-U-Bits: 001101010110100011000011111000100110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 35c
-
-Encoding: 35c
-U-Bits: 001101010110100011011110110101101111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 35c
-
-Encoding: 35c
-U-Bits: 001101010110100011011001111101010001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 35c
-
-Encoding: 35d
-U-Bits: 111010110110100011011110110100000100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 35d
-
-Encoding: 35d
-U-Bits: 111010110110100011000011111001001101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 35d
-
-Encoding: 35d
-U-Bits: 111010110110100011000100110001110011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 35d
-
-Encoding: 35e
-U-Bits: 011100101110100011000100101011101111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 35e
-
-Encoding: 35e
-U-Bits: 011100101110100011011001100110100110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 35e
-
-Encoding: 35e
-U-Bits: 011100101110100011011110101110011000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 35e
-
-Encoding: 35f
-U-Bits: 101011001110100011011001100111001101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 35f
-
-Encoding: 35f
-U-Bits: 101011001110100011000100101010000100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 35f
-
-Encoding: 35f
-U-Bits: 101011001110100011000011100010111010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 35f
-
-Encoding: 360
-U-Bits: 000000011100100101011000010100101001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 360
-
-Encoding: 360
-U-Bits: 000000011100100101000101011001100000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 360
-
-Encoding: 360
-U-Bits: 000000011100100101000010010001011110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 360
-
-Encoding: 361
-U-Bits: 110111111100100101000101011000001011
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 361
-
-Encoding: 361
-U-Bits: 110111111100100101011000010101000010
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 361
-
-Encoding: 361
-U-Bits: 110111111100100101011111011101111100
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 361
-
-Encoding: 362
-U-Bits: 010001100100100101011111000111100000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 362
-
-Encoding: 362
-U-Bits: 010001100100100101000010001010101001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 362
-
-Encoding: 362
-U-Bits: 010001100100100101000101000010010111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 362
-
-Encoding: 363
-U-Bits: 100110000100100101000010001011000010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 363
-
-Encoding: 363
-U-Bits: 100110000100100101011111000110001011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 363
-
-Encoding: 363
-U-Bits: 100110000100100101011000001110110101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 363
-
-Encoding: 364
-U-Bits: 001010000010100101000101000100110001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 364
-
-Encoding: 364
-U-Bits: 001010000010100101011000001001111000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 364
-
-Encoding: 364
-U-Bits: 001010000010100101011111000001000110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 364
-
-Encoding: 365
-U-Bits: 111101100010100101011000001000010011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 365
-
-Encoding: 365
-U-Bits: 111101100010100101000101000101011010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 365
-
-Encoding: 365
-U-Bits: 111101100010100101000010001101100100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 365
-
-Encoding: 366
-U-Bits: 011011111010100101000010010111111000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 366
-
-Encoding: 366
-U-Bits: 011011111010100101011111011010110001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 366
-
-Encoding: 366
-U-Bits: 011011111010100101011000010010001111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 366
-
-Encoding: 367
-U-Bits: 101100011010100101011111011011011010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 367
-
-Encoding: 367
-U-Bits: 101100011010100101000010010110010011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 367
-
-Encoding: 367
-U-Bits: 101100011010100101000101011110101101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 367
-
-Encoding: 368
-U-Bits: 000110111011000101011111000000101101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 368
-
-Encoding: 368
-U-Bits: 000110111011000101000010001101100100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 368
-
-Encoding: 368
-U-Bits: 000110111011000101000101000101011010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 368
-
-Encoding: 369
-U-Bits: 110001011011000101000010001100001111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 369
-
-Encoding: 369
-U-Bits: 110001011011000101011111000001000110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 369
-
-Encoding: 369
-U-Bits: 110001011011000101011000001001111000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 369
-
-Encoding: 36a
-U-Bits: 010111000011000101011000010011100100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 36a
-
-Encoding: 36a
-U-Bits: 010111000011000101000101011110101101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 36a
-
-Encoding: 36a
-U-Bits: 010111000011000101000010010110010011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 36a
-
-Encoding: 36b
-U-Bits: 100000100011000101000101011111000110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 36b
-
-Encoding: 36b
-U-Bits: 100000100011000101011000010010001111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 36b
-
-Encoding: 36b
-U-Bits: 100000100011000101011111011010110001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 36b
-
-Encoding: 36c
-U-Bits: 001100100101000101000010010000110101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 36c
-
-Encoding: 36c
-U-Bits: 001100100101000101011111011101111100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 36c
-
-Encoding: 36c
-U-Bits: 001100100101000101011000010101000010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 36c
-
-Encoding: 36d
-U-Bits: 111011000101000101011111011100010111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 36d
-
-Encoding: 36d
-U-Bits: 111011000101000101000010010001011110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 36d
-
-Encoding: 36d
-U-Bits: 111011000101000101000101011001100000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 36d
-
-Encoding: 36e
-U-Bits: 011101011101000101000101000011111100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 36e
-
-Encoding: 36e
-U-Bits: 011101011101000101011000001110110101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 36e
-
-Encoding: 36e
-U-Bits: 011101011101000101011111000110001011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 36e
-
-Encoding: 36f
-U-Bits: 101010111101000101011000001111011110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 36f
-
-Encoding: 36f
-U-Bits: 101010111101000101000101000010010111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 36f
-
-Encoding: 36f
-U-Bits: 101010111101000101000010001010101001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 36f
-
-Encoding: 370
-U-Bits: 000001110101011101011001100001101011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 370
-
-Encoding: 370
-U-Bits: 000001110101011101000100101100100010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 370
-
-Encoding: 370
-U-Bits: 000001110101011101000011100100011100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 370
-
-Encoding: 371
-U-Bits: 110110010101011101000100101101001001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 371
-
-Encoding: 371
-U-Bits: 110110010101011101011001100000000000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 371
-
-Encoding: 371
-U-Bits: 110110010101011101011110101000111110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 371
-
-Encoding: 372
-U-Bits: 010000001101011101011110110010100010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 372
-
-Encoding: 372
-U-Bits: 010000001101011101000011111111101011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 372
-
-Encoding: 372
-U-Bits: 010000001101011101000100110111010101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 372
-
-Encoding: 373
-U-Bits: 100111101101011101000011111110000000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 373
-
-Encoding: 373
-U-Bits: 100111101101011101011110110011001001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 373
-
-Encoding: 373
-U-Bits: 100111101101011101011001111011110111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 373
-
-Encoding: 374
-U-Bits: 001011101011011101000100110001110011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 374
-
-Encoding: 374
-U-Bits: 001011101011011101011001111100111010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 374
-
-Encoding: 374
-U-Bits: 001011101011011101011110110100000100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 374
-
-Encoding: 375
-U-Bits: 111100001011011101011001111101010001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 375
-
-Encoding: 375
-U-Bits: 111100001011011101000100110000011000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 375
-
-Encoding: 375
-U-Bits: 111100001011011101000011111000100110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 375
-
-Encoding: 376
-U-Bits: 011010010011011101000011100010111010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 376
-
-Encoding: 376
-U-Bits: 011010010011011101011110101111110011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 376
-
-Encoding: 376
-U-Bits: 011010010011011101011001100111001101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 376
-
-Encoding: 377
-U-Bits: 101101110011011101011110101110011000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 377
-
-Encoding: 377
-U-Bits: 101101110011011101000011100011010001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 377
-
-Encoding: 377
-U-Bits: 101101110011011101000100101011101111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 377
-
-Encoding: 378
-U-Bits: 000111010010111101011110110101101111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 378
-
-Encoding: 378
-U-Bits: 000111010010111101000011111000100110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 378
-
-Encoding: 378
-U-Bits: 000111010010111101000100110000011000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 378
-
-Encoding: 379
-U-Bits: 110000110010111101000011111001001101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 379
-
-Encoding: 379
-U-Bits: 110000110010111101011110110100000100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 379
-
-Encoding: 379
-U-Bits: 110000110010111101011001111100111010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 379
-
-Encoding: 37a
-U-Bits: 010110101010111101011001100110100110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 37a
-
-Encoding: 37a
-U-Bits: 010110101010111101000100101011101111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 37a
-
-Encoding: 37a
-U-Bits: 010110101010111101000011100011010001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 37a
-
-Encoding: 37b
-U-Bits: 100001001010111101000100101010000100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 37b
-
-Encoding: 37b
-U-Bits: 100001001010111101011001100111001101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 37b
-
-Encoding: 37b
-U-Bits: 100001001010111101011110101111110011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 37b
-
-Encoding: 37c
-U-Bits: 001101001100111101000011100101110111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 37c
-
-Encoding: 37c
-U-Bits: 001101001100111101011110101000111110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 37c
-
-Encoding: 37c
-U-Bits: 001101001100111101011001100000000000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 37c
-
-Encoding: 37d
-U-Bits: 111010101100111101011110101001010101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 37d
-
-Encoding: 37d
-U-Bits: 111010101100111101000011100100011100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 37d
-
-Encoding: 37d
-U-Bits: 111010101100111101000100101100100010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 37d
-
-Encoding: 37e
-U-Bits: 011100110100111101000100110110111110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 37e
-
-Encoding: 37e
-U-Bits: 011100110100111101011001111011110111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 37e
-
-Encoding: 37e
-U-Bits: 011100110100111101011110110011001001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 37e
-
-Encoding: 37f
-U-Bits: 101011010100111101011001111010011100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 37f
-
-Encoding: 37f
-U-Bits: 101011010100111101000100110111010101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 37f
-
-Encoding: 37f
-U-Bits: 101011010100111101000011111111101011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 37f
-
-Encoding: 380
-U-Bits: 000000000001110101011011100010111010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 380
-
-Encoding: 380
-U-Bits: 000000000001110101000110101111110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 380
-
-Encoding: 380
-U-Bits: 000000000001110101000001100111001101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 380
-
-Encoding: 381
-U-Bits: 110111100001110101000110101110011000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 381
-
-Encoding: 381
-U-Bits: 110111100001110101011011100011010001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 381
-
-Encoding: 381
-U-Bits: 110111100001110101011100101011101111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 381
-
-Encoding: 382
-U-Bits: 010001111001110101011100110001110011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 382
-
-Encoding: 382
-U-Bits: 010001111001110101000001111100111010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 382
-
-Encoding: 382
-U-Bits: 010001111001110101000110110100000100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 382
-
-Encoding: 383
-U-Bits: 100110011001110101000001111101010001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 383
-
-Encoding: 383
-U-Bits: 100110011001110101011100110000011000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 383
-
-Encoding: 383
-U-Bits: 100110011001110101011011111000100110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 383
-
-Encoding: 384
-U-Bits: 001010011111110101000110110010100010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 384
-
-Encoding: 384
-U-Bits: 001010011111110101011011111111101011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 384
-
-Encoding: 384
-U-Bits: 001010011111110101011100110111010101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 384
-
-Encoding: 385
-U-Bits: 111101111111110101011011111110000000
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 385
-
-Encoding: 385
-U-Bits: 111101111111110101000110110011001001
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 385
-
-Encoding: 385
-U-Bits: 111101111111110101000001111011110111
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 385
-
-Encoding: 386
-U-Bits: 011011100111110101000001100001101011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 386
-
-Encoding: 386
-U-Bits: 011011100111110101011100101100100010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 386
-
-Encoding: 386
-U-Bits: 011011100111110101011011100100011100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 386
-
-Encoding: 387
-U-Bits: 101100000111110101011100101101001001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 387
-
-Encoding: 387
-U-Bits: 101100000111110101000001100000000000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 387
-
-Encoding: 387
-U-Bits: 101100000111110101000110101000111110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 387
-
-Encoding: 388
-U-Bits: 000110100110010101011100110110111110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 388
-
-Encoding: 388
-U-Bits: 000110100110010101000001111011110111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 388
-
-Encoding: 388
-U-Bits: 000110100110010101000110110011001001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 388
-
-Encoding: 389
-U-Bits: 110001000110010101000001111010011100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 389
-
-Encoding: 389
-U-Bits: 110001000110010101011100110111010101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 389
-
-Encoding: 389
-U-Bits: 110001000110010101011011111111101011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 389
-
-Encoding: 38a
-U-Bits: 010111011110010101011011100101110111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 38a
-
-Encoding: 38a
-U-Bits: 010111011110010101000110101000111110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 38a
-
-Encoding: 38a
-U-Bits: 010111011110010101000001100000000000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 38a
-
-Encoding: 38b
-U-Bits: 100000111110010101000110101001010101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 38b
-
-Encoding: 38b
-U-Bits: 100000111110010101011011100100011100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 38b
-
-Encoding: 38b
-U-Bits: 100000111110010101011100101100100010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 38b
-
-Encoding: 38c
-U-Bits: 001100111000010101000001100110100110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 38c
-
-Encoding: 38c
-U-Bits: 001100111000010101011100101011101111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 38c
-
-Encoding: 38c
-U-Bits: 001100111000010101011011100011010001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 38c
-
-Encoding: 38d
-U-Bits: 111011011000010101011100101010000100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 38d
-
-Encoding: 38d
-U-Bits: 111011011000010101000001100111001101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 38d
-
-Encoding: 38d
-U-Bits: 111011011000010101000110101111110011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 38d
-
-Encoding: 38e
-U-Bits: 011101000000010101000110110101101111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 38e
-
-Encoding: 38e
-U-Bits: 011101000000010101011011111000100110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 38e
-
-Encoding: 38e
-U-Bits: 011101000000010101011100110000011000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 38e
-
-Encoding: 38f
-U-Bits: 101010100000010101011011111001001101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 38f
-
-Encoding: 38f
-U-Bits: 101010100000010101000110110100000100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 38f
-
-Encoding: 38f
-U-Bits: 101010100000010101000001111100111010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 38f
-
-Encoding: 390
-U-Bits: 000001101000001101011010010111111000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 390
-
-Encoding: 390
-U-Bits: 000001101000001101000111011010110001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 390
-
-Encoding: 390
-U-Bits: 000001101000001101000000010010001111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 390
-
-Encoding: 391
-U-Bits: 110110001000001101000111011011011010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 391
-
-Encoding: 391
-U-Bits: 110110001000001101011010010110010011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 391
-
-Encoding: 391
-U-Bits: 110110001000001101011101011110101101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 391
-
-Encoding: 392
-U-Bits: 010000010000001101011101000100110001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 392
-
-Encoding: 392
-U-Bits: 010000010000001101000000001001111000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 392
-
-Encoding: 392
-U-Bits: 010000010000001101000111000001000110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 392
-
-Encoding: 393
-U-Bits: 100111110000001101000000001000010011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 393
-
-Encoding: 393
-U-Bits: 100111110000001101011101000101011010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 393
-
-Encoding: 393
-U-Bits: 100111110000001101011010001101100100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 393
-
-Encoding: 394
-U-Bits: 001011110110001101000111000111100000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 394
-
-Encoding: 394
-U-Bits: 001011110110001101011010001010101001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 394
-
-Encoding: 394
-U-Bits: 001011110110001101011101000010010111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 394
-
-Encoding: 395
-U-Bits: 111100010110001101011010001011000010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 395
-
-Encoding: 395
-U-Bits: 111100010110001101000111000110001011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 395
-
-Encoding: 395
-U-Bits: 111100010110001101000000001110110101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 395
-
-Encoding: 396
-U-Bits: 011010001110001101000000010100101001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 396
-
-Encoding: 396
-U-Bits: 011010001110001101011101011001100000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 396
-
-Encoding: 396
-U-Bits: 011010001110001101011010010001011110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 396
-
-Encoding: 397
-U-Bits: 101101101110001101011101011000001011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 397
-
-Encoding: 397
-U-Bits: 101101101110001101000000010101000010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 397
-
-Encoding: 397
-U-Bits: 101101101110001101000111011101111100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 397
-
-Encoding: 398
-U-Bits: 000111001111101101011101000011111100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 398
-
-Encoding: 398
-U-Bits: 000111001111101101000000001110110101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 398
-
-Encoding: 398
-U-Bits: 000111001111101101000111000110001011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 398
-
-Encoding: 399
-U-Bits: 110000101111101101000000001111011110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 399
-
-Encoding: 399
-U-Bits: 110000101111101101011101000010010111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 399
-
-Encoding: 399
-U-Bits: 110000101111101101011010001010101001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 399
-
-Encoding: 39a
-U-Bits: 010110110111101101011010010000110101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 39a
-
-Encoding: 39a
-U-Bits: 010110110111101101000111011101111100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 39a
-
-Encoding: 39a
-U-Bits: 010110110111101101000000010101000010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 39a
-
-Encoding: 39b
-U-Bits: 100001010111101101000111011100010111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 39b
-
-Encoding: 39b
-U-Bits: 100001010111101101011010010001011110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 39b
-
-Encoding: 39b
-U-Bits: 100001010111101101011101011001100000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 39b
-
-Encoding: 39c
-U-Bits: 001101010001101101000000010011100100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 39c
-
-Encoding: 39c
-U-Bits: 001101010001101101011101011110101101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 39c
-
-Encoding: 39c
-U-Bits: 001101010001101101011010010110010011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 39c
-
-Encoding: 39d
-U-Bits: 111010110001101101011101011111000110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 39d
-
-Encoding: 39d
-U-Bits: 111010110001101101000000010010001111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 39d
-
-Encoding: 39d
-U-Bits: 111010110001101101000111011010110001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 39d
-
-Encoding: 39e
-U-Bits: 011100101001101101000111000000101101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 39e
-
-Encoding: 39e
-U-Bits: 011100101001101101011010001101100100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 39e
-
-Encoding: 39e
-U-Bits: 011100101001101101011101000101011010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 39e
-
-Encoding: 39f
-U-Bits: 101011001001101101011010001100001111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 39f
-
-Encoding: 39f
-U-Bits: 101011001001101101000111000001000110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 39f
-
-Encoding: 39f
-U-Bits: 101011001001101101000000001001111000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 39f
-
-Encoding: 3a0
-U-Bits: 000000011011101011011011111111101011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 3a0
-
-Encoding: 3a0
-U-Bits: 000000011011101011000110110010100010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 3a0
-
-Encoding: 3a0
-U-Bits: 000000011011101011000001111010011100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 3a0
-
-Encoding: 3a1
-U-Bits: 110111111011101011000110110011001001
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 3a1
-
-Encoding: 3a1
-U-Bits: 110111111011101011011011111110000000
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 3a1
-
-Encoding: 3a1
-U-Bits: 110111111011101011011100110110111110
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 3a1
-
-Encoding: 3a2
-U-Bits: 010001100011101011011100101100100010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 3a2
-
-Encoding: 3a2
-U-Bits: 010001100011101011000001100001101011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 3a2
-
-Encoding: 3a2
-U-Bits: 010001100011101011000110101001010101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 3a2
-
-Encoding: 3a3
-U-Bits: 100110000011101011000001100000000000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 3a3
-
-Encoding: 3a3
-U-Bits: 100110000011101011011100101101001001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 3a3
-
-Encoding: 3a3
-U-Bits: 100110000011101011011011100101110111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 3a3
-
-Encoding: 3a4
-U-Bits: 001010000101101011000110101111110011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 3a4
-
-Encoding: 3a4
-U-Bits: 001010000101101011011011100010111010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 3a4
-
-Encoding: 3a4
-U-Bits: 001010000101101011011100101010000100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 3a4
-
-Encoding: 3a5
-U-Bits: 111101100101101011011011100011010001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 3a5
-
-Encoding: 3a5
-U-Bits: 111101100101101011000110101110011000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 3a5
-
-Encoding: 3a5
-U-Bits: 111101100101101011000001100110100110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 3a5
-
-Encoding: 3a6
-U-Bits: 011011111101101011000001111100111010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 3a6
-
-Encoding: 3a6
-U-Bits: 011011111101101011011100110001110011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 3a6
-
-Encoding: 3a6
-U-Bits: 011011111101101011011011111001001101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 3a6
-
-Encoding: 3a7
-U-Bits: 101100011101101011011100110000011000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 3a7
-
-Encoding: 3a7
-U-Bits: 101100011101101011000001111101010001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 3a7
-
-Encoding: 3a7
-U-Bits: 101100011101101011000110110101101111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 3a7
-
-Encoding: 3a8
-U-Bits: 000110111100001011011100101011101111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 3a8
-
-Encoding: 3a8
-U-Bits: 000110111100001011000001100110100110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 3a8
-
-Encoding: 3a8
-U-Bits: 000110111100001011000110101110011000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 3a8
-
-Encoding: 3a9
-U-Bits: 110001011100001011000001100111001101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 3a9
-
-Encoding: 3a9
-U-Bits: 110001011100001011011100101010000100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 3a9
-
-Encoding: 3a9
-U-Bits: 110001011100001011011011100010111010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 3a9
-
-Encoding: 3aa
-U-Bits: 010111000100001011011011111000100110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 3aa
-
-Encoding: 3aa
-U-Bits: 010111000100001011000110110101101111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 3aa
-
-Encoding: 3aa
-U-Bits: 010111000100001011000001111101010001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 3aa
-
-Encoding: 3ab
-U-Bits: 100000100100001011000110110100000100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 3ab
-
-Encoding: 3ab
-U-Bits: 100000100100001011011011111001001101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 3ab
-
-Encoding: 3ab
-U-Bits: 100000100100001011011100110001110011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 3ab
-
-Encoding: 3ac
-U-Bits: 001100100010001011000001111011110111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 3ac
-
-Encoding: 3ac
-U-Bits: 001100100010001011011100110110111110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 3ac
-
-Encoding: 3ac
-U-Bits: 001100100010001011011011111110000000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 3ac
-
-Encoding: 3ad
-U-Bits: 111011000010001011011100110111010101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 3ad
-
-Encoding: 3ad
-U-Bits: 111011000010001011000001111010011100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 3ad
-
-Encoding: 3ad
-U-Bits: 111011000010001011000110110010100010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 3ad
-
-Encoding: 3ae
-U-Bits: 011101011010001011000110101000111110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 3ae
-
-Encoding: 3ae
-U-Bits: 011101011010001011011011100101110111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 3ae
-
-Encoding: 3ae
-U-Bits: 011101011010001011011100101101001001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 3ae
-
-Encoding: 3af
-U-Bits: 101010111010001011011011100100011100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 3af
-
-Encoding: 3af
-U-Bits: 101010111010001011000110101001010101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 3af
-
-Encoding: 3af
-U-Bits: 101010111010001011000001100001101011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 3af
-
-Encoding: 3b0
-U-Bits: 000001110010010011011010001010101001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 3b0
-
-Encoding: 3b0
-U-Bits: 000001110010010011000111000111100000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 3b0
-
-Encoding: 3b0
-U-Bits: 000001110010010011000000001111011110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 3b0
-
-Encoding: 3b1
-U-Bits: 110110010010010011000111000110001011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 3b1
-
-Encoding: 3b1
-U-Bits: 110110010010010011011010001011000010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 3b1
-
-Encoding: 3b1
-U-Bits: 110110010010010011011101000011111100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 3b1
-
-Encoding: 3b2
-U-Bits: 010000001010010011011101011001100000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 3b2
-
-Encoding: 3b2
-U-Bits: 010000001010010011000000010100101001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 3b2
-
-Encoding: 3b2
-U-Bits: 010000001010010011000111011100010111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 3b2
-
-Encoding: 3b3
-U-Bits: 100111101010010011000000010101000010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 3b3
-
-Encoding: 3b3
-U-Bits: 100111101010010011011101011000001011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 3b3
-
-Encoding: 3b3
-U-Bits: 100111101010010011011010010000110101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 3b3
-
-Encoding: 3b4
-U-Bits: 001011101100010011000111011010110001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 3b4
-
-Encoding: 3b4
-U-Bits: 001011101100010011011010010111111000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 3b4
-
-Encoding: 3b4
-U-Bits: 001011101100010011011101011111000110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 3b4
-
-Encoding: 3b5
-U-Bits: 111100001100010011011010010110010011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 3b5
-
-Encoding: 3b5
-U-Bits: 111100001100010011000111011011011010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 3b5
-
-Encoding: 3b5
-U-Bits: 111100001100010011000000010011100100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 3b5
-
-Encoding: 3b6
-U-Bits: 011010010100010011000000001001111000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 3b6
-
-Encoding: 3b6
-U-Bits: 011010010100010011011101000100110001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 3b6
-
-Encoding: 3b6
-U-Bits: 011010010100010011011010001100001111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 3b6
-
-Encoding: 3b7
-U-Bits: 101101110100010011011101000101011010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 3b7
-
-Encoding: 3b7
-U-Bits: 101101110100010011000000001000010011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 3b7
-
-Encoding: 3b7
-U-Bits: 101101110100010011000111000000101101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 3b7
-
-Encoding: 3b8
-U-Bits: 000111010101110011011101011110101101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 3b8
-
-Encoding: 3b8
-U-Bits: 000111010101110011000000010011100100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 3b8
-
-Encoding: 3b8
-U-Bits: 000111010101110011000111011011011010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 3b8
-
-Encoding: 3b9
-U-Bits: 110000110101110011000000010010001111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 3b9
-
-Encoding: 3b9
-U-Bits: 110000110101110011011101011111000110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 3b9
-
-Encoding: 3b9
-U-Bits: 110000110101110011011010010111111000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 3b9
-
-Encoding: 3ba
-U-Bits: 010110101101110011011010001101100100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 3ba
-
-Encoding: 3ba
-U-Bits: 010110101101110011000111000000101101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 3ba
-
-Encoding: 3ba
-U-Bits: 010110101101110011000000001000010011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 3ba
-
-Encoding: 3bb
-U-Bits: 100001001101110011000111000001000110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 3bb
-
-Encoding: 3bb
-U-Bits: 100001001101110011011010001100001111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 3bb
-
-Encoding: 3bb
-U-Bits: 100001001101110011011101000100110001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 3bb
-
-Encoding: 3bc
-U-Bits: 001101001011110011000000001110110101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 3bc
-
-Encoding: 3bc
-U-Bits: 001101001011110011011101000011111100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 3bc
-
-Encoding: 3bc
-U-Bits: 001101001011110011011010001011000010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 3bc
-
-Encoding: 3bd
-U-Bits: 111010101011110011011101000010010111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 3bd
-
-Encoding: 3bd
-U-Bits: 111010101011110011000000001111011110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 3bd
-
-Encoding: 3bd
-U-Bits: 111010101011110011000111000111100000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 3bd
-
-Encoding: 3be
-U-Bits: 011100110011110011000111011101111100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 3be
-
-Encoding: 3be
-U-Bits: 011100110011110011011010010000110101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 3be
-
-Encoding: 3be
-U-Bits: 011100110011110011011101011000001011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 3be
-
-Encoding: 3bf
-U-Bits: 101011010011110011011010010001011110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 3bf
-
-Encoding: 3bf
-U-Bits: 101011010011110011000111011100010111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 3bf
-
-Encoding: 3bf
-U-Bits: 101011010011110011000000010100101001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 3bf
-
-Encoding: 3c0
-U-Bits: 000000000111010010100111000001000110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 3c0
-
-Encoding: 3c0
-U-Bits: 000000000111010010111010001100001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 3c0
-
-Encoding: 3c0
-U-Bits: 000000000111010010111101000100110001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 3c0
-
-Encoding: 3c1
-U-Bits: 110111100111010010111010001101100100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 3c1
-
-Encoding: 3c1
-U-Bits: 110111100111010010100111000000101101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 3c1
-
-Encoding: 3c1
-U-Bits: 110111100111010010100000001000010011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 3c1
-
-Encoding: 3c2
-U-Bits: 010001111111010010100000010010001111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 3c2
-
-Encoding: 3c2
-U-Bits: 010001111111010010111101011111000110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 3c2
-
-Encoding: 3c2
-U-Bits: 010001111111010010111010010111111000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 3c2
-
-Encoding: 3c3
-U-Bits: 100110011111010010111101011110101101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 3c3
-
-Encoding: 3c3
-U-Bits: 100110011111010010100000010011100100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 3c3
-
-Encoding: 3c3
-U-Bits: 100110011111010010100111011011011010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 3c3
-
-Encoding: 3c4
-U-Bits: 001010011001010010111010010001011110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 3c4
-
-Encoding: 3c4
-U-Bits: 001010011001010010100111011100010111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 3c4
-
-Encoding: 3c4
-U-Bits: 001010011001010010100000010100101001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 3c4
-
-Encoding: 3c5
-U-Bits: 111101111001010010100111011101111100
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 3c5
-
-Encoding: 3c5
-U-Bits: 111101111001010010111010010000110101
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 3c5
-
-Encoding: 3c5
-U-Bits: 111101111001010010111101011000001011
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 3c5
-
-Encoding: 3c6
-U-Bits: 011011100001010010111101000010010111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 3c6
-
-Encoding: 3c6
-U-Bits: 011011100001010010100000001111011110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 3c6
-
-Encoding: 3c6
-U-Bits: 011011100001010010100111000111100000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 3c6
-
-Encoding: 3c7
-U-Bits: 101100000001010010100000001110110101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 3c7
-
-Encoding: 3c7
-U-Bits: 101100000001010010111101000011111100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 3c7
-
-Encoding: 3c7
-U-Bits: 101100000001010010111010001011000010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 3c7
-
-Encoding: 3c8
-U-Bits: 000110100000110010100000010101000010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 3c8
-
-Encoding: 3c8
-U-Bits: 000110100000110010111101011000001011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 3c8
-
-Encoding: 3c8
-U-Bits: 000110100000110010111010010000110101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 3c8
-
-Encoding: 3c9
-U-Bits: 110001000000110010111101011001100000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 3c9
-
-Encoding: 3c9
-U-Bits: 110001000000110010100000010100101001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 3c9
-
-Encoding: 3c9
-U-Bits: 110001000000110010100111011100010111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 3c9
-
-Encoding: 3ca
-U-Bits: 010111011000110010100111000110001011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 3ca
-
-Encoding: 3ca
-U-Bits: 010111011000110010111010001011000010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 3ca
-
-Encoding: 3ca
-U-Bits: 010111011000110010111101000011111100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 3ca
-
-Encoding: 3cb
-U-Bits: 100000111000110010111010001010101001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 3cb
-
-Encoding: 3cb
-U-Bits: 100000111000110010100111000111100000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 3cb
-
-Encoding: 3cb
-U-Bits: 100000111000110010100000001111011110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 3cb
-
-Encoding: 3cc
-U-Bits: 001100111110110010111101000101011010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 3cc
-
-Encoding: 3cc
-U-Bits: 001100111110110010100000001000010011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 3cc
-
-Encoding: 3cc
-U-Bits: 001100111110110010100111000000101101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 3cc
-
-Encoding: 3cd
-U-Bits: 111011011110110010100000001001111000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 3cd
-
-Encoding: 3cd
-U-Bits: 111011011110110010111101000100110001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 3cd
-
-Encoding: 3cd
-U-Bits: 111011011110110010111010001100001111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 3cd
-
-Encoding: 3ce
-U-Bits: 011101000110110010111010010110010011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 3ce
-
-Encoding: 3ce
-U-Bits: 011101000110110010100111011011011010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 3ce
-
-Encoding: 3ce
-U-Bits: 011101000110110010100000010011100100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 3ce
-
-Encoding: 3cf
-U-Bits: 101010100110110010100111011010110001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 3cf
-
-Encoding: 3cf
-U-Bits: 101010100110110010111010010111111000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 3cf
-
-Encoding: 3cf
-U-Bits: 101010100110110010111101011111000110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 3cf
-
-Encoding: 3d0
-U-Bits: 000001101110101010100110110100000100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 3d0
-
-Encoding: 3d0
-U-Bits: 000001101110101010111011111001001101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 3d0
-
-Encoding: 3d0
-U-Bits: 000001101110101010111100110001110011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 3d0
-
-Encoding: 3d1
-U-Bits: 110110001110101010111011111000100110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 3d1
-
-Encoding: 3d1
-U-Bits: 110110001110101010100110110101101111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 3d1
-
-Encoding: 3d1
-U-Bits: 110110001110101010100001111101010001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 3d1
-
-Encoding: 3d2
-U-Bits: 010000010110101010100001100111001101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 3d2
-
-Encoding: 3d2
-U-Bits: 010000010110101010111100101010000100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 3d2
-
-Encoding: 3d2
-U-Bits: 010000010110101010111011100010111010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 3d2
-
-Encoding: 3d3
-U-Bits: 100111110110101010111100101011101111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 3d3
-
-Encoding: 3d3
-U-Bits: 100111110110101010100001100110100110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 3d3
-
-Encoding: 3d3
-U-Bits: 100111110110101010100110101110011000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 3d3
-
-Encoding: 3d4
-U-Bits: 001011110000101010111011100100011100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 3d4
-
-Encoding: 3d4
-U-Bits: 001011110000101010100110101001010101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 3d4
-
-Encoding: 3d4
-U-Bits: 001011110000101010100001100001101011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 3d4
-
-Encoding: 3d5
-U-Bits: 111100010000101010100110101000111110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 3d5
-
-Encoding: 3d5
-U-Bits: 111100010000101010111011100101110111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 3d5
-
-Encoding: 3d5
-U-Bits: 111100010000101010111100101101001001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 3d5
-
-Encoding: 3d6
-U-Bits: 011010001000101010111100110111010101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 3d6
-
-Encoding: 3d6
-U-Bits: 011010001000101010100001111010011100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 3d6
-
-Encoding: 3d6
-U-Bits: 011010001000101010100110110010100010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 3d6
-
-Encoding: 3d7
-U-Bits: 101101101000101010100001111011110111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 3d7
-
-Encoding: 3d7
-U-Bits: 101101101000101010111100110110111110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 3d7
-
-Encoding: 3d7
-U-Bits: 101101101000101010111011111110000000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 3d7
-
-Encoding: 3d8
-U-Bits: 000111001001001010100001100000000000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 3d8
-
-Encoding: 3d8
-U-Bits: 000111001001001010111100101101001001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 3d8
-
-Encoding: 3d8
-U-Bits: 000111001001001010111011100101110111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 3d8
-
-Encoding: 3d9
-U-Bits: 110000101001001010111100101100100010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 3d9
-
-Encoding: 3d9
-U-Bits: 110000101001001010100001100001101011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 3d9
-
-Encoding: 3d9
-U-Bits: 110000101001001010100110101001010101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 3d9
-
-Encoding: 3da
-U-Bits: 010110110001001010100110110011001001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 3da
-
-Encoding: 3da
-U-Bits: 010110110001001010111011111110000000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 3da
-
-Encoding: 3da
-U-Bits: 010110110001001010111100110110111110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 3da
-
-Encoding: 3db
-U-Bits: 100001010001001010111011111111101011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 3db
-
-Encoding: 3db
-U-Bits: 100001010001001010100110110010100010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 3db
-
-Encoding: 3db
-U-Bits: 100001010001001010100001111010011100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 3db
-
-Encoding: 3dc
-U-Bits: 001101010111001010111100110000011000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 3dc
-
-Encoding: 3dc
-U-Bits: 001101010111001010100001111101010001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 3dc
-
-Encoding: 3dc
-U-Bits: 001101010111001010100110110101101111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 3dc
-
-Encoding: 3dd
-U-Bits: 111010110111001010100001111100111010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 3dd
-
-Encoding: 3dd
-U-Bits: 111010110111001010111100110001110011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 3dd
-
-Encoding: 3dd
-U-Bits: 111010110111001010111011111001001101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 3dd
-
-Encoding: 3de
-U-Bits: 011100101111001010111011100011010001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 3de
-
-Encoding: 3de
-U-Bits: 011100101111001010100110101110011000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 3de
-
-Encoding: 3de
-U-Bits: 011100101111001010100001100110100110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 3de
-
-Encoding: 3df
-U-Bits: 101011001111001010100110101111110011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 3df
-
-Encoding: 3df
-U-Bits: 101011001111001010111011100010111010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 3df
-
-Encoding: 3df
-U-Bits: 101011001111001010111100101010000100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 3df
-
-Encoding: 3e0
-U-Bits: 000000011101001100100111011100010111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 3e0
-
-Encoding: 3e0
-U-Bits: 000000011101001100111010010001011110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 3e0
-
-Encoding: 3e0
-U-Bits: 000000011101001100111101011001100000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 3e0
-
-Encoding: 3e1
-U-Bits: 110111111101001100111010010000110101
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 3e1
-
-Encoding: 3e1
-U-Bits: 110111111101001100100111011101111100
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 3e1
-
-Encoding: 3e1
-U-Bits: 110111111101001100100000010101000010
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 3e1
-
-Encoding: 3e2
-U-Bits: 010001100101001100100000001111011110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f
-Decoded: 3e2
-
-Encoding: 3e2
-U-Bits: 010001100101001100111101000010010111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 3e2
-
-Encoding: 3e2
-U-Bits: 010001100101001100111010001010101001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 3e2
-
-Encoding: 3e3
-U-Bits: 100110000101001100111101000011111100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 7f 7f
-Decoded: 3e3
-
-Encoding: 3e3
-U-Bits: 100110000101001100100000001110110101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 3e3
-
-Encoding: 3e3
-U-Bits: 100110000101001100100111000110001011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 3e3
-
-Encoding: 3e4
-U-Bits: 001010000011001100111010001100001111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 3e4
-
-Encoding: 3e4
-U-Bits: 001010000011001100100111000001000110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 3e4
-
-Encoding: 3e4
-U-Bits: 001010000011001100100000001001111000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 3e4
-
-Encoding: 3e5
-U-Bits: 111101100011001100100111000000101101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 3e5
-
-Encoding: 3e5
-U-Bits: 111101100011001100111010001101100100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 3e5
-
-Encoding: 3e5
-U-Bits: 111101100011001100111101000101011010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 3e5
-
-Encoding: 3e6
-U-Bits: 011011111011001100111101011111000110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 3e6
-
-Encoding: 3e6
-U-Bits: 011011111011001100100000010010001111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 3e6
-
-Encoding: 3e6
-U-Bits: 011011111011001100100111011010110001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 3e6
-
-Encoding: 3e7
-U-Bits: 101100011011001100100000010011100100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 3e7
-
-Encoding: 3e7
-U-Bits: 101100011011001100111101011110101101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 3e7
-
-Encoding: 3e7
-U-Bits: 101100011011001100111010010110010011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 3e7
-
-Encoding: 3e8
-U-Bits: 000110111010101100100000001000010011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 3e8
-
-Encoding: 3e8
-U-Bits: 000110111010101100111101000101011010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 3e8
-
-Encoding: 3e8
-U-Bits: 000110111010101100111010001101100100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 3e8
-
-Encoding: 3e9
-U-Bits: 110001011010101100111101000100110001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 3e9
-
-Encoding: 3e9
-U-Bits: 110001011010101100100000001001111000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 3e9
-
-Encoding: 3e9
-U-Bits: 110001011010101100100111000001000110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 3e9
-
-Encoding: 3ea
-U-Bits: 010111000010101100100111011011011010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 3ea
-
-Encoding: 3ea
-U-Bits: 010111000010101100111010010110010011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 3ea
-
-Encoding: 3ea
-U-Bits: 010111000010101100111101011110101101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81
-Decoded: 3ea
-
-Encoding: 3eb
-U-Bits: 100000100010101100111010010111111000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f
-Decoded: 3eb
-
-Encoding: 3eb
-U-Bits: 100000100010101100100111011010110001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 3eb
-
-Encoding: 3eb
-U-Bits: 100000100010101100100000010010001111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 3eb
-
-Encoding: 3ec
-U-Bits: 001100100100101100111101011000001011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 3ec
-
-Encoding: 3ec
-U-Bits: 001100100100101100100000010101000010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 3ec
-
-Encoding: 3ec
-U-Bits: 001100100100101100100111011101111100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f
-Decoded: 3ec
-
-Encoding: 3ed
-U-Bits: 111011000100101100100000010100101001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 3ed
-
-Encoding: 3ed
-U-Bits: 111011000100101100111101011001100000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 3ed
-
-Encoding: 3ed
-U-Bits: 111011000100101100111010010001011110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 3ed
-
-Encoding: 3ee
-U-Bits: 011101011100101100111010001011000010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 3ee
-
-Encoding: 3ee
-U-Bits: 011101011100101100100111000110001011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 3ee
-
-Encoding: 3ee
-U-Bits: 011101011100101100100000001110110101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81
-Decoded: 3ee
-
-Encoding: 3ef
-U-Bits: 101010111100101100100111000111100000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 3ef
-
-Encoding: 3ef
-U-Bits: 101010111100101100111010001010101001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 3ef
-
-Encoding: 3ef
-U-Bits: 101010111100101100111101000010010111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 3ef
-
-Encoding: 3f0
-U-Bits: 000001110100110100100110101001010101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 3f0
-
-Encoding: 3f0
-U-Bits: 000001110100110100111011100100011100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 3f0
-
-Encoding: 3f0
-U-Bits: 000001110100110100111100101100100010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 3f0
-
-Encoding: 3f1
-U-Bits: 110110010100110100111011100101110111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81
-Decoded: 3f1
-
-Encoding: 3f1
-U-Bits: 110110010100110100100110101000111110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 3f1
-
-Encoding: 3f1
-U-Bits: 110110010100110100100001100000000000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 3f1
-
-Encoding: 3f2
-U-Bits: 010000001100110100100001111010011100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 3f2
-
-Encoding: 3f2
-U-Bits: 010000001100110100111100110111010101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 3f2
-
-Encoding: 3f2
-U-Bits: 010000001100110100111011111111101011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 3f2
-
-Encoding: 3f3
-U-Bits: 100111101100110100111100110110111110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f
-Decoded: 3f3
-
-Encoding: 3f3
-U-Bits: 100111101100110100100001111011110111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 3f3
-
-Encoding: 3f3
-U-Bits: 100111101100110100100110110011001001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 3f3
-
-Encoding: 3f4
-U-Bits: 001011101010110100111011111001001101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 3f4
-
-Encoding: 3f4
-U-Bits: 001011101010110100100110110100000100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 3f4
-
-Encoding: 3f4
-U-Bits: 001011101010110100100001111100111010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 3f4
-
-Encoding: 3f5
-U-Bits: 111100001010110100100110110101101111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81
-Decoded: 3f5
-
-Encoding: 3f5
-U-Bits: 111100001010110100111011111000100110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 3f5
-
-Encoding: 3f5
-U-Bits: 111100001010110100111100110000011000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 3f5
-
-Encoding: 3f6
-U-Bits: 011010010010110100111100101010000100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 3f6
-
-Encoding: 3f6
-U-Bits: 011010010010110100100001100111001101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 3f6
-
-Encoding: 3f6
-U-Bits: 011010010010110100100110101111110011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 3f6
-
-Encoding: 3f7
-U-Bits: 101101110010110100100001100110100110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 3f7
-
-Encoding: 3f7
-U-Bits: 101101110010110100111100101011101111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 3f7
-
-Encoding: 3f7
-U-Bits: 101101110010110100111011100011010001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 3f7
-
-Encoding: 3f8
-U-Bits: 000111010011010100100001111101010001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 3f8
-
-Encoding: 3f8
-U-Bits: 000111010011010100111100110000011000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 3f8
-
-Encoding: 3f8
-U-Bits: 000111010011010100111011111000100110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 3f8
-
-Encoding: 3f9
-U-Bits: 110000110011010100111100110001110011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 3f9
-
-Encoding: 3f9
-U-Bits: 110000110011010100100001111100111010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 3f9
-
-Encoding: 3f9
-U-Bits: 110000110011010100100110110100000100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 3f9
-
-Encoding: 3fa
-U-Bits: 010110101011010100100110101110011000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 3fa
-
-Encoding: 3fa
-U-Bits: 010110101011010100111011100011010001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 3fa
-
-Encoding: 3fa
-U-Bits: 010110101011010100111100101011101111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81
-Decoded: 3fa
-
-Encoding: 3fb
-U-Bits: 100001001011010100111011100010111010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 3fb
-
-Encoding: 3fb
-U-Bits: 100001001011010100100110101111110011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81
-Decoded: 3fb
-
-Encoding: 3fb
-U-Bits: 100001001011010100100001100111001101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 81
-Decoded: 3fb
-
-Encoding: 3fc
-U-Bits: 001101001101010100111100101101001001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 3fc
-
-Encoding: 3fc
-U-Bits: 001101001101010100100001100000000000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 3fc
-
-Encoding: 3fc
-U-Bits: 001101001101010100100110101000111110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 3fc
-
-Encoding: 3fd
-U-Bits: 111010101101010100100001100001101011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 3fd
-
-Encoding: 3fd
-U-Bits: 111010101101010100111100101100100010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 3fd
-
-Encoding: 3fd
-U-Bits: 111010101101010100111011100100011100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 3fd
-
-Encoding: 3fe
-U-Bits: 011100110101010100111011111110000000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 3fe
-
-Encoding: 3fe
-U-Bits: 011100110101010100100110110011001001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 3fe
-
-Encoding: 3fe
-U-Bits: 011100110101010100100001111011110111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81
-Decoded: 3fe
-
-Encoding: 3ff
-U-Bits: 101011010101010100100110110010100010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 3ff
-
-Encoding: 3ff
-U-Bits: 101011010101010100111011111111101011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81
-Decoded: 3ff
-
-Encoding: 3ff
-U-Bits: 101011010101010100111100110111010101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81
-Decoded: 3ff
-
-Encoding: 400
-U-Bits: 000000000000000001110010010010101001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 400
-
-Encoding: 400
-U-Bits: 000000000000000001101111011111100000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 400
-
-Encoding: 400
-U-Bits: 000000000000000001101000010111011110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 400
-
-Encoding: 401
-U-Bits: 110111100000000001101111011110001011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 401
-
-Encoding: 401
-U-Bits: 110111100000000001110010010011000010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 401
-
-Encoding: 401
-U-Bits: 110111100000000001110101011011111100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 401
-
-Encoding: 402
-U-Bits: 010001111000000001110101000001100000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 402
-
-Encoding: 402
-U-Bits: 010001111000000001101000001100101001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 402
-
-Encoding: 402
-U-Bits: 010001111000000001101111000100010111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 402
-
-Encoding: 403
-U-Bits: 100110011000000001101000001101000010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 403
-
-Encoding: 403
-U-Bits: 100110011000000001110101000000001011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 403
-
-Encoding: 403
-U-Bits: 100110011000000001110010001000110101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 403
-
-Encoding: 404
-U-Bits: 001010011110000001101111000010110001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 404
-
-Encoding: 404
-U-Bits: 001010011110000001110010001111111000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 404
-
-Encoding: 404
-U-Bits: 001010011110000001110101000111000110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 404
-
-Encoding: 405
-U-Bits: 111101111110000001110010001110010011
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 405
-
-Encoding: 405
-U-Bits: 111101111110000001101111000011011010
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 405
-
-Encoding: 405
-U-Bits: 111101111110000001101000001011100100
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 405
-
-Encoding: 406
-U-Bits: 011011100110000001101000010001111000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 406
-
-Encoding: 406
-U-Bits: 011011100110000001110101011100110001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 406
-
-Encoding: 406
-U-Bits: 011011100110000001110010010100001111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 406
-
-Encoding: 407
-U-Bits: 101100000110000001110101011101011010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 407
-
-Encoding: 407
-U-Bits: 101100000110000001101000010000010011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 407
-
-Encoding: 407
-U-Bits: 101100000110000001101111011000101101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 407
-
-Encoding: 408
-U-Bits: 000110100111100001110101000110101101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 408
-
-Encoding: 408
-U-Bits: 000110100111100001101000001011100100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 408
-
-Encoding: 408
-U-Bits: 000110100111100001101111000011011010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 408
-
-Encoding: 409
-U-Bits: 110001000111100001101000001010001111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 409
-
-Encoding: 409
-U-Bits: 110001000111100001110101000111000110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 409
-
-Encoding: 409
-U-Bits: 110001000111100001110010001111111000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 409
-
-Encoding: 40a
-U-Bits: 010111011111100001110010010101100100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 40a
-
-Encoding: 40a
-U-Bits: 010111011111100001101111011000101101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 40a
-
-Encoding: 40a
-U-Bits: 010111011111100001101000010000010011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 40a
-
-Encoding: 40b
-U-Bits: 100000111111100001101111011001000110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 40b
-
-Encoding: 40b
-U-Bits: 100000111111100001110010010100001111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 40b
-
-Encoding: 40b
-U-Bits: 100000111111100001110101011100110001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 40b
-
-Encoding: 40c
-U-Bits: 001100111001100001101000010110110101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 40c
-
-Encoding: 40c
-U-Bits: 001100111001100001110101011011111100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 40c
-
-Encoding: 40c
-U-Bits: 001100111001100001110010010011000010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 40c
-
-Encoding: 40d
-U-Bits: 111011011001100001110101011010010111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 40d
-
-Encoding: 40d
-U-Bits: 111011011001100001101000010111011110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 40d
-
-Encoding: 40d
-U-Bits: 111011011001100001101111011111100000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 40d
-
-Encoding: 40e
-U-Bits: 011101000001100001101111000101111100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 40e
-
-Encoding: 40e
-U-Bits: 011101000001100001110010001000110101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 40e
-
-Encoding: 40e
-U-Bits: 011101000001100001110101000000001011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 40e
-
-Encoding: 40f
-U-Bits: 101010100001100001110010001001011110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 40f
-
-Encoding: 40f
-U-Bits: 101010100001100001101111000100010111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 40f
-
-Encoding: 40f
-U-Bits: 101010100001100001101000001100101001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 40f
-
-Encoding: 410
-U-Bits: 000001101001111001110011100111101011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 410
-
-Encoding: 410
-U-Bits: 000001101001111001101110101010100010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 410
-
-Encoding: 410
-U-Bits: 000001101001111001101001100010011100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 410
-
-Encoding: 411
-U-Bits: 110110001001111001101110101011001001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 411
-
-Encoding: 411
-U-Bits: 110110001001111001110011100110000000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 411
-
-Encoding: 411
-U-Bits: 110110001001111001110100101110111110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 411
-
-Encoding: 412
-U-Bits: 010000010001111001110100110100100010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 412
-
-Encoding: 412
-U-Bits: 010000010001111001101001111001101011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 412
-
-Encoding: 412
-U-Bits: 010000010001111001101110110001010101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 412
-
-Encoding: 413
-U-Bits: 100111110001111001101001111000000000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 413
-
-Encoding: 413
-U-Bits: 100111110001111001110100110101001001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 413
-
-Encoding: 413
-U-Bits: 100111110001111001110011111101110111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 413
-
-Encoding: 414
-U-Bits: 001011110111111001101110110111110011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 414
-
-Encoding: 414
-U-Bits: 001011110111111001110011111010111010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 414
-
-Encoding: 414
-U-Bits: 001011110111111001110100110010000100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 414
-
-Encoding: 415
-U-Bits: 111100010111111001110011111011010001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 415
-
-Encoding: 415
-U-Bits: 111100010111111001101110110110011000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 415
-
-Encoding: 415
-U-Bits: 111100010111111001101001111110100110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 415
-
-Encoding: 416
-U-Bits: 011010001111111001101001100100111010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 416
-
-Encoding: 416
-U-Bits: 011010001111111001110100101001110011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 416
-
-Encoding: 416
-U-Bits: 011010001111111001110011100001001101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 416
-
-Encoding: 417
-U-Bits: 101101101111111001110100101000011000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 417
-
-Encoding: 417
-U-Bits: 101101101111111001101001100101010001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 417
-
-Encoding: 417
-U-Bits: 101101101111111001101110101101101111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 417
-
-Encoding: 418
-U-Bits: 000111001110011001110100110011101111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 418
-
-Encoding: 418
-U-Bits: 000111001110011001101001111110100110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 418
-
-Encoding: 418
-U-Bits: 000111001110011001101110110110011000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 418
-
-Encoding: 419
-U-Bits: 110000101110011001101001111111001101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 419
-
-Encoding: 419
-U-Bits: 110000101110011001110100110010000100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 419
-
-Encoding: 419
-U-Bits: 110000101110011001110011111010111010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 419
-
-Encoding: 41a
-U-Bits: 010110110110011001110011100000100110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 41a
-
-Encoding: 41a
-U-Bits: 010110110110011001101110101101101111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 41a
-
-Encoding: 41a
-U-Bits: 010110110110011001101001100101010001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 41a
-
-Encoding: 41b
-U-Bits: 100001010110011001101110101100000100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 41b
-
-Encoding: 41b
-U-Bits: 100001010110011001110011100001001101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 41b
-
-Encoding: 41b
-U-Bits: 100001010110011001110100101001110011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 41b
-
-Encoding: 41c
-U-Bits: 001101010000011001101001100011110111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 41c
-
-Encoding: 41c
-U-Bits: 001101010000011001110100101110111110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 41c
-
-Encoding: 41c
-U-Bits: 001101010000011001110011100110000000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 41c
-
-Encoding: 41d
-U-Bits: 111010110000011001110100101111010101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 41d
-
-Encoding: 41d
-U-Bits: 111010110000011001101001100010011100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 41d
-
-Encoding: 41d
-U-Bits: 111010110000011001101110101010100010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 41d
-
-Encoding: 41e
-U-Bits: 011100101000011001101110110000111110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 41e
-
-Encoding: 41e
-U-Bits: 011100101000011001110011111101110111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 41e
-
-Encoding: 41e
-U-Bits: 011100101000011001110100110101001001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 41e
-
-Encoding: 41f
-U-Bits: 101011001000011001110011111100011100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 41f
-
-Encoding: 41f
-U-Bits: 101011001000011001101110110001010101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 41f
-
-Encoding: 41f
-U-Bits: 101011001000011001101001111001101011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 41f
-
-Encoding: 420
-U-Bits: 000000011010011111110010001111111000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 420
-
-Encoding: 420
-U-Bits: 000000011010011111101111000010110001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 420
-
-Encoding: 420
-U-Bits: 000000011010011111101000001010001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 420
-
-Encoding: 421
-U-Bits: 110111111010011111101111000011011010
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 421
-
-Encoding: 421
-U-Bits: 110111111010011111110010001110010011
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 421
-
-Encoding: 421
-U-Bits: 110111111010011111110101000110101101
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 421
-
-Encoding: 422
-U-Bits: 010001100010011111110101011100110001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 422
-
-Encoding: 422
-U-Bits: 010001100010011111101000010001111000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 422
-
-Encoding: 422
-U-Bits: 010001100010011111101111011001000110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 422
-
-Encoding: 423
-U-Bits: 100110000010011111101000010000010011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 423
-
-Encoding: 423
-U-Bits: 100110000010011111110101011101011010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 423
-
-Encoding: 423
-U-Bits: 100110000010011111110010010101100100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 423
-
-Encoding: 424
-U-Bits: 001010000100011111101111011111100000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 424
-
-Encoding: 424
-U-Bits: 001010000100011111110010010010101001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 424
-
-Encoding: 424
-U-Bits: 001010000100011111110101011010010111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 424
-
-Encoding: 425
-U-Bits: 111101100100011111110010010011000010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 425
-
-Encoding: 425
-U-Bits: 111101100100011111101111011110001011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 425
-
-Encoding: 425
-U-Bits: 111101100100011111101000010110110101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 425
-
-Encoding: 426
-U-Bits: 011011111100011111101000001100101001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 426
-
-Encoding: 426
-U-Bits: 011011111100011111110101000001100000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 426
-
-Encoding: 426
-U-Bits: 011011111100011111110010001001011110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 426
-
-Encoding: 427
-U-Bits: 101100011100011111110101000000001011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 427
-
-Encoding: 427
-U-Bits: 101100011100011111101000001101000010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 427
-
-Encoding: 427
-U-Bits: 101100011100011111101111000101111100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 427
-
-Encoding: 428
-U-Bits: 000110111101111111110101011011111100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 428
-
-Encoding: 428
-U-Bits: 000110111101111111101000010110110101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 428
-
-Encoding: 428
-U-Bits: 000110111101111111101111011110001011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 428
-
-Encoding: 429
-U-Bits: 110001011101111111101000010111011110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 429
-
-Encoding: 429
-U-Bits: 110001011101111111110101011010010111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 429
-
-Encoding: 429
-U-Bits: 110001011101111111110010010010101001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 429
-
-Encoding: 42a
-U-Bits: 010111000101111111110010001000110101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 42a
-
-Encoding: 42a
-U-Bits: 010111000101111111101111000101111100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 42a
-
-Encoding: 42a
-U-Bits: 010111000101111111101000001101000010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 42a
-
-Encoding: 42b
-U-Bits: 100000100101111111101111000100010111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 42b
-
-Encoding: 42b
-U-Bits: 100000100101111111110010001001011110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 42b
-
-Encoding: 42b
-U-Bits: 100000100101111111110101000001100000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 42b
-
-Encoding: 42c
-U-Bits: 001100100011111111101000001011100100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 42c
-
-Encoding: 42c
-U-Bits: 001100100011111111110101000110101101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 42c
-
-Encoding: 42c
-U-Bits: 001100100011111111110010001110010011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 42c
-
-Encoding: 42d
-U-Bits: 111011000011111111110101000111000110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 42d
-
-Encoding: 42d
-U-Bits: 111011000011111111101000001010001111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 42d
-
-Encoding: 42d
-U-Bits: 111011000011111111101111000010110001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 42d
-
-Encoding: 42e
-U-Bits: 011101011011111111101111011000101101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 42e
-
-Encoding: 42e
-U-Bits: 011101011011111111110010010101100100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 42e
-
-Encoding: 42e
-U-Bits: 011101011011111111110101011101011010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 42e
-
-Encoding: 42f
-U-Bits: 101010111011111111110010010100001111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 42f
-
-Encoding: 42f
-U-Bits: 101010111011111111101111011001000110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 42f
-
-Encoding: 42f
-U-Bits: 101010111011111111101000010001111000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 42f
-
-Encoding: 430
-U-Bits: 000001110011100111110011111010111010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 430
-
-Encoding: 430
-U-Bits: 000001110011100111101110110111110011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 430
-
-Encoding: 430
-U-Bits: 000001110011100111101001111111001101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 430
-
-Encoding: 431
-U-Bits: 110110010011100111101110110110011000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 431
-
-Encoding: 431
-U-Bits: 110110010011100111110011111011010001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 431
-
-Encoding: 431
-U-Bits: 110110010011100111110100110011101111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 431
-
-Encoding: 432
-U-Bits: 010000001011100111110100101001110011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 432
-
-Encoding: 432
-U-Bits: 010000001011100111101001100100111010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 432
-
-Encoding: 432
-U-Bits: 010000001011100111101110101100000100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 432
-
-Encoding: 433
-U-Bits: 100111101011100111101001100101010001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 433
-
-Encoding: 433
-U-Bits: 100111101011100111110100101000011000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 433
-
-Encoding: 433
-U-Bits: 100111101011100111110011100000100110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 433
-
-Encoding: 434
-U-Bits: 001011101101100111101110101010100010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 434
-
-Encoding: 434
-U-Bits: 001011101101100111110011100111101011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 434
-
-Encoding: 434
-U-Bits: 001011101101100111110100101111010101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 434
-
-Encoding: 435
-U-Bits: 111100001101100111110011100110000000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 435
-
-Encoding: 435
-U-Bits: 111100001101100111101110101011001001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 435
-
-Encoding: 435
-U-Bits: 111100001101100111101001100011110111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 435
-
-Encoding: 436
-U-Bits: 011010010101100111101001111001101011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 436
-
-Encoding: 436
-U-Bits: 011010010101100111110100110100100010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 436
-
-Encoding: 436
-U-Bits: 011010010101100111110011111100011100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 436
-
-Encoding: 437
-U-Bits: 101101110101100111110100110101001001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 437
-
-Encoding: 437
-U-Bits: 101101110101100111101001111000000000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 437
-
-Encoding: 437
-U-Bits: 101101110101100111101110110000111110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 437
-
-Encoding: 438
-U-Bits: 000111010100000111110100101110111110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 438
-
-Encoding: 438
-U-Bits: 000111010100000111101001100011110111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 438
-
-Encoding: 438
-U-Bits: 000111010100000111101110101011001001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 438
-
-Encoding: 439
-U-Bits: 110000110100000111101001100010011100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 439
-
-Encoding: 439
-U-Bits: 110000110100000111110100101111010101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 439
-
-Encoding: 439
-U-Bits: 110000110100000111110011100111101011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 439
-
-Encoding: 43a
-U-Bits: 010110101100000111110011111101110111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 43a
-
-Encoding: 43a
-U-Bits: 010110101100000111101110110000111110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 43a
-
-Encoding: 43a
-U-Bits: 010110101100000111101001111000000000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 43a
-
-Encoding: 43b
-U-Bits: 100001001100000111101110110001010101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 43b
-
-Encoding: 43b
-U-Bits: 100001001100000111110011111100011100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 43b
-
-Encoding: 43b
-U-Bits: 100001001100000111110100110100100010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 43b
-
-Encoding: 43c
-U-Bits: 001101001010000111101001111110100110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 43c
-
-Encoding: 43c
-U-Bits: 001101001010000111110100110011101111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 43c
-
-Encoding: 43c
-U-Bits: 001101001010000111110011111011010001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 43c
-
-Encoding: 43d
-U-Bits: 111010101010000111110100110010000100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 43d
-
-Encoding: 43d
-U-Bits: 111010101010000111101001111111001101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 43d
-
-Encoding: 43d
-U-Bits: 111010101010000111101110110111110011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 43d
-
-Encoding: 43e
-U-Bits: 011100110010000111101110101101101111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 43e
-
-Encoding: 43e
-U-Bits: 011100110010000111110011100000100110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 43e
-
-Encoding: 43e
-U-Bits: 011100110010000111110100101000011000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 43e
-
-Encoding: 43f
-U-Bits: 101011010010000111110011100001001101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 43f
-
-Encoding: 43f
-U-Bits: 101011010010000111101110101100000100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 43f
-
-Encoding: 43f
-U-Bits: 101011010010000111101001100100111010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 43f
-
-Encoding: 440
-U-Bits: 000000000110100110001110110001010101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 440
-
-Encoding: 440
-U-Bits: 000000000110100110010011111100011100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 440
-
-Encoding: 440
-U-Bits: 000000000110100110010100110100100010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 440
-
-Encoding: 441
-U-Bits: 110111100110100110010011111101110111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 441
-
-Encoding: 441
-U-Bits: 110111100110100110001110110000111110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 441
-
-Encoding: 441
-U-Bits: 110111100110100110001001111000000000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 441
-
-Encoding: 442
-U-Bits: 010001111110100110001001100010011100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 442
-
-Encoding: 442
-U-Bits: 010001111110100110010100101111010101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 442
-
-Encoding: 442
-U-Bits: 010001111110100110010011100111101011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 442
-
-Encoding: 443
-U-Bits: 100110011110100110010100101110111110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 443
-
-Encoding: 443
-U-Bits: 100110011110100110001001100011110111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 443
-
-Encoding: 443
-U-Bits: 100110011110100110001110101011001001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 443
-
-Encoding: 444
-U-Bits: 001010011000100110010011100001001101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 444
-
-Encoding: 444
-U-Bits: 001010011000100110001110101100000100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 444
-
-Encoding: 444
-U-Bits: 001010011000100110001001100100111010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 444
-
-Encoding: 445
-U-Bits: 111101111000100110001110101101101111
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 445
-
-Encoding: 445
-U-Bits: 111101111000100110010011100000100110
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 445
-
-Encoding: 445
-U-Bits: 111101111000100110010100101000011000
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 445
-
-Encoding: 446
-U-Bits: 011011100000100110010100110010000100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 446
-
-Encoding: 446
-U-Bits: 011011100000100110001001111111001101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 446
-
-Encoding: 446
-U-Bits: 011011100000100110001110110111110011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 446
-
-Encoding: 447
-U-Bits: 101100000000100110001001111110100110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 447
-
-Encoding: 447
-U-Bits: 101100000000100110010100110011101111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 447
-
-Encoding: 447
-U-Bits: 101100000000100110010011111011010001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 447
-
-Encoding: 448
-U-Bits: 000110100001000110001001100101010001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 448
-
-Encoding: 448
-U-Bits: 000110100001000110010100101000011000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 448
-
-Encoding: 448
-U-Bits: 000110100001000110010011100000100110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 448
-
-Encoding: 449
-U-Bits: 110001000001000110010100101001110011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 449
-
-Encoding: 449
-U-Bits: 110001000001000110001001100100111010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 449
-
-Encoding: 449
-U-Bits: 110001000001000110001110101100000100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 449
-
-Encoding: 44a
-U-Bits: 010111011001000110001110110110011000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 44a
-
-Encoding: 44a
-U-Bits: 010111011001000110010011111011010001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 44a
-
-Encoding: 44a
-U-Bits: 010111011001000110010100110011101111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 44a
-
-Encoding: 44b
-U-Bits: 100000111001000110010011111010111010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 44b
-
-Encoding: 44b
-U-Bits: 100000111001000110001110110111110011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 44b
-
-Encoding: 44b
-U-Bits: 100000111001000110001001111111001101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 44b
-
-Encoding: 44c
-U-Bits: 001100111111000110010100110101001001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 44c
-
-Encoding: 44c
-U-Bits: 001100111111000110001001111000000000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 44c
-
-Encoding: 44c
-U-Bits: 001100111111000110001110110000111110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 44c
-
-Encoding: 44d
-U-Bits: 111011011111000110001001111001101011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 44d
-
-Encoding: 44d
-U-Bits: 111011011111000110010100110100100010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 44d
-
-Encoding: 44d
-U-Bits: 111011011111000110010011111100011100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 44d
-
-Encoding: 44e
-U-Bits: 011101000111000110010011100110000000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 44e
-
-Encoding: 44e
-U-Bits: 011101000111000110001110101011001001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 44e
-
-Encoding: 44e
-U-Bits: 011101000111000110001001100011110111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 44e
-
-Encoding: 44f
-U-Bits: 101010100111000110001110101010100010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 44f
-
-Encoding: 44f
-U-Bits: 101010100111000110010011100111101011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 44f
-
-Encoding: 44f
-U-Bits: 101010100111000110010100101111010101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 44f
-
-Encoding: 450
-U-Bits: 000001101111011110001111000100010111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 450
-
-Encoding: 450
-U-Bits: 000001101111011110010010001001011110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 450
-
-Encoding: 450
-U-Bits: 000001101111011110010101000001100000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 450
-
-Encoding: 451
-U-Bits: 110110001111011110010010001000110101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 451
-
-Encoding: 451
-U-Bits: 110110001111011110001111000101111100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 451
-
-Encoding: 451
-U-Bits: 110110001111011110001000001101000010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 451
-
-Encoding: 452
-U-Bits: 010000010111011110001000010111011110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 452
-
-Encoding: 452
-U-Bits: 010000010111011110010101011010010111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 452
-
-Encoding: 452
-U-Bits: 010000010111011110010010010010101001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 452
-
-Encoding: 453
-U-Bits: 100111110111011110010101011011111100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 453
-
-Encoding: 453
-U-Bits: 100111110111011110001000010110110101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 453
-
-Encoding: 453
-U-Bits: 100111110111011110001111011110001011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 453
-
-Encoding: 454
-U-Bits: 001011110001011110010010010100001111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 454
-
-Encoding: 454
-U-Bits: 001011110001011110001111011001000110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 454
-
-Encoding: 454
-U-Bits: 001011110001011110001000010001111000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 454
-
-Encoding: 455
-U-Bits: 111100010001011110001111011000101101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 455
-
-Encoding: 455
-U-Bits: 111100010001011110010010010101100100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 455
-
-Encoding: 455
-U-Bits: 111100010001011110010101011101011010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 455
-
-Encoding: 456
-U-Bits: 011010001001011110010101000111000110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 456
-
-Encoding: 456
-U-Bits: 011010001001011110001000001010001111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 456
-
-Encoding: 456
-U-Bits: 011010001001011110001111000010110001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 456
-
-Encoding: 457
-U-Bits: 101101101001011110001000001011100100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 457
-
-Encoding: 457
-U-Bits: 101101101001011110010101000110101101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 457
-
-Encoding: 457
-U-Bits: 101101101001011110010010001110010011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 457
-
-Encoding: 458
-U-Bits: 000111001000111110001000010000010011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 458
-
-Encoding: 458
-U-Bits: 000111001000111110010101011101011010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 458
-
-Encoding: 458
-U-Bits: 000111001000111110010010010101100100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 458
-
-Encoding: 459
-U-Bits: 110000101000111110010101011100110001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 459
-
-Encoding: 459
-U-Bits: 110000101000111110001000010001111000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 459
-
-Encoding: 459
-U-Bits: 110000101000111110001111011001000110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 459
-
-Encoding: 45a
-U-Bits: 010110110000111110001111000011011010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 45a
-
-Encoding: 45a
-U-Bits: 010110110000111110010010001110010011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 45a
-
-Encoding: 45a
-U-Bits: 010110110000111110010101000110101101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 45a
-
-Encoding: 45b
-U-Bits: 100001010000111110010010001111111000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 45b
-
-Encoding: 45b
-U-Bits: 100001010000111110001111000010110001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 45b
-
-Encoding: 45b
-U-Bits: 100001010000111110001000001010001111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 45b
-
-Encoding: 45c
-U-Bits: 001101010110111110010101000000001011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 45c
-
-Encoding: 45c
-U-Bits: 001101010110111110001000001101000010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 45c
-
-Encoding: 45c
-U-Bits: 001101010110111110001111000101111100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 45c
-
-Encoding: 45d
-U-Bits: 111010110110111110001000001100101001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 45d
-
-Encoding: 45d
-U-Bits: 111010110110111110010101000001100000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 45d
-
-Encoding: 45d
-U-Bits: 111010110110111110010010001001011110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 45d
-
-Encoding: 45e
-U-Bits: 011100101110111110010010010011000010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 45e
-
-Encoding: 45e
-U-Bits: 011100101110111110001111011110001011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 45e
-
-Encoding: 45e
-U-Bits: 011100101110111110001000010110110101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 45e
-
-Encoding: 45f
-U-Bits: 101011001110111110001111011111100000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 45f
-
-Encoding: 45f
-U-Bits: 101011001110111110010010010010101001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 45f
-
-Encoding: 45f
-U-Bits: 101011001110111110010101011010010111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 45f
-
-Encoding: 460
-U-Bits: 000000011100111000001110101100000100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 460
-
-Encoding: 460
-U-Bits: 000000011100111000010011100001001101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 460
-
-Encoding: 460
-U-Bits: 000000011100111000010100101001110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 460
-
-Encoding: 461
-U-Bits: 110111111100111000010011100000100110
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 461
-
-Encoding: 461
-U-Bits: 110111111100111000001110101101101111
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 461
-
-Encoding: 461
-U-Bits: 110111111100111000001001100101010001
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 461
-
-Encoding: 462
-U-Bits: 010001100100111000001001111111001101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 462
-
-Encoding: 462
-U-Bits: 010001100100111000010100110010000100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 462
-
-Encoding: 462
-U-Bits: 010001100100111000010011111010111010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 462
-
-Encoding: 463
-U-Bits: 100110000100111000010100110011101111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 463
-
-Encoding: 463
-U-Bits: 100110000100111000001001111110100110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 463
-
-Encoding: 463
-U-Bits: 100110000100111000001110110110011000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 463
-
-Encoding: 464
-U-Bits: 001010000010111000010011111100011100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 464
-
-Encoding: 464
-U-Bits: 001010000010111000001110110001010101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 464
-
-Encoding: 464
-U-Bits: 001010000010111000001001111001101011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 464
-
-Encoding: 465
-U-Bits: 111101100010111000001110110000111110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 465
-
-Encoding: 465
-U-Bits: 111101100010111000010011111101110111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 465
-
-Encoding: 465
-U-Bits: 111101100010111000010100110101001001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 465
-
-Encoding: 466
-U-Bits: 011011111010111000010100101111010101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 466
-
-Encoding: 466
-U-Bits: 011011111010111000001001100010011100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 466
-
-Encoding: 466
-U-Bits: 011011111010111000001110101010100010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 466
-
-Encoding: 467
-U-Bits: 101100011010111000001001100011110111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 467
-
-Encoding: 467
-U-Bits: 101100011010111000010100101110111110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 467
-
-Encoding: 467
-U-Bits: 101100011010111000010011100110000000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 467
-
-Encoding: 468
-U-Bits: 000110111011011000001001111000000000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 468
-
-Encoding: 468
-U-Bits: 000110111011011000010100110101001001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 468
-
-Encoding: 468
-U-Bits: 000110111011011000010011111101110111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 468
-
-Encoding: 469
-U-Bits: 110001011011011000010100110100100010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 469
-
-Encoding: 469
-U-Bits: 110001011011011000001001111001101011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 469
-
-Encoding: 469
-U-Bits: 110001011011011000001110110001010101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 469
-
-Encoding: 46a
-U-Bits: 010111000011011000001110101011001001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 46a
-
-Encoding: 46a
-U-Bits: 010111000011011000010011100110000000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 46a
-
-Encoding: 46a
-U-Bits: 010111000011011000010100101110111110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 46a
-
-Encoding: 46b
-U-Bits: 100000100011011000010011100111101011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 46b
-
-Encoding: 46b
-U-Bits: 100000100011011000001110101010100010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 46b
-
-Encoding: 46b
-U-Bits: 100000100011011000001001100010011100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 46b
-
-Encoding: 46c
-U-Bits: 001100100101011000010100101000011000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 46c
-
-Encoding: 46c
-U-Bits: 001100100101011000001001100101010001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 46c
-
-Encoding: 46c
-U-Bits: 001100100101011000001110101101101111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 46c
-
-Encoding: 46d
-U-Bits: 111011000101011000001001100100111010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 46d
-
-Encoding: 46d
-U-Bits: 111011000101011000010100101001110011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 46d
-
-Encoding: 46d
-U-Bits: 111011000101011000010011100001001101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 46d
-
-Encoding: 46e
-U-Bits: 011101011101011000010011111011010001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 46e
-
-Encoding: 46e
-U-Bits: 011101011101011000001110110110011000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 46e
-
-Encoding: 46e
-U-Bits: 011101011101011000001001111110100110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 46e
-
-Encoding: 46f
-U-Bits: 101010111101011000001110110111110011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 46f
-
-Encoding: 46f
-U-Bits: 101010111101011000010011111010111010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 46f
-
-Encoding: 46f
-U-Bits: 101010111101011000010100110010000100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 46f
-
-Encoding: 470
-U-Bits: 000001110101000000001111011001000110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 470
-
-Encoding: 470
-U-Bits: 000001110101000000010010010100001111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 470
-
-Encoding: 470
-U-Bits: 000001110101000000010101011100110001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 470
-
-Encoding: 471
-U-Bits: 110110010101000000010010010101100100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 471
-
-Encoding: 471
-U-Bits: 110110010101000000001111011000101101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 471
-
-Encoding: 471
-U-Bits: 110110010101000000001000010000010011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 471
-
-Encoding: 472
-U-Bits: 010000001101000000001000001010001111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 472
-
-Encoding: 472
-U-Bits: 010000001101000000010101000111000110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 472
-
-Encoding: 472
-U-Bits: 010000001101000000010010001111111000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 472
-
-Encoding: 473
-U-Bits: 100111101101000000010101000110101101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 473
-
-Encoding: 473
-U-Bits: 100111101101000000001000001011100100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 473
-
-Encoding: 473
-U-Bits: 100111101101000000001111000011011010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 473
-
-Encoding: 474
-U-Bits: 001011101011000000010010001001011110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 474
-
-Encoding: 474
-U-Bits: 001011101011000000001111000100010111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 474
-
-Encoding: 474
-U-Bits: 001011101011000000001000001100101001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 474
-
-Encoding: 475
-U-Bits: 111100001011000000001111000101111100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 475
-
-Encoding: 475
-U-Bits: 111100001011000000010010001000110101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 475
-
-Encoding: 475
-U-Bits: 111100001011000000010101000000001011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 475
-
-Encoding: 476
-U-Bits: 011010010011000000010101011010010111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 476
-
-Encoding: 476
-U-Bits: 011010010011000000001000010111011110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 476
-
-Encoding: 476
-U-Bits: 011010010011000000001111011111100000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 476
-
-Encoding: 477
-U-Bits: 101101110011000000001000010110110101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 477
-
-Encoding: 477
-U-Bits: 101101110011000000010101011011111100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 477
-
-Encoding: 477
-U-Bits: 101101110011000000010010010011000010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 477
-
-Encoding: 478
-U-Bits: 000111010010100000001000001101000010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 478
-
-Encoding: 478
-U-Bits: 000111010010100000010101000000001011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 478
-
-Encoding: 478
-U-Bits: 000111010010100000010010001000110101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 478
-
-Encoding: 479
-U-Bits: 110000110010100000010101000001100000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 479
-
-Encoding: 479
-U-Bits: 110000110010100000001000001100101001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 479
-
-Encoding: 479
-U-Bits: 110000110010100000001111000100010111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 479
-
-Encoding: 47a
-U-Bits: 010110101010100000001111011110001011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 47a
-
-Encoding: 47a
-U-Bits: 010110101010100000010010010011000010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 47a
-
-Encoding: 47a
-U-Bits: 010110101010100000010101011011111100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 47a
-
-Encoding: 47b
-U-Bits: 100001001010100000010010010010101001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 47b
-
-Encoding: 47b
-U-Bits: 100001001010100000001111011111100000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 47b
-
-Encoding: 47b
-U-Bits: 100001001010100000001000010111011110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 47b
-
-Encoding: 47c
-U-Bits: 001101001100100000010101011101011010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 47c
-
-Encoding: 47c
-U-Bits: 001101001100100000001000010000010011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 47c
-
-Encoding: 47c
-U-Bits: 001101001100100000001111011000101101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 47c
-
-Encoding: 47d
-U-Bits: 111010101100100000001000010001111000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 47d
-
-Encoding: 47d
-U-Bits: 111010101100100000010101011100110001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 47d
-
-Encoding: 47d
-U-Bits: 111010101100100000010010010100001111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 47d
-
-Encoding: 47e
-U-Bits: 011100110100100000010010001110010011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 47e
-
-Encoding: 47e
-U-Bits: 011100110100100000001111000011011010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 47e
-
-Encoding: 47e
-U-Bits: 011100110100100000001000001011100100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 47e
-
-Encoding: 47f
-U-Bits: 101011010100100000001111000010110001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 47f
-
-Encoding: 47f
-U-Bits: 101011010100100000010010001111111000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 47f
-
-Encoding: 47f
-U-Bits: 101011010100100000010101000111000110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 47f
-
-Encoding: 480
-U-Bits: 000000000001101000001101011010010111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 480
-
-Encoding: 480
-U-Bits: 000000000001101000010000010111011110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 480
-
-Encoding: 480
-U-Bits: 000000000001101000010111011111100000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 480
-
-Encoding: 481
-U-Bits: 110111100001101000010000010110110101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 481
-
-Encoding: 481
-U-Bits: 110111100001101000001101011011111100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 481
-
-Encoding: 481
-U-Bits: 110111100001101000001010010011000010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 481
-
-Encoding: 482
-U-Bits: 010001111001101000001010001001011110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 482
-
-Encoding: 482
-U-Bits: 010001111001101000010111000100010111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 482
-
-Encoding: 482
-U-Bits: 010001111001101000010000001100101001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 482
-
-Encoding: 483
-U-Bits: 100110011001101000010111000101111100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 483
-
-Encoding: 483
-U-Bits: 100110011001101000001010001000110101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 483
-
-Encoding: 483
-U-Bits: 100110011001101000001101000000001011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 483
-
-Encoding: 484
-U-Bits: 001010011111101000010000001010001111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 484
-
-Encoding: 484
-U-Bits: 001010011111101000001101000111000110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 484
-
-Encoding: 484
-U-Bits: 001010011111101000001010001111111000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 484
-
-Encoding: 485
-U-Bits: 111101111111101000001101000110101101
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 485
-
-Encoding: 485
-U-Bits: 111101111111101000010000001011100100
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 485
-
-Encoding: 485
-U-Bits: 111101111111101000010111000011011010
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 485
-
-Encoding: 486
-U-Bits: 011011100111101000010111011001000110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 486
-
-Encoding: 486
-U-Bits: 011011100111101000001010010100001111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 486
-
-Encoding: 486
-U-Bits: 011011100111101000001101011100110001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 486
-
-Encoding: 487
-U-Bits: 101100000111101000001010010101100100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 487
-
-Encoding: 487
-U-Bits: 101100000111101000010111011000101101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 487
-
-Encoding: 487
-U-Bits: 101100000111101000010000010000010011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 487
-
-Encoding: 488
-U-Bits: 000110100110001000001010001110010011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 488
-
-Encoding: 488
-U-Bits: 000110100110001000010111000011011010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 488
-
-Encoding: 488
-U-Bits: 000110100110001000010000001011100100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 488
-
-Encoding: 489
-U-Bits: 110001000110001000010111000010110001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 489
-
-Encoding: 489
-U-Bits: 110001000110001000001010001111111000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 489
-
-Encoding: 489
-U-Bits: 110001000110001000001101000111000110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 489
-
-Encoding: 48a
-U-Bits: 010111011110001000001101011101011010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 48a
-
-Encoding: 48a
-U-Bits: 010111011110001000010000010000010011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 48a
-
-Encoding: 48a
-U-Bits: 010111011110001000010111011000101101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 48a
-
-Encoding: 48b
-U-Bits: 100000111110001000010000010001111000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 48b
-
-Encoding: 48b
-U-Bits: 100000111110001000001101011100110001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 48b
-
-Encoding: 48b
-U-Bits: 100000111110001000001010010100001111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 48b
-
-Encoding: 48c
-U-Bits: 001100111000001000010111011110001011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 48c
-
-Encoding: 48c
-U-Bits: 001100111000001000001010010011000010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 48c
-
-Encoding: 48c
-U-Bits: 001100111000001000001101011011111100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 48c
-
-Encoding: 48d
-U-Bits: 111011011000001000001010010010101001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 48d
-
-Encoding: 48d
-U-Bits: 111011011000001000010111011111100000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 48d
-
-Encoding: 48d
-U-Bits: 111011011000001000010000010111011110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 48d
-
-Encoding: 48e
-U-Bits: 011101000000001000010000001101000010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 48e
-
-Encoding: 48e
-U-Bits: 011101000000001000001101000000001011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 48e
-
-Encoding: 48e
-U-Bits: 011101000000001000001010001000110101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 48e
-
-Encoding: 48f
-U-Bits: 101010100000001000001101000001100000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 48f
-
-Encoding: 48f
-U-Bits: 101010100000001000010000001100101001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 48f
-
-Encoding: 48f
-U-Bits: 101010100000001000010111000100010111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 48f
-
-Encoding: 490
-U-Bits: 000001101000010000001100101111010101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 490
-
-Encoding: 490
-U-Bits: 000001101000010000010001100010011100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 490
-
-Encoding: 490
-U-Bits: 000001101000010000010110101010100010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 490
-
-Encoding: 491
-U-Bits: 110110001000010000010001100011110111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 491
-
-Encoding: 491
-U-Bits: 110110001000010000001100101110111110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 491
-
-Encoding: 491
-U-Bits: 110110001000010000001011100110000000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 491
-
-Encoding: 492
-U-Bits: 010000010000010000001011111100011100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 492
-
-Encoding: 492
-U-Bits: 010000010000010000010110110001010101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 492
-
-Encoding: 492
-U-Bits: 010000010000010000010001111001101011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 492
-
-Encoding: 493
-U-Bits: 100111110000010000010110110000111110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 493
-
-Encoding: 493
-U-Bits: 100111110000010000001011111101110111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 493
-
-Encoding: 493
-U-Bits: 100111110000010000001100110101001001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 493
-
-Encoding: 494
-U-Bits: 001011110110010000010001111111001101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 494
-
-Encoding: 494
-U-Bits: 001011110110010000001100110010000100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 494
-
-Encoding: 494
-U-Bits: 001011110110010000001011111010111010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 494
-
-Encoding: 495
-U-Bits: 111100010110010000001100110011101111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 495
-
-Encoding: 495
-U-Bits: 111100010110010000010001111110100110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 495
-
-Encoding: 495
-U-Bits: 111100010110010000010110110110011000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 495
-
-Encoding: 496
-U-Bits: 011010001110010000010110101100000100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 496
-
-Encoding: 496
-U-Bits: 011010001110010000001011100001001101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 496
-
-Encoding: 496
-U-Bits: 011010001110010000001100101001110011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 496
-
-Encoding: 497
-U-Bits: 101101101110010000001011100000100110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 497
-
-Encoding: 497
-U-Bits: 101101101110010000010110101101101111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 497
-
-Encoding: 497
-U-Bits: 101101101110010000010001100101010001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 497
-
-Encoding: 498
-U-Bits: 000111001111110000001011111011010001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 498
-
-Encoding: 498
-U-Bits: 000111001111110000010110110110011000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 498
-
-Encoding: 498
-U-Bits: 000111001111110000010001111110100110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 498
-
-Encoding: 499
-U-Bits: 110000101111110000010110110111110011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 499
-
-Encoding: 499
-U-Bits: 110000101111110000001011111010111010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 499
-
-Encoding: 499
-U-Bits: 110000101111110000001100110010000100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 499
-
-Encoding: 49a
-U-Bits: 010110110111110000001100101000011000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 49a
-
-Encoding: 49a
-U-Bits: 010110110111110000010001100101010001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 49a
-
-Encoding: 49a
-U-Bits: 010110110111110000010110101101101111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 49a
-
-Encoding: 49b
-U-Bits: 100001010111110000010001100100111010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 49b
-
-Encoding: 49b
-U-Bits: 100001010111110000001100101001110011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 49b
-
-Encoding: 49b
-U-Bits: 100001010111110000001011100001001101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 49b
-
-Encoding: 49c
-U-Bits: 001101010001110000010110101011001001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 49c
-
-Encoding: 49c
-U-Bits: 001101010001110000001011100110000000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 49c
-
-Encoding: 49c
-U-Bits: 001101010001110000001100101110111110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 49c
-
-Encoding: 49d
-U-Bits: 111010110001110000001011100111101011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 49d
-
-Encoding: 49d
-U-Bits: 111010110001110000010110101010100010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 49d
-
-Encoding: 49d
-U-Bits: 111010110001110000010001100010011100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 49d
-
-Encoding: 49e
-U-Bits: 011100101001110000010001111000000000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 49e
-
-Encoding: 49e
-U-Bits: 011100101001110000001100110101001001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 49e
-
-Encoding: 49e
-U-Bits: 011100101001110000001011111101110111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 49e
-
-Encoding: 49f
-U-Bits: 101011001001110000001100110100100010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 49f
-
-Encoding: 49f
-U-Bits: 101011001001110000010001111001101011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 49f
-
-Encoding: 49f
-U-Bits: 101011001001110000010110110001010101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 49f
-
-Encoding: 4a0
-U-Bits: 000000011011110110001101000111000110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 4a0
-
-Encoding: 4a0
-U-Bits: 000000011011110110010000001010001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 4a0
-
-Encoding: 4a0
-U-Bits: 000000011011110110010111000010110001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 4a0
-
-Encoding: 4a1
-U-Bits: 110111111011110110010000001011100100
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 4a1
-
-Encoding: 4a1
-U-Bits: 110111111011110110001101000110101101
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 4a1
-
-Encoding: 4a1
-U-Bits: 110111111011110110001010001110010011
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 4a1
-
-Encoding: 4a2
-U-Bits: 010001100011110110001010010100001111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 4a2
-
-Encoding: 4a2
-U-Bits: 010001100011110110010111011001000110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 4a2
-
-Encoding: 4a2
-U-Bits: 010001100011110110010000010001111000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 4a2
-
-Encoding: 4a3
-U-Bits: 100110000011110110010111011000101101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 4a3
-
-Encoding: 4a3
-U-Bits: 100110000011110110001010010101100100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 4a3
-
-Encoding: 4a3
-U-Bits: 100110000011110110001101011101011010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 4a3
-
-Encoding: 4a4
-U-Bits: 001010000101110110010000010111011110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 4a4
-
-Encoding: 4a4
-U-Bits: 001010000101110110001101011010010111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 4a4
-
-Encoding: 4a4
-U-Bits: 001010000101110110001010010010101001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 4a4
-
-Encoding: 4a5
-U-Bits: 111101100101110110001101011011111100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 4a5
-
-Encoding: 4a5
-U-Bits: 111101100101110110010000010110110101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 4a5
-
-Encoding: 4a5
-U-Bits: 111101100101110110010111011110001011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 4a5
-
-Encoding: 4a6
-U-Bits: 011011111101110110010111000100010111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 4a6
-
-Encoding: 4a6
-U-Bits: 011011111101110110001010001001011110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 4a6
-
-Encoding: 4a6
-U-Bits: 011011111101110110001101000001100000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 4a6
-
-Encoding: 4a7
-U-Bits: 101100011101110110001010001000110101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 4a7
-
-Encoding: 4a7
-U-Bits: 101100011101110110010111000101111100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 4a7
-
-Encoding: 4a7
-U-Bits: 101100011101110110010000001101000010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 4a7
-
-Encoding: 4a8
-U-Bits: 000110111100010110001010010011000010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 4a8
-
-Encoding: 4a8
-U-Bits: 000110111100010110010111011110001011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 4a8
-
-Encoding: 4a8
-U-Bits: 000110111100010110010000010110110101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 4a8
-
-Encoding: 4a9
-U-Bits: 110001011100010110010111011111100000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 4a9
-
-Encoding: 4a9
-U-Bits: 110001011100010110001010010010101001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 4a9
-
-Encoding: 4a9
-U-Bits: 110001011100010110001101011010010111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 4a9
-
-Encoding: 4aa
-U-Bits: 010111000100010110001101000000001011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 4aa
-
-Encoding: 4aa
-U-Bits: 010111000100010110010000001101000010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 4aa
-
-Encoding: 4aa
-U-Bits: 010111000100010110010111000101111100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 4aa
-
-Encoding: 4ab
-U-Bits: 100000100100010110010000001100101001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 4ab
-
-Encoding: 4ab
-U-Bits: 100000100100010110001101000001100000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 4ab
-
-Encoding: 4ab
-U-Bits: 100000100100010110001010001001011110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 4ab
-
-Encoding: 4ac
-U-Bits: 001100100010010110010111000011011010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 4ac
-
-Encoding: 4ac
-U-Bits: 001100100010010110001010001110010011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 4ac
-
-Encoding: 4ac
-U-Bits: 001100100010010110001101000110101101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 4ac
-
-Encoding: 4ad
-U-Bits: 111011000010010110001010001111111000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 4ad
-
-Encoding: 4ad
-U-Bits: 111011000010010110010111000010110001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 4ad
-
-Encoding: 4ad
-U-Bits: 111011000010010110010000001010001111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 4ad
-
-Encoding: 4ae
-U-Bits: 011101011010010110010000010000010011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 4ae
-
-Encoding: 4ae
-U-Bits: 011101011010010110001101011101011010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 4ae
-
-Encoding: 4ae
-U-Bits: 011101011010010110001010010101100100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 4ae
-
-Encoding: 4af
-U-Bits: 101010111010010110001101011100110001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 4af
-
-Encoding: 4af
-U-Bits: 101010111010010110010000010001111000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 4af
-
-Encoding: 4af
-U-Bits: 101010111010010110010111011001000110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 4af
-
-Encoding: 4b0
-U-Bits: 000001110010001110001100110010000100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 4b0
-
-Encoding: 4b0
-U-Bits: 000001110010001110010001111111001101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 4b0
-
-Encoding: 4b0
-U-Bits: 000001110010001110010110110111110011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 4b0
-
-Encoding: 4b1
-U-Bits: 110110010010001110010001111110100110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 4b1
-
-Encoding: 4b1
-U-Bits: 110110010010001110001100110011101111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 4b1
-
-Encoding: 4b1
-U-Bits: 110110010010001110001011111011010001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 4b1
-
-Encoding: 4b2
-U-Bits: 010000001010001110001011100001001101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 4b2
-
-Encoding: 4b2
-U-Bits: 010000001010001110010110101100000100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 4b2
-
-Encoding: 4b2
-U-Bits: 010000001010001110010001100100111010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 4b2
-
-Encoding: 4b3
-U-Bits: 100111101010001110010110101101101111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 4b3
-
-Encoding: 4b3
-U-Bits: 100111101010001110001011100000100110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 4b3
-
-Encoding: 4b3
-U-Bits: 100111101010001110001100101000011000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 4b3
-
-Encoding: 4b4
-U-Bits: 001011101100001110010001100010011100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 4b4
-
-Encoding: 4b4
-U-Bits: 001011101100001110001100101111010101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 4b4
-
-Encoding: 4b4
-U-Bits: 001011101100001110001011100111101011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 4b4
-
-Encoding: 4b5
-U-Bits: 111100001100001110001100101110111110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 4b5
-
-Encoding: 4b5
-U-Bits: 111100001100001110010001100011110111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 4b5
-
-Encoding: 4b5
-U-Bits: 111100001100001110010110101011001001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 4b5
-
-Encoding: 4b6
-U-Bits: 011010010100001110010110110001010101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 4b6
-
-Encoding: 4b6
-U-Bits: 011010010100001110001011111100011100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 4b6
-
-Encoding: 4b6
-U-Bits: 011010010100001110001100110100100010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 4b6
-
-Encoding: 4b7
-U-Bits: 101101110100001110001011111101110111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 4b7
-
-Encoding: 4b7
-U-Bits: 101101110100001110010110110000111110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 4b7
-
-Encoding: 4b7
-U-Bits: 101101110100001110010001111000000000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4b7
-
-Encoding: 4b8
-U-Bits: 000111010101101110001011100110000000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4b8
-
-Encoding: 4b8
-U-Bits: 000111010101101110010110101011001001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 4b8
-
-Encoding: 4b8
-U-Bits: 000111010101101110010001100011110111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 4b8
-
-Encoding: 4b9
-U-Bits: 110000110101101110010110101010100010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 4b9
-
-Encoding: 4b9
-U-Bits: 110000110101101110001011100111101011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 4b9
-
-Encoding: 4b9
-U-Bits: 110000110101101110001100101111010101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 4b9
-
-Encoding: 4ba
-U-Bits: 010110101101101110001100110101001001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 4ba
-
-Encoding: 4ba
-U-Bits: 010110101101101110010001111000000000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4ba
-
-Encoding: 4ba
-U-Bits: 010110101101101110010110110000111110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 4ba
-
-Encoding: 4bb
-U-Bits: 100001001101101110010001111001101011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 4bb
-
-Encoding: 4bb
-U-Bits: 100001001101101110001100110100100010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 4bb
-
-Encoding: 4bb
-U-Bits: 100001001101101110001011111100011100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 4bb
-
-Encoding: 4bc
-U-Bits: 001101001011101110010110110110011000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 4bc
-
-Encoding: 4bc
-U-Bits: 001101001011101110001011111011010001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 4bc
-
-Encoding: 4bc
-U-Bits: 001101001011101110001100110011101111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 4bc
-
-Encoding: 4bd
-U-Bits: 111010101011101110001011111010111010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 4bd
-
-Encoding: 4bd
-U-Bits: 111010101011101110010110110111110011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 4bd
-
-Encoding: 4bd
-U-Bits: 111010101011101110010001111111001101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 4bd
-
-Encoding: 4be
-U-Bits: 011100110011101110010001100101010001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 4be
-
-Encoding: 4be
-U-Bits: 011100110011101110001100101000011000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 4be
-
-Encoding: 4be
-U-Bits: 011100110011101110001011100000100110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 4be
-
-Encoding: 4bf
-U-Bits: 101011010011101110001100101001110011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 4bf
-
-Encoding: 4bf
-U-Bits: 101011010011101110010001100100111010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 4bf
-
-Encoding: 4bf
-U-Bits: 101011010011101110010110101100000100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 4bf
-
-Encoding: 4c0
-U-Bits: 000000000111001111110001111001101011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 4c0
-
-Encoding: 4c0
-U-Bits: 000000000111001111101100110100100010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 4c0
-
-Encoding: 4c0
-U-Bits: 000000000111001111101011111100011100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 4c0
-
-Encoding: 4c1
-U-Bits: 110111100111001111101100110101001001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 4c1
-
-Encoding: 4c1
-U-Bits: 110111100111001111110001111000000000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4c1
-
-Encoding: 4c1
-U-Bits: 110111100111001111110110110000111110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 4c1
-
-Encoding: 4c2
-U-Bits: 010001111111001111110110101010100010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 4c2
-
-Encoding: 4c2
-U-Bits: 010001111111001111101011100111101011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 4c2
-
-Encoding: 4c2
-U-Bits: 010001111111001111101100101111010101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 4c2
-
-Encoding: 4c3
-U-Bits: 100110011111001111101011100110000000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4c3
-
-Encoding: 4c3
-U-Bits: 100110011111001111110110101011001001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 4c3
-
-Encoding: 4c3
-U-Bits: 100110011111001111110001100011110111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 4c3
-
-Encoding: 4c4
-U-Bits: 001010011001001111101100101001110011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 4c4
-
-Encoding: 4c4
-U-Bits: 001010011001001111110001100100111010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 4c4
-
-Encoding: 4c4
-U-Bits: 001010011001001111110110101100000100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 4c4
-
-Encoding: 4c5
-U-Bits: 111101111001001111110001100101010001
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 4c5
-
-Encoding: 4c5
-U-Bits: 111101111001001111101100101000011000
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 4c5
-
-Encoding: 4c5
-U-Bits: 111101111001001111101011100000100110
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 4c5
-
-Encoding: 4c6
-U-Bits: 011011100001001111101011111010111010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 4c6
-
-Encoding: 4c6
-U-Bits: 011011100001001111110110110111110011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 4c6
-
-Encoding: 4c6
-U-Bits: 011011100001001111110001111111001101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 4c6
-
-Encoding: 4c7
-U-Bits: 101100000001001111110110110110011000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 4c7
-
-Encoding: 4c7
-U-Bits: 101100000001001111101011111011010001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 4c7
-
-Encoding: 4c7
-U-Bits: 101100000001001111101100110011101111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 4c7
-
-Encoding: 4c8
-U-Bits: 000110100000101111110110101101101111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 4c8
-
-Encoding: 4c8
-U-Bits: 000110100000101111101011100000100110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 4c8
-
-Encoding: 4c8
-U-Bits: 000110100000101111101100101000011000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 4c8
-
-Encoding: 4c9
-U-Bits: 110001000000101111101011100001001101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 4c9
-
-Encoding: 4c9
-U-Bits: 110001000000101111110110101100000100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 4c9
-
-Encoding: 4c9
-U-Bits: 110001000000101111110001100100111010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 4c9
-
-Encoding: 4ca
-U-Bits: 010111011000101111110001111110100110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 4ca
-
-Encoding: 4ca
-U-Bits: 010111011000101111101100110011101111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 4ca
-
-Encoding: 4ca
-U-Bits: 010111011000101111101011111011010001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 4ca
-
-Encoding: 4cb
-U-Bits: 100000111000101111101100110010000100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 4cb
-
-Encoding: 4cb
-U-Bits: 100000111000101111110001111111001101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 4cb
-
-Encoding: 4cb
-U-Bits: 100000111000101111110110110111110011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 4cb
-
-Encoding: 4cc
-U-Bits: 001100111110101111101011111101110111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 4cc
-
-Encoding: 4cc
-U-Bits: 001100111110101111110110110000111110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 4cc
-
-Encoding: 4cc
-U-Bits: 001100111110101111110001111000000000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4cc
-
-Encoding: 4cd
-U-Bits: 111011011110101111110110110001010101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 4cd
-
-Encoding: 4cd
-U-Bits: 111011011110101111101011111100011100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 4cd
-
-Encoding: 4cd
-U-Bits: 111011011110101111101100110100100010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 4cd
-
-Encoding: 4ce
-U-Bits: 011101000110101111101100101110111110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 4ce
-
-Encoding: 4ce
-U-Bits: 011101000110101111110001100011110111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 4ce
-
-Encoding: 4ce
-U-Bits: 011101000110101111110110101011001001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 4ce
-
-Encoding: 4cf
-U-Bits: 101010100110101111110001100010011100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 4cf
-
-Encoding: 4cf
-U-Bits: 101010100110101111101100101111010101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 4cf
-
-Encoding: 4cf
-U-Bits: 101010100110101111101011100111101011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 4cf
-
-Encoding: 4d0
-U-Bits: 000001101110110111110000001100101001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 4d0
-
-Encoding: 4d0
-U-Bits: 000001101110110111101101000001100000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 4d0
-
-Encoding: 4d0
-U-Bits: 000001101110110111101010001001011110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 4d0
-
-Encoding: 4d1
-U-Bits: 110110001110110111101101000000001011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 4d1
-
-Encoding: 4d1
-U-Bits: 110110001110110111110000001101000010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 4d1
-
-Encoding: 4d1
-U-Bits: 110110001110110111110111000101111100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 4d1
-
-Encoding: 4d2
-U-Bits: 010000010110110111110111011111100000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 4d2
-
-Encoding: 4d2
-U-Bits: 010000010110110111101010010010101001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 4d2
-
-Encoding: 4d2
-U-Bits: 010000010110110111101101011010010111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 4d2
-
-Encoding: 4d3
-U-Bits: 100111110110110111101010010011000010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 4d3
-
-Encoding: 4d3
-U-Bits: 100111110110110111110111011110001011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 4d3
-
-Encoding: 4d3
-U-Bits: 100111110110110111110000010110110101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 4d3
-
-Encoding: 4d4
-U-Bits: 001011110000110111101101011100110001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 4d4
-
-Encoding: 4d4
-U-Bits: 001011110000110111110000010001111000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 4d4
-
-Encoding: 4d4
-U-Bits: 001011110000110111110111011001000110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 4d4
-
-Encoding: 4d5
-U-Bits: 111100010000110111110000010000010011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 4d5
-
-Encoding: 4d5
-U-Bits: 111100010000110111101101011101011010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 4d5
-
-Encoding: 4d5
-U-Bits: 111100010000110111101010010101100100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 4d5
-
-Encoding: 4d6
-U-Bits: 011010001000110111101010001111111000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 4d6
-
-Encoding: 4d6
-U-Bits: 011010001000110111110111000010110001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 4d6
-
-Encoding: 4d6
-U-Bits: 011010001000110111110000001010001111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 4d6
-
-Encoding: 4d7
-U-Bits: 101101101000110111110111000011011010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 4d7
-
-Encoding: 4d7
-U-Bits: 101101101000110111101010001110010011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 4d7
-
-Encoding: 4d7
-U-Bits: 101101101000110111101101000110101101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 4d7
-
-Encoding: 4d8
-U-Bits: 000111001001010111110111011000101101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 4d8
-
-Encoding: 4d8
-U-Bits: 000111001001010111101010010101100100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 4d8
-
-Encoding: 4d8
-U-Bits: 000111001001010111101101011101011010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 4d8
-
-Encoding: 4d9
-U-Bits: 110000101001010111101010010100001111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 4d9
-
-Encoding: 4d9
-U-Bits: 110000101001010111110111011001000110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 4d9
-
-Encoding: 4d9
-U-Bits: 110000101001010111110000010001111000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 4d9
-
-Encoding: 4da
-U-Bits: 010110110001010111110000001011100100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 4da
-
-Encoding: 4da
-U-Bits: 010110110001010111101101000110101101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 4da
-
-Encoding: 4da
-U-Bits: 010110110001010111101010001110010011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 4da
-
-Encoding: 4db
-U-Bits: 100001010001010111101101000111000110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 4db
-
-Encoding: 4db
-U-Bits: 100001010001010111110000001010001111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 4db
-
-Encoding: 4db
-U-Bits: 100001010001010111110111000010110001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 4db
-
-Encoding: 4dc
-U-Bits: 001101010111010111101010001000110101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 4dc
-
-Encoding: 4dc
-U-Bits: 001101010111010111110111000101111100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 4dc
-
-Encoding: 4dc
-U-Bits: 001101010111010111110000001101000010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 4dc
-
-Encoding: 4dd
-U-Bits: 111010110111010111110111000100010111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 4dd
-
-Encoding: 4dd
-U-Bits: 111010110111010111101010001001011110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 4dd
-
-Encoding: 4dd
-U-Bits: 111010110111010111101101000001100000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 4dd
-
-Encoding: 4de
-U-Bits: 011100101111010111101101011011111100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 4de
-
-Encoding: 4de
-U-Bits: 011100101111010111110000010110110101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 4de
-
-Encoding: 4de
-U-Bits: 011100101111010111110111011110001011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 4de
-
-Encoding: 4df
-U-Bits: 101011001111010111110000010111011110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 4df
-
-Encoding: 4df
-U-Bits: 101011001111010111101101011010010111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 4df
-
-Encoding: 4df
-U-Bits: 101011001111010111101010010010101001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 4df
-
-Encoding: 4e0
-U-Bits: 000000011101010001110001100100111010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 4e0
-
-Encoding: 4e0
-U-Bits: 000000011101010001101100101001110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 4e0
-
-Encoding: 4e0
-U-Bits: 000000011101010001101011100001001101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 4e0
-
-Encoding: 4e1
-U-Bits: 110111111101010001101100101000011000
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 4e1
-
-Encoding: 4e1
-U-Bits: 110111111101010001110001100101010001
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 4e1
-
-Encoding: 4e1
-U-Bits: 110111111101010001110110101101101111
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 4e1
-
-Encoding: 4e2
-U-Bits: 010001100101010001110110110111110011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 4e2
-
-Encoding: 4e2
-U-Bits: 010001100101010001101011111010111010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 4e2
-
-Encoding: 4e2
-U-Bits: 010001100101010001101100110010000100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 4e2
-
-Encoding: 4e3
-U-Bits: 100110000101010001101011111011010001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 4e3
-
-Encoding: 4e3
-U-Bits: 100110000101010001110110110110011000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 4e3
-
-Encoding: 4e3
-U-Bits: 100110000101010001110001111110100110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 4e3
-
-Encoding: 4e4
-U-Bits: 001010000011010001101100110100100010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 4e4
-
-Encoding: 4e4
-U-Bits: 001010000011010001110001111001101011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 4e4
-
-Encoding: 4e4
-U-Bits: 001010000011010001110110110001010101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 4e4
-
-Encoding: 4e5
-U-Bits: 111101100011010001110001111000000000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4e5
-
-Encoding: 4e5
-U-Bits: 111101100011010001101100110101001001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 4e5
-
-Encoding: 4e5
-U-Bits: 111101100011010001101011111101110111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 4e5
-
-Encoding: 4e6
-U-Bits: 011011111011010001101011100111101011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 4e6
-
-Encoding: 4e6
-U-Bits: 011011111011010001110110101010100010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 4e6
-
-Encoding: 4e6
-U-Bits: 011011111011010001110001100010011100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 4e6
-
-Encoding: 4e7
-U-Bits: 101100011011010001110110101011001001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 4e7
-
-Encoding: 4e7
-U-Bits: 101100011011010001101011100110000000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4e7
-
-Encoding: 4e7
-U-Bits: 101100011011010001101100101110111110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 4e7
-
-Encoding: 4e8
-U-Bits: 000110111010110001110110110000111110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 4e8
-
-Encoding: 4e8
-U-Bits: 000110111010110001101011111101110111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 4e8
-
-Encoding: 4e8
-U-Bits: 000110111010110001101100110101001001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 4e8
-
-Encoding: 4e9
-U-Bits: 110001011010110001101011111100011100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 4e9
-
-Encoding: 4e9
-U-Bits: 110001011010110001110110110001010101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 4e9
-
-Encoding: 4e9
-U-Bits: 110001011010110001110001111001101011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 4e9
-
-Encoding: 4ea
-U-Bits: 010111000010110001110001100011110111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 4ea
-
-Encoding: 4ea
-U-Bits: 010111000010110001101100101110111110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 4ea
-
-Encoding: 4ea
-U-Bits: 010111000010110001101011100110000000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 4ea
-
-Encoding: 4eb
-U-Bits: 100000100010110001101100101111010101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 4eb
-
-Encoding: 4eb
-U-Bits: 100000100010110001110001100010011100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 4eb
-
-Encoding: 4eb
-U-Bits: 100000100010110001110110101010100010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 4eb
-
-Encoding: 4ec
-U-Bits: 001100100100110001101011100000100110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 4ec
-
-Encoding: 4ec
-U-Bits: 001100100100110001110110101101101111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 4ec
-
-Encoding: 4ec
-U-Bits: 001100100100110001110001100101010001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 4ec
-
-Encoding: 4ed
-U-Bits: 111011000100110001110110101100000100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 4ed
-
-Encoding: 4ed
-U-Bits: 111011000100110001101011100001001101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 4ed
-
-Encoding: 4ed
-U-Bits: 111011000100110001101100101001110011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 4ed
-
-Encoding: 4ee
-U-Bits: 011101011100110001101100110011101111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 4ee
-
-Encoding: 4ee
-U-Bits: 011101011100110001110001111110100110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 4ee
-
-Encoding: 4ee
-U-Bits: 011101011100110001110110110110011000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 4ee
-
-Encoding: 4ef
-U-Bits: 101010111100110001110001111111001101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 4ef
-
-Encoding: 4ef
-U-Bits: 101010111100110001101100110010000100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 4ef
-
-Encoding: 4ef
-U-Bits: 101010111100110001101011111010111010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 4ef
-
-Encoding: 4f0
-U-Bits: 000001110100101001110000010001111000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 4f0
-
-Encoding: 4f0
-U-Bits: 000001110100101001101101011100110001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 4f0
-
-Encoding: 4f0
-U-Bits: 000001110100101001101010010100001111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 4f0
-
-Encoding: 4f1
-U-Bits: 110110010100101001101101011101011010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 4f1
-
-Encoding: 4f1
-U-Bits: 110110010100101001110000010000010011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 4f1
-
-Encoding: 4f1
-U-Bits: 110110010100101001110111011000101101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 4f1
-
-Encoding: 4f2
-U-Bits: 010000001100101001110111000010110001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 4f2
-
-Encoding: 4f2
-U-Bits: 010000001100101001101010001111111000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 4f2
-
-Encoding: 4f2
-U-Bits: 010000001100101001101101000111000110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 4f2
-
-Encoding: 4f3
-U-Bits: 100111101100101001101010001110010011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 4f3
-
-Encoding: 4f3
-U-Bits: 100111101100101001110111000011011010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 4f3
-
-Encoding: 4f3
-U-Bits: 100111101100101001110000001011100100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 4f3
-
-Encoding: 4f4
-U-Bits: 001011101010101001101101000001100000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 4f4
-
-Encoding: 4f4
-U-Bits: 001011101010101001110000001100101001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 4f4
-
-Encoding: 4f4
-U-Bits: 001011101010101001110111000100010111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 4f4
-
-Encoding: 4f5
-U-Bits: 111100001010101001110000001101000010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 4f5
-
-Encoding: 4f5
-U-Bits: 111100001010101001101101000000001011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 4f5
-
-Encoding: 4f5
-U-Bits: 111100001010101001101010001000110101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 4f5
-
-Encoding: 4f6
-U-Bits: 011010010010101001101010010010101001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 4f6
-
-Encoding: 4f6
-U-Bits: 011010010010101001110111011111100000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 4f6
-
-Encoding: 4f6
-U-Bits: 011010010010101001110000010111011110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 4f6
-
-Encoding: 4f7
-U-Bits: 101101110010101001110111011110001011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 4f7
-
-Encoding: 4f7
-U-Bits: 101101110010101001101010010011000010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 4f7
-
-Encoding: 4f7
-U-Bits: 101101110010101001101101011011111100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 4f7
-
-Encoding: 4f8
-U-Bits: 000111010011001001110111000101111100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 4f8
-
-Encoding: 4f8
-U-Bits: 000111010011001001101010001000110101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 4f8
-
-Encoding: 4f8
-U-Bits: 000111010011001001101101000000001011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 4f8
-
-Encoding: 4f9
-U-Bits: 110000110011001001101010001001011110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 4f9
-
-Encoding: 4f9
-U-Bits: 110000110011001001110111000100010111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 4f9
-
-Encoding: 4f9
-U-Bits: 110000110011001001110000001100101001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 4f9
-
-Encoding: 4fa
-U-Bits: 010110101011001001110000010110110101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 4fa
-
-Encoding: 4fa
-U-Bits: 010110101011001001101101011011111100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 4fa
-
-Encoding: 4fa
-U-Bits: 010110101011001001101010010011000010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 4fa
-
-Encoding: 4fb
-U-Bits: 100001001011001001101101011010010111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 4fb
-
-Encoding: 4fb
-U-Bits: 100001001011001001110000010111011110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 4fb
-
-Encoding: 4fb
-U-Bits: 100001001011001001110111011111100000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 4fb
-
-Encoding: 4fc
-U-Bits: 001101001101001001101010010101100100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 4fc
-
-Encoding: 4fc
-U-Bits: 001101001101001001110111011000101101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 4fc
-
-Encoding: 4fc
-U-Bits: 001101001101001001110000010000010011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 4fc
-
-Encoding: 4fd
-U-Bits: 111010101101001001110111011001000110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 4fd
-
-Encoding: 4fd
-U-Bits: 111010101101001001101010010100001111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 4fd
-
-Encoding: 4fd
-U-Bits: 111010101101001001101101011100110001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 4fd
-
-Encoding: 4fe
-U-Bits: 011100110101001001101101000110101101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 4fe
-
-Encoding: 4fe
-U-Bits: 011100110101001001110000001011100100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 4fe
-
-Encoding: 4fe
-U-Bits: 011100110101001001110111000011011010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 4fe
-
-Encoding: 4ff
-U-Bits: 101011010101001001110000001010001111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 4ff
-
-Encoding: 4ff
-U-Bits: 101011010101001001101101000111000110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 4ff
-
-Encoding: 4ff
-U-Bits: 101011010101001001101010001111111000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 4ff
-
-Encoding: 500
-U-Bits: 000000000000011011101101100000100110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 500
-
-Encoding: 500
-U-Bits: 000000000000011011110000101101101111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 500
-
-Encoding: 500
-U-Bits: 000000000000011011110111100101010001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 500
-
-Encoding: 501
-U-Bits: 110111100000011011110000101100000100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 501
-
-Encoding: 501
-U-Bits: 110111100000011011101101100001001101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 501
-
-Encoding: 501
-U-Bits: 110111100000011011101010101001110011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 501
-
-Encoding: 502
-U-Bits: 010001111000011011101010110011101111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 502
-
-Encoding: 502
-U-Bits: 010001111000011011110111111110100110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 502
-
-Encoding: 502
-U-Bits: 010001111000011011110000110110011000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 502
-
-Encoding: 503
-U-Bits: 100110011000011011110111111111001101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 503
-
-Encoding: 503
-U-Bits: 100110011000011011101010110010000100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 503
-
-Encoding: 503
-U-Bits: 100110011000011011101101111010111010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 503
-
-Encoding: 504
-U-Bits: 001010011110011011110000110000111110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 504
-
-Encoding: 504
-U-Bits: 001010011110011011101101111101110111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 504
-
-Encoding: 504
-U-Bits: 001010011110011011101010110101001001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 504
-
-Encoding: 505
-U-Bits: 111101111110011011101101111100011100
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 505
-
-Encoding: 505
-U-Bits: 111101111110011011110000110001010101
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 505
-
-Encoding: 505
-U-Bits: 111101111110011011110111111001101011
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 505
-
-Encoding: 506
-U-Bits: 011011100110011011110111100011110111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 506
-
-Encoding: 506
-U-Bits: 011011100110011011101010101110111110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 506
-
-Encoding: 506
-U-Bits: 011011100110011011101101100110000000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 506
-
-Encoding: 507
-U-Bits: 101100000110011011101010101111010101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 507
-
-Encoding: 507
-U-Bits: 101100000110011011110111100010011100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 507
-
-Encoding: 507
-U-Bits: 101100000110011011110000101010100010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 507
-
-Encoding: 508
-U-Bits: 000110100111111011101010110100100010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 508
-
-Encoding: 508
-U-Bits: 000110100111111011110111111001101011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 508
-
-Encoding: 508
-U-Bits: 000110100111111011110000110001010101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 508
-
-Encoding: 509
-U-Bits: 110001000111111011110111111000000000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 509
-
-Encoding: 509
-U-Bits: 110001000111111011101010110101001001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 509
-
-Encoding: 509
-U-Bits: 110001000111111011101101111101110111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 509
-
-Encoding: 50a
-U-Bits: 010111011111111011101101100111101011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 50a
-
-Encoding: 50a
-U-Bits: 010111011111111011110000101010100010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 50a
-
-Encoding: 50a
-U-Bits: 010111011111111011110111100010011100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 50a
-
-Encoding: 50b
-U-Bits: 100000111111111011110000101011001001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 50b
-
-Encoding: 50b
-U-Bits: 100000111111111011101101100110000000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 50b
-
-Encoding: 50b
-U-Bits: 100000111111111011101010101110111110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 50b
-
-Encoding: 50c
-U-Bits: 001100111001111011110111100100111010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 50c
-
-Encoding: 50c
-U-Bits: 001100111001111011101010101001110011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 50c
-
-Encoding: 50c
-U-Bits: 001100111001111011101101100001001101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 50c
-
-Encoding: 50d
-U-Bits: 111011011001111011101010101000011000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 50d
-
-Encoding: 50d
-U-Bits: 111011011001111011110111100101010001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 50d
-
-Encoding: 50d
-U-Bits: 111011011001111011110000101101101111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 50d
-
-Encoding: 50e
-U-Bits: 011101000001111011110000110111110011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 50e
-
-Encoding: 50e
-U-Bits: 011101000001111011101101111010111010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 50e
-
-Encoding: 50e
-U-Bits: 011101000001111011101010110010000100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 50e
-
-Encoding: 50f
-U-Bits: 101010100001111011101101111011010001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 50f
-
-Encoding: 50f
-U-Bits: 101010100001111011110000110110011000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 50f
-
-Encoding: 50f
-U-Bits: 101010100001111011110111111110100110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 50f
-
-Encoding: 510
-U-Bits: 000001101001100011101100010101100100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 510
-
-Encoding: 510
-U-Bits: 000001101001100011110001011000101101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 510
-
-Encoding: 510
-U-Bits: 000001101001100011110110010000010011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 510
-
-Encoding: 511
-U-Bits: 110110001001100011110001011001000110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 511
-
-Encoding: 511
-U-Bits: 110110001001100011101100010100001111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 511
-
-Encoding: 511
-U-Bits: 110110001001100011101011011100110001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 511
-
-Encoding: 512
-U-Bits: 010000010001100011101011000110101101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 512
-
-Encoding: 512
-U-Bits: 010000010001100011110110001011100100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 512
-
-Encoding: 512
-U-Bits: 010000010001100011110001000011011010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 512
-
-Encoding: 513
-U-Bits: 100111110001100011110110001010001111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 513
-
-Encoding: 513
-U-Bits: 100111110001100011101011000111000110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 513
-
-Encoding: 513
-U-Bits: 100111110001100011101100001111111000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 513
-
-Encoding: 514
-U-Bits: 001011110111100011110001000101111100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 514
-
-Encoding: 514
-U-Bits: 001011110111100011101100001000110101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 514
-
-Encoding: 514
-U-Bits: 001011110111100011101011000000001011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 514
-
-Encoding: 515
-U-Bits: 111100010111100011101100001001011110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 515
-
-Encoding: 515
-U-Bits: 111100010111100011110001000100010111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 515
-
-Encoding: 515
-U-Bits: 111100010111100011110110001100101001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 515
-
-Encoding: 516
-U-Bits: 011010001111100011110110010110110101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 516
-
-Encoding: 516
-U-Bits: 011010001111100011101011011011111100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 516
-
-Encoding: 516
-U-Bits: 011010001111100011101100010011000010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 516
-
-Encoding: 517
-U-Bits: 101101101111100011101011011010010111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 517
-
-Encoding: 517
-U-Bits: 101101101111100011110110010111011110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 517
-
-Encoding: 517
-U-Bits: 101101101111100011110001011111100000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 517
-
-Encoding: 518
-U-Bits: 000111001110000011101011000001100000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 518
-
-Encoding: 518
-U-Bits: 000111001110000011110110001100101001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 518
-
-Encoding: 518
-U-Bits: 000111001110000011110001000100010111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 518
-
-Encoding: 519
-U-Bits: 110000101110000011110110001101000010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 519
-
-Encoding: 519
-U-Bits: 110000101110000011101011000000001011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 519
-
-Encoding: 519
-U-Bits: 110000101110000011101100001000110101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 519
-
-Encoding: 51a
-U-Bits: 010110110110000011101100010010101001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 51a
-
-Encoding: 51a
-U-Bits: 010110110110000011110001011111100000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 51a
-
-Encoding: 51a
-U-Bits: 010110110110000011110110010111011110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 51a
-
-Encoding: 51b
-U-Bits: 100001010110000011110001011110001011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 51b
-
-Encoding: 51b
-U-Bits: 100001010110000011101100010011000010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 51b
-
-Encoding: 51b
-U-Bits: 100001010110000011101011011011111100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 51b
-
-Encoding: 51c
-U-Bits: 001101010000000011110110010001111000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 51c
-
-Encoding: 51c
-U-Bits: 001101010000000011101011011100110001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 51c
-
-Encoding: 51c
-U-Bits: 001101010000000011101100010100001111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 51c
-
-Encoding: 51d
-U-Bits: 111010110000000011101011011101011010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 51d
-
-Encoding: 51d
-U-Bits: 111010110000000011110110010000010011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 51d
-
-Encoding: 51d
-U-Bits: 111010110000000011110001011000101101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 51d
-
-Encoding: 51e
-U-Bits: 011100101000000011110001000010110001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 51e
-
-Encoding: 51e
-U-Bits: 011100101000000011101100001111111000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 51e
-
-Encoding: 51e
-U-Bits: 011100101000000011101011000111000110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 51e
-
-Encoding: 51f
-U-Bits: 101011001000000011101100001110010011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 51f
-
-Encoding: 51f
-U-Bits: 101011001000000011110001000011011010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 51f
-
-Encoding: 51f
-U-Bits: 101011001000000011110110001011100100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 51f
-
-Encoding: 520
-U-Bits: 000000011010000101101101111101110111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 520
-
-Encoding: 520
-U-Bits: 000000011010000101110000110000111110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 520
-
-Encoding: 520
-U-Bits: 000000011010000101110111111000000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 520
-
-Encoding: 521
-U-Bits: 110111111010000101110000110001010101
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 521
-
-Encoding: 521
-U-Bits: 110111111010000101101101111100011100
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 521
-
-Encoding: 521
-U-Bits: 110111111010000101101010110100100010
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 521
-
-Encoding: 522
-U-Bits: 010001100010000101101010101110111110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 522
-
-Encoding: 522
-U-Bits: 010001100010000101110111100011110111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 522
-
-Encoding: 522
-U-Bits: 010001100010000101110000101011001001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 522
-
-Encoding: 523
-U-Bits: 100110000010000101110111100010011100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 523
-
-Encoding: 523
-U-Bits: 100110000010000101101010101111010101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 523
-
-Encoding: 523
-U-Bits: 100110000010000101101101100111101011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 523
-
-Encoding: 524
-U-Bits: 001010000100000101110000101101101111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 524
-
-Encoding: 524
-U-Bits: 001010000100000101101101100000100110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 524
-
-Encoding: 524
-U-Bits: 001010000100000101101010101000011000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 524
-
-Encoding: 525
-U-Bits: 111101100100000101101101100001001101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 525
-
-Encoding: 525
-U-Bits: 111101100100000101110000101100000100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 525
-
-Encoding: 525
-U-Bits: 111101100100000101110111100100111010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 525
-
-Encoding: 526
-U-Bits: 011011111100000101110111111110100110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 526
-
-Encoding: 526
-U-Bits: 011011111100000101101010110011101111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 526
-
-Encoding: 526
-U-Bits: 011011111100000101101101111011010001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 526
-
-Encoding: 527
-U-Bits: 101100011100000101101010110010000100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 527
-
-Encoding: 527
-U-Bits: 101100011100000101110111111111001101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 527
-
-Encoding: 527
-U-Bits: 101100011100000101110000110111110011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 527
-
-Encoding: 528
-U-Bits: 000110111101100101101010101001110011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 528
-
-Encoding: 528
-U-Bits: 000110111101100101110111100100111010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 528
-
-Encoding: 528
-U-Bits: 000110111101100101110000101100000100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 528
-
-Encoding: 529
-U-Bits: 110001011101100101110111100101010001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 529
-
-Encoding: 529
-U-Bits: 110001011101100101101010101000011000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 529
-
-Encoding: 529
-U-Bits: 110001011101100101101101100000100110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 529
-
-Encoding: 52a
-U-Bits: 010111000101100101101101111010111010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 52a
-
-Encoding: 52a
-U-Bits: 010111000101100101110000110111110011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 52a
-
-Encoding: 52a
-U-Bits: 010111000101100101110111111111001101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 52a
-
-Encoding: 52b
-U-Bits: 100000100101100101110000110110011000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 52b
-
-Encoding: 52b
-U-Bits: 100000100101100101101101111011010001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 52b
-
-Encoding: 52b
-U-Bits: 100000100101100101101010110011101111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 52b
-
-Encoding: 52c
-U-Bits: 001100100011100101110111111001101011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 52c
-
-Encoding: 52c
-U-Bits: 001100100011100101101010110100100010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 52c
-
-Encoding: 52c
-U-Bits: 001100100011100101101101111100011100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 52c
-
-Encoding: 52d
-U-Bits: 111011000011100101101010110101001001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 52d
-
-Encoding: 52d
-U-Bits: 111011000011100101110111111000000000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 52d
-
-Encoding: 52d
-U-Bits: 111011000011100101110000110000111110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 52d
-
-Encoding: 52e
-U-Bits: 011101011011100101110000101010100010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 52e
-
-Encoding: 52e
-U-Bits: 011101011011100101101101100111101011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 52e
-
-Encoding: 52e
-U-Bits: 011101011011100101101010101111010101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 52e
-
-Encoding: 52f
-U-Bits: 101010111011100101101101100110000000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 52f
-
-Encoding: 52f
-U-Bits: 101010111011100101110000101011001001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 52f
-
-Encoding: 52f
-U-Bits: 101010111011100101110111100011110111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 52f
-
-Encoding: 530
-U-Bits: 000001110011111101101100001000110101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 530
-
-Encoding: 530
-U-Bits: 000001110011111101110001000101111100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 530
-
-Encoding: 530
-U-Bits: 000001110011111101110110001101000010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 530
-
-Encoding: 531
-U-Bits: 110110010011111101110001000100010111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 531
-
-Encoding: 531
-U-Bits: 110110010011111101101100001001011110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 531
-
-Encoding: 531
-U-Bits: 110110010011111101101011000001100000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 531
-
-Encoding: 532
-U-Bits: 010000001011111101101011011011111100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 532
-
-Encoding: 532
-U-Bits: 010000001011111101110110010110110101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 532
-
-Encoding: 532
-U-Bits: 010000001011111101110001011110001011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 532
-
-Encoding: 533
-U-Bits: 100111101011111101110110010111011110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 533
-
-Encoding: 533
-U-Bits: 100111101011111101101011011010010111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 533
-
-Encoding: 533
-U-Bits: 100111101011111101101100010010101001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 533
-
-Encoding: 534
-U-Bits: 001011101101111101110001011000101101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 534
-
-Encoding: 534
-U-Bits: 001011101101111101101100010101100100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 534
-
-Encoding: 534
-U-Bits: 001011101101111101101011011101011010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 534
-
-Encoding: 535
-U-Bits: 111100001101111101101100010100001111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 535
-
-Encoding: 535
-U-Bits: 111100001101111101110001011001000110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 535
-
-Encoding: 535
-U-Bits: 111100001101111101110110010001111000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 535
-
-Encoding: 536
-U-Bits: 011010010101111101110110001011100100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 536
-
-Encoding: 536
-U-Bits: 011010010101111101101011000110101101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 536
-
-Encoding: 536
-U-Bits: 011010010101111101101100001110010011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 536
-
-Encoding: 537
-U-Bits: 101101110101111101101011000111000110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 537
-
-Encoding: 537
-U-Bits: 101101110101111101110110001010001111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 537
-
-Encoding: 537
-U-Bits: 101101110101111101110001000010110001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 537
-
-Encoding: 538
-U-Bits: 000111010100011101101011011100110001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 538
-
-Encoding: 538
-U-Bits: 000111010100011101110110010001111000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 538
-
-Encoding: 538
-U-Bits: 000111010100011101110001011001000110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 538
-
-Encoding: 539
-U-Bits: 110000110100011101110110010000010011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 539
-
-Encoding: 539
-U-Bits: 110000110100011101101011011101011010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 539
-
-Encoding: 539
-U-Bits: 110000110100011101101100010101100100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 539
-
-Encoding: 53a
-U-Bits: 010110101100011101101100001111111000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 53a
-
-Encoding: 53a
-U-Bits: 010110101100011101110001000010110001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 53a
-
-Encoding: 53a
-U-Bits: 010110101100011101110110001010001111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 53a
-
-Encoding: 53b
-U-Bits: 100001001100011101110001000011011010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 53b
-
-Encoding: 53b
-U-Bits: 100001001100011101101100001110010011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 53b
-
-Encoding: 53b
-U-Bits: 100001001100011101101011000110101101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 53b
-
-Encoding: 53c
-U-Bits: 001101001010011101110110001100101001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 53c
-
-Encoding: 53c
-U-Bits: 001101001010011101101011000001100000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 53c
-
-Encoding: 53c
-U-Bits: 001101001010011101101100001001011110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 53c
-
-Encoding: 53d
-U-Bits: 111010101010011101101011000000001011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 53d
-
-Encoding: 53d
-U-Bits: 111010101010011101110110001101000010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 53d
-
-Encoding: 53d
-U-Bits: 111010101010011101110001000101111100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 53d
-
-Encoding: 53e
-U-Bits: 011100110010011101110001011111100000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 53e
-
-Encoding: 53e
-U-Bits: 011100110010011101101100010010101001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 53e
-
-Encoding: 53e
-U-Bits: 011100110010011101101011011010010111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 53e
-
-Encoding: 53f
-U-Bits: 101011010010011101101100010011000010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 53f
-
-Encoding: 53f
-U-Bits: 101011010010011101110001011110001011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 53f
-
-Encoding: 53f
-U-Bits: 101011010010011101110110010110110101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 53f
-
-Encoding: 540
-U-Bits: 000000000110111100010001000011011010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 540
-
-Encoding: 540
-U-Bits: 000000000110111100001100001110010011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 540
-
-Encoding: 540
-U-Bits: 000000000110111100001011000110101101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 540
-
-Encoding: 541
-U-Bits: 110111100110111100001100001111111000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 541
-
-Encoding: 541
-U-Bits: 110111100110111100010001000010110001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 541
-
-Encoding: 541
-U-Bits: 110111100110111100010110001010001111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 541
-
-Encoding: 542
-U-Bits: 010001111110111100010110010000010011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 542
-
-Encoding: 542
-U-Bits: 010001111110111100001011011101011010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 542
-
-Encoding: 542
-U-Bits: 010001111110111100001100010101100100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 542
-
-Encoding: 543
-U-Bits: 100110011110111100001011011100110001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 543
-
-Encoding: 543
-U-Bits: 100110011110111100010110010001111000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 543
-
-Encoding: 543
-U-Bits: 100110011110111100010001011001000110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 543
-
-Encoding: 544
-U-Bits: 001010011000111100001100010011000010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 544
-
-Encoding: 544
-U-Bits: 001010011000111100010001011110001011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 544
-
-Encoding: 544
-U-Bits: 001010011000111100010110010110110101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 544
-
-Encoding: 545
-U-Bits: 111101111000111100010001011111100000
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 545
-
-Encoding: 545
-U-Bits: 111101111000111100001100010010101001
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 545
-
-Encoding: 545
-U-Bits: 111101111000111100001011011010010111
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 545
-
-Encoding: 546
-U-Bits: 011011100000111100001011000000001011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 546
-
-Encoding: 546
-U-Bits: 011011100000111100010110001101000010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 546
-
-Encoding: 546
-U-Bits: 011011100000111100010001000101111100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 546
-
-Encoding: 547
-U-Bits: 101100000000111100010110001100101001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 547
-
-Encoding: 547
-U-Bits: 101100000000111100001011000001100000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 547
-
-Encoding: 547
-U-Bits: 101100000000111100001100001001011110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 547
-
-Encoding: 548
-U-Bits: 000110100001011100010110010111011110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 548
-
-Encoding: 548
-U-Bits: 000110100001011100001011011010010111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 548
-
-Encoding: 548
-U-Bits: 000110100001011100001100010010101001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 548
-
-Encoding: 549
-U-Bits: 110001000001011100001011011011111100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 549
-
-Encoding: 549
-U-Bits: 110001000001011100010110010110110101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 549
-
-Encoding: 549
-U-Bits: 110001000001011100010001011110001011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 549
-
-Encoding: 54a
-U-Bits: 010111011001011100010001000100010111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 54a
-
-Encoding: 54a
-U-Bits: 010111011001011100001100001001011110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 54a
-
-Encoding: 54a
-U-Bits: 010111011001011100001011000001100000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 54a
-
-Encoding: 54b
-U-Bits: 100000111001011100001100001000110101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 54b
-
-Encoding: 54b
-U-Bits: 100000111001011100010001000101111100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 54b
-
-Encoding: 54b
-U-Bits: 100000111001011100010110001101000010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 54b
-
-Encoding: 54c
-U-Bits: 001100111111011100001011000111000110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 54c
-
-Encoding: 54c
-U-Bits: 001100111111011100010110001010001111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 54c
-
-Encoding: 54c
-U-Bits: 001100111111011100010001000010110001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 54c
-
-Encoding: 54d
-U-Bits: 111011011111011100010110001011100100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 54d
-
-Encoding: 54d
-U-Bits: 111011011111011100001011000110101101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 54d
-
-Encoding: 54d
-U-Bits: 111011011111011100001100001110010011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 54d
-
-Encoding: 54e
-U-Bits: 011101000111011100001100010100001111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 54e
-
-Encoding: 54e
-U-Bits: 011101000111011100010001011001000110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 54e
-
-Encoding: 54e
-U-Bits: 011101000111011100010110010001111000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 54e
-
-Encoding: 54f
-U-Bits: 101010100111011100010001011000101101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 54f
-
-Encoding: 54f
-U-Bits: 101010100111011100001100010101100100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 54f
-
-Encoding: 54f
-U-Bits: 101010100111011100001011011101011010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 54f
-
-Encoding: 550
-U-Bits: 000001101111000100010000110110011000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 550
-
-Encoding: 550
-U-Bits: 000001101111000100001101111011010001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 550
-
-Encoding: 550
-U-Bits: 000001101111000100001010110011101111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 550
-
-Encoding: 551
-U-Bits: 110110001111000100001101111010111010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 551
-
-Encoding: 551
-U-Bits: 110110001111000100010000110111110011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 551
-
-Encoding: 551
-U-Bits: 110110001111000100010111111111001101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 551
-
-Encoding: 552
-U-Bits: 010000010111000100010111100101010001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 552
-
-Encoding: 552
-U-Bits: 010000010111000100001010101000011000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 552
-
-Encoding: 552
-U-Bits: 010000010111000100001101100000100110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 552
-
-Encoding: 553
-U-Bits: 100111110111000100001010101001110011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 553
-
-Encoding: 553
-U-Bits: 100111110111000100010111100100111010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 553
-
-Encoding: 553
-U-Bits: 100111110111000100010000101100000100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 553
-
-Encoding: 554
-U-Bits: 001011110001000100001101100110000000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 554
-
-Encoding: 554
-U-Bits: 001011110001000100010000101011001001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 554
-
-Encoding: 554
-U-Bits: 001011110001000100010111100011110111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 554
-
-Encoding: 555
-U-Bits: 111100010001000100010000101010100010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 555
-
-Encoding: 555
-U-Bits: 111100010001000100001101100111101011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 555
-
-Encoding: 555
-U-Bits: 111100010001000100001010101111010101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 555
-
-Encoding: 556
-U-Bits: 011010001001000100001010110101001001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 556
-
-Encoding: 556
-U-Bits: 011010001001000100010111111000000000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 556
-
-Encoding: 556
-U-Bits: 011010001001000100010000110000111110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 556
-
-Encoding: 557
-U-Bits: 101101101001000100010111111001101011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 557
-
-Encoding: 557
-U-Bits: 101101101001000100001010110100100010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 557
-
-Encoding: 557
-U-Bits: 101101101001000100001101111100011100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 557
-
-Encoding: 558
-U-Bits: 000111001000100100010111100010011100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 558
-
-Encoding: 558
-U-Bits: 000111001000100100001010101111010101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 558
-
-Encoding: 558
-U-Bits: 000111001000100100001101100111101011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 558
-
-Encoding: 559
-U-Bits: 110000101000100100001010101110111110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 559
-
-Encoding: 559
-U-Bits: 110000101000100100010111100011110111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 559
-
-Encoding: 559
-U-Bits: 110000101000100100010000101011001001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 559
-
-Encoding: 55a
-U-Bits: 010110110000100100010000110001010101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 55a
-
-Encoding: 55a
-U-Bits: 010110110000100100001101111100011100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 55a
-
-Encoding: 55a
-U-Bits: 010110110000100100001010110100100010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 55a
-
-Encoding: 55b
-U-Bits: 100001010000100100001101111101110111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 55b
-
-Encoding: 55b
-U-Bits: 100001010000100100010000110000111110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 55b
-
-Encoding: 55b
-U-Bits: 100001010000100100010111111000000000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 55b
-
-Encoding: 55c
-U-Bits: 001101010110100100001010110010000100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 55c
-
-Encoding: 55c
-U-Bits: 001101010110100100010111111111001101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 55c
-
-Encoding: 55c
-U-Bits: 001101010110100100010000110111110011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 55c
-
-Encoding: 55d
-U-Bits: 111010110110100100010111111110100110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 55d
-
-Encoding: 55d
-U-Bits: 111010110110100100001010110011101111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 55d
-
-Encoding: 55d
-U-Bits: 111010110110100100001101111011010001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 55d
-
-Encoding: 55e
-U-Bits: 011100101110100100001101100001001101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 55e
-
-Encoding: 55e
-U-Bits: 011100101110100100010000101100000100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 55e
-
-Encoding: 55e
-U-Bits: 011100101110100100010111100100111010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 55e
-
-Encoding: 55f
-U-Bits: 101011001110100100010000101101101111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 55f
-
-Encoding: 55f
-U-Bits: 101011001110100100001101100000100110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 55f
-
-Encoding: 55f
-U-Bits: 101011001110100100001010101000011000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 55f
-
-Encoding: 560
-U-Bits: 000000011100100010010001011110001011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 560
-
-Encoding: 560
-U-Bits: 000000011100100010001100010011000010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 560
-
-Encoding: 560
-U-Bits: 000000011100100010001011011011111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 560
-
-Encoding: 561
-U-Bits: 110111111100100010001100010010101001
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 561
-
-Encoding: 561
-U-Bits: 110111111100100010010001011111100000
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 561
-
-Encoding: 561
-U-Bits: 110111111100100010010110010111011110
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 561
-
-Encoding: 562
-U-Bits: 010001100100100010010110001101000010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 562
-
-Encoding: 562
-U-Bits: 010001100100100010001011000000001011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 562
-
-Encoding: 562
-U-Bits: 010001100100100010001100001000110101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 562
-
-Encoding: 563
-U-Bits: 100110000100100010001011000001100000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 563
-
-Encoding: 563
-U-Bits: 100110000100100010010110001100101001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 563
-
-Encoding: 563
-U-Bits: 100110000100100010010001000100010111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 563
-
-Encoding: 564
-U-Bits: 001010000010100010001100001110010011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 564
-
-Encoding: 564
-U-Bits: 001010000010100010010001000011011010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 564
-
-Encoding: 564
-U-Bits: 001010000010100010010110001011100100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 564
-
-Encoding: 565
-U-Bits: 111101100010100010010001000010110001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 565
-
-Encoding: 565
-U-Bits: 111101100010100010001100001111111000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 565
-
-Encoding: 565
-U-Bits: 111101100010100010001011000111000110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 565
-
-Encoding: 566
-U-Bits: 011011111010100010001011011101011010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 566
-
-Encoding: 566
-U-Bits: 011011111010100010010110010000010011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 566
-
-Encoding: 566
-U-Bits: 011011111010100010010001011000101101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 566
-
-Encoding: 567
-U-Bits: 101100011010100010010110010001111000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 567
-
-Encoding: 567
-U-Bits: 101100011010100010001011011100110001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 567
-
-Encoding: 567
-U-Bits: 101100011010100010001100010100001111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 567
-
-Encoding: 568
-U-Bits: 000110111011000010010110001010001111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 568
-
-Encoding: 568
-U-Bits: 000110111011000010001011000111000110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 568
-
-Encoding: 568
-U-Bits: 000110111011000010001100001111111000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 568
-
-Encoding: 569
-U-Bits: 110001011011000010001011000110101101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 569
-
-Encoding: 569
-U-Bits: 110001011011000010010110001011100100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 569
-
-Encoding: 569
-U-Bits: 110001011011000010010001000011011010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 569
-
-Encoding: 56a
-U-Bits: 010111000011000010010001011001000110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 56a
-
-Encoding: 56a
-U-Bits: 010111000011000010001100010100001111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 56a
-
-Encoding: 56a
-U-Bits: 010111000011000010001011011100110001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 56a
-
-Encoding: 56b
-U-Bits: 100000100011000010001100010101100100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 56b
-
-Encoding: 56b
-U-Bits: 100000100011000010010001011000101101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 56b
-
-Encoding: 56b
-U-Bits: 100000100011000010010110010000010011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 56b
-
-Encoding: 56c
-U-Bits: 001100100101000010001011011010010111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 56c
-
-Encoding: 56c
-U-Bits: 001100100101000010010110010111011110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 56c
-
-Encoding: 56c
-U-Bits: 001100100101000010010001011111100000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 56c
-
-Encoding: 56d
-U-Bits: 111011000101000010010110010110110101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 56d
-
-Encoding: 56d
-U-Bits: 111011000101000010001011011011111100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 56d
-
-Encoding: 56d
-U-Bits: 111011000101000010001100010011000010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 56d
-
-Encoding: 56e
-U-Bits: 011101011101000010001100001001011110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 56e
-
-Encoding: 56e
-U-Bits: 011101011101000010010001000100010111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 56e
-
-Encoding: 56e
-U-Bits: 011101011101000010010110001100101001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 56e
-
-Encoding: 56f
-U-Bits: 101010111101000010010001000101111100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 56f
-
-Encoding: 56f
-U-Bits: 101010111101000010001100001000110101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 56f
-
-Encoding: 56f
-U-Bits: 101010111101000010001011000000001011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 56f
-
-Encoding: 570
-U-Bits: 000001110101011010010000101011001001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 570
-
-Encoding: 570
-U-Bits: 000001110101011010001101100110000000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 570
-
-Encoding: 570
-U-Bits: 000001110101011010001010101110111110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 570
-
-Encoding: 571
-U-Bits: 110110010101011010001101100111101011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 571
-
-Encoding: 571
-U-Bits: 110110010101011010010000101010100010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 571
-
-Encoding: 571
-U-Bits: 110110010101011010010111100010011100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 571
-
-Encoding: 572
-U-Bits: 010000001101011010010111111000000000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 572
-
-Encoding: 572
-U-Bits: 010000001101011010001010110101001001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 572
-
-Encoding: 572
-U-Bits: 010000001101011010001101111101110111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 572
-
-Encoding: 573
-U-Bits: 100111101101011010001010110100100010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 573
-
-Encoding: 573
-U-Bits: 100111101101011010010111111001101011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 573
-
-Encoding: 573
-U-Bits: 100111101101011010010000110001010101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 573
-
-Encoding: 574
-U-Bits: 001011101011011010001101111011010001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 574
-
-Encoding: 574
-U-Bits: 001011101011011010010000110110011000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 574
-
-Encoding: 574
-U-Bits: 001011101011011010010111111110100110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 574
-
-Encoding: 575
-U-Bits: 111100001011011010010000110111110011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 575
-
-Encoding: 575
-U-Bits: 111100001011011010001101111010111010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 575
-
-Encoding: 575
-U-Bits: 111100001011011010001010110010000100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 575
-
-Encoding: 576
-U-Bits: 011010010011011010001010101000011000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 576
-
-Encoding: 576
-U-Bits: 011010010011011010010111100101010001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 576
-
-Encoding: 576
-U-Bits: 011010010011011010010000101101101111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 576
-
-Encoding: 577
-U-Bits: 101101110011011010010111100100111010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 577
-
-Encoding: 577
-U-Bits: 101101110011011010001010101001110011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 577
-
-Encoding: 577
-U-Bits: 101101110011011010001101100001001101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 577
-
-Encoding: 578
-U-Bits: 000111010010111010010111111111001101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 578
-
-Encoding: 578
-U-Bits: 000111010010111010001010110010000100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 578
-
-Encoding: 578
-U-Bits: 000111010010111010001101111010111010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 578
-
-Encoding: 579
-U-Bits: 110000110010111010001010110011101111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 579
-
-Encoding: 579
-U-Bits: 110000110010111010010111111110100110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 579
-
-Encoding: 579
-U-Bits: 110000110010111010010000110110011000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 579
-
-Encoding: 57a
-U-Bits: 010110101010111010010000101100000100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 57a
-
-Encoding: 57a
-U-Bits: 010110101010111010001101100001001101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 57a
-
-Encoding: 57a
-U-Bits: 010110101010111010001010101001110011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 57a
-
-Encoding: 57b
-U-Bits: 100001001010111010001101100000100110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 57b
-
-Encoding: 57b
-U-Bits: 100001001010111010010000101101101111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 57b
-
-Encoding: 57b
-U-Bits: 100001001010111010010111100101010001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 57b
-
-Encoding: 57c
-U-Bits: 001101001100111010001010101111010101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 57c
-
-Encoding: 57c
-U-Bits: 001101001100111010010111100010011100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 57c
-
-Encoding: 57c
-U-Bits: 001101001100111010010000101010100010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 57c
-
-Encoding: 57d
-U-Bits: 111010101100111010010111100011110111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 57d
-
-Encoding: 57d
-U-Bits: 111010101100111010001010101110111110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 57d
-
-Encoding: 57d
-U-Bits: 111010101100111010001101100110000000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 57d
-
-Encoding: 57e
-U-Bits: 011100110100111010001101111100011100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 57e
-
-Encoding: 57e
-U-Bits: 011100110100111010010000110001010101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 57e
-
-Encoding: 57e
-U-Bits: 011100110100111010010111111001101011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 57e
-
-Encoding: 57f
-U-Bits: 101011010100111010010000110000111110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 57f
-
-Encoding: 57f
-U-Bits: 101011010100111010001101111101110111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 57f
-
-Encoding: 57f
-U-Bits: 101011010100111010001010110101001001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 57f
-
-Encoding: 580
-U-Bits: 000000000001110010010010101000011000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 580
-
-Encoding: 580
-U-Bits: 000000000001110010001111100101010001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 580
-
-Encoding: 580
-U-Bits: 000000000001110010001000101101101111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 580
-
-Encoding: 581
-U-Bits: 110111100001110010001111100100111010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 581
-
-Encoding: 581
-U-Bits: 110111100001110010010010101001110011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 581
-
-Encoding: 581
-U-Bits: 110111100001110010010101100001001101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 581
-
-Encoding: 582
-U-Bits: 010001111001110010010101111011010001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 582
-
-Encoding: 582
-U-Bits: 010001111001110010001000110110011000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 582
-
-Encoding: 582
-U-Bits: 010001111001110010001111111110100110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 582
-
-Encoding: 583
-U-Bits: 100110011001110010001000110111110011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 583
-
-Encoding: 583
-U-Bits: 100110011001110010010101111010111010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 583
-
-Encoding: 583
-U-Bits: 100110011001110010010010110010000100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 583
-
-Encoding: 584
-U-Bits: 001010011111110010001111111000000000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 584
-
-Encoding: 584
-U-Bits: 001010011111110010010010110101001001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 584
-
-Encoding: 584
-U-Bits: 001010011111110010010101111101110111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 584
-
-Encoding: 585
-U-Bits: 111101111111110010010010110100100010
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 585
-
-Encoding: 585
-U-Bits: 111101111111110010001111111001101011
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 585
-
-Encoding: 585
-U-Bits: 111101111111110010001000110001010101
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 585
-
-Encoding: 586
-U-Bits: 011011100111110010001000101011001001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 586
-
-Encoding: 586
-U-Bits: 011011100111110010010101100110000000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 586
-
-Encoding: 586
-U-Bits: 011011100111110010010010101110111110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 586
-
-Encoding: 587
-U-Bits: 101100000111110010010101100111101011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 587
-
-Encoding: 587
-U-Bits: 101100000111110010001000101010100010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 587
-
-Encoding: 587
-U-Bits: 101100000111110010001111100010011100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 587
-
-Encoding: 588
-U-Bits: 000110100110010010010101111100011100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 588
-
-Encoding: 588
-U-Bits: 000110100110010010001000110001010101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 588
-
-Encoding: 588
-U-Bits: 000110100110010010001111111001101011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 588
-
-Encoding: 589
-U-Bits: 110001000110010010001000110000111110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 589
-
-Encoding: 589
-U-Bits: 110001000110010010010101111101110111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 589
-
-Encoding: 589
-U-Bits: 110001000110010010010010110101001001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 589
-
-Encoding: 58a
-U-Bits: 010111011110010010010010101111010101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 58a
-
-Encoding: 58a
-U-Bits: 010111011110010010001111100010011100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 58a
-
-Encoding: 58a
-U-Bits: 010111011110010010001000101010100010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 58a
-
-Encoding: 58b
-U-Bits: 100000111110010010001111100011110111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 58b
-
-Encoding: 58b
-U-Bits: 100000111110010010010010101110111110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 58b
-
-Encoding: 58b
-U-Bits: 100000111110010010010101100110000000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 58b
-
-Encoding: 58c
-U-Bits: 001100111000010010001000101100000100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 58c
-
-Encoding: 58c
-U-Bits: 001100111000010010010101100001001101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 58c
-
-Encoding: 58c
-U-Bits: 001100111000010010010010101001110011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 58c
-
-Encoding: 58d
-U-Bits: 111011011000010010010101100000100110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 58d
-
-Encoding: 58d
-U-Bits: 111011011000010010001000101101101111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 58d
-
-Encoding: 58d
-U-Bits: 111011011000010010001111100101010001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 58d
-
-Encoding: 58e
-U-Bits: 011101000000010010001111111111001101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 58e
-
-Encoding: 58e
-U-Bits: 011101000000010010010010110010000100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 58e
-
-Encoding: 58e
-U-Bits: 011101000000010010010101111010111010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 58e
-
-Encoding: 58f
-U-Bits: 101010100000010010010010110011101111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 58f
-
-Encoding: 58f
-U-Bits: 101010100000010010001111111110100110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 58f
-
-Encoding: 58f
-U-Bits: 101010100000010010001000110110011000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 58f
-
-Encoding: 590
-U-Bits: 000001101000001010010011011101011010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 590
-
-Encoding: 590
-U-Bits: 000001101000001010001110010000010011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 590
-
-Encoding: 590
-U-Bits: 000001101000001010001001011000101101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 590
-
-Encoding: 591
-U-Bits: 110110001000001010001110010001111000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 591
-
-Encoding: 591
-U-Bits: 110110001000001010010011011100110001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 591
-
-Encoding: 591
-U-Bits: 110110001000001010010100010100001111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 591
-
-Encoding: 592
-U-Bits: 010000010000001010010100001110010011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 592
-
-Encoding: 592
-U-Bits: 010000010000001010001001000011011010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 592
-
-Encoding: 592
-U-Bits: 010000010000001010001110001011100100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 592
-
-Encoding: 593
-U-Bits: 100111110000001010001001000010110001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 593
-
-Encoding: 593
-U-Bits: 100111110000001010010100001111111000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 593
-
-Encoding: 593
-U-Bits: 100111110000001010010011000111000110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 593
-
-Encoding: 594
-U-Bits: 001011110110001010001110001101000010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 594
-
-Encoding: 594
-U-Bits: 001011110110001010010011000000001011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 594
-
-Encoding: 594
-U-Bits: 001011110110001010010100001000110101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 594
-
-Encoding: 595
-U-Bits: 111100010110001010010011000001100000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 595
-
-Encoding: 595
-U-Bits: 111100010110001010001110001100101001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 595
-
-Encoding: 595
-U-Bits: 111100010110001010001001000100010111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 595
-
-Encoding: 596
-U-Bits: 011010001110001010001001011110001011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 596
-
-Encoding: 596
-U-Bits: 011010001110001010010100010011000010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 596
-
-Encoding: 596
-U-Bits: 011010001110001010010011011011111100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 596
-
-Encoding: 597
-U-Bits: 101101101110001010010100010010101001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 597
-
-Encoding: 597
-U-Bits: 101101101110001010001001011111100000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 597
-
-Encoding: 597
-U-Bits: 101101101110001010001110010111011110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 597
-
-Encoding: 598
-U-Bits: 000111001111101010010100001001011110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 598
-
-Encoding: 598
-U-Bits: 000111001111101010001001000100010111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 598
-
-Encoding: 598
-U-Bits: 000111001111101010001110001100101001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 598
-
-Encoding: 599
-U-Bits: 110000101111101010001001000101111100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 599
-
-Encoding: 599
-U-Bits: 110000101111101010010100001000110101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 599
-
-Encoding: 599
-U-Bits: 110000101111101010010011000000001011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 599
-
-Encoding: 59a
-U-Bits: 010110110111101010010011011010010111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 59a
-
-Encoding: 59a
-U-Bits: 010110110111101010001110010111011110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 59a
-
-Encoding: 59a
-U-Bits: 010110110111101010001001011111100000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 59a
-
-Encoding: 59b
-U-Bits: 100001010111101010001110010110110101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 59b
-
-Encoding: 59b
-U-Bits: 100001010111101010010011011011111100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 59b
-
-Encoding: 59b
-U-Bits: 100001010111101010010100010011000010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 59b
-
-Encoding: 59c
-U-Bits: 001101010001101010001001011001000110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 59c
-
-Encoding: 59c
-U-Bits: 001101010001101010010100010100001111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 59c
-
-Encoding: 59c
-U-Bits: 001101010001101010010011011100110001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 59c
-
-Encoding: 59d
-U-Bits: 111010110001101010010100010101100100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 59d
-
-Encoding: 59d
-U-Bits: 111010110001101010001001011000101101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 59d
-
-Encoding: 59d
-U-Bits: 111010110001101010001110010000010011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 59d
-
-Encoding: 59e
-U-Bits: 011100101001101010001110001010001111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 59e
-
-Encoding: 59e
-U-Bits: 011100101001101010010011000111000110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 59e
-
-Encoding: 59e
-U-Bits: 011100101001101010010100001111111000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 59e
-
-Encoding: 59f
-U-Bits: 101011001001101010010011000110101101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 59f
-
-Encoding: 59f
-U-Bits: 101011001001101010001110001011100100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 59f
-
-Encoding: 59f
-U-Bits: 101011001001101010001001000011011010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 59f
-
-Encoding: 5a0
-U-Bits: 000000011011101100010010110101001001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 5a0
-
-Encoding: 5a0
-U-Bits: 000000011011101100001111111000000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5a0
-
-Encoding: 5a0
-U-Bits: 000000011011101100001000110000111110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 5a0
-
-Encoding: 5a1
-U-Bits: 110111111011101100001111111001101011
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 5a1
-
-Encoding: 5a1
-U-Bits: 110111111011101100010010110100100010
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 5a1
-
-Encoding: 5a1
-U-Bits: 110111111011101100010101111100011100
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 5a1
-
-Encoding: 5a2
-U-Bits: 010001100011101100010101100110000000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5a2
-
-Encoding: 5a2
-U-Bits: 010001100011101100001000101011001001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 5a2
-
-Encoding: 5a2
-U-Bits: 010001100011101100001111100011110111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 5a2
-
-Encoding: 5a3
-U-Bits: 100110000011101100001000101010100010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 5a3
-
-Encoding: 5a3
-U-Bits: 100110000011101100010101100111101011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 5a3
-
-Encoding: 5a3
-U-Bits: 100110000011101100010010101111010101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 5a3
-
-Encoding: 5a4
-U-Bits: 001010000101101100001111100101010001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 5a4
-
-Encoding: 5a4
-U-Bits: 001010000101101100010010101000011000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 5a4
-
-Encoding: 5a4
-U-Bits: 001010000101101100010101100000100110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 5a4
-
-Encoding: 5a5
-U-Bits: 111101100101101100010010101001110011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 5a5
-
-Encoding: 5a5
-U-Bits: 111101100101101100001111100100111010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 5a5
-
-Encoding: 5a5
-U-Bits: 111101100101101100001000101100000100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 5a5
-
-Encoding: 5a6
-U-Bits: 011011111101101100001000110110011000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 5a6
-
-Encoding: 5a6
-U-Bits: 011011111101101100010101111011010001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 5a6
-
-Encoding: 5a6
-U-Bits: 011011111101101100010010110011101111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 5a6
-
-Encoding: 5a7
-U-Bits: 101100011101101100010101111010111010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 5a7
-
-Encoding: 5a7
-U-Bits: 101100011101101100001000110111110011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 5a7
-
-Encoding: 5a7
-U-Bits: 101100011101101100001111111111001101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 5a7
-
-Encoding: 5a8
-U-Bits: 000110111100001100010101100001001101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 5a8
-
-Encoding: 5a8
-U-Bits: 000110111100001100001000101100000100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 5a8
-
-Encoding: 5a8
-U-Bits: 000110111100001100001111100100111010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 5a8
-
-Encoding: 5a9
-U-Bits: 110001011100001100001000101101101111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 5a9
-
-Encoding: 5a9
-U-Bits: 110001011100001100010101100000100110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 5a9
-
-Encoding: 5a9
-U-Bits: 110001011100001100010010101000011000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 5a9
-
-Encoding: 5aa
-U-Bits: 010111000100001100010010110010000100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 5aa
-
-Encoding: 5aa
-U-Bits: 010111000100001100001111111111001101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 5aa
-
-Encoding: 5aa
-U-Bits: 010111000100001100001000110111110011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 5aa
-
-Encoding: 5ab
-U-Bits: 100000100100001100001111111110100110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 5ab
-
-Encoding: 5ab
-U-Bits: 100000100100001100010010110011101111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 5ab
-
-Encoding: 5ab
-U-Bits: 100000100100001100010101111011010001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 5ab
-
-Encoding: 5ac
-U-Bits: 001100100010001100001000110001010101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 5ac
-
-Encoding: 5ac
-U-Bits: 001100100010001100010101111100011100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 5ac
-
-Encoding: 5ac
-U-Bits: 001100100010001100010010110100100010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 5ac
-
-Encoding: 5ad
-U-Bits: 111011000010001100010101111101110111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 5ad
-
-Encoding: 5ad
-U-Bits: 111011000010001100001000110000111110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 5ad
-
-Encoding: 5ad
-U-Bits: 111011000010001100001111111000000000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5ad
-
-Encoding: 5ae
-U-Bits: 011101011010001100001111100010011100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 5ae
-
-Encoding: 5ae
-U-Bits: 011101011010001100010010101111010101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 5ae
-
-Encoding: 5ae
-U-Bits: 011101011010001100010101100111101011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 5ae
-
-Encoding: 5af
-U-Bits: 101010111010001100010010101110111110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 5af
-
-Encoding: 5af
-U-Bits: 101010111010001100001111100011110111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 5af
-
-Encoding: 5af
-U-Bits: 101010111010001100001000101011001001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 5af
-
-Encoding: 5b0
-U-Bits: 000001110010010100010011000000001011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 5b0
-
-Encoding: 5b0
-U-Bits: 000001110010010100001110001101000010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 5b0
-
-Encoding: 5b0
-U-Bits: 000001110010010100001001000101111100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 5b0
-
-Encoding: 5b1
-U-Bits: 110110010010010100001110001100101001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 5b1
-
-Encoding: 5b1
-U-Bits: 110110010010010100010011000001100000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 5b1
-
-Encoding: 5b1
-U-Bits: 110110010010010100010100001001011110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 5b1
-
-Encoding: 5b2
-U-Bits: 010000001010010100010100010011000010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 5b2
-
-Encoding: 5b2
-U-Bits: 010000001010010100001001011110001011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 5b2
-
-Encoding: 5b2
-U-Bits: 010000001010010100001110010110110101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 5b2
-
-Encoding: 5b3
-U-Bits: 100111101010010100001001011111100000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 5b3
-
-Encoding: 5b3
-U-Bits: 100111101010010100010100010010101001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 5b3
-
-Encoding: 5b3
-U-Bits: 100111101010010100010011011010010111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 5b3
-
-Encoding: 5b4
-U-Bits: 001011101100010100001110010000010011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 5b4
-
-Encoding: 5b4
-U-Bits: 001011101100010100010011011101011010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 5b4
-
-Encoding: 5b4
-U-Bits: 001011101100010100010100010101100100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 5b4
-
-Encoding: 5b5
-U-Bits: 111100001100010100010011011100110001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 5b5
-
-Encoding: 5b5
-U-Bits: 111100001100010100001110010001111000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 5b5
-
-Encoding: 5b5
-U-Bits: 111100001100010100001001011001000110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 5b5
-
-Encoding: 5b6
-U-Bits: 011010010100010100001001000011011010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 5b6
-
-Encoding: 5b6
-U-Bits: 011010010100010100010100001110010011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 5b6
-
-Encoding: 5b6
-U-Bits: 011010010100010100010011000110101101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 5b6
-
-Encoding: 5b7
-U-Bits: 101101110100010100010100001111111000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 5b7
-
-Encoding: 5b7
-U-Bits: 101101110100010100001001000010110001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 5b7
-
-Encoding: 5b7
-U-Bits: 101101110100010100001110001010001111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 5b7
-
-Encoding: 5b8
-U-Bits: 000111010101110100010100010100001111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 5b8
-
-Encoding: 5b8
-U-Bits: 000111010101110100001001011001000110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 5b8
-
-Encoding: 5b8
-U-Bits: 000111010101110100001110010001111000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 5b8
-
-Encoding: 5b9
-U-Bits: 110000110101110100001001011000101101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 5b9
-
-Encoding: 5b9
-U-Bits: 110000110101110100010100010101100100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 5b9
-
-Encoding: 5b9
-U-Bits: 110000110101110100010011011101011010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 5b9
-
-Encoding: 5ba
-U-Bits: 010110101101110100010011000111000110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 5ba
-
-Encoding: 5ba
-U-Bits: 010110101101110100001110001010001111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 5ba
-
-Encoding: 5ba
-U-Bits: 010110101101110100001001000010110001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 5ba
-
-Encoding: 5bb
-U-Bits: 100001001101110100001110001011100100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 5bb
-
-Encoding: 5bb
-U-Bits: 100001001101110100010011000110101101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 5bb
-
-Encoding: 5bb
-U-Bits: 100001001101110100010100001110010011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 5bb
-
-Encoding: 5bc
-U-Bits: 001101001011110100001001000100010111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 5bc
-
-Encoding: 5bc
-U-Bits: 001101001011110100010100001001011110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 5bc
-
-Encoding: 5bc
-U-Bits: 001101001011110100010011000001100000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 5bc
-
-Encoding: 5bd
-U-Bits: 111010101011110100010100001000110101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 5bd
-
-Encoding: 5bd
-U-Bits: 111010101011110100001001000101111100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 5bd
-
-Encoding: 5bd
-U-Bits: 111010101011110100001110001101000010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 5bd
-
-Encoding: 5be
-U-Bits: 011100110011110100001110010111011110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 5be
-
-Encoding: 5be
-U-Bits: 011100110011110100010011011010010111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 5be
-
-Encoding: 5be
-U-Bits: 011100110011110100010100010010101001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 5be
-
-Encoding: 5bf
-U-Bits: 101011010011110100010011011011111100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 5bf
-
-Encoding: 5bf
-U-Bits: 101011010011110100001110010110110101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 5bf
-
-Encoding: 5bf
-U-Bits: 101011010011110100001001011110001011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 5bf
-
-Encoding: 5c0
-U-Bits: 000000000111010101101110001011100100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 5c0
-
-Encoding: 5c0
-U-Bits: 000000000111010101110011000110101101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 5c0
-
-Encoding: 5c0
-U-Bits: 000000000111010101110100001110010011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 5c0
-
-Encoding: 5c1
-U-Bits: 110111100111010101110011000111000110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 5c1
-
-Encoding: 5c1
-U-Bits: 110111100111010101101110001010001111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 5c1
-
-Encoding: 5c1
-U-Bits: 110111100111010101101001000010110001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 5c1
-
-Encoding: 5c2
-U-Bits: 010001111111010101101001011000101101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 5c2
-
-Encoding: 5c2
-U-Bits: 010001111111010101110100010101100100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 5c2
-
-Encoding: 5c2
-U-Bits: 010001111111010101110011011101011010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 5c2
-
-Encoding: 5c3
-U-Bits: 100110011111010101110100010100001111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 5c3
-
-Encoding: 5c3
-U-Bits: 100110011111010101101001011001000110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 5c3
-
-Encoding: 5c3
-U-Bits: 100110011111010101101110010001111000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 5c3
-
-Encoding: 5c4
-U-Bits: 001010011001010101110011011011111100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 5c4
-
-Encoding: 5c4
-U-Bits: 001010011001010101101110010110110101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 5c4
-
-Encoding: 5c4
-U-Bits: 001010011001010101101001011110001011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 5c4
-
-Encoding: 5c5
-U-Bits: 111101111001010101101110010111011110
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 5c5
-
-Encoding: 5c5
-U-Bits: 111101111001010101110011011010010111
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 5c5
-
-Encoding: 5c5
-U-Bits: 111101111001010101110100010010101001
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 5c5
-
-Encoding: 5c6
-U-Bits: 011011100001010101110100001000110101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 5c6
-
-Encoding: 5c6
-U-Bits: 011011100001010101101001000101111100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 5c6
-
-Encoding: 5c6
-U-Bits: 011011100001010101101110001101000010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 5c6
-
-Encoding: 5c7
-U-Bits: 101100000001010101101001000100010111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 5c7
-
-Encoding: 5c7
-U-Bits: 101100000001010101110100001001011110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 5c7
-
-Encoding: 5c7
-U-Bits: 101100000001010101110011000001100000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 5c7
-
-Encoding: 5c8
-U-Bits: 000110100000110101101001011111100000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 5c8
-
-Encoding: 5c8
-U-Bits: 000110100000110101110100010010101001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 5c8
-
-Encoding: 5c8
-U-Bits: 000110100000110101110011011010010111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 5c8
-
-Encoding: 5c9
-U-Bits: 110001000000110101110100010011000010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 5c9
-
-Encoding: 5c9
-U-Bits: 110001000000110101101001011110001011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 5c9
-
-Encoding: 5c9
-U-Bits: 110001000000110101101110010110110101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 5c9
-
-Encoding: 5ca
-U-Bits: 010111011000110101101110001100101001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 5ca
-
-Encoding: 5ca
-U-Bits: 010111011000110101110011000001100000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 5ca
-
-Encoding: 5ca
-U-Bits: 010111011000110101110100001001011110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 5ca
-
-Encoding: 5cb
-U-Bits: 100000111000110101110011000000001011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 5cb
-
-Encoding: 5cb
-U-Bits: 100000111000110101101110001101000010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 5cb
-
-Encoding: 5cb
-U-Bits: 100000111000110101101001000101111100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 5cb
-
-Encoding: 5cc
-U-Bits: 001100111110110101110100001111111000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 5cc
-
-Encoding: 5cc
-U-Bits: 001100111110110101101001000010110001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 5cc
-
-Encoding: 5cc
-U-Bits: 001100111110110101101110001010001111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 5cc
-
-Encoding: 5cd
-U-Bits: 111011011110110101101001000011011010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 5cd
-
-Encoding: 5cd
-U-Bits: 111011011110110101110100001110010011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 5cd
-
-Encoding: 5cd
-U-Bits: 111011011110110101110011000110101101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 5cd
-
-Encoding: 5ce
-U-Bits: 011101000110110101110011011100110001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 5ce
-
-Encoding: 5ce
-U-Bits: 011101000110110101101110010001111000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 5ce
-
-Encoding: 5ce
-U-Bits: 011101000110110101101001011001000110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 5ce
-
-Encoding: 5cf
-U-Bits: 101010100110110101101110010000010011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 5cf
-
-Encoding: 5cf
-U-Bits: 101010100110110101110011011101011010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 5cf
-
-Encoding: 5cf
-U-Bits: 101010100110110101110100010101100100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 5cf
-
-Encoding: 5d0
-U-Bits: 000001101110101101101111111110100110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 5d0
-
-Encoding: 5d0
-U-Bits: 000001101110101101110010110011101111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 5d0
-
-Encoding: 5d0
-U-Bits: 000001101110101101110101111011010001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 5d0
-
-Encoding: 5d1
-U-Bits: 110110001110101101110010110010000100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 5d1
-
-Encoding: 5d1
-U-Bits: 110110001110101101101111111111001101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 5d1
-
-Encoding: 5d1
-U-Bits: 110110001110101101101000110111110011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 5d1
-
-Encoding: 5d2
-U-Bits: 010000010110101101101000101101101111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 5d2
-
-Encoding: 5d2
-U-Bits: 010000010110101101110101100000100110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 5d2
-
-Encoding: 5d2
-U-Bits: 010000010110101101110010101000011000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 5d2
-
-Encoding: 5d3
-U-Bits: 100111110110101101110101100001001101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 5d3
-
-Encoding: 5d3
-U-Bits: 100111110110101101101000101100000100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 5d3
-
-Encoding: 5d3
-U-Bits: 100111110110101101101111100100111010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 5d3
-
-Encoding: 5d4
-U-Bits: 001011110000101101110010101110111110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 5d4
-
-Encoding: 5d4
-U-Bits: 001011110000101101101111100011110111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 5d4
-
-Encoding: 5d4
-U-Bits: 001011110000101101101000101011001001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 5d4
-
-Encoding: 5d5
-U-Bits: 111100010000101101101111100010011100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 5d5
-
-Encoding: 5d5
-U-Bits: 111100010000101101110010101111010101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 5d5
-
-Encoding: 5d5
-U-Bits: 111100010000101101110101100111101011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 5d5
-
-Encoding: 5d6
-U-Bits: 011010001000101101110101111101110111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 5d6
-
-Encoding: 5d6
-U-Bits: 011010001000101101101000110000111110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 5d6
-
-Encoding: 5d6
-U-Bits: 011010001000101101101111111000000000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5d6
-
-Encoding: 5d7
-U-Bits: 101101101000101101101000110001010101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 5d7
-
-Encoding: 5d7
-U-Bits: 101101101000101101110101111100011100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 5d7
-
-Encoding: 5d7
-U-Bits: 101101101000101101110010110100100010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 5d7
-
-Encoding: 5d8
-U-Bits: 000111001001001101101000101010100010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 5d8
-
-Encoding: 5d8
-U-Bits: 000111001001001101110101100111101011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 5d8
-
-Encoding: 5d8
-U-Bits: 000111001001001101110010101111010101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 5d8
-
-Encoding: 5d9
-U-Bits: 110000101001001101110101100110000000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5d9
-
-Encoding: 5d9
-U-Bits: 110000101001001101101000101011001001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 5d9
-
-Encoding: 5d9
-U-Bits: 110000101001001101101111100011110111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 5d9
-
-Encoding: 5da
-U-Bits: 010110110001001101101111111001101011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 5da
-
-Encoding: 5da
-U-Bits: 010110110001001101110010110100100010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 5da
-
-Encoding: 5da
-U-Bits: 010110110001001101110101111100011100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 5da
-
-Encoding: 5db
-U-Bits: 100001010001001101110010110101001001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 5db
-
-Encoding: 5db
-U-Bits: 100001010001001101101111111000000000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5db
-
-Encoding: 5db
-U-Bits: 100001010001001101101000110000111110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 5db
-
-Encoding: 5dc
-U-Bits: 001101010111001101110101111010111010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 5dc
-
-Encoding: 5dc
-U-Bits: 001101010111001101101000110111110011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 5dc
-
-Encoding: 5dc
-U-Bits: 001101010111001101101111111111001101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 5dc
-
-Encoding: 5dd
-U-Bits: 111010110111001101101000110110011000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 5dd
-
-Encoding: 5dd
-U-Bits: 111010110111001101110101111011010001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 5dd
-
-Encoding: 5dd
-U-Bits: 111010110111001101110010110011101111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 5dd
-
-Encoding: 5de
-U-Bits: 011100101111001101110010101001110011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 5de
-
-Encoding: 5de
-U-Bits: 011100101111001101101111100100111010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 5de
-
-Encoding: 5de
-U-Bits: 011100101111001101101000101100000100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 5de
-
-Encoding: 5df
-U-Bits: 101011001111001101101111100101010001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 5df
-
-Encoding: 5df
-U-Bits: 101011001111001101110010101000011000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 5df
-
-Encoding: 5df
-U-Bits: 101011001111001101110101100000100110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 5df
-
-Encoding: 5e0
-U-Bits: 000000011101001011101110010110110101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 5e0
-
-Encoding: 5e0
-U-Bits: 000000011101001011110011011011111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 5e0
-
-Encoding: 5e0
-U-Bits: 000000011101001011110100010011000010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 5e0
-
-Encoding: 5e1
-U-Bits: 110111111101001011110011011010010111
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 5e1
-
-Encoding: 5e1
-U-Bits: 110111111101001011101110010111011110
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 5e1
-
-Encoding: 5e1
-U-Bits: 110111111101001011101001011111100000
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 5e1
-
-Encoding: 5e2
-U-Bits: 010001100101001011101001000101111100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 5e2
-
-Encoding: 5e2
-U-Bits: 010001100101001011110100001000110101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 5e2
-
-Encoding: 5e2
-U-Bits: 010001100101001011110011000000001011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 5e2
-
-Encoding: 5e3
-U-Bits: 100110000101001011110100001001011110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 5e3
-
-Encoding: 5e3
-U-Bits: 100110000101001011101001000100010111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 5e3
-
-Encoding: 5e3
-U-Bits: 100110000101001011101110001100101001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 5e3
-
-Encoding: 5e4
-U-Bits: 001010000011001011110011000110101101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 5e4
-
-Encoding: 5e4
-U-Bits: 001010000011001011101110001011100100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 5e4
-
-Encoding: 5e4
-U-Bits: 001010000011001011101001000011011010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 5e4
-
-Encoding: 5e5
-U-Bits: 111101100011001011101110001010001111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 5e5
-
-Encoding: 5e5
-U-Bits: 111101100011001011110011000111000110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 5e5
-
-Encoding: 5e5
-U-Bits: 111101100011001011110100001111111000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 5e5
-
-Encoding: 5e6
-U-Bits: 011011111011001011110100010101100100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 5e6
-
-Encoding: 5e6
-U-Bits: 011011111011001011101001011000101101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 5e6
-
-Encoding: 5e6
-U-Bits: 011011111011001011101110010000010011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 5e6
-
-Encoding: 5e7
-U-Bits: 101100011011001011101001011001000110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 5e7
-
-Encoding: 5e7
-U-Bits: 101100011011001011110100010100001111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 5e7
-
-Encoding: 5e7
-U-Bits: 101100011011001011110011011100110001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 5e7
-
-Encoding: 5e8
-U-Bits: 000110111010101011101001000010110001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 5e8
-
-Encoding: 5e8
-U-Bits: 000110111010101011110100001111111000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 5e8
-
-Encoding: 5e8
-U-Bits: 000110111010101011110011000111000110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 5e8
-
-Encoding: 5e9
-U-Bits: 110001011010101011110100001110010011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 5e9
-
-Encoding: 5e9
-U-Bits: 110001011010101011101001000011011010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 5e9
-
-Encoding: 5e9
-U-Bits: 110001011010101011101110001011100100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 5e9
-
-Encoding: 5ea
-U-Bits: 010111000010101011101110010001111000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 5ea
-
-Encoding: 5ea
-U-Bits: 010111000010101011110011011100110001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 5ea
-
-Encoding: 5ea
-U-Bits: 010111000010101011110100010100001111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 5ea
-
-Encoding: 5eb
-U-Bits: 100000100010101011110011011101011010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 5eb
-
-Encoding: 5eb
-U-Bits: 100000100010101011101110010000010011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 5eb
-
-Encoding: 5eb
-U-Bits: 100000100010101011101001011000101101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 5eb
-
-Encoding: 5ec
-U-Bits: 001100100100101011110100010010101001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 5ec
-
-Encoding: 5ec
-U-Bits: 001100100100101011101001011111100000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 5ec
-
-Encoding: 5ec
-U-Bits: 001100100100101011101110010111011110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 5ec
-
-Encoding: 5ed
-U-Bits: 111011000100101011101001011110001011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 5ed
-
-Encoding: 5ed
-U-Bits: 111011000100101011110100010011000010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 5ed
-
-Encoding: 5ed
-U-Bits: 111011000100101011110011011011111100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 5ed
-
-Encoding: 5ee
-U-Bits: 011101011100101011110011000001100000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 5ee
-
-Encoding: 5ee
-U-Bits: 011101011100101011101110001100101001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 5ee
-
-Encoding: 5ee
-U-Bits: 011101011100101011101001000100010111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 5ee
-
-Encoding: 5ef
-U-Bits: 101010111100101011101110001101000010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 5ef
-
-Encoding: 5ef
-U-Bits: 101010111100101011110011000000001011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 5ef
-
-Encoding: 5ef
-U-Bits: 101010111100101011110100001000110101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 5ef
-
-Encoding: 5f0
-U-Bits: 000001110100110011101111100011110111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 5f0
-
-Encoding: 5f0
-U-Bits: 000001110100110011110010101110111110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 5f0
-
-Encoding: 5f0
-U-Bits: 000001110100110011110101100110000000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5f0
-
-Encoding: 5f1
-U-Bits: 110110010100110011110010101111010101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 5f1
-
-Encoding: 5f1
-U-Bits: 110110010100110011101111100010011100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 5f1
-
-Encoding: 5f1
-U-Bits: 110110010100110011101000101010100010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 5f1
-
-Encoding: 5f2
-U-Bits: 010000001100110011101000110000111110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 5f2
-
-Encoding: 5f2
-U-Bits: 010000001100110011110101111101110111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 5f2
-
-Encoding: 5f2
-U-Bits: 010000001100110011110010110101001001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 5f2
-
-Encoding: 5f3
-U-Bits: 100111101100110011110101111100011100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 5f3
-
-Encoding: 5f3
-U-Bits: 100111101100110011101000110001010101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 5f3
-
-Encoding: 5f3
-U-Bits: 100111101100110011101111111001101011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 5f3
-
-Encoding: 5f4
-U-Bits: 001011101010110011110010110011101111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 5f4
-
-Encoding: 5f4
-U-Bits: 001011101010110011101111111110100110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 5f4
-
-Encoding: 5f4
-U-Bits: 001011101010110011101000110110011000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 5f4
-
-Encoding: 5f5
-U-Bits: 111100001010110011101111111111001101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 5f5
-
-Encoding: 5f5
-U-Bits: 111100001010110011110010110010000100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 5f5
-
-Encoding: 5f5
-U-Bits: 111100001010110011110101111010111010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 5f5
-
-Encoding: 5f6
-U-Bits: 011010010010110011110101100000100110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 5f6
-
-Encoding: 5f6
-U-Bits: 011010010010110011101000101101101111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 5f6
-
-Encoding: 5f6
-U-Bits: 011010010010110011101111100101010001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 5f6
-
-Encoding: 5f7
-U-Bits: 101101110010110011101000101100000100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 5f7
-
-Encoding: 5f7
-U-Bits: 101101110010110011110101100001001101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 5f7
-
-Encoding: 5f7
-U-Bits: 101101110010110011110010101001110011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 5f7
-
-Encoding: 5f8
-U-Bits: 000111010011010011101000110111110011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 5f8
-
-Encoding: 5f8
-U-Bits: 000111010011010011110101111010111010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 5f8
-
-Encoding: 5f8
-U-Bits: 000111010011010011110010110010000100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 5f8
-
-Encoding: 5f9
-U-Bits: 110000110011010011110101111011010001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 5f9
-
-Encoding: 5f9
-U-Bits: 110000110011010011101000110110011000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 5f9
-
-Encoding: 5f9
-U-Bits: 110000110011010011101111111110100110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 5f9
-
-Encoding: 5fa
-U-Bits: 010110101011010011101111100100111010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 5fa
-
-Encoding: 5fa
-U-Bits: 010110101011010011110010101001110011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 5fa
-
-Encoding: 5fa
-U-Bits: 010110101011010011110101100001001101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 5fa
-
-Encoding: 5fb
-U-Bits: 100001001011010011110010101000011000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 5fb
-
-Encoding: 5fb
-U-Bits: 100001001011010011101111100101010001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 5fb
-
-Encoding: 5fb
-U-Bits: 100001001011010011101000101101101111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 5fb
-
-Encoding: 5fc
-U-Bits: 001101001101010011110101100111101011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 5fc
-
-Encoding: 5fc
-U-Bits: 001101001101010011101000101010100010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 5fc
-
-Encoding: 5fc
-U-Bits: 001101001101010011101111100010011100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 5fc
-
-Encoding: 5fd
-U-Bits: 111010101101010011101000101011001001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 5fd
-
-Encoding: 5fd
-U-Bits: 111010101101010011110101100110000000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5fd
-
-Encoding: 5fd
-U-Bits: 111010101101010011110010101110111110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 5fd
-
-Encoding: 5fe
-U-Bits: 011100110101010011110010110100100010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 5fe
-
-Encoding: 5fe
-U-Bits: 011100110101010011101111111001101011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 5fe
-
-Encoding: 5fe
-U-Bits: 011100110101010011101000110001010101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 5fe
-
-Encoding: 5ff
-U-Bits: 101011010101010011101111111000000000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 5ff
-
-Encoding: 5ff
-U-Bits: 101011010101010011110010110101001001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 5ff
-
-Encoding: 5ff
-U-Bits: 101011010101010011110101111101110111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 5ff
-
-Encoding: 600
-U-Bits: 000000000000000111001001001010100010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 600
-
-Encoding: 600
-U-Bits: 000000000000000111010100000111101011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 600
-
-Encoding: 600
-U-Bits: 000000000000000111010011001111010101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 600
-
-Encoding: 601
-U-Bits: 110111100000000111010100000110000000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 601
-
-Encoding: 601
-U-Bits: 110111100000000111001001001011001001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 601
-
-Encoding: 601
-U-Bits: 110111100000000111001110000011110111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 601
-
-Encoding: 602
-U-Bits: 010001111000000111001110011001101011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 602
-
-Encoding: 602
-U-Bits: 010001111000000111010011010100100010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 602
-
-Encoding: 602
-U-Bits: 010001111000000111010100011100011100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 602
-
-Encoding: 603
-U-Bits: 100110011000000111010011010101001001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 603
-
-Encoding: 603
-U-Bits: 100110011000000111001110011000000000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 603
-
-Encoding: 603
-U-Bits: 100110011000000111001001010000111110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 603
-
-Encoding: 604
-U-Bits: 001010011110000111010100011010111010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 604
-
-Encoding: 604
-U-Bits: 001010011110000111001001010111110011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 604
-
-Encoding: 604
-U-Bits: 001010011110000111001110011111001101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 604
-
-Encoding: 605
-U-Bits: 111101111110000111001001010110011000
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 605
-
-Encoding: 605
-U-Bits: 111101111110000111010100011011010001
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 605
-
-Encoding: 605
-U-Bits: 111101111110000111010011010011101111
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 605
-
-Encoding: 606
-U-Bits: 011011100110000111010011001001110011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 606
-
-Encoding: 606
-U-Bits: 011011100110000111001110000100111010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 606
-
-Encoding: 606
-U-Bits: 011011100110000111001001001100000100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 606
-
-Encoding: 607
-U-Bits: 101100000110000111001110000101010001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 607
-
-Encoding: 607
-U-Bits: 101100000110000111010011001000011000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 607
-
-Encoding: 607
-U-Bits: 101100000110000111010100000000100110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 607
-
-Encoding: 608
-U-Bits: 000110100111100111001110011110100110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 608
-
-Encoding: 608
-U-Bits: 000110100111100111010011010011101111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 608
-
-Encoding: 608
-U-Bits: 000110100111100111010100011011010001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 608
-
-Encoding: 609
-U-Bits: 110001000111100111010011010010000100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 609
-
-Encoding: 609
-U-Bits: 110001000111100111001110011111001101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 609
-
-Encoding: 609
-U-Bits: 110001000111100111001001010111110011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 609
-
-Encoding: 60a
-U-Bits: 010111011111100111001001001101101111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 60a
-
-Encoding: 60a
-U-Bits: 010111011111100111010100000000100110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 60a
-
-Encoding: 60a
-U-Bits: 010111011111100111010011001000011000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 60a
-
-Encoding: 60b
-U-Bits: 100000111111100111010100000001001101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 60b
-
-Encoding: 60b
-U-Bits: 100000111111100111001001001100000100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 60b
-
-Encoding: 60b
-U-Bits: 100000111111100111001110000100111010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 60b
-
-Encoding: 60c
-U-Bits: 001100111001100111010011001110111110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 60c
-
-Encoding: 60c
-U-Bits: 001100111001100111001110000011110111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 60c
-
-Encoding: 60c
-U-Bits: 001100111001100111001001001011001001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 60c
-
-Encoding: 60d
-U-Bits: 111011011001100111001110000010011100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 60d
-
-Encoding: 60d
-U-Bits: 111011011001100111010011001111010101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 60d
-
-Encoding: 60d
-U-Bits: 111011011001100111010100000111101011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 60d
-
-Encoding: 60e
-U-Bits: 011101000001100111010100011101110111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 60e
-
-Encoding: 60e
-U-Bits: 011101000001100111001001010000111110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 60e
-
-Encoding: 60e
-U-Bits: 011101000001100111001110011000000000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 60e
-
-Encoding: 60f
-U-Bits: 101010100001100111001001010001010101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 60f
-
-Encoding: 60f
-U-Bits: 101010100001100111010100011100011100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 60f
-
-Encoding: 60f
-U-Bits: 101010100001100111010011010100100010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 60f
-
-Encoding: 610
-U-Bits: 000001101001111111001000111111100000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 610
-
-Encoding: 610
-U-Bits: 000001101001111111010101110010101001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 610
-
-Encoding: 610
-U-Bits: 000001101001111111010010111010010111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 610
-
-Encoding: 611
-U-Bits: 110110001001111111010101110011000010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 611
-
-Encoding: 611
-U-Bits: 110110001001111111001000111110001011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 611
-
-Encoding: 611
-U-Bits: 110110001001111111001111110110110101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 611
-
-Encoding: 612
-U-Bits: 010000010001111111001111101100101001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 612
-
-Encoding: 612
-U-Bits: 010000010001111111010010100001100000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 612
-
-Encoding: 612
-U-Bits: 010000010001111111010101101001011110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 612
-
-Encoding: 613
-U-Bits: 100111110001111111010010100000001011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 613
-
-Encoding: 613
-U-Bits: 100111110001111111001111101101000010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 613
-
-Encoding: 613
-U-Bits: 100111110001111111001000100101111100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 613
-
-Encoding: 614
-U-Bits: 001011110111111111010101101111111000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 614
-
-Encoding: 614
-U-Bits: 001011110111111111001000100010110001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 614
-
-Encoding: 614
-U-Bits: 001011110111111111001111101010001111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 614
-
-Encoding: 615
-U-Bits: 111100010111111111001000100011011010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 615
-
-Encoding: 615
-U-Bits: 111100010111111111010101101110010011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 615
-
-Encoding: 615
-U-Bits: 111100010111111111010010100110101101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 615
-
-Encoding: 616
-U-Bits: 011010001111111111010010111100110001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 616
-
-Encoding: 616
-U-Bits: 011010001111111111001111110001111000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 616
-
-Encoding: 616
-U-Bits: 011010001111111111001000111001000110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 616
-
-Encoding: 617
-U-Bits: 101101101111111111001111110000010011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 617
-
-Encoding: 617
-U-Bits: 101101101111111111010010111101011010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 617
-
-Encoding: 617
-U-Bits: 101101101111111111010101110101100100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 617
-
-Encoding: 618
-U-Bits: 000111001110011111001111101011100100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 618
-
-Encoding: 618
-U-Bits: 000111001110011111010010100110101101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 618
-
-Encoding: 618
-U-Bits: 000111001110011111010101101110010011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 618
-
-Encoding: 619
-U-Bits: 110000101110011111010010100111000110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 619
-
-Encoding: 619
-U-Bits: 110000101110011111001111101010001111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 619
-
-Encoding: 619
-U-Bits: 110000101110011111001000100010110001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 619
-
-Encoding: 61a
-U-Bits: 010110110110011111001000111000101101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 61a
-
-Encoding: 61a
-U-Bits: 010110110110011111010101110101100100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 61a
-
-Encoding: 61a
-U-Bits: 010110110110011111010010111101011010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 61a
-
-Encoding: 61b
-U-Bits: 100001010110011111010101110100001111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 61b
-
-Encoding: 61b
-U-Bits: 100001010110011111001000111001000110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 61b
-
-Encoding: 61b
-U-Bits: 100001010110011111001111110001111000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 61b
-
-Encoding: 61c
-U-Bits: 001101010000011111010010111011111100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 61c
-
-Encoding: 61c
-U-Bits: 001101010000011111001111110110110101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 61c
-
-Encoding: 61c
-U-Bits: 001101010000011111001000111110001011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 61c
-
-Encoding: 61d
-U-Bits: 111010110000011111001111110111011110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 61d
-
-Encoding: 61d
-U-Bits: 111010110000011111010010111010010111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 61d
-
-Encoding: 61d
-U-Bits: 111010110000011111010101110010101001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 61d
-
-Encoding: 61e
-U-Bits: 011100101000011111010101101000110101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 61e
-
-Encoding: 61e
-U-Bits: 011100101000011111001000100101111100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 61e
-
-Encoding: 61e
-U-Bits: 011100101000011111001111101101000010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 61e
-
-Encoding: 61f
-U-Bits: 101011001000011111001000100100010111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 61f
-
-Encoding: 61f
-U-Bits: 101011001000011111010101101001011110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 61f
-
-Encoding: 61f
-U-Bits: 101011001000011111010010100001100000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 61f
-
-Encoding: 620
-U-Bits: 000000011010011001001001010111110011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 620
-
-Encoding: 620
-U-Bits: 000000011010011001010100011010111010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 620
-
-Encoding: 620
-U-Bits: 000000011010011001010011010010000100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 620
-
-Encoding: 621
-U-Bits: 110111111010011001010100011011010001
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 621
-
-Encoding: 621
-U-Bits: 110111111010011001001001010110011000
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 621
-
-Encoding: 621
-U-Bits: 110111111010011001001110011110100110
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 621
-
-Encoding: 622
-U-Bits: 010001100010011001001110000100111010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 622
-
-Encoding: 622
-U-Bits: 010001100010011001010011001001110011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 622
-
-Encoding: 622
-U-Bits: 010001100010011001010100000001001101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 622
-
-Encoding: 623
-U-Bits: 100110000010011001010011001000011000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 623
-
-Encoding: 623
-U-Bits: 100110000010011001001110000101010001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 623
-
-Encoding: 623
-U-Bits: 100110000010011001001001001101101111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 623
-
-Encoding: 624
-U-Bits: 001010000100011001010100000111101011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 624
-
-Encoding: 624
-U-Bits: 001010000100011001001001001010100010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 624
-
-Encoding: 624
-U-Bits: 001010000100011001001110000010011100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 624
-
-Encoding: 625
-U-Bits: 111101100100011001001001001011001001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 625
-
-Encoding: 625
-U-Bits: 111101100100011001010100000110000000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 625
-
-Encoding: 625
-U-Bits: 111101100100011001010011001110111110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 625
-
-Encoding: 626
-U-Bits: 011011111100011001010011010100100010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 626
-
-Encoding: 626
-U-Bits: 011011111100011001001110011001101011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 626
-
-Encoding: 626
-U-Bits: 011011111100011001001001010001010101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 626
-
-Encoding: 627
-U-Bits: 101100011100011001001110011000000000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 627
-
-Encoding: 627
-U-Bits: 101100011100011001010011010101001001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 627
-
-Encoding: 627
-U-Bits: 101100011100011001010100011101110111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 627
-
-Encoding: 628
-U-Bits: 000110111101111001001110000011110111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 628
-
-Encoding: 628
-U-Bits: 000110111101111001010011001110111110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 628
-
-Encoding: 628
-U-Bits: 000110111101111001010100000110000000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 628
-
-Encoding: 629
-U-Bits: 110001011101111001010011001111010101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 629
-
-Encoding: 629
-U-Bits: 110001011101111001001110000010011100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 629
-
-Encoding: 629
-U-Bits: 110001011101111001001001001010100010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 629
-
-Encoding: 62a
-U-Bits: 010111000101111001001001010000111110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 62a
-
-Encoding: 62a
-U-Bits: 010111000101111001010100011101110111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 62a
-
-Encoding: 62a
-U-Bits: 010111000101111001010011010101001001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 62a
-
-Encoding: 62b
-U-Bits: 100000100101111001010100011100011100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 62b
-
-Encoding: 62b
-U-Bits: 100000100101111001001001010001010101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 62b
-
-Encoding: 62b
-U-Bits: 100000100101111001001110011001101011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 62b
-
-Encoding: 62c
-U-Bits: 001100100011111001010011010011101111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 62c
-
-Encoding: 62c
-U-Bits: 001100100011111001001110011110100110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 62c
-
-Encoding: 62c
-U-Bits: 001100100011111001001001010110011000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 62c
-
-Encoding: 62d
-U-Bits: 111011000011111001001110011111001101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 62d
-
-Encoding: 62d
-U-Bits: 111011000011111001010011010010000100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 62d
-
-Encoding: 62d
-U-Bits: 111011000011111001010100011010111010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 62d
-
-Encoding: 62e
-U-Bits: 011101011011111001010100000000100110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 62e
-
-Encoding: 62e
-U-Bits: 011101011011111001001001001101101111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 62e
-
-Encoding: 62e
-U-Bits: 011101011011111001001110000101010001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 62e
-
-Encoding: 62f
-U-Bits: 101010111011111001001001001100000100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 62f
-
-Encoding: 62f
-U-Bits: 101010111011111001010100000001001101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 62f
-
-Encoding: 62f
-U-Bits: 101010111011111001010011001001110011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 62f
-
-Encoding: 630
-U-Bits: 000001110011100001001000100010110001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 630
-
-Encoding: 630
-U-Bits: 000001110011100001010101101111111000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 630
-
-Encoding: 630
-U-Bits: 000001110011100001010010100111000110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 630
-
-Encoding: 631
-U-Bits: 110110010011100001010101101110010011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 631
-
-Encoding: 631
-U-Bits: 110110010011100001001000100011011010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 631
-
-Encoding: 631
-U-Bits: 110110010011100001001111101011100100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 631
-
-Encoding: 632
-U-Bits: 010000001011100001001111110001111000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 632
-
-Encoding: 632
-U-Bits: 010000001011100001010010111100110001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 632
-
-Encoding: 632
-U-Bits: 010000001011100001010101110100001111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 632
-
-Encoding: 633
-U-Bits: 100111101011100001010010111101011010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 633
-
-Encoding: 633
-U-Bits: 100111101011100001001111110000010011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 633
-
-Encoding: 633
-U-Bits: 100111101011100001001000111000101101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 633
-
-Encoding: 634
-U-Bits: 001011101101100001010101110010101001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 634
-
-Encoding: 634
-U-Bits: 001011101101100001001000111111100000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 634
-
-Encoding: 634
-U-Bits: 001011101101100001001111110111011110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 634
-
-Encoding: 635
-U-Bits: 111100001101100001001000111110001011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 635
-
-Encoding: 635
-U-Bits: 111100001101100001010101110011000010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 635
-
-Encoding: 635
-U-Bits: 111100001101100001010010111011111100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 635
-
-Encoding: 636
-U-Bits: 011010010101100001010010100001100000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 636
-
-Encoding: 636
-U-Bits: 011010010101100001001111101100101001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 636
-
-Encoding: 636
-U-Bits: 011010010101100001001000100100010111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 636
-
-Encoding: 637
-U-Bits: 101101110101100001001111101101000010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 637
-
-Encoding: 637
-U-Bits: 101101110101100001010010100000001011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 637
-
-Encoding: 637
-U-Bits: 101101110101100001010101101000110101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 637
-
-Encoding: 638
-U-Bits: 000111010100000001001111110110110101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 638
-
-Encoding: 638
-U-Bits: 000111010100000001010010111011111100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 638
-
-Encoding: 638
-U-Bits: 000111010100000001010101110011000010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 638
-
-Encoding: 639
-U-Bits: 110000110100000001010010111010010111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 639
-
-Encoding: 639
-U-Bits: 110000110100000001001111110111011110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 639
-
-Encoding: 639
-U-Bits: 110000110100000001001000111111100000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 639
-
-Encoding: 63a
-U-Bits: 010110101100000001001000100101111100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 63a
-
-Encoding: 63a
-U-Bits: 010110101100000001010101101000110101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 63a
-
-Encoding: 63a
-U-Bits: 010110101100000001010010100000001011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 63a
-
-Encoding: 63b
-U-Bits: 100001001100000001010101101001011110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 63b
-
-Encoding: 63b
-U-Bits: 100001001100000001001000100100010111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 63b
-
-Encoding: 63b
-U-Bits: 100001001100000001001111101100101001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 63b
-
-Encoding: 63c
-U-Bits: 001101001010000001010010100110101101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 63c
-
-Encoding: 63c
-U-Bits: 001101001010000001001111101011100100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 63c
-
-Encoding: 63c
-U-Bits: 001101001010000001001000100011011010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 63c
-
-Encoding: 63d
-U-Bits: 111010101010000001001111101010001111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 63d
-
-Encoding: 63d
-U-Bits: 111010101010000001010010100111000110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 63d
-
-Encoding: 63d
-U-Bits: 111010101010000001010101101111111000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 63d
-
-Encoding: 63e
-U-Bits: 011100110010000001010101110101100100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 63e
-
-Encoding: 63e
-U-Bits: 011100110010000001001000111000101101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 63e
-
-Encoding: 63e
-U-Bits: 011100110010000001001111110000010011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 63e
-
-Encoding: 63f
-U-Bits: 101011010010000001001000111001000110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 63f
-
-Encoding: 63f
-U-Bits: 101011010010000001010101110100001111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 63f
-
-Encoding: 63f
-U-Bits: 101011010010000001010010111100110001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 63f
-
-Encoding: 640
-U-Bits: 000000000110100000110101101001011110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 640
-
-Encoding: 640
-U-Bits: 000000000110100000101000100100010111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 640
-
-Encoding: 640
-U-Bits: 000000000110100000101111101100101001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 640
-
-Encoding: 641
-U-Bits: 110111100110100000101000100101111100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 641
-
-Encoding: 641
-U-Bits: 110111100110100000110101101000110101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 641
-
-Encoding: 641
-U-Bits: 110111100110100000110010100000001011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 641
-
-Encoding: 642
-U-Bits: 010001111110100000110010111010010111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 642
-
-Encoding: 642
-U-Bits: 010001111110100000101111110111011110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 642
-
-Encoding: 642
-U-Bits: 010001111110100000101000111111100000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 642
-
-Encoding: 643
-U-Bits: 100110011110100000101111110110110101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 643
-
-Encoding: 643
-U-Bits: 100110011110100000110010111011111100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 643
-
-Encoding: 643
-U-Bits: 100110011110100000110101110011000010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 643
-
-Encoding: 644
-U-Bits: 001010011000100000101000111001000110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 644
-
-Encoding: 644
-U-Bits: 001010011000100000110101110100001111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 644
-
-Encoding: 644
-U-Bits: 001010011000100000110010111100110001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 644
-
-Encoding: 645
-U-Bits: 111101111000100000110101110101100100
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 645
-
-Encoding: 645
-U-Bits: 111101111000100000101000111000101101
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 645
-
-Encoding: 645
-U-Bits: 111101111000100000101111110000010011
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 645
-
-Encoding: 646
-U-Bits: 011011100000100000101111101010001111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 646
-
-Encoding: 646
-U-Bits: 011011100000100000110010100111000110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 646
-
-Encoding: 646
-U-Bits: 011011100000100000110101101111111000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 646
-
-Encoding: 647
-U-Bits: 101100000000100000110010100110101101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 647
-
-Encoding: 647
-U-Bits: 101100000000100000101111101011100100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 647
-
-Encoding: 647
-U-Bits: 101100000000100000101000100011011010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 647
-
-Encoding: 648
-U-Bits: 000110100001000000110010111101011010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 648
-
-Encoding: 648
-U-Bits: 000110100001000000101111110000010011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 648
-
-Encoding: 648
-U-Bits: 000110100001000000101000111000101101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 648
-
-Encoding: 649
-U-Bits: 110001000001000000101111110001111000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 649
-
-Encoding: 649
-U-Bits: 110001000001000000110010111100110001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 649
-
-Encoding: 649
-U-Bits: 110001000001000000110101110100001111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 649
-
-Encoding: 64a
-U-Bits: 010111011001000000110101101110010011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 64a
-
-Encoding: 64a
-U-Bits: 010111011001000000101000100011011010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 64a
-
-Encoding: 64a
-U-Bits: 010111011001000000101111101011100100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 64a
-
-Encoding: 64b
-U-Bits: 100000111001000000101000100010110001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 64b
-
-Encoding: 64b
-U-Bits: 100000111001000000110101101111111000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 64b
-
-Encoding: 64b
-U-Bits: 100000111001000000110010100111000110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 64b
-
-Encoding: 64c
-U-Bits: 001100111111000000101111101101000010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 64c
-
-Encoding: 64c
-U-Bits: 001100111111000000110010100000001011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 64c
-
-Encoding: 64c
-U-Bits: 001100111111000000110101101000110101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 64c
-
-Encoding: 64d
-U-Bits: 111011011111000000110010100001100000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 64d
-
-Encoding: 64d
-U-Bits: 111011011111000000101111101100101001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 64d
-
-Encoding: 64d
-U-Bits: 111011011111000000101000100100010111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 64d
-
-Encoding: 64e
-U-Bits: 011101000111000000101000111110001011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 64e
-
-Encoding: 64e
-U-Bits: 011101000111000000110101110011000010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 64e
-
-Encoding: 64e
-U-Bits: 011101000111000000110010111011111100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 64e
-
-Encoding: 64f
-U-Bits: 101010100111000000110101110010101001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 64f
-
-Encoding: 64f
-U-Bits: 101010100111000000101000111111100000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 64f
-
-Encoding: 64f
-U-Bits: 101010100111000000101111110111011110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 64f
-
-Encoding: 650
-U-Bits: 000001101111011000110100011100011100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 650
-
-Encoding: 650
-U-Bits: 000001101111011000101001010001010101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 650
-
-Encoding: 650
-U-Bits: 000001101111011000101110011001101011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 650
-
-Encoding: 651
-U-Bits: 110110001111011000101001010000111110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 651
-
-Encoding: 651
-U-Bits: 110110001111011000110100011101110111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 651
-
-Encoding: 651
-U-Bits: 110110001111011000110011010101001001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 651
-
-Encoding: 652
-U-Bits: 010000010111011000110011001111010101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 652
-
-Encoding: 652
-U-Bits: 010000010111011000101110000010011100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 652
-
-Encoding: 652
-U-Bits: 010000010111011000101001001010100010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 652
-
-Encoding: 653
-U-Bits: 100111110111011000101110000011110111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 653
-
-Encoding: 653
-U-Bits: 100111110111011000110011001110111110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 653
-
-Encoding: 653
-U-Bits: 100111110111011000110100000110000000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 653
-
-Encoding: 654
-U-Bits: 001011110001011000101001001100000100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 654
-
-Encoding: 654
-U-Bits: 001011110001011000110100000001001101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 654
-
-Encoding: 654
-U-Bits: 001011110001011000110011001001110011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 654
-
-Encoding: 655
-U-Bits: 111100010001011000110100000000100110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 655
-
-Encoding: 655
-U-Bits: 111100010001011000101001001101101111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 655
-
-Encoding: 655
-U-Bits: 111100010001011000101110000101010001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 655
-
-Encoding: 656
-U-Bits: 011010001001011000101110011111001101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 656
-
-Encoding: 656
-U-Bits: 011010001001011000110011010010000100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 656
-
-Encoding: 656
-U-Bits: 011010001001011000110100011010111010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 656
-
-Encoding: 657
-U-Bits: 101101101001011000110011010011101111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 657
-
-Encoding: 657
-U-Bits: 101101101001011000101110011110100110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 657
-
-Encoding: 657
-U-Bits: 101101101001011000101001010110011000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 657
-
-Encoding: 658
-U-Bits: 000111001000111000110011001000011000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 658
-
-Encoding: 658
-U-Bits: 000111001000111000101110000101010001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 658
-
-Encoding: 658
-U-Bits: 000111001000111000101001001101101111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 658
-
-Encoding: 659
-U-Bits: 110000101000111000101110000100111010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 659
-
-Encoding: 659
-U-Bits: 110000101000111000110011001001110011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 659
-
-Encoding: 659
-U-Bits: 110000101000111000110100000001001101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 659
-
-Encoding: 65a
-U-Bits: 010110110000111000110100011011010001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 65a
-
-Encoding: 65a
-U-Bits: 010110110000111000101001010110011000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 65a
-
-Encoding: 65a
-U-Bits: 010110110000111000101110011110100110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 65a
-
-Encoding: 65b
-U-Bits: 100001010000111000101001010111110011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 65b
-
-Encoding: 65b
-U-Bits: 100001010000111000110100011010111010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 65b
-
-Encoding: 65b
-U-Bits: 100001010000111000110011010010000100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 65b
-
-Encoding: 65c
-U-Bits: 001101010110111000101110011000000000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 65c
-
-Encoding: 65c
-U-Bits: 001101010110111000110011010101001001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 65c
-
-Encoding: 65c
-U-Bits: 001101010110111000110100011101110111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 65c
-
-Encoding: 65d
-U-Bits: 111010110110111000110011010100100010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 65d
-
-Encoding: 65d
-U-Bits: 111010110110111000101110011001101011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 65d
-
-Encoding: 65d
-U-Bits: 111010110110111000101001010001010101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 65d
-
-Encoding: 65e
-U-Bits: 011100101110111000101001001011001001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 65e
-
-Encoding: 65e
-U-Bits: 011100101110111000110100000110000000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 65e
-
-Encoding: 65e
-U-Bits: 011100101110111000110011001110111110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 65e
-
-Encoding: 65f
-U-Bits: 101011001110111000110100000111101011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 65f
-
-Encoding: 65f
-U-Bits: 101011001110111000101001001010100010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 65f
-
-Encoding: 65f
-U-Bits: 101011001110111000101110000010011100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 65f
-
-Encoding: 660
-U-Bits: 000000011100111110110101110100001111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 660
-
-Encoding: 660
-U-Bits: 000000011100111110101000111001000110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 660
-
-Encoding: 660
-U-Bits: 000000011100111110101111110001111000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 660
-
-Encoding: 661
-U-Bits: 110111111100111110101000111000101101
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 661
-
-Encoding: 661
-U-Bits: 110111111100111110110101110101100100
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 661
-
-Encoding: 661
-U-Bits: 110111111100111110110010111101011010
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 661
-
-Encoding: 662
-U-Bits: 010001100100111110110010100111000110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 662
-
-Encoding: 662
-U-Bits: 010001100100111110101111101010001111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 662
-
-Encoding: 662
-U-Bits: 010001100100111110101000100010110001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 662
-
-Encoding: 663
-U-Bits: 100110000100111110101111101011100100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 663
-
-Encoding: 663
-U-Bits: 100110000100111110110010100110101101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 663
-
-Encoding: 663
-U-Bits: 100110000100111110110101101110010011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 663
-
-Encoding: 664
-U-Bits: 001010000010111110101000100100010111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 664
-
-Encoding: 664
-U-Bits: 001010000010111110110101101001011110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 664
-
-Encoding: 664
-U-Bits: 001010000010111110110010100001100000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 664
-
-Encoding: 665
-U-Bits: 111101100010111110110101101000110101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 665
-
-Encoding: 665
-U-Bits: 111101100010111110101000100101111100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 665
-
-Encoding: 665
-U-Bits: 111101100010111110101111101101000010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 665
-
-Encoding: 666
-U-Bits: 011011111010111110101111110111011110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 666
-
-Encoding: 666
-U-Bits: 011011111010111110110010111010010111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 666
-
-Encoding: 666
-U-Bits: 011011111010111110110101110010101001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 666
-
-Encoding: 667
-U-Bits: 101100011010111110110010111011111100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 667
-
-Encoding: 667
-U-Bits: 101100011010111110101111110110110101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 667
-
-Encoding: 667
-U-Bits: 101100011010111110101000111110001011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 667
-
-Encoding: 668
-U-Bits: 000110111011011110110010100000001011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 668
-
-Encoding: 668
-U-Bits: 000110111011011110101111101101000010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 668
-
-Encoding: 668
-U-Bits: 000110111011011110101000100101111100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 668
-
-Encoding: 669
-U-Bits: 110001011011011110101111101100101001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 669
-
-Encoding: 669
-U-Bits: 110001011011011110110010100001100000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 669
-
-Encoding: 669
-U-Bits: 110001011011011110110101101001011110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 669
-
-Encoding: 66a
-U-Bits: 010111000011011110110101110011000010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 66a
-
-Encoding: 66a
-U-Bits: 010111000011011110101000111110001011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 66a
-
-Encoding: 66a
-U-Bits: 010111000011011110101111110110110101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 66a
-
-Encoding: 66b
-U-Bits: 100000100011011110101000111111100000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 66b
-
-Encoding: 66b
-U-Bits: 100000100011011110110101110010101001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 66b
-
-Encoding: 66b
-U-Bits: 100000100011011110110010111010010111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 66b
-
-Encoding: 66c
-U-Bits: 001100100101011110101111110000010011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 66c
-
-Encoding: 66c
-U-Bits: 001100100101011110110010111101011010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 66c
-
-Encoding: 66c
-U-Bits: 001100100101011110110101110101100100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 66c
-
-Encoding: 66d
-U-Bits: 111011000101011110110010111100110001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 66d
-
-Encoding: 66d
-U-Bits: 111011000101011110101111110001111000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 66d
-
-Encoding: 66d
-U-Bits: 111011000101011110101000111001000110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 66d
-
-Encoding: 66e
-U-Bits: 011101011101011110101000100011011010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 66e
-
-Encoding: 66e
-U-Bits: 011101011101011110110101101110010011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 66e
-
-Encoding: 66e
-U-Bits: 011101011101011110110010100110101101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 66e
-
-Encoding: 66f
-U-Bits: 101010111101011110110101101111111000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 66f
-
-Encoding: 66f
-U-Bits: 101010111101011110101000100010110001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 66f
-
-Encoding: 66f
-U-Bits: 101010111101011110101111101010001111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 66f
-
-Encoding: 670
-U-Bits: 000001110101000110110100000001001101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 670
-
-Encoding: 670
-U-Bits: 000001110101000110101001001100000100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 670
-
-Encoding: 670
-U-Bits: 000001110101000110101110000100111010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 670
-
-Encoding: 671
-U-Bits: 110110010101000110101001001101101111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 671
-
-Encoding: 671
-U-Bits: 110110010101000110110100000000100110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 671
-
-Encoding: 671
-U-Bits: 110110010101000110110011001000011000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 671
-
-Encoding: 672
-U-Bits: 010000001101000110110011010010000100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 672
-
-Encoding: 672
-U-Bits: 010000001101000110101110011111001101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 672
-
-Encoding: 672
-U-Bits: 010000001101000110101001010111110011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 672
-
-Encoding: 673
-U-Bits: 100111101101000110101110011110100110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 673
-
-Encoding: 673
-U-Bits: 100111101101000110110011010011101111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 673
-
-Encoding: 673
-U-Bits: 100111101101000110110100011011010001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 673
-
-Encoding: 674
-U-Bits: 001011101011000110101001010001010101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 674
-
-Encoding: 674
-U-Bits: 001011101011000110110100011100011100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 674
-
-Encoding: 674
-U-Bits: 001011101011000110110011010100100010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 674
-
-Encoding: 675
-U-Bits: 111100001011000110110100011101110111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 675
-
-Encoding: 675
-U-Bits: 111100001011000110101001010000111110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 675
-
-Encoding: 675
-U-Bits: 111100001011000110101110011000000000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 675
-
-Encoding: 676
-U-Bits: 011010010011000110101110000010011100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 676
-
-Encoding: 676
-U-Bits: 011010010011000110110011001111010101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 676
-
-Encoding: 676
-U-Bits: 011010010011000110110100000111101011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 676
-
-Encoding: 677
-U-Bits: 101101110011000110110011001110111110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 677
-
-Encoding: 677
-U-Bits: 101101110011000110101110000011110111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 677
-
-Encoding: 677
-U-Bits: 101101110011000110101001001011001001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 677
-
-Encoding: 678
-U-Bits: 000111010010100110110011010101001001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 678
-
-Encoding: 678
-U-Bits: 000111010010100110101110011000000000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 678
-
-Encoding: 678
-U-Bits: 000111010010100110101001010000111110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 678
-
-Encoding: 679
-U-Bits: 110000110010100110101110011001101011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 679
-
-Encoding: 679
-U-Bits: 110000110010100110110011010100100010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 679
-
-Encoding: 679
-U-Bits: 110000110010100110110100011100011100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 679
-
-Encoding: 67a
-U-Bits: 010110101010100110110100000110000000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 67a
-
-Encoding: 67a
-U-Bits: 010110101010100110101001001011001001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 67a
-
-Encoding: 67a
-U-Bits: 010110101010100110101110000011110111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 67a
-
-Encoding: 67b
-U-Bits: 100001001010100110101001001010100010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 67b
-
-Encoding: 67b
-U-Bits: 100001001010100110110100000111101011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 67b
-
-Encoding: 67b
-U-Bits: 100001001010100110110011001111010101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 67b
-
-Encoding: 67c
-U-Bits: 001101001100100110101110000101010001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 67c
-
-Encoding: 67c
-U-Bits: 001101001100100110110011001000011000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 67c
-
-Encoding: 67c
-U-Bits: 001101001100100110110100000000100110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 67c
-
-Encoding: 67d
-U-Bits: 111010101100100110110011001001110011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 67d
-
-Encoding: 67d
-U-Bits: 111010101100100110101110000100111010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 67d
-
-Encoding: 67d
-U-Bits: 111010101100100110101001001100000100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 67d
-
-Encoding: 67e
-U-Bits: 011100110100100110101001010110011000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 67e
-
-Encoding: 67e
-U-Bits: 011100110100100110110100011011010001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 67e
-
-Encoding: 67e
-U-Bits: 011100110100100110110011010011101111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 67e
-
-Encoding: 67f
-U-Bits: 101011010100100110110100011010111010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 67f
-
-Encoding: 67f
-U-Bits: 101011010100100110101001010111110011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 67f
-
-Encoding: 67f
-U-Bits: 101011010100100110101110011111001101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 67f
-
-Encoding: 680
-U-Bits: 000000000001101110110110000010011100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 680
-
-Encoding: 680
-U-Bits: 000000000001101110101011001111010101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 680
-
-Encoding: 680
-U-Bits: 000000000001101110101100000111101011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 680
-
-Encoding: 681
-U-Bits: 110111100001101110101011001110111110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 681
-
-Encoding: 681
-U-Bits: 110111100001101110110110000011110111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 681
-
-Encoding: 681
-U-Bits: 110111100001101110110001001011001001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 681
-
-Encoding: 682
-U-Bits: 010001111001101110110001010001010101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 682
-
-Encoding: 682
-U-Bits: 010001111001101110101100011100011100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 682
-
-Encoding: 682
-U-Bits: 010001111001101110101011010100100010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 682
-
-Encoding: 683
-U-Bits: 100110011001101110101100011101110111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 683
-
-Encoding: 683
-U-Bits: 100110011001101110110001010000111110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 683
-
-Encoding: 683
-U-Bits: 100110011001101110110110011000000000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 683
-
-Encoding: 684
-U-Bits: 001010011111101110101011010010000100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 684
-
-Encoding: 684
-U-Bits: 001010011111101110110110011111001101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 684
-
-Encoding: 684
-U-Bits: 001010011111101110110001010111110011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 684
-
-Encoding: 685
-U-Bits: 111101111111101110110110011110100110
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 685
-
-Encoding: 685
-U-Bits: 111101111111101110101011010011101111
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 685
-
-Encoding: 685
-U-Bits: 111101111111101110101100011011010001
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 685
-
-Encoding: 686
-U-Bits: 011011100111101110101100000001001101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 686
-
-Encoding: 686
-U-Bits: 011011100111101110110001001100000100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 686
-
-Encoding: 686
-U-Bits: 011011100111101110110110000100111010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 686
-
-Encoding: 687
-U-Bits: 101100000111101110110001001101101111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 687
-
-Encoding: 687
-U-Bits: 101100000111101110101100000000100110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 687
-
-Encoding: 687
-U-Bits: 101100000111101110101011001000011000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 687
-
-Encoding: 688
-U-Bits: 000110100110001110110001010110011000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 688
-
-Encoding: 688
-U-Bits: 000110100110001110101100011011010001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 688
-
-Encoding: 688
-U-Bits: 000110100110001110101011010011101111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 688
-
-Encoding: 689
-U-Bits: 110001000110001110101100011010111010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 689
-
-Encoding: 689
-U-Bits: 110001000110001110110001010111110011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 689
-
-Encoding: 689
-U-Bits: 110001000110001110110110011111001101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 689
-
-Encoding: 68a
-U-Bits: 010111011110001110110110000101010001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 68a
-
-Encoding: 68a
-U-Bits: 010111011110001110101011001000011000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 68a
-
-Encoding: 68a
-U-Bits: 010111011110001110101100000000100110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 68a
-
-Encoding: 68b
-U-Bits: 100000111110001110101011001001110011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 68b
-
-Encoding: 68b
-U-Bits: 100000111110001110110110000100111010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 68b
-
-Encoding: 68b
-U-Bits: 100000111110001110110001001100000100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 68b
-
-Encoding: 68c
-U-Bits: 001100111000001110101100000110000000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 68c
-
-Encoding: 68c
-U-Bits: 001100111000001110110001001011001001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 68c
-
-Encoding: 68c
-U-Bits: 001100111000001110110110000011110111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 68c
-
-Encoding: 68d
-U-Bits: 111011011000001110110001001010100010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 68d
-
-Encoding: 68d
-U-Bits: 111011011000001110101100000111101011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 68d
-
-Encoding: 68d
-U-Bits: 111011011000001110101011001111010101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 68d
-
-Encoding: 68e
-U-Bits: 011101000000001110101011010101001001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 68e
-
-Encoding: 68e
-U-Bits: 011101000000001110110110011000000000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 68e
-
-Encoding: 68e
-U-Bits: 011101000000001110110001010000111110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 68e
-
-Encoding: 68f
-U-Bits: 101010100000001110110110011001101011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 68f
-
-Encoding: 68f
-U-Bits: 101010100000001110101011010100100010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 68f
-
-Encoding: 68f
-U-Bits: 101010100000001110101100011100011100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 68f
-
-Encoding: 690
-U-Bits: 000001101000010110110111110111011110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 690
-
-Encoding: 690
-U-Bits: 000001101000010110101010111010010111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 690
-
-Encoding: 690
-U-Bits: 000001101000010110101101110010101001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 690
-
-Encoding: 691
-U-Bits: 110110001000010110101010111011111100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 691
-
-Encoding: 691
-U-Bits: 110110001000010110110111110110110101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 691
-
-Encoding: 691
-U-Bits: 110110001000010110110000111110001011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 691
-
-Encoding: 692
-U-Bits: 010000010000010110110000100100010111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 692
-
-Encoding: 692
-U-Bits: 010000010000010110101101101001011110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 692
-
-Encoding: 692
-U-Bits: 010000010000010110101010100001100000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 692
-
-Encoding: 693
-U-Bits: 100111110000010110101101101000110101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 693
-
-Encoding: 693
-U-Bits: 100111110000010110110000100101111100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 693
-
-Encoding: 693
-U-Bits: 100111110000010110110111101101000010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 693
-
-Encoding: 694
-U-Bits: 001011110110010110101010100111000110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 694
-
-Encoding: 694
-U-Bits: 001011110110010110110111101010001111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 694
-
-Encoding: 694
-U-Bits: 001011110110010110110000100010110001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 694
-
-Encoding: 695
-U-Bits: 111100010110010110110111101011100100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 695
-
-Encoding: 695
-U-Bits: 111100010110010110101010100110101101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 695
-
-Encoding: 695
-U-Bits: 111100010110010110101101101110010011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 695
-
-Encoding: 696
-U-Bits: 011010001110010110101101110100001111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 696
-
-Encoding: 696
-U-Bits: 011010001110010110110000111001000110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 696
-
-Encoding: 696
-U-Bits: 011010001110010110110111110001111000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 696
-
-Encoding: 697
-U-Bits: 101101101110010110110000111000101101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 697
-
-Encoding: 697
-U-Bits: 101101101110010110101101110101100100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 697
-
-Encoding: 697
-U-Bits: 101101101110010110101010111101011010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 697
-
-Encoding: 698
-U-Bits: 000111001111110110110000100011011010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 698
-
-Encoding: 698
-U-Bits: 000111001111110110101101101110010011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 698
-
-Encoding: 698
-U-Bits: 000111001111110110101010100110101101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 698
-
-Encoding: 699
-U-Bits: 110000101111110110101101101111111000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 699
-
-Encoding: 699
-U-Bits: 110000101111110110110000100010110001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 699
-
-Encoding: 699
-U-Bits: 110000101111110110110111101010001111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 699
-
-Encoding: 69a
-U-Bits: 010110110111110110110111110000010011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 69a
-
-Encoding: 69a
-U-Bits: 010110110111110110101010111101011010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 69a
-
-Encoding: 69a
-U-Bits: 010110110111110110101101110101100100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 69a
-
-Encoding: 69b
-U-Bits: 100001010111110110101010111100110001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 69b
-
-Encoding: 69b
-U-Bits: 100001010111110110110111110001111000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 69b
-
-Encoding: 69b
-U-Bits: 100001010111110110110000111001000110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 69b
-
-Encoding: 69c
-U-Bits: 001101010001110110101101110011000010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 69c
-
-Encoding: 69c
-U-Bits: 001101010001110110110000111110001011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 69c
-
-Encoding: 69c
-U-Bits: 001101010001110110110111110110110101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 69c
-
-Encoding: 69d
-U-Bits: 111010110001110110110000111111100000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 69d
-
-Encoding: 69d
-U-Bits: 111010110001110110101101110010101001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 69d
-
-Encoding: 69d
-U-Bits: 111010110001110110101010111010010111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 69d
-
-Encoding: 69e
-U-Bits: 011100101001110110101010100000001011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 69e
-
-Encoding: 69e
-U-Bits: 011100101001110110110111101101000010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 69e
-
-Encoding: 69e
-U-Bits: 011100101001110110110000100101111100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 69e
-
-Encoding: 69f
-U-Bits: 101011001001110110110111101100101001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 69f
-
-Encoding: 69f
-U-Bits: 101011001001110110101010100001100000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 69f
-
-Encoding: 69f
-U-Bits: 101011001001110110101101101001011110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 69f
-
-Encoding: 6a0
-U-Bits: 000000011011110000110110011111001101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 6a0
-
-Encoding: 6a0
-U-Bits: 000000011011110000101011010010000100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 6a0
-
-Encoding: 6a0
-U-Bits: 000000011011110000101100011010111010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 6a0
-
-Encoding: 6a1
-U-Bits: 110111111011110000101011010011101111
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 6a1
-
-Encoding: 6a1
-U-Bits: 110111111011110000110110011110100110
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 6a1
-
-Encoding: 6a1
-U-Bits: 110111111011110000110001010110011000
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 6a1
-
-Encoding: 6a2
-U-Bits: 010001100011110000110001001100000100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 6a2
-
-Encoding: 6a2
-U-Bits: 010001100011110000101100000001001101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 6a2
-
-Encoding: 6a2
-U-Bits: 010001100011110000101011001001110011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 6a2
-
-Encoding: 6a3
-U-Bits: 100110000011110000101100000000100110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 6a3
-
-Encoding: 6a3
-U-Bits: 100110000011110000110001001101101111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 6a3
-
-Encoding: 6a3
-U-Bits: 100110000011110000110110000101010001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 6a3
-
-Encoding: 6a4
-U-Bits: 001010000101110000101011001111010101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 6a4
-
-Encoding: 6a4
-U-Bits: 001010000101110000110110000010011100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 6a4
-
-Encoding: 6a4
-U-Bits: 001010000101110000110001001010100010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 6a4
-
-Encoding: 6a5
-U-Bits: 111101100101110000110110000011110111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 6a5
-
-Encoding: 6a5
-U-Bits: 111101100101110000101011001110111110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 6a5
-
-Encoding: 6a5
-U-Bits: 111101100101110000101100000110000000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6a5
-
-Encoding: 6a6
-U-Bits: 011011111101110000101100011100011100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 6a6
-
-Encoding: 6a6
-U-Bits: 011011111101110000110001010001010101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 6a6
-
-Encoding: 6a6
-U-Bits: 011011111101110000110110011001101011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 6a6
-
-Encoding: 6a7
-U-Bits: 101100011101110000110001010000111110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 6a7
-
-Encoding: 6a7
-U-Bits: 101100011101110000101100011101110111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 6a7
-
-Encoding: 6a7
-U-Bits: 101100011101110000101011010101001001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 6a7
-
-Encoding: 6a8
-U-Bits: 000110111100010000110001001011001001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 6a8
-
-Encoding: 6a8
-U-Bits: 000110111100010000101100000110000000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6a8
-
-Encoding: 6a8
-U-Bits: 000110111100010000101011001110111110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 6a8
-
-Encoding: 6a9
-U-Bits: 110001011100010000101100000111101011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 6a9
-
-Encoding: 6a9
-U-Bits: 110001011100010000110001001010100010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 6a9
-
-Encoding: 6a9
-U-Bits: 110001011100010000110110000010011100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 6a9
-
-Encoding: 6aa
-U-Bits: 010111000100010000110110011000000000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6aa
-
-Encoding: 6aa
-U-Bits: 010111000100010000101011010101001001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 6aa
-
-Encoding: 6aa
-U-Bits: 010111000100010000101100011101110111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 6aa
-
-Encoding: 6ab
-U-Bits: 100000100100010000101011010100100010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 6ab
-
-Encoding: 6ab
-U-Bits: 100000100100010000110110011001101011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 6ab
-
-Encoding: 6ab
-U-Bits: 100000100100010000110001010001010101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 6ab
-
-Encoding: 6ac
-U-Bits: 001100100010010000101100011011010001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 6ac
-
-Encoding: 6ac
-U-Bits: 001100100010010000110001010110011000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 6ac
-
-Encoding: 6ac
-U-Bits: 001100100010010000110110011110100110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 6ac
-
-Encoding: 6ad
-U-Bits: 111011000010010000110001010111110011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 6ad
-
-Encoding: 6ad
-U-Bits: 111011000010010000101100011010111010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 6ad
-
-Encoding: 6ad
-U-Bits: 111011000010010000101011010010000100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 6ad
-
-Encoding: 6ae
-U-Bits: 011101011010010000101011001000011000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 6ae
-
-Encoding: 6ae
-U-Bits: 011101011010010000110110000101010001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 6ae
-
-Encoding: 6ae
-U-Bits: 011101011010010000110001001101101111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 6ae
-
-Encoding: 6af
-U-Bits: 101010111010010000110110000100111010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 6af
-
-Encoding: 6af
-U-Bits: 101010111010010000101011001001110011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 6af
-
-Encoding: 6af
-U-Bits: 101010111010010000101100000001001101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 6af
-
-Encoding: 6b0
-U-Bits: 000001110010001000110111101010001111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 6b0
-
-Encoding: 6b0
-U-Bits: 000001110010001000101010100111000110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 6b0
-
-Encoding: 6b0
-U-Bits: 000001110010001000101101101111111000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 6b0
-
-Encoding: 6b1
-U-Bits: 110110010010001000101010100110101101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 6b1
-
-Encoding: 6b1
-U-Bits: 110110010010001000110111101011100100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 6b1
-
-Encoding: 6b1
-U-Bits: 110110010010001000110000100011011010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 6b1
-
-Encoding: 6b2
-U-Bits: 010000001010001000110000111001000110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 6b2
-
-Encoding: 6b2
-U-Bits: 010000001010001000101101110100001111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 6b2
-
-Encoding: 6b2
-U-Bits: 010000001010001000101010111100110001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 6b2
-
-Encoding: 6b3
-U-Bits: 100111101010001000101101110101100100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 6b3
-
-Encoding: 6b3
-U-Bits: 100111101010001000110000111000101101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 6b3
-
-Encoding: 6b3
-U-Bits: 100111101010001000110111110000010011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 6b3
-
-Encoding: 6b4
-U-Bits: 001011101100001000101010111010010111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 6b4
-
-Encoding: 6b4
-U-Bits: 001011101100001000110111110111011110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 6b4
-
-Encoding: 6b4
-U-Bits: 001011101100001000110000111111100000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 6b4
-
-Encoding: 6b5
-U-Bits: 111100001100001000110111110110110101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 6b5
-
-Encoding: 6b5
-U-Bits: 111100001100001000101010111011111100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 6b5
-
-Encoding: 6b5
-U-Bits: 111100001100001000101101110011000010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 6b5
-
-Encoding: 6b6
-U-Bits: 011010010100001000101101101001011110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 6b6
-
-Encoding: 6b6
-U-Bits: 011010010100001000110000100100010111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 6b6
-
-Encoding: 6b6
-U-Bits: 011010010100001000110111101100101001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 6b6
-
-Encoding: 6b7
-U-Bits: 101101110100001000110000100101111100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 6b7
-
-Encoding: 6b7
-U-Bits: 101101110100001000101101101000110101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 6b7
-
-Encoding: 6b7
-U-Bits: 101101110100001000101010100000001011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 6b7
-
-Encoding: 6b8
-U-Bits: 000111010101101000110000111110001011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 6b8
-
-Encoding: 6b8
-U-Bits: 000111010101101000101101110011000010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 6b8
-
-Encoding: 6b8
-U-Bits: 000111010101101000101010111011111100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 6b8
-
-Encoding: 6b9
-U-Bits: 110000110101101000101101110010101001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 6b9
-
-Encoding: 6b9
-U-Bits: 110000110101101000110000111111100000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 6b9
-
-Encoding: 6b9
-U-Bits: 110000110101101000110111110111011110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 6b9
-
-Encoding: 6ba
-U-Bits: 010110101101101000110111101101000010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 6ba
-
-Encoding: 6ba
-U-Bits: 010110101101101000101010100000001011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 6ba
-
-Encoding: 6ba
-U-Bits: 010110101101101000101101101000110101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 6ba
-
-Encoding: 6bb
-U-Bits: 100001001101101000101010100001100000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 6bb
-
-Encoding: 6bb
-U-Bits: 100001001101101000110111101100101001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 6bb
-
-Encoding: 6bb
-U-Bits: 100001001101101000110000100100010111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 6bb
-
-Encoding: 6bc
-U-Bits: 001101001011101000101101101110010011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 6bc
-
-Encoding: 6bc
-U-Bits: 001101001011101000110000100011011010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 6bc
-
-Encoding: 6bc
-U-Bits: 001101001011101000110111101011100100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 6bc
-
-Encoding: 6bd
-U-Bits: 111010101011101000110000100010110001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 6bd
-
-Encoding: 6bd
-U-Bits: 111010101011101000101101101111111000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 6bd
-
-Encoding: 6bd
-U-Bits: 111010101011101000101010100111000110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 6bd
-
-Encoding: 6be
-U-Bits: 011100110011101000101010111101011010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 6be
-
-Encoding: 6be
-U-Bits: 011100110011101000110111110000010011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 6be
-
-Encoding: 6be
-U-Bits: 011100110011101000110000111000101101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 6be
-
-Encoding: 6bf
-U-Bits: 101011010011101000110111110001111000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 6bf
-
-Encoding: 6bf
-U-Bits: 101011010011101000101010111100110001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 6bf
-
-Encoding: 6bf
-U-Bits: 101011010011101000101101110100001111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 6bf
-
-Encoding: 6c0
-U-Bits: 000000000111001001001010100001100000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 6c0
-
-Encoding: 6c0
-U-Bits: 000000000111001001010111101100101001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 6c0
-
-Encoding: 6c0
-U-Bits: 000000000111001001010000100100010111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 6c0
-
-Encoding: 6c1
-U-Bits: 110111100111001001010111101101000010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 6c1
-
-Encoding: 6c1
-U-Bits: 110111100111001001001010100000001011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 6c1
-
-Encoding: 6c1
-U-Bits: 110111100111001001001101101000110101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 6c1
-
-Encoding: 6c2
-U-Bits: 010001111111001001001101110010101001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 6c2
-
-Encoding: 6c2
-U-Bits: 010001111111001001010000111111100000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 6c2
-
-Encoding: 6c2
-U-Bits: 010001111111001001010111110111011110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 6c2
-
-Encoding: 6c3
-U-Bits: 100110011111001001010000111110001011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 6c3
-
-Encoding: 6c3
-U-Bits: 100110011111001001001101110011000010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 6c3
-
-Encoding: 6c3
-U-Bits: 100110011111001001001010111011111100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 6c3
-
-Encoding: 6c4
-U-Bits: 001010011001001001010111110001111000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 6c4
-
-Encoding: 6c4
-U-Bits: 001010011001001001001010111100110001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 6c4
-
-Encoding: 6c4
-U-Bits: 001010011001001001001101110100001111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 6c4
-
-Encoding: 6c5
-U-Bits: 111101111001001001001010111101011010
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 6c5
-
-Encoding: 6c5
-U-Bits: 111101111001001001010111110000010011
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 6c5
-
-Encoding: 6c5
-U-Bits: 111101111001001001010000111000101101
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 6c5
-
-Encoding: 6c6
-U-Bits: 011011100001001001010000100010110001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 6c6
-
-Encoding: 6c6
-U-Bits: 011011100001001001001101101111111000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 6c6
-
-Encoding: 6c6
-U-Bits: 011011100001001001001010100111000110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 6c6
-
-Encoding: 6c7
-U-Bits: 101100000001001001001101101110010011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 6c7
-
-Encoding: 6c7
-U-Bits: 101100000001001001010000100011011010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 6c7
-
-Encoding: 6c7
-U-Bits: 101100000001001001010111101011100100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 6c7
-
-Encoding: 6c8
-U-Bits: 000110100000101001001101110101100100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 6c8
-
-Encoding: 6c8
-U-Bits: 000110100000101001010000111000101101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 6c8
-
-Encoding: 6c8
-U-Bits: 000110100000101001010111110000010011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 6c8
-
-Encoding: 6c9
-U-Bits: 110001000000101001010000111001000110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 6c9
-
-Encoding: 6c9
-U-Bits: 110001000000101001001101110100001111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 6c9
-
-Encoding: 6c9
-U-Bits: 110001000000101001001010111100110001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 6c9
-
-Encoding: 6ca
-U-Bits: 010111011000101001001010100110101101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 6ca
-
-Encoding: 6ca
-U-Bits: 010111011000101001010111101011100100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 6ca
-
-Encoding: 6ca
-U-Bits: 010111011000101001010000100011011010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 6ca
-
-Encoding: 6cb
-U-Bits: 100000111000101001010111101010001111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 6cb
-
-Encoding: 6cb
-U-Bits: 100000111000101001001010100111000110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 6cb
-
-Encoding: 6cb
-U-Bits: 100000111000101001001101101111111000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 6cb
-
-Encoding: 6cc
-U-Bits: 001100111110101001010000100101111100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 6cc
-
-Encoding: 6cc
-U-Bits: 001100111110101001001101101000110101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 6cc
-
-Encoding: 6cc
-U-Bits: 001100111110101001001010100000001011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 6cc
-
-Encoding: 6cd
-U-Bits: 111011011110101001001101101001011110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 6cd
-
-Encoding: 6cd
-U-Bits: 111011011110101001010000100100010111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 6cd
-
-Encoding: 6cd
-U-Bits: 111011011110101001010111101100101001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 6cd
-
-Encoding: 6ce
-U-Bits: 011101000110101001010111110110110101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 6ce
-
-Encoding: 6ce
-U-Bits: 011101000110101001001010111011111100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 6ce
-
-Encoding: 6ce
-U-Bits: 011101000110101001001101110011000010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 6ce
-
-Encoding: 6cf
-U-Bits: 101010100110101001001010111010010111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 6cf
-
-Encoding: 6cf
-U-Bits: 101010100110101001010111110111011110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 6cf
-
-Encoding: 6cf
-U-Bits: 101010100110101001010000111111100000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 6cf
-
-Encoding: 6d0
-U-Bits: 000001101110110001001011010100100010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 6d0
-
-Encoding: 6d0
-U-Bits: 000001101110110001010110011001101011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 6d0
-
-Encoding: 6d0
-U-Bits: 000001101110110001010001010001010101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 6d0
-
-Encoding: 6d1
-U-Bits: 110110001110110001010110011000000000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6d1
-
-Encoding: 6d1
-U-Bits: 110110001110110001001011010101001001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 6d1
-
-Encoding: 6d1
-U-Bits: 110110001110110001001100011101110111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 6d1
-
-Encoding: 6d2
-U-Bits: 010000010110110001001100000111101011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 6d2
-
-Encoding: 6d2
-U-Bits: 010000010110110001010001001010100010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 6d2
-
-Encoding: 6d2
-U-Bits: 010000010110110001010110000010011100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 6d2
-
-Encoding: 6d3
-U-Bits: 100111110110110001010001001011001001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 6d3
-
-Encoding: 6d3
-U-Bits: 100111110110110001001100000110000000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6d3
-
-Encoding: 6d3
-U-Bits: 100111110110110001001011001110111110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 6d3
-
-Encoding: 6d4
-U-Bits: 001011110000110001010110000100111010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 6d4
-
-Encoding: 6d4
-U-Bits: 001011110000110001001011001001110011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 6d4
-
-Encoding: 6d4
-U-Bits: 001011110000110001001100000001001101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 6d4
-
-Encoding: 6d5
-U-Bits: 111100010000110001001011001000011000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 6d5
-
-Encoding: 6d5
-U-Bits: 111100010000110001010110000101010001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 6d5
-
-Encoding: 6d5
-U-Bits: 111100010000110001010001001101101111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 6d5
-
-Encoding: 6d6
-U-Bits: 011010001000110001010001010111110011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 6d6
-
-Encoding: 6d6
-U-Bits: 011010001000110001001100011010111010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 6d6
-
-Encoding: 6d6
-U-Bits: 011010001000110001001011010010000100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 6d6
-
-Encoding: 6d7
-U-Bits: 101101101000110001001100011011010001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 6d7
-
-Encoding: 6d7
-U-Bits: 101101101000110001010001010110011000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 6d7
-
-Encoding: 6d7
-U-Bits: 101101101000110001010110011110100110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 6d7
-
-Encoding: 6d8
-U-Bits: 000111001001010001001100000000100110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 6d8
-
-Encoding: 6d8
-U-Bits: 000111001001010001010001001101101111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 6d8
-
-Encoding: 6d8
-U-Bits: 000111001001010001010110000101010001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 6d8
-
-Encoding: 6d9
-U-Bits: 110000101001010001010001001100000100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 6d9
-
-Encoding: 6d9
-U-Bits: 110000101001010001001100000001001101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 6d9
-
-Encoding: 6d9
-U-Bits: 110000101001010001001011001001110011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 6d9
-
-Encoding: 6da
-U-Bits: 010110110001010001001011010011101111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 6da
-
-Encoding: 6da
-U-Bits: 010110110001010001010110011110100110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 6da
-
-Encoding: 6da
-U-Bits: 010110110001010001010001010110011000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 6da
-
-Encoding: 6db
-U-Bits: 100001010001010001010110011111001101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 6db
-
-Encoding: 6db
-U-Bits: 100001010001010001001011010010000100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 6db
-
-Encoding: 6db
-U-Bits: 100001010001010001001100011010111010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 6db
-
-Encoding: 6dc
-U-Bits: 001101010111010001010001010000111110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 6dc
-
-Encoding: 6dc
-U-Bits: 001101010111010001001100011101110111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 6dc
-
-Encoding: 6dc
-U-Bits: 001101010111010001001011010101001001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 6dc
-
-Encoding: 6dd
-U-Bits: 111010110111010001001100011100011100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 6dd
-
-Encoding: 6dd
-U-Bits: 111010110111010001010001010001010101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 6dd
-
-Encoding: 6dd
-U-Bits: 111010110111010001010110011001101011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 6dd
-
-Encoding: 6de
-U-Bits: 011100101111010001010110000011110111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 6de
-
-Encoding: 6de
-U-Bits: 011100101111010001001011001110111110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 6de
-
-Encoding: 6de
-U-Bits: 011100101111010001001100000110000000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6de
-
-Encoding: 6df
-U-Bits: 101011001111010001001011001111010101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 6df
-
-Encoding: 6df
-U-Bits: 101011001111010001010110000010011100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 6df
-
-Encoding: 6df
-U-Bits: 101011001111010001010001001010100010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 6df
-
-Encoding: 6e0
-U-Bits: 000000011101010111001010111100110001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 6e0
-
-Encoding: 6e0
-U-Bits: 000000011101010111010111110001111000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 6e0
-
-Encoding: 6e0
-U-Bits: 000000011101010111010000111001000110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 6e0
-
-Encoding: 6e1
-U-Bits: 110111111101010111010111110000010011
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 6e1
-
-Encoding: 6e1
-U-Bits: 110111111101010111001010111101011010
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 6e1
-
-Encoding: 6e1
-U-Bits: 110111111101010111001101110101100100
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 6e1
-
-Encoding: 6e2
-U-Bits: 010001100101010111001101101111111000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 6e2
-
-Encoding: 6e2
-U-Bits: 010001100101010111010000100010110001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 6e2
-
-Encoding: 6e2
-U-Bits: 010001100101010111010111101010001111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 6e2
-
-Encoding: 6e3
-U-Bits: 100110000101010111010000100011011010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 6e3
-
-Encoding: 6e3
-U-Bits: 100110000101010111001101101110010011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 6e3
-
-Encoding: 6e3
-U-Bits: 100110000101010111001010100110101101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 6e3
-
-Encoding: 6e4
-U-Bits: 001010000011010111010111101100101001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 6e4
-
-Encoding: 6e4
-U-Bits: 001010000011010111001010100001100000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 6e4
-
-Encoding: 6e4
-U-Bits: 001010000011010111001101101001011110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 6e4
-
-Encoding: 6e5
-U-Bits: 111101100011010111001010100000001011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 6e5
-
-Encoding: 6e5
-U-Bits: 111101100011010111010111101101000010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 6e5
-
-Encoding: 6e5
-U-Bits: 111101100011010111010000100101111100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 6e5
-
-Encoding: 6e6
-U-Bits: 011011111011010111010000111111100000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 6e6
-
-Encoding: 6e6
-U-Bits: 011011111011010111001101110010101001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 6e6
-
-Encoding: 6e6
-U-Bits: 011011111011010111001010111010010111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 6e6
-
-Encoding: 6e7
-U-Bits: 101100011011010111001101110011000010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 6e7
-
-Encoding: 6e7
-U-Bits: 101100011011010111010000111110001011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 6e7
-
-Encoding: 6e7
-U-Bits: 101100011011010111010111110110110101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 6e7
-
-Encoding: 6e8
-U-Bits: 000110111010110111001101101000110101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 6e8
-
-Encoding: 6e8
-U-Bits: 000110111010110111010000100101111100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 6e8
-
-Encoding: 6e8
-U-Bits: 000110111010110111010111101101000010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 6e8
-
-Encoding: 6e9
-U-Bits: 110001011010110111010000100100010111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 6e9
-
-Encoding: 6e9
-U-Bits: 110001011010110111001101101001011110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 6e9
-
-Encoding: 6e9
-U-Bits: 110001011010110111001010100001100000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 6e9
-
-Encoding: 6ea
-U-Bits: 010111000010110111001010111011111100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 6ea
-
-Encoding: 6ea
-U-Bits: 010111000010110111010111110110110101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 6ea
-
-Encoding: 6ea
-U-Bits: 010111000010110111010000111110001011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 6ea
-
-Encoding: 6eb
-U-Bits: 100000100010110111010111110111011110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 6eb
-
-Encoding: 6eb
-U-Bits: 100000100010110111001010111010010111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 6eb
-
-Encoding: 6eb
-U-Bits: 100000100010110111001101110010101001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 6eb
-
-Encoding: 6ec
-U-Bits: 001100100100110111010000111000101101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 6ec
-
-Encoding: 6ec
-U-Bits: 001100100100110111001101110101100100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 6ec
-
-Encoding: 6ec
-U-Bits: 001100100100110111001010111101011010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 6ec
-
-Encoding: 6ed
-U-Bits: 111011000100110111001101110100001111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 6ed
-
-Encoding: 6ed
-U-Bits: 111011000100110111010000111001000110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 6ed
-
-Encoding: 6ed
-U-Bits: 111011000100110111010111110001111000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 6ed
-
-Encoding: 6ee
-U-Bits: 011101011100110111010111101011100100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 6ee
-
-Encoding: 6ee
-U-Bits: 011101011100110111001010100110101101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 6ee
-
-Encoding: 6ee
-U-Bits: 011101011100110111001101101110010011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 6ee
-
-Encoding: 6ef
-U-Bits: 101010111100110111001010100111000110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 6ef
-
-Encoding: 6ef
-U-Bits: 101010111100110111010111101010001111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 6ef
-
-Encoding: 6ef
-U-Bits: 101010111100110111010000100010110001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 6ef
-
-Encoding: 6f0
-U-Bits: 000001110100101111001011001001110011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 6f0
-
-Encoding: 6f0
-U-Bits: 000001110100101111010110000100111010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 6f0
-
-Encoding: 6f0
-U-Bits: 000001110100101111010001001100000100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 6f0
-
-Encoding: 6f1
-U-Bits: 110110010100101111010110000101010001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 6f1
-
-Encoding: 6f1
-U-Bits: 110110010100101111001011001000011000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 6f1
-
-Encoding: 6f1
-U-Bits: 110110010100101111001100000000100110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 6f1
-
-Encoding: 6f2
-U-Bits: 010000001100101111001100011010111010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 6f2
-
-Encoding: 6f2
-U-Bits: 010000001100101111010001010111110011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 6f2
-
-Encoding: 6f2
-U-Bits: 010000001100101111010110011111001101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 6f2
-
-Encoding: 6f3
-U-Bits: 100111101100101111010001010110011000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 6f3
-
-Encoding: 6f3
-U-Bits: 100111101100101111001100011011010001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 6f3
-
-Encoding: 6f3
-U-Bits: 100111101100101111001011010011101111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 6f3
-
-Encoding: 6f4
-U-Bits: 001011101010101111010110011001101011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 6f4
-
-Encoding: 6f4
-U-Bits: 001011101010101111001011010100100010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 6f4
-
-Encoding: 6f4
-U-Bits: 001011101010101111001100011100011100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 6f4
-
-Encoding: 6f5
-U-Bits: 111100001010101111001011010101001001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 6f5
-
-Encoding: 6f5
-U-Bits: 111100001010101111010110011000000000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6f5
-
-Encoding: 6f5
-U-Bits: 111100001010101111010001010000111110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 6f5
-
-Encoding: 6f6
-U-Bits: 011010010010101111010001001010100010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 6f6
-
-Encoding: 6f6
-U-Bits: 011010010010101111001100000111101011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 6f6
-
-Encoding: 6f6
-U-Bits: 011010010010101111001011001111010101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 6f6
-
-Encoding: 6f7
-U-Bits: 101101110010101111001100000110000000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6f7
-
-Encoding: 6f7
-U-Bits: 101101110010101111010001001011001001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 6f7
-
-Encoding: 6f7
-U-Bits: 101101110010101111010110000011110111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 6f7
-
-Encoding: 6f8
-U-Bits: 000111010011001111001100011101110111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 6f8
-
-Encoding: 6f8
-U-Bits: 000111010011001111010001010000111110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 6f8
-
-Encoding: 6f8
-U-Bits: 000111010011001111010110011000000000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 6f8
-
-Encoding: 6f9
-U-Bits: 110000110011001111010001010001010101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 6f9
-
-Encoding: 6f9
-U-Bits: 110000110011001111001100011100011100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 6f9
-
-Encoding: 6f9
-U-Bits: 110000110011001111001011010100100010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 6f9
-
-Encoding: 6fa
-U-Bits: 010110101011001111001011001110111110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 6fa
-
-Encoding: 6fa
-U-Bits: 010110101011001111010110000011110111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 6fa
-
-Encoding: 6fa
-U-Bits: 010110101011001111010001001011001001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 6fa
-
-Encoding: 6fb
-U-Bits: 100001001011001111010110000010011100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 6fb
-
-Encoding: 6fb
-U-Bits: 100001001011001111001011001111010101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 6fb
-
-Encoding: 6fb
-U-Bits: 100001001011001111001100000111101011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 6fb
-
-Encoding: 6fc
-U-Bits: 001101001101001111010001001101101111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 6fc
-
-Encoding: 6fc
-U-Bits: 001101001101001111001100000000100110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 6fc
-
-Encoding: 6fc
-U-Bits: 001101001101001111001011001000011000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 6fc
-
-Encoding: 6fd
-U-Bits: 111010101101001111001100000001001101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 6fd
-
-Encoding: 6fd
-U-Bits: 111010101101001111010001001100000100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 6fd
-
-Encoding: 6fd
-U-Bits: 111010101101001111010110000100111010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 6fd
-
-Encoding: 6fe
-U-Bits: 011100110101001111010110011110100110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 6fe
-
-Encoding: 6fe
-U-Bits: 011100110101001111001011010011101111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 6fe
-
-Encoding: 6fe
-U-Bits: 011100110101001111001100011011010001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 6fe
-
-Encoding: 6ff
-U-Bits: 101011010101001111001011010010000100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 6ff
-
-Encoding: 6ff
-U-Bits: 101011010101001111010110011111001101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 6ff
-
-Encoding: 6ff
-U-Bits: 101011010101001111010001010111110011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 6ff
-
-Encoding: 700
-U-Bits: 000000000000011101010110111000101101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 700
-
-Encoding: 700
-U-Bits: 000000000000011101001011110101100100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 700
-
-Encoding: 700
-U-Bits: 000000000000011101001100111101011010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 700
-
-Encoding: 701
-U-Bits: 110111100000011101001011110100001111
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 701
-
-Encoding: 701
-U-Bits: 110111100000011101010110111001000110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 701
-
-Encoding: 701
-U-Bits: 110111100000011101010001110001111000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 701
-
-Encoding: 702
-U-Bits: 010001111000011101010001101011100100
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 702
-
-Encoding: 702
-U-Bits: 010001111000011101001100100110101101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 702
-
-Encoding: 702
-U-Bits: 010001111000011101001011101110010011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 702
-
-Encoding: 703
-U-Bits: 100110011000011101001100100111000110
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 703
-
-Encoding: 703
-U-Bits: 100110011000011101010001101010001111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 703
-
-Encoding: 703
-U-Bits: 100110011000011101010110100010110001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 703
-
-Encoding: 704
-U-Bits: 001010011110011101001011101000110101
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 704
-
-Encoding: 704
-U-Bits: 001010011110011101010110100101111100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 704
-
-Encoding: 704
-U-Bits: 001010011110011101010001101101000010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 704
-
-Encoding: 705
-U-Bits: 111101111110011101010110100100010111
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 705
-
-Encoding: 705
-U-Bits: 111101111110011101001011101001011110
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 705
-
-Encoding: 705
-U-Bits: 111101111110011101001100100001100000
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 705
-
-Encoding: 706
-U-Bits: 011011100110011101001100111011111100
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 706
-
-Encoding: 706
-U-Bits: 011011100110011101010001110110110101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 706
-
-Encoding: 706
-U-Bits: 011011100110011101010110111110001011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 706
-
-Encoding: 707
-U-Bits: 101100000110011101010001110111011110
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 707
-
-Encoding: 707
-U-Bits: 101100000110011101001100111010010111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 707
-
-Encoding: 707
-U-Bits: 101100000110011101001011110010101001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 707
-
-Encoding: 708
-U-Bits: 000110100111111101010001101100101001
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 708
-
-Encoding: 708
-U-Bits: 000110100111111101001100100001100000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 708
-
-Encoding: 708
-U-Bits: 000110100111111101001011101001011110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 708
-
-Encoding: 709
-U-Bits: 110001000111111101001100100000001011
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 709
-
-Encoding: 709
-U-Bits: 110001000111111101010001101101000010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 709
-
-Encoding: 709
-U-Bits: 110001000111111101010110100101111100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 709
-
-Encoding: 70a
-U-Bits: 010111011111111101010110111111100000
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 70a
-
-Encoding: 70a
-U-Bits: 010111011111111101001011110010101001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 70a
-
-Encoding: 70a
-U-Bits: 010111011111111101001100111010010111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 70a
-
-Encoding: 70b
-U-Bits: 100000111111111101001011110011000010
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 70b
-
-Encoding: 70b
-U-Bits: 100000111111111101010110111110001011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 70b
-
-Encoding: 70b
-U-Bits: 100000111111111101010001110110110101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 70b
-
-Encoding: 70c
-U-Bits: 001100111001111101001100111100110001
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 70c
-
-Encoding: 70c
-U-Bits: 001100111001111101010001110001111000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 70c
-
-Encoding: 70c
-U-Bits: 001100111001111101010110111001000110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 70c
-
-Encoding: 70d
-U-Bits: 111011011001111101010001110000010011
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 70d
-
-Encoding: 70d
-U-Bits: 111011011001111101001100111101011010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 70d
-
-Encoding: 70d
-U-Bits: 111011011001111101001011110101100100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 70d
-
-Encoding: 70e
-U-Bits: 011101000001111101001011101111111000
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 70e
-
-Encoding: 70e
-U-Bits: 011101000001111101010110100010110001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 70e
-
-Encoding: 70e
-U-Bits: 011101000001111101010001101010001111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 70e
-
-Encoding: 70f
-U-Bits: 101010100001111101010110100011011010
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 70f
-
-Encoding: 70f
-U-Bits: 101010100001111101001011101110010011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 70f
-
-Encoding: 70f
-U-Bits: 101010100001111101001100100110101101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 70f
-
-Encoding: 710
-U-Bits: 000001101001100101010111001101101111
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 710
-
-Encoding: 710
-U-Bits: 000001101001100101001010000000100110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 710
-
-Encoding: 710
-U-Bits: 000001101001100101001101001000011000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 710
-
-Encoding: 711
-U-Bits: 110110001001100101001010000001001101
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 711
-
-Encoding: 711
-U-Bits: 110110001001100101010111001100000100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 711
-
-Encoding: 711
-U-Bits: 110110001001100101010000000100111010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 711
-
-Encoding: 712
-U-Bits: 010000010001100101010000011110100110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 712
-
-Encoding: 712
-U-Bits: 010000010001100101001101010011101111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 712
-
-Encoding: 712
-U-Bits: 010000010001100101001010011011010001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 712
-
-Encoding: 713
-U-Bits: 100111110001100101001101010010000100
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 713
-
-Encoding: 713
-U-Bits: 100111110001100101010000011111001101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 713
-
-Encoding: 713
-U-Bits: 100111110001100101010111010111110011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 713
-
-Encoding: 714
-U-Bits: 001011110111100101001010011101110111
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 714
-
-Encoding: 714
-U-Bits: 001011110111100101010111010000111110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 714
-
-Encoding: 714
-U-Bits: 001011110111100101010000011000000000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 714
-
-Encoding: 715
-U-Bits: 111100010111100101010111010001010101
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 715
-
-Encoding: 715
-U-Bits: 111100010111100101001010011100011100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 715
-
-Encoding: 715
-U-Bits: 111100010111100101001101010100100010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 715
-
-Encoding: 716
-U-Bits: 011010001111100101001101001110111110
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 716
-
-Encoding: 716
-U-Bits: 011010001111100101010000000011110111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 716
-
-Encoding: 716
-U-Bits: 011010001111100101010111001011001001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 716
-
-Encoding: 717
-U-Bits: 101101101111100101010000000010011100
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 717
-
-Encoding: 717
-U-Bits: 101101101111100101001101001111010101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 717
-
-Encoding: 717
-U-Bits: 101101101111100101001010000111101011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 717
-
-Encoding: 718
-U-Bits: 000111001110000101010000011001101011
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 718
-
-Encoding: 718
-U-Bits: 000111001110000101001101010100100010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 718
-
-Encoding: 718
-U-Bits: 000111001110000101001010011100011100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 718
-
-Encoding: 719
-U-Bits: 110000101110000101001101010101001001
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 719
-
-Encoding: 719
-U-Bits: 110000101110000101010000011000000000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 719
-
-Encoding: 719
-U-Bits: 110000101110000101010111010000111110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 719
-
-Encoding: 71a
-U-Bits: 010110110110000101010111001010100010
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 71a
-
-Encoding: 71a
-U-Bits: 010110110110000101001010000111101011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 71a
-
-Encoding: 71a
-U-Bits: 010110110110000101001101001111010101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 71a
-
-Encoding: 71b
-U-Bits: 100001010110000101001010000110000000
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 71b
-
-Encoding: 71b
-U-Bits: 100001010110000101010111001011001001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 71b
-
-Encoding: 71b
-U-Bits: 100001010110000101010000000011110111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 71b
-
-Encoding: 71c
-U-Bits: 001101010000000101001101001001110011
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 71c
-
-Encoding: 71c
-U-Bits: 001101010000000101010000000100111010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 71c
-
-Encoding: 71c
-U-Bits: 001101010000000101010111001100000100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 71c
-
-Encoding: 71d
-U-Bits: 111010110000000101010000000101010001
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 71d
-
-Encoding: 71d
-U-Bits: 111010110000000101001101001000011000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 71d
-
-Encoding: 71d
-U-Bits: 111010110000000101001010000000100110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 71d
-
-Encoding: 71e
-U-Bits: 011100101000000101001010011010111010
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 71e
-
-Encoding: 71e
-U-Bits: 011100101000000101010111010111110011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 71e
-
-Encoding: 71e
-U-Bits: 011100101000000101010000011111001101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 71e
-
-Encoding: 71f
-U-Bits: 101011001000000101010111010110011000
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 71f
-
-Encoding: 71f
-U-Bits: 101011001000000101001010011011010001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 71f
-
-Encoding: 71f
-U-Bits: 101011001000000101001101010011101111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 71f
-
-Encoding: 720
-U-Bits: 000000011010000011010110100101111100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 720
-
-Encoding: 720
-U-Bits: 000000011010000011001011101000110101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 720
-
-Encoding: 720
-U-Bits: 000000011010000011001100100000001011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 720
-
-Encoding: 721
-U-Bits: 110111111010000011001011101001011110
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 721
-
-Encoding: 721
-U-Bits: 110111111010000011010110100100010111
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 721
-
-Encoding: 721
-U-Bits: 110111111010000011010001101100101001
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 721
-
-Encoding: 722
-U-Bits: 010001100010000011010001110110110101
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 722
-
-Encoding: 722
-U-Bits: 010001100010000011001100111011111100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 722
-
-Encoding: 722
-U-Bits: 010001100010000011001011110011000010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 722
-
-Encoding: 723
-U-Bits: 100110000010000011001100111010010111
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 723
-
-Encoding: 723
-U-Bits: 100110000010000011010001110111011110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 723
-
-Encoding: 723
-U-Bits: 100110000010000011010110111111100000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 723
-
-Encoding: 724
-U-Bits: 001010000100000011001011110101100100
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 724
-
-Encoding: 724
-U-Bits: 001010000100000011010110111000101101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 724
-
-Encoding: 724
-U-Bits: 001010000100000011010001110000010011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 724
-
-Encoding: 725
-U-Bits: 111101100100000011010110111001000110
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 725
-
-Encoding: 725
-U-Bits: 111101100100000011001011110100001111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 725
-
-Encoding: 725
-U-Bits: 111101100100000011001100111100110001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 725
-
-Encoding: 726
-U-Bits: 011011111100000011001100100110101101
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 726
-
-Encoding: 726
-U-Bits: 011011111100000011010001101011100100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 726
-
-Encoding: 726
-U-Bits: 011011111100000011010110100011011010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 726
-
-Encoding: 727
-U-Bits: 101100011100000011010001101010001111
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 727
-
-Encoding: 727
-U-Bits: 101100011100000011001100100111000110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 727
-
-Encoding: 727
-U-Bits: 101100011100000011001011101111111000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 727
-
-Encoding: 728
-U-Bits: 000110111101100011010001110001111000
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 728
-
-Encoding: 728
-U-Bits: 000110111101100011001100111100110001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 728
-
-Encoding: 728
-U-Bits: 000110111101100011001011110100001111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 728
-
-Encoding: 729
-U-Bits: 110001011101100011001100111101011010
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 729
-
-Encoding: 729
-U-Bits: 110001011101100011010001110000010011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 729
-
-Encoding: 729
-U-Bits: 110001011101100011010110111000101101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 729
-
-Encoding: 72a
-U-Bits: 010111000101100011010110100010110001
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 72a
-
-Encoding: 72a
-U-Bits: 010111000101100011001011101111111000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 72a
-
-Encoding: 72a
-U-Bits: 010111000101100011001100100111000110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 72a
-
-Encoding: 72b
-U-Bits: 100000100101100011001011101110010011
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 72b
-
-Encoding: 72b
-U-Bits: 100000100101100011010110100011011010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 72b
-
-Encoding: 72b
-U-Bits: 100000100101100011010001101011100100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 72b
-
-Encoding: 72c
-U-Bits: 001100100011100011001100100001100000
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 72c
-
-Encoding: 72c
-U-Bits: 001100100011100011010001101100101001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 72c
-
-Encoding: 72c
-U-Bits: 001100100011100011010110100100010111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 72c
-
-Encoding: 72d
-U-Bits: 111011000011100011010001101101000010
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 72d
-
-Encoding: 72d
-U-Bits: 111011000011100011001100100000001011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 72d
-
-Encoding: 72d
-U-Bits: 111011000011100011001011101000110101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 72d
-
-Encoding: 72e
-U-Bits: 011101011011100011001011110010101001
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 72e
-
-Encoding: 72e
-U-Bits: 011101011011100011010110111111100000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 72e
-
-Encoding: 72e
-U-Bits: 011101011011100011010001110111011110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 72e
-
-Encoding: 72f
-U-Bits: 101010111011100011010110111110001011
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 72f
-
-Encoding: 72f
-U-Bits: 101010111011100011001011110011000010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 72f
-
-Encoding: 72f
-U-Bits: 101010111011100011001100111011111100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 72f
-
-Encoding: 730
-U-Bits: 000001110011111011010111010000111110
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 730
-
-Encoding: 730
-U-Bits: 000001110011111011001010011101110111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 730
-
-Encoding: 730
-U-Bits: 000001110011111011001101010101001001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 730
-
-Encoding: 731
-U-Bits: 110110010011111011001010011100011100
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 731
-
-Encoding: 731
-U-Bits: 110110010011111011010111010001010101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 731
-
-Encoding: 731
-U-Bits: 110110010011111011010000011001101011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 731
-
-Encoding: 732
-U-Bits: 010000001011111011010000000011110111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 732
-
-Encoding: 732
-U-Bits: 010000001011111011001101001110111110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 732
-
-Encoding: 732
-U-Bits: 010000001011111011001010000110000000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 732
-
-Encoding: 733
-U-Bits: 100111101011111011001101001111010101
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 733
-
-Encoding: 733
-U-Bits: 100111101011111011010000000010011100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 733
-
-Encoding: 733
-U-Bits: 100111101011111011010111001010100010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 733
-
-Encoding: 734
-U-Bits: 001011101101111011001010000000100110
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 734
-
-Encoding: 734
-U-Bits: 001011101101111011010111001101101111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 734
-
-Encoding: 734
-U-Bits: 001011101101111011010000000101010001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 734
-
-Encoding: 735
-U-Bits: 111100001101111011010111001100000100
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 735
-
-Encoding: 735
-U-Bits: 111100001101111011001010000001001101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 735
-
-Encoding: 735
-U-Bits: 111100001101111011001101001001110011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 735
-
-Encoding: 736
-U-Bits: 011010010101111011001101010011101111
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 736
-
-Encoding: 736
-U-Bits: 011010010101111011010000011110100110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 736
-
-Encoding: 736
-U-Bits: 011010010101111011010111010110011000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 736
-
-Encoding: 737
-U-Bits: 101101110101111011010000011111001101
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 737
-
-Encoding: 737
-U-Bits: 101101110101111011001101010010000100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 737
-
-Encoding: 737
-U-Bits: 101101110101111011001010011010111010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 737
-
-Encoding: 738
-U-Bits: 000111010100011011010000000100111010
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 738
-
-Encoding: 738
-U-Bits: 000111010100011011001101001001110011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 738
-
-Encoding: 738
-U-Bits: 000111010100011011001010000001001101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 738
-
-Encoding: 739
-U-Bits: 110000110100011011001101001000011000
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 739
-
-Encoding: 739
-U-Bits: 110000110100011011010000000101010001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 739
-
-Encoding: 739
-U-Bits: 110000110100011011010111001101101111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 739
-
-Encoding: 73a
-U-Bits: 010110101100011011010111010111110011
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 73a
-
-Encoding: 73a
-U-Bits: 010110101100011011001010011010111010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 73a
-
-Encoding: 73a
-U-Bits: 010110101100011011001101010010000100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 73a
-
-Encoding: 73b
-U-Bits: 100001001100011011001010011011010001
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 73b
-
-Encoding: 73b
-U-Bits: 100001001100011011010111010110011000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 73b
-
-Encoding: 73b
-U-Bits: 100001001100011011010000011110100110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 73b
-
-Encoding: 73c
-U-Bits: 001101001010011011001101010100100010
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 73c
-
-Encoding: 73c
-U-Bits: 001101001010011011010000011001101011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 73c
-
-Encoding: 73c
-U-Bits: 001101001010011011010111010001010101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 73c
-
-Encoding: 73d
-U-Bits: 111010101010011011010000011000000000
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 73d
-
-Encoding: 73d
-U-Bits: 111010101010011011001101010101001001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 73d
-
-Encoding: 73d
-U-Bits: 111010101010011011001010011101110111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 73d
-
-Encoding: 73e
-U-Bits: 011100110010011011001010000111101011
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 73e
-
-Encoding: 73e
-U-Bits: 011100110010011011010111001010100010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 73e
-
-Encoding: 73e
-U-Bits: 011100110010011011010000000010011100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 73e
-
-Encoding: 73f
-U-Bits: 101011010010011011010111001011001001
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 73f
-
-Encoding: 73f
-U-Bits: 101011010010011011001010000110000000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 73f
-
-Encoding: 73f
-U-Bits: 101011010010011011001101001110111110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 73f
-
-Encoding: 740
-U-Bits: 000000000110111010101010011011010001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 740
-
-Encoding: 740
-U-Bits: 000000000110111010110111010110011000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 740
-
-Encoding: 740
-U-Bits: 000000000110111010110000011110100110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 740
-
-Encoding: 741
-U-Bits: 110111100110111010110111010111110011
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 741
-
-Encoding: 741
-U-Bits: 110111100110111010101010011010111010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 741
-
-Encoding: 741
-U-Bits: 110111100110111010101101010010000100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 741
-
-Encoding: 742
-U-Bits: 010001111110111010101101001000011000
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 742
-
-Encoding: 742
-U-Bits: 010001111110111010110000000101010001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 742
-
-Encoding: 742
-U-Bits: 010001111110111010110111001101101111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 742
-
-Encoding: 743
-U-Bits: 100110011110111010110000000100111010
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 743
-
-Encoding: 743
-U-Bits: 100110011110111010101101001001110011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 743
-
-Encoding: 743
-U-Bits: 100110011110111010101010000001001101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 743
-
-Encoding: 744
-U-Bits: 001010011000111010110111001011001001
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 744
-
-Encoding: 744
-U-Bits: 001010011000111010101010000110000000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 744
-
-Encoding: 744
-U-Bits: 001010011000111010101101001110111110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 744
-
-Encoding: 745
-U-Bits: 111101111000111010101010000111101011
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 745
-
-Encoding: 745
-U-Bits: 111101111000111010110111001010100010
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 745
-
-Encoding: 745
-U-Bits: 111101111000111010110000000010011100
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 745
-
-Encoding: 746
-U-Bits: 011011100000111010110000011000000000
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 746
-
-Encoding: 746
-U-Bits: 011011100000111010101101010101001001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 746
-
-Encoding: 746
-U-Bits: 011011100000111010101010011101110111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 746
-
-Encoding: 747
-U-Bits: 101100000000111010101101010100100010
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 747
-
-Encoding: 747
-U-Bits: 101100000000111010110000011001101011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 747
-
-Encoding: 747
-U-Bits: 101100000000111010110111010001010101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 747
-
-Encoding: 748
-U-Bits: 000110100001011010101101001111010101
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 748
-
-Encoding: 748
-U-Bits: 000110100001011010110000000010011100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 748
-
-Encoding: 748
-U-Bits: 000110100001011010110111001010100010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 748
-
-Encoding: 749
-U-Bits: 110001000001011010110000000011110111
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 749
-
-Encoding: 749
-U-Bits: 110001000001011010101101001110111110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 749
-
-Encoding: 749
-U-Bits: 110001000001011010101010000110000000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 749
-
-Encoding: 74a
-U-Bits: 010111011001011010101010011100011100
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 74a
-
-Encoding: 74a
-U-Bits: 010111011001011010110111010001010101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 74a
-
-Encoding: 74a
-U-Bits: 010111011001011010110000011001101011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 74a
-
-Encoding: 74b
-U-Bits: 100000111001011010110111010000111110
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 74b
-
-Encoding: 74b
-U-Bits: 100000111001011010101010011101110111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 74b
-
-Encoding: 74b
-U-Bits: 100000111001011010101101010101001001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 74b
-
-Encoding: 74c
-U-Bits: 001100111111011010110000011111001101
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 74c
-
-Encoding: 74c
-U-Bits: 001100111111011010101101010010000100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 74c
-
-Encoding: 74c
-U-Bits: 001100111111011010101010011010111010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 74c
-
-Encoding: 74d
-U-Bits: 111011011111011010101101010011101111
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 74d
-
-Encoding: 74d
-U-Bits: 111011011111011010110000011110100110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 74d
-
-Encoding: 74d
-U-Bits: 111011011111011010110111010110011000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 74d
-
-Encoding: 74e
-U-Bits: 011101000111011010110111001100000100
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 74e
-
-Encoding: 74e
-U-Bits: 011101000111011010101010000001001101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 74e
-
-Encoding: 74e
-U-Bits: 011101000111011010101101001001110011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 74e
-
-Encoding: 74f
-U-Bits: 101010100111011010101010000000100110
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 74f
-
-Encoding: 74f
-U-Bits: 101010100111011010110111001101101111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 74f
-
-Encoding: 74f
-U-Bits: 101010100111011010110000000101010001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 74f
-
-Encoding: 750
-U-Bits: 000001101111000010101011101110010011
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 750
-
-Encoding: 750
-U-Bits: 000001101111000010110110100011011010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 750
-
-Encoding: 750
-U-Bits: 000001101111000010110001101011100100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 750
-
-Encoding: 751
-U-Bits: 110110001111000010110110100010110001
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 751
-
-Encoding: 751
-U-Bits: 110110001111000010101011101111111000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 751
-
-Encoding: 751
-U-Bits: 110110001111000010101100100111000110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 751
-
-Encoding: 752
-U-Bits: 010000010111000010101100111101011010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 752
-
-Encoding: 752
-U-Bits: 010000010111000010110001110000010011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 752
-
-Encoding: 752
-U-Bits: 010000010111000010110110111000101101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 752
-
-Encoding: 753
-U-Bits: 100111110111000010110001110001111000
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 753
-
-Encoding: 753
-U-Bits: 100111110111000010101100111100110001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 753
-
-Encoding: 753
-U-Bits: 100111110111000010101011110100001111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 753
-
-Encoding: 754
-U-Bits: 001011110001000010110110111110001011
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 754
-
-Encoding: 754
-U-Bits: 001011110001000010101011110011000010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 754
-
-Encoding: 754
-U-Bits: 001011110001000010101100111011111100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 754
-
-Encoding: 755
-U-Bits: 111100010001000010101011110010101001
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 755
-
-Encoding: 755
-U-Bits: 111100010001000010110110111111100000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 755
-
-Encoding: 755
-U-Bits: 111100010001000010110001110111011110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 755
-
-Encoding: 756
-U-Bits: 011010001001000010110001101101000010
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 756
-
-Encoding: 756
-U-Bits: 011010001001000010101100100000001011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 756
-
-Encoding: 756
-U-Bits: 011010001001000010101011101000110101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 756
-
-Encoding: 757
-U-Bits: 101101101001000010101100100001100000
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 757
-
-Encoding: 757
-U-Bits: 101101101001000010110001101100101001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 757
-
-Encoding: 757
-U-Bits: 101101101001000010110110100100010111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 757
-
-Encoding: 758
-U-Bits: 000111001000100010101100111010010111
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 758
-
-Encoding: 758
-U-Bits: 000111001000100010110001110111011110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 758
-
-Encoding: 758
-U-Bits: 000111001000100010110110111111100000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 758
-
-Encoding: 759
-U-Bits: 110000101000100010110001110110110101
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 759
-
-Encoding: 759
-U-Bits: 110000101000100010101100111011111100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 759
-
-Encoding: 759
-U-Bits: 110000101000100010101011110011000010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 759
-
-Encoding: 75a
-U-Bits: 010110110000100010101011101001011110
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 75a
-
-Encoding: 75a
-U-Bits: 010110110000100010110110100100010111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 75a
-
-Encoding: 75a
-U-Bits: 010110110000100010110001101100101001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 75a
-
-Encoding: 75b
-U-Bits: 100001010000100010110110100101111100
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 75b
-
-Encoding: 75b
-U-Bits: 100001010000100010101011101000110101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 75b
-
-Encoding: 75b
-U-Bits: 100001010000100010101100100000001011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 75b
-
-Encoding: 75c
-U-Bits: 001101010110100010110001101010001111
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 75c
-
-Encoding: 75c
-U-Bits: 001101010110100010101100100111000110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 75c
-
-Encoding: 75c
-U-Bits: 001101010110100010101011101111111000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 75c
-
-Encoding: 75d
-U-Bits: 111010110110100010101100100110101101
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 75d
-
-Encoding: 75d
-U-Bits: 111010110110100010110001101011100100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 75d
-
-Encoding: 75d
-U-Bits: 111010110110100010110110100011011010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 75d
-
-Encoding: 75e
-U-Bits: 011100101110100010110110111001000110
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 75e
-
-Encoding: 75e
-U-Bits: 011100101110100010101011110100001111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 75e
-
-Encoding: 75e
-U-Bits: 011100101110100010101100111100110001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 75e
-
-Encoding: 75f
-U-Bits: 101011001110100010101011110101100100
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 75f
-
-Encoding: 75f
-U-Bits: 101011001110100010110110111000101101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 75f
-
-Encoding: 75f
-U-Bits: 101011001110100010110001110000010011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 75f
-
-Encoding: 760
-U-Bits: 000000011100100100101010000110000000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 760
-
-Encoding: 760
-U-Bits: 000000011100100100110111001011001001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 760
-
-Encoding: 760
-U-Bits: 000000011100100100110000000011110111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 760
-
-Encoding: 761
-U-Bits: 110111111100100100110111001010100010
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 761
-
-Encoding: 761
-U-Bits: 110111111100100100101010000111101011
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 761
-
-Encoding: 761
-U-Bits: 110111111100100100101101001111010101
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 761
-
-Encoding: 762
-U-Bits: 010001100100100100101101010101001001
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 762
-
-Encoding: 762
-U-Bits: 010001100100100100110000011000000000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 762
-
-Encoding: 762
-U-Bits: 010001100100100100110111010000111110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 762
-
-Encoding: 763
-U-Bits: 100110000100100100110000011001101011
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 763
-
-Encoding: 763
-U-Bits: 100110000100100100101101010100100010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 763
-
-Encoding: 763
-U-Bits: 100110000100100100101010011100011100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 763
-
-Encoding: 764
-U-Bits: 001010000010100100110111010110011000
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 764
-
-Encoding: 764
-U-Bits: 001010000010100100101010011011010001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 764
-
-Encoding: 764
-U-Bits: 001010000010100100101101010011101111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 764
-
-Encoding: 765
-U-Bits: 111101100010100100101010011010111010
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 765
-
-Encoding: 765
-U-Bits: 111101100010100100110111010111110011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 765
-
-Encoding: 765
-U-Bits: 111101100010100100110000011111001101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 765
-
-Encoding: 766
-U-Bits: 011011111010100100110000000101010001
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 766
-
-Encoding: 766
-U-Bits: 011011111010100100101101001000011000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 766
-
-Encoding: 766
-U-Bits: 011011111010100100101010000000100110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 766
-
-Encoding: 767
-U-Bits: 101100011010100100101101001001110011
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 767
-
-Encoding: 767
-U-Bits: 101100011010100100110000000100111010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 767
-
-Encoding: 767
-U-Bits: 101100011010100100110111001100000100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 767
-
-Encoding: 768
-U-Bits: 000110111011000100101101010010000100
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 768
-
-Encoding: 768
-U-Bits: 000110111011000100110000011111001101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 768
-
-Encoding: 768
-U-Bits: 000110111011000100110111010111110011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 768
-
-Encoding: 769
-U-Bits: 110001011011000100110000011110100110
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 769
-
-Encoding: 769
-U-Bits: 110001011011000100101101010011101111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 769
-
-Encoding: 769
-U-Bits: 110001011011000100101010011011010001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 769
-
-Encoding: 76a
-U-Bits: 010111000011000100101010000001001101
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 76a
-
-Encoding: 76a
-U-Bits: 010111000011000100110111001100000100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 76a
-
-Encoding: 76a
-U-Bits: 010111000011000100110000000100111010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 76a
-
-Encoding: 76b
-U-Bits: 100000100011000100110111001101101111
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 76b
-
-Encoding: 76b
-U-Bits: 100000100011000100101010000000100110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 76b
-
-Encoding: 76b
-U-Bits: 100000100011000100101101001000011000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 76b
-
-Encoding: 76c
-U-Bits: 001100100101000100110000000010011100
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 76c
-
-Encoding: 76c
-U-Bits: 001100100101000100101101001111010101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 76c
-
-Encoding: 76c
-U-Bits: 001100100101000100101010000111101011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 76c
-
-Encoding: 76d
-U-Bits: 111011000101000100101101001110111110
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 76d
-
-Encoding: 76d
-U-Bits: 111011000101000100110000000011110111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 76d
-
-Encoding: 76d
-U-Bits: 111011000101000100110111001011001001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 76d
-
-Encoding: 76e
-U-Bits: 011101011101000100110111010001010101
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 76e
-
-Encoding: 76e
-U-Bits: 011101011101000100101010011100011100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 76e
-
-Encoding: 76e
-U-Bits: 011101011101000100101101010100100010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 76e
-
-Encoding: 76f
-U-Bits: 101010111101000100101010011101110111
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 76f
-
-Encoding: 76f
-U-Bits: 101010111101000100110111010000111110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 76f
-
-Encoding: 76f
-U-Bits: 101010111101000100110000011000000000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 76f
-
-Encoding: 770
-U-Bits: 000001110101011100101011110011000010
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 770
-
-Encoding: 770
-U-Bits: 000001110101011100110110111110001011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 770
-
-Encoding: 770
-U-Bits: 000001110101011100110001110110110101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 770
-
-Encoding: 771
-U-Bits: 110110010101011100110110111111100000
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 771
-
-Encoding: 771
-U-Bits: 110110010101011100101011110010101001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 771
-
-Encoding: 771
-U-Bits: 110110010101011100101100111010010111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 771
-
-Encoding: 772
-U-Bits: 010000001101011100101100100000001011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 772
-
-Encoding: 772
-U-Bits: 010000001101011100110001101101000010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 772
-
-Encoding: 772
-U-Bits: 010000001101011100110110100101111100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 772
-
-Encoding: 773
-U-Bits: 100111101101011100110001101100101001
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 773
-
-Encoding: 773
-U-Bits: 100111101101011100101100100001100000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 773
-
-Encoding: 773
-U-Bits: 100111101101011100101011101001011110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 773
-
-Encoding: 774
-U-Bits: 001011101011011100110110100011011010
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 774
-
-Encoding: 774
-U-Bits: 001011101011011100101011101110010011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 774
-
-Encoding: 774
-U-Bits: 001011101011011100101100100110101101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 774
-
-Encoding: 775
-U-Bits: 111100001011011100101011101111111000
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 775
-
-Encoding: 775
-U-Bits: 111100001011011100110110100010110001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 775
-
-Encoding: 775
-U-Bits: 111100001011011100110001101010001111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 775
-
-Encoding: 776
-U-Bits: 011010010011011100110001110000010011
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 776
-
-Encoding: 776
-U-Bits: 011010010011011100101100111101011010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 776
-
-Encoding: 776
-U-Bits: 011010010011011100101011110101100100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 776
-
-Encoding: 777
-U-Bits: 101101110011011100101100111100110001
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 777
-
-Encoding: 777
-U-Bits: 101101110011011100110001110001111000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 777
-
-Encoding: 777
-U-Bits: 101101110011011100110110111001000110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 777
-
-Encoding: 778
-U-Bits: 000111010010111100101100100111000110
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 778
-
-Encoding: 778
-U-Bits: 000111010010111100110001101010001111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 778
-
-Encoding: 778
-U-Bits: 000111010010111100110110100010110001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 778
-
-Encoding: 779
-U-Bits: 110000110010111100110001101011100100
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 779
-
-Encoding: 779
-U-Bits: 110000110010111100101100100110101101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 779
-
-Encoding: 779
-U-Bits: 110000110010111100101011101110010011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 779
-
-Encoding: 77a
-U-Bits: 010110101010111100101011110100001111
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 77a
-
-Encoding: 77a
-U-Bits: 010110101010111100110110111001000110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 77a
-
-Encoding: 77a
-U-Bits: 010110101010111100110001110001111000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 77a
-
-Encoding: 77b
-U-Bits: 100001001010111100110110111000101101
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 77b
-
-Encoding: 77b
-U-Bits: 100001001010111100101011110101100100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 77b
-
-Encoding: 77b
-U-Bits: 100001001010111100101100111101011010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 77b
-
-Encoding: 77c
-U-Bits: 001101001100111100110001110111011110
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 77c
-
-Encoding: 77c
-U-Bits: 001101001100111100101100111010010111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 77c
-
-Encoding: 77c
-U-Bits: 001101001100111100101011110010101001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 77c
-
-Encoding: 77d
-U-Bits: 111010101100111100101100111011111100
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 77d
-
-Encoding: 77d
-U-Bits: 111010101100111100110001110110110101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 77d
-
-Encoding: 77d
-U-Bits: 111010101100111100110110111110001011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 77d
-
-Encoding: 77e
-U-Bits: 011100110100111100110110100100010111
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 77e
-
-Encoding: 77e
-U-Bits: 011100110100111100101011101001011110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 77e
-
-Encoding: 77e
-U-Bits: 011100110100111100101100100001100000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 77e
-
-Encoding: 77f
-U-Bits: 101011010100111100101011101000110101
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 77f
-
-Encoding: 77f
-U-Bits: 101011010100111100110110100101111100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 77f
-
-Encoding: 77f
-U-Bits: 101011010100111100110001101101000010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 77f
-
-Encoding: 780
-U-Bits: 000000000001110100101001110000010011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 780
-
-Encoding: 780
-U-Bits: 000000000001110100110100111101011010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 780
-
-Encoding: 780
-U-Bits: 000000000001110100110011110101100100
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 780
-
-Encoding: 781
-U-Bits: 110111100001110100110100111100110001
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 781
-
-Encoding: 781
-U-Bits: 110111100001110100101001110001111000
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 781
-
-Encoding: 781
-U-Bits: 110111100001110100101110111001000110
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 781
-
-Encoding: 782
-U-Bits: 010001111001110100101110100011011010
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 782
-
-Encoding: 782
-U-Bits: 010001111001110100110011101110010011
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 782
-
-Encoding: 782
-U-Bits: 010001111001110100110100100110101101
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 782
-
-Encoding: 783
-U-Bits: 100110011001110100110011101111111000
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 783
-
-Encoding: 783
-U-Bits: 100110011001110100101110100010110001
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 783
-
-Encoding: 783
-U-Bits: 100110011001110100101001101010001111
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 783
-
-Encoding: 784
-U-Bits: 001010011111110100110100100000001011
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 784
-
-Encoding: 784
-U-Bits: 001010011111110100101001101101000010
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 784
-
-Encoding: 784
-U-Bits: 001010011111110100101110100101111100
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 784
-
-Encoding: 785
-U-Bits: 111101111111110100101001101100101001
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 785
-
-Encoding: 785
-U-Bits: 111101111111110100110100100001100000
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 785
-
-Encoding: 785
-U-Bits: 111101111111110100110011101001011110
-S-Bits: 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 785
-
-Encoding: 786
-U-Bits: 011011100111110100110011110011000010
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 786
-
-Encoding: 786
-U-Bits: 011011100111110100101110111110001011
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 786
-
-Encoding: 786
-U-Bits: 011011100111110100101001110110110101
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 786
-
-Encoding: 787
-U-Bits: 101100000111110100101110111111100000
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 787
-
-Encoding: 787
-U-Bits: 101100000111110100110011110010101001
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 787
-
-Encoding: 787
-U-Bits: 101100000111110100110100111010010111
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 787
-
-Encoding: 788
-U-Bits: 000110100110010100101110100100010111
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 788
-
-Encoding: 788
-U-Bits: 000110100110010100110011101001011110
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 788
-
-Encoding: 788
-U-Bits: 000110100110010100110100100001100000
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 788
-
-Encoding: 789
-U-Bits: 110001000110010100110011101000110101
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 789
-
-Encoding: 789
-U-Bits: 110001000110010100101110100101111100
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 789
-
-Encoding: 789
-U-Bits: 110001000110010100101001101101000010
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 789
-
-Encoding: 78a
-U-Bits: 010111011110010100101001110111011110
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 78a
-
-Encoding: 78a
-U-Bits: 010111011110010100110100111010010111
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 78a
-
-Encoding: 78a
-U-Bits: 010111011110010100110011110010101001
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 78a
-
-Encoding: 78b
-U-Bits: 100000111110010100110100111011111100
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 78b
-
-Encoding: 78b
-U-Bits: 100000111110010100101001110110110101
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 78b
-
-Encoding: 78b
-U-Bits: 100000111110010100101110111110001011
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 78b
-
-Encoding: 78c
-U-Bits: 001100111000010100110011110100001111
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 78c
-
-Encoding: 78c
-U-Bits: 001100111000010100101110111001000110
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 78c
-
-Encoding: 78c
-U-Bits: 001100111000010100101001110001111000
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 78c
-
-Encoding: 78d
-U-Bits: 111011011000010100101110111000101101
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 78d
-
-Encoding: 78d
-U-Bits: 111011011000010100110011110101100100
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 78d
-
-Encoding: 78d
-U-Bits: 111011011000010100110100111101011010
-S-Bits: 81 81 81 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 78d
-
-Encoding: 78e
-U-Bits: 011101000000010100110100100111000110
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 78e
-
-Encoding: 78e
-U-Bits: 011101000000010100101001101010001111
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 78e
-
-Encoding: 78e
-U-Bits: 011101000000010100101110100010110001
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 78e
-
-Encoding: 78f
-U-Bits: 101010100000010100101001101011100100
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 78f
-
-Encoding: 78f
-U-Bits: 101010100000010100110100100110101101
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 78f
-
-Encoding: 78f
-U-Bits: 101010100000010100110011101110010011
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 78f
-
-Encoding: 790
-U-Bits: 000001101000001100101000000101010001
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 790
-
-Encoding: 790
-U-Bits: 000001101000001100110101001000011000
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 790
-
-Encoding: 790
-U-Bits: 000001101000001100110010000000100110
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 790
-
-Encoding: 791
-U-Bits: 110110001000001100110101001001110011
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 791
-
-Encoding: 791
-U-Bits: 110110001000001100101000000100111010
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 791
-
-Encoding: 791
-U-Bits: 110110001000001100101111001100000100
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 791
-
-Encoding: 792
-U-Bits: 010000010000001100101111010110011000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 792
-
-Encoding: 792
-U-Bits: 010000010000001100110010011011010001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 792
-
-Encoding: 792
-U-Bits: 010000010000001100110101010011101111
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 792
-
-Encoding: 793
-U-Bits: 100111110000001100110010011010111010
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 793
-
-Encoding: 793
-U-Bits: 100111110000001100101111010111110011
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 793
-
-Encoding: 793
-U-Bits: 100111110000001100101000011111001101
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 793
-
-Encoding: 794
-U-Bits: 001011110110001100110101010101001001
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 794
-
-Encoding: 794
-U-Bits: 001011110110001100101000011000000000
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 794
-
-Encoding: 794
-U-Bits: 001011110110001100101111010000111110
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 794
-
-Encoding: 795
-U-Bits: 111100010110001100101000011001101011
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 795
-
-Encoding: 795
-U-Bits: 111100010110001100110101010100100010
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 795
-
-Encoding: 795
-U-Bits: 111100010110001100110010011100011100
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 795
-
-Encoding: 796
-U-Bits: 011010001110001100110010000110000000
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 796
-
-Encoding: 796
-U-Bits: 011010001110001100101111001011001001
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 796
-
-Encoding: 796
-U-Bits: 011010001110001100101000000011110111
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 796
-
-Encoding: 797
-U-Bits: 101101101110001100101111001010100010
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 797
-
-Encoding: 797
-U-Bits: 101101101110001100110010000111101011
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 797
-
-Encoding: 797
-U-Bits: 101101101110001100110101001111010101
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 797
-
-Encoding: 798
-U-Bits: 000111001111101100101111010001010101
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 798
-
-Encoding: 798
-U-Bits: 000111001111101100110010011100011100
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 798
-
-Encoding: 798
-U-Bits: 000111001111101100110101010100100010
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 798
-
-Encoding: 799
-U-Bits: 110000101111101100110010011101110111
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 799
-
-Encoding: 799
-U-Bits: 110000101111101100101111010000111110
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 799
-
-Encoding: 799
-U-Bits: 110000101111101100101000011000000000
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 799
-
-Encoding: 79a
-U-Bits: 010110110111101100101000000010011100
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 79a
-
-Encoding: 79a
-U-Bits: 010110110111101100110101001111010101
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 79a
-
-Encoding: 79a
-U-Bits: 010110110111101100110010000111101011
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 79a
-
-Encoding: 79b
-U-Bits: 100001010111101100110101001110111110
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 79b
-
-Encoding: 79b
-U-Bits: 100001010111101100101000000011110111
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 79b
-
-Encoding: 79b
-U-Bits: 100001010111101100101111001011001001
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 79b
-
-Encoding: 79c
-U-Bits: 001101010001101100110010000001001101
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 79c
-
-Encoding: 79c
-U-Bits: 001101010001101100101111001100000100
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 79c
-
-Encoding: 79c
-U-Bits: 001101010001101100101000000100111010
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 79c
-
-Encoding: 79d
-U-Bits: 111010110001101100101111001101101111
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 79d
-
-Encoding: 79d
-U-Bits: 111010110001101100110010000000100110
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 79d
-
-Encoding: 79d
-U-Bits: 111010110001101100110101001000011000
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 79d
-
-Encoding: 79e
-U-Bits: 011100101001101100110101010010000100
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 79e
-
-Encoding: 79e
-U-Bits: 011100101001101100101000011111001101
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 79e
-
-Encoding: 79e
-U-Bits: 011100101001101100101111010111110011
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 79e
-
-Encoding: 79f
-U-Bits: 101011001001101100101000011110100110
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 79f
-
-Encoding: 79f
-U-Bits: 101011001001101100110101010011101111
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 79f
-
-Encoding: 79f
-U-Bits: 101011001001101100110010011011010001
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 79f
-
-Encoding: 7a0
-U-Bits: 000000011011101010101001101101000010
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 7a0
-
-Encoding: 7a0
-U-Bits: 000000011011101010110100100000001011
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 7a0
-
-Encoding: 7a0
-U-Bits: 000000011011101010110011101000110101
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 7a0
-
-Encoding: 7a1
-U-Bits: 110111111011101010110100100001100000
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 7a1
-
-Encoding: 7a1
-U-Bits: 110111111011101010101001101100101001
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 7a1
-
-Encoding: 7a1
-U-Bits: 110111111011101010101110100100010111
-S-Bits: 81 81 7f 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 7a1
-
-Encoding: 7a2
-U-Bits: 010001100011101010101110111110001011
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 7a2
-
-Encoding: 7a2
-U-Bits: 010001100011101010110011110011000010
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 7a2
-
-Encoding: 7a2
-U-Bits: 010001100011101010110100111011111100
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 7a2
-
-Encoding: 7a3
-U-Bits: 100110000011101010110011110010101001
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 7a3
-
-Encoding: 7a3
-U-Bits: 100110000011101010101110111111100000
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 7a3
-
-Encoding: 7a3
-U-Bits: 100110000011101010101001110111011110
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 7a3
-
-Encoding: 7a4
-U-Bits: 001010000101101010110100111101011010
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 7a4
-
-Encoding: 7a4
-U-Bits: 001010000101101010101001110000010011
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 7a4
-
-Encoding: 7a4
-U-Bits: 001010000101101010101110111000101101
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 7a4
-
-Encoding: 7a5
-U-Bits: 111101100101101010101001110001111000
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 7a5
-
-Encoding: 7a5
-U-Bits: 111101100101101010110100111100110001
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 7a5
-
-Encoding: 7a5
-U-Bits: 111101100101101010110011110100001111
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 7a5
-
-Encoding: 7a6
-U-Bits: 011011111101101010110011101110010011
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 7a6
-
-Encoding: 7a6
-U-Bits: 011011111101101010101110100011011010
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 7a6
-
-Encoding: 7a6
-U-Bits: 011011111101101010101001101011100100
-S-Bits: 7f 81 81 7f 81 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 7a6
-
-Encoding: 7a7
-U-Bits: 101100011101101010101110100010110001
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 7a7
-
-Encoding: 7a7
-U-Bits: 101100011101101010110011101111111000
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 7a7
-
-Encoding: 7a7
-U-Bits: 101100011101101010110100100111000110
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 7a7
-
-Encoding: 7a8
-U-Bits: 000110111100001010101110111001000110
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 7a8
-
-Encoding: 7a8
-U-Bits: 000110111100001010110011110100001111
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 7a8
-
-Encoding: 7a8
-U-Bits: 000110111100001010110100111100110001
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 7a8
-
-Encoding: 7a9
-U-Bits: 110001011100001010110011110101100100
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 7a9
-
-Encoding: 7a9
-U-Bits: 110001011100001010101110111000101101
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 7a9
-
-Encoding: 7a9
-U-Bits: 110001011100001010101001110000010011
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 7a9
-
-Encoding: 7aa
-U-Bits: 010111000100001010101001101010001111
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 7aa
-
-Encoding: 7aa
-U-Bits: 010111000100001010110100100111000110
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 7aa
-
-Encoding: 7aa
-U-Bits: 010111000100001010110011101111111000
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 7aa
-
-Encoding: 7ab
-U-Bits: 100000100100001010110100100110101101
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 7ab
-
-Encoding: 7ab
-U-Bits: 100000100100001010101001101011100100
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 7ab
-
-Encoding: 7ab
-U-Bits: 100000100100001010101110100011011010
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 7ab
-
-Encoding: 7ac
-U-Bits: 001100100010001010110011101001011110
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 7ac
-
-Encoding: 7ac
-U-Bits: 001100100010001010101110100100010111
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 7ac
-
-Encoding: 7ac
-U-Bits: 001100100010001010101001101100101001
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 7ac
-
-Encoding: 7ad
-U-Bits: 111011000010001010101110100101111100
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 7ad
-
-Encoding: 7ad
-U-Bits: 111011000010001010110011101000110101
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 7ad
-
-Encoding: 7ad
-U-Bits: 111011000010001010110100100000001011
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 7ad
-
-Encoding: 7ae
-U-Bits: 011101011010001010110100111010010111
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 7ae
-
-Encoding: 7ae
-U-Bits: 011101011010001010101001110111011110
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 7ae
-
-Encoding: 7ae
-U-Bits: 011101011010001010101110111111100000
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 7ae
-
-Encoding: 7af
-U-Bits: 101010111010001010101001110110110101
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 7af
-
-Encoding: 7af
-U-Bits: 101010111010001010110100111011111100
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 7af
-
-Encoding: 7af
-U-Bits: 101010111010001010110011110011000010
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 7af
-
-Encoding: 7b0
-U-Bits: 000001110010010010101000011000000000
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7b0
-
-Encoding: 7b0
-U-Bits: 000001110010010010110101010101001001
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 7b0
-
-Encoding: 7b0
-U-Bits: 000001110010010010110010011101110111
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 7b0
-
-Encoding: 7b1
-U-Bits: 110110010010010010110101010100100010
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 7b1
-
-Encoding: 7b1
-U-Bits: 110110010010010010101000011001101011
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 7b1
-
-Encoding: 7b1
-U-Bits: 110110010010010010101111010001010101
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 7b1
-
-Encoding: 7b2
-U-Bits: 010000001010010010101111001011001001
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 7b2
-
-Encoding: 7b2
-U-Bits: 010000001010010010110010000110000000
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7b2
-
-Encoding: 7b2
-U-Bits: 010000001010010010110101001110111110
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 7b2
-
-Encoding: 7b3
-U-Bits: 100111101010010010110010000111101011
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 7b3
-
-Encoding: 7b3
-U-Bits: 100111101010010010101111001010100010
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 7b3
-
-Encoding: 7b3
-U-Bits: 100111101010010010101000000010011100
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 7b3
-
-Encoding: 7b4
-U-Bits: 001011101100010010110101001000011000
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 7b4
-
-Encoding: 7b4
-U-Bits: 001011101100010010101000000101010001
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 7b4
-
-Encoding: 7b4
-U-Bits: 001011101100010010101111001101101111
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 7b4
-
-Encoding: 7b5
-U-Bits: 111100001100010010101000000100111010
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 7b5
-
-Encoding: 7b5
-U-Bits: 111100001100010010110101001001110011
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 7b5
-
-Encoding: 7b5
-U-Bits: 111100001100010010110010000001001101
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 7b5
-
-Encoding: 7b6
-U-Bits: 011010010100010010110010011011010001
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 7b6
-
-Encoding: 7b6
-U-Bits: 011010010100010010101111010110011000
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 7b6
-
-Encoding: 7b6
-U-Bits: 011010010100010010101000011110100110
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 7b6
-
-Encoding: 7b7
-U-Bits: 101101110100010010101111010111110011
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 7b7
-
-Encoding: 7b7
-U-Bits: 101101110100010010110010011010111010
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 7b7
-
-Encoding: 7b7
-U-Bits: 101101110100010010110101010010000100
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 7b7
-
-Encoding: 7b8
-U-Bits: 000111010101110010101111001100000100
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 7b8
-
-Encoding: 7b8
-U-Bits: 000111010101110010110010000001001101
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 7b8
-
-Encoding: 7b8
-U-Bits: 000111010101110010110101001001110011
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 7b8
-
-Encoding: 7b9
-U-Bits: 110000110101110010110010000000100110
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 7b9
-
-Encoding: 7b9
-U-Bits: 110000110101110010101111001101101111
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 7b9
-
-Encoding: 7b9
-U-Bits: 110000110101110010101000000101010001
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 7b9
-
-Encoding: 7ba
-U-Bits: 010110101101110010101000011111001101
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 7ba
-
-Encoding: 7ba
-U-Bits: 010110101101110010110101010010000100
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 7ba
-
-Encoding: 7ba
-U-Bits: 010110101101110010110010011010111010
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 7ba
-
-Encoding: 7bb
-U-Bits: 100001001101110010110101010011101111
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 7bb
-
-Encoding: 7bb
-U-Bits: 100001001101110010101000011110100110
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 7bb
-
-Encoding: 7bb
-U-Bits: 100001001101110010101111010110011000
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 7bb
-
-Encoding: 7bc
-U-Bits: 001101001011110010110010011100011100
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 7bc
-
-Encoding: 7bc
-U-Bits: 001101001011110010101111010001010101
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 7bc
-
-Encoding: 7bc
-U-Bits: 001101001011110010101000011001101011
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 7bc
-
-Encoding: 7bd
-U-Bits: 111010101011110010101111010000111110
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 7bd
-
-Encoding: 7bd
-U-Bits: 111010101011110010110010011101110111
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 7bd
-
-Encoding: 7bd
-U-Bits: 111010101011110010110101010101001001
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 7bd
-
-Encoding: 7be
-U-Bits: 011100110011110010110101001111010101
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 7be
-
-Encoding: 7be
-U-Bits: 011100110011110010101000000010011100
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 7be
-
-Encoding: 7be
-U-Bits: 011100110011110010101111001010100010
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 7be
-
-Encoding: 7bf
-U-Bits: 101011010011110010101000000011110111
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 7bf
-
-Encoding: 7bf
-U-Bits: 101011010011110010110101001110111110
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 7bf
-
-Encoding: 7bf
-U-Bits: 101011010011110010110010000110000000
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7bf
-
-Encoding: 7c0
-U-Bits: 000000000111010011010101010011101111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 7c0
-
-Encoding: 7c0
-U-Bits: 000000000111010011001000011110100110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 7c0
-
-Encoding: 7c0
-U-Bits: 000000000111010011001111010110011000
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 7c0
-
-Encoding: 7c1
-U-Bits: 110111100111010011001000011111001101
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 7c1
-
-Encoding: 7c1
-U-Bits: 110111100111010011010101010010000100
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 7c1
-
-Encoding: 7c1
-U-Bits: 110111100111010011010010011010111010
-S-Bits: 81 81 7f 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 7c1
-
-Encoding: 7c2
-U-Bits: 010001111111010011010010000000100110
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 7c2
-
-Encoding: 7c2
-U-Bits: 010001111111010011001111001101101111
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 7c2
-
-Encoding: 7c2
-U-Bits: 010001111111010011001000000101010001
-S-Bits: 7f 81 7f 7f 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 7c2
-
-Encoding: 7c3
-U-Bits: 100110011111010011001111001100000100
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 7c3
-
-Encoding: 7c3
-U-Bits: 100110011111010011010010000001001101
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 7c3
-
-Encoding: 7c3
-U-Bits: 100110011111010011010101001001110011
-S-Bits: 81 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 7c3
-
-Encoding: 7c4
-U-Bits: 001010011001010011001000000011110111
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 7c4
-
-Encoding: 7c4
-U-Bits: 001010011001010011010101001110111110
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 7c4
-
-Encoding: 7c4
-U-Bits: 001010011001010011010010000110000000
-S-Bits: 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7c4
-
-Encoding: 7c5
-U-Bits: 111101111001010011010101001111010101
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 7c5
-
-Encoding: 7c5
-U-Bits: 111101111001010011001000000010011100
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 7c5
-
-Encoding: 7c5
-U-Bits: 111101111001010011001111001010100010
-S-Bits: 81 81 81 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 7c5
-
-Encoding: 7c6
-U-Bits: 011011100001010011001111010000111110
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 7c6
-
-Encoding: 7c6
-U-Bits: 011011100001010011010010011101110111
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 7c6
-
-Encoding: 7c6
-U-Bits: 011011100001010011010101010101001001
-S-Bits: 7f 81 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 7c6
-
-Encoding: 7c7
-U-Bits: 101100000001010011010010011100011100
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 7c7
-
-Encoding: 7c7
-U-Bits: 101100000001010011001111010001010101
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 7c7
-
-Encoding: 7c7
-U-Bits: 101100000001010011001000011001101011
-S-Bits: 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 7c7
-
-Encoding: 7c8
-U-Bits: 000110100000110011010010000111101011
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 7c8
-
-Encoding: 7c8
-U-Bits: 000110100000110011001111001010100010
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 7c8
-
-Encoding: 7c8
-U-Bits: 000110100000110011001000000010011100
-S-Bits: 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 7c8
-
-Encoding: 7c9
-U-Bits: 110001000000110011001111001011001001
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 7c9
-
-Encoding: 7c9
-U-Bits: 110001000000110011010010000110000000
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7c9
-
-Encoding: 7c9
-U-Bits: 110001000000110011010101001110111110
-S-Bits: 81 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 7c9
-
-Encoding: 7ca
-U-Bits: 010111011000110011010101010100100010
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 7ca
-
-Encoding: 7ca
-U-Bits: 010111011000110011001000011001101011
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 7ca
-
-Encoding: 7ca
-U-Bits: 010111011000110011001111010001010101
-S-Bits: 7f 81 7f 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 7ca
-
-Encoding: 7cb
-U-Bits: 100000111000110011001000011000000000
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7cb
-
-Encoding: 7cb
-U-Bits: 100000111000110011010101010101001001
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 7cb
-
-Encoding: 7cb
-U-Bits: 100000111000110011010010011101110111
-S-Bits: 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 7cb
-
-Encoding: 7cc
-U-Bits: 001100111110110011001111010111110011
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 7cc
-
-Encoding: 7cc
-U-Bits: 001100111110110011010010011010111010
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 7cc
-
-Encoding: 7cc
-U-Bits: 001100111110110011010101010010000100
-S-Bits: 7f 7f 81 81 7f 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 7cc
-
-Encoding: 7cd
-U-Bits: 111011011110110011010010011011010001
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 7cd
-
-Encoding: 7cd
-U-Bits: 111011011110110011001111010110011000
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 7cd
-
-Encoding: 7cd
-U-Bits: 111011011110110011001000011110100110
-S-Bits: 81 81 81 7f 81 81 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 7cd
-
-Encoding: 7ce
-U-Bits: 011101000110110011001000000100111010
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 7ce
-
-Encoding: 7ce
-U-Bits: 011101000110110011010101001001110011
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 7ce
-
-Encoding: 7ce
-U-Bits: 011101000110110011010010000001001101
-S-Bits: 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 7ce
-
-Encoding: 7cf
-U-Bits: 101010100110110011010101001000011000
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 7cf
-
-Encoding: 7cf
-U-Bits: 101010100110110011001000000101010001
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 7cf
-
-Encoding: 7cf
-U-Bits: 101010100110110011001111001101101111
-S-Bits: 81 7f 81 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 7cf
-
-Encoding: 7d0
-U-Bits: 000001101110101011010100100110101101
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 7d0
-
-Encoding: 7d0
-U-Bits: 000001101110101011001001101011100100
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 7d0
-
-Encoding: 7d0
-U-Bits: 000001101110101011001110100011011010
-S-Bits: 7f 7f 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 7d0
-
-Encoding: 7d1
-U-Bits: 110110001110101011001001101010001111
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 7d1
-
-Encoding: 7d1
-U-Bits: 110110001110101011010100100111000110
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 7d1
-
-Encoding: 7d1
-U-Bits: 110110001110101011010011101111111000
-S-Bits: 81 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 7d1
-
-Encoding: 7d2
-U-Bits: 010000010110101011010011110101100100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 7d2
-
-Encoding: 7d2
-U-Bits: 010000010110101011001110111000101101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 7d2
-
-Encoding: 7d2
-U-Bits: 010000010110101011001001110000010011
-S-Bits: 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 7d2
-
-Encoding: 7d3
-U-Bits: 100111110110101011001110111001000110
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 7d3
-
-Encoding: 7d3
-U-Bits: 100111110110101011010011110100001111
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 7d3
-
-Encoding: 7d3
-U-Bits: 100111110110101011010100111100110001
-S-Bits: 81 7f 7f 81 81 81 81 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 7d3
-
-Encoding: 7d4
-U-Bits: 001011110000101011001001110110110101
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 7d4
-
-Encoding: 7d4
-U-Bits: 001011110000101011010100111011111100
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 7d4
-
-Encoding: 7d4
-U-Bits: 001011110000101011010011110011000010
-S-Bits: 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 7d4
-
-Encoding: 7d5
-U-Bits: 111100010000101011010100111010010111
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 7d5
-
-Encoding: 7d5
-U-Bits: 111100010000101011001001110111011110
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 7d5
-
-Encoding: 7d5
-U-Bits: 111100010000101011001110111111100000
-S-Bits: 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 7d5
-
-Encoding: 7d6
-U-Bits: 011010001000101011001110100101111100
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 7d6
-
-Encoding: 7d6
-U-Bits: 011010001000101011010011101000110101
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 7d6
-
-Encoding: 7d6
-U-Bits: 011010001000101011010100100000001011
-S-Bits: 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 7d6
-
-Encoding: 7d7
-U-Bits: 101101101000101011010011101001011110
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 7d7
-
-Encoding: 7d7
-U-Bits: 101101101000101011001110100100010111
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 7d7
-
-Encoding: 7d7
-U-Bits: 101101101000101011001001101100101001
-S-Bits: 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 7d7
-
-Encoding: 7d8
-U-Bits: 000111001001001011010011110010101001
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 7d8
-
-Encoding: 7d8
-U-Bits: 000111001001001011001110111111100000
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 7d8
-
-Encoding: 7d8
-U-Bits: 000111001001001011001001110111011110
-S-Bits: 7f 7f 7f 81 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 7d8
-
-Encoding: 7d9
-U-Bits: 110000101001001011001110111110001011
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 7d9
-
-Encoding: 7d9
-U-Bits: 110000101001001011010011110011000010
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 7d9
-
-Encoding: 7d9
-U-Bits: 110000101001001011010100111011111100
-S-Bits: 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 7d9
-
-Encoding: 7da
-U-Bits: 010110110001001011010100100001100000
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 7da
-
-Encoding: 7da
-U-Bits: 010110110001001011001001101100101001
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 7da
-
-Encoding: 7da
-U-Bits: 010110110001001011001110100100010111
-S-Bits: 7f 81 7f 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 7da
-
-Encoding: 7db
-U-Bits: 100001010001001011001001101101000010
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 7db
-
-Encoding: 7db
-U-Bits: 100001010001001011010100100000001011
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 7db
-
-Encoding: 7db
-U-Bits: 100001010001001011010011101000110101
-S-Bits: 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 7db
-
-Encoding: 7dc
-U-Bits: 001101010111001011001110100010110001
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 7dc
-
-Encoding: 7dc
-U-Bits: 001101010111001011010011101111111000
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 7dc
-
-Encoding: 7dc
-U-Bits: 001101010111001011010100100111000110
-S-Bits: 7f 7f 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 7dc
-
-Encoding: 7dd
-U-Bits: 111010110111001011010011101110010011
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 7dd
-
-Encoding: 7dd
-U-Bits: 111010110111001011001110100011011010
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 7dd
-
-Encoding: 7dd
-U-Bits: 111010110111001011001001101011100100
-S-Bits: 81 81 81 7f 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 7dd
-
-Encoding: 7de
-U-Bits: 011100101111001011001001110001111000
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 7de
-
-Encoding: 7de
-U-Bits: 011100101111001011010100111100110001
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 7de
-
-Encoding: 7de
-U-Bits: 011100101111001011010011110100001111
-S-Bits: 7f 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 7de
-
-Encoding: 7df
-U-Bits: 101011001111001011010100111101011010
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 7df
-
-Encoding: 7df
-U-Bits: 101011001111001011001001110000010011
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 7df
-
-Encoding: 7df
-U-Bits: 101011001111001011001110111000101101
-S-Bits: 81 7f 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 7df
-
-Encoding: 7e0
-U-Bits: 000000011101001101010101001110111110
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f
-Decoded: 7e0
-
-Encoding: 7e0
-U-Bits: 000000011101001101001000000011110111
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 7e0
-
-Encoding: 7e0
-U-Bits: 000000011101001101001111001011001001
-S-Bits: 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 7e0
-
-Encoding: 7e1
-U-Bits: 110111111101001101001000000010011100
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 7f
-Decoded: 7e1
-
-Encoding: 7e1
-U-Bits: 110111111101001101010101001111010101
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 7e1
-
-Encoding: 7e1
-U-Bits: 110111111101001101010010000111101011
-S-Bits: 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 7e1
-
-Encoding: 7e2
-U-Bits: 010001100101001101010010011101110111
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81
-Decoded: 7e2
-
-Encoding: 7e2
-U-Bits: 010001100101001101001111010000111110
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 7e2
-
-Encoding: 7e2
-U-Bits: 010001100101001101001000011000000000
-S-Bits: 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7e2
-
-Encoding: 7e3
-U-Bits: 100110000101001101001111010001010101
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81
-Decoded: 7e3
-
-Encoding: 7e3
-U-Bits: 100110000101001101010010011100011100
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 7e3
-
-Encoding: 7e3
-U-Bits: 100110000101001101010101010100100010
-S-Bits: 81 7f 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 7e3
-
-Encoding: 7e4
-U-Bits: 001010000011001101001000011110100110
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f
-Decoded: 7e4
-
-Encoding: 7e4
-U-Bits: 001010000011001101010101010011101111
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 7e4
-
-Encoding: 7e4
-U-Bits: 001010000011001101010010011011010001
-S-Bits: 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 7e4
-
-Encoding: 7e5
-U-Bits: 111101100011001101010101010010000100
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f
-Decoded: 7e5
-
-Encoding: 7e5
-U-Bits: 111101100011001101001000011111001101
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 7e5
-
-Encoding: 7e5
-U-Bits: 111101100011001101001111010111110011
-S-Bits: 81 81 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 7e5
-
-Encoding: 7e6
-U-Bits: 011011111011001101001111001101101111
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 81 81 7f 81 81 81 81
-Decoded: 7e6
-
-Encoding: 7e6
-U-Bits: 011011111011001101010010000000100110
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 7e6
-
-Encoding: 7e6
-U-Bits: 011011111011001101010101001000011000
-S-Bits: 7f 81 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 7e6
-
-Encoding: 7e7
-U-Bits: 101100011011001101010010000001001101
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 81
-Decoded: 7e7
-
-Encoding: 7e7
-U-Bits: 101100011011001101001111001100000100
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 7e7
-
-Encoding: 7e7
-U-Bits: 101100011011001101001000000100111010
-S-Bits: 81 7f 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 7e7
-
-Encoding: 7e8
-U-Bits: 000110111010101101010010011010111010
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 81 7f
-Decoded: 7e8
-
-Encoding: 7e8
-U-Bits: 000110111010101101001111010111110011
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81
-Decoded: 7e8
-
-Encoding: 7e8
-U-Bits: 000110111010101101001000011111001101
-S-Bits: 7f 7f 7f 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 81
-Decoded: 7e8
-
-Encoding: 7e9
-U-Bits: 110001011010101101001111010110011000
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 7f 7f
-Decoded: 7e9
-
-Encoding: 7e9
-U-Bits: 110001011010101101010010011011010001
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81
-Decoded: 7e9
-
-Encoding: 7e9
-U-Bits: 110001011010101101010101010011101111
-S-Bits: 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81
-Decoded: 7e9
-
-Encoding: 7ea
-U-Bits: 010111000010101101010101001001110011
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 81 81
-Decoded: 7ea
-
-Encoding: 7ea
-U-Bits: 010111000010101101001000000100111010
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f
-Decoded: 7ea
-
-Encoding: 7ea
-U-Bits: 010111000010101101001111001100000100
-S-Bits: 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f
-Decoded: 7ea
-
-Encoding: 7eb
-U-Bits: 100000100010101101001000000101010001
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81
-Decoded: 7eb
-
-Encoding: 7eb
-U-Bits: 100000100010101101010101001000011000
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f
-Decoded: 7eb
-
-Encoding: 7eb
-U-Bits: 100000100010101101010010000000100110
-S-Bits: 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f
-Decoded: 7eb
-
-Encoding: 7ec
-U-Bits: 001100100100101101001111001010100010
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f
-Decoded: 7ec
-
-Encoding: 7ec
-U-Bits: 001100100100101101010010000111101011
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 81 81 7f 81 7f 81 81
-Decoded: 7ec
-
-Encoding: 7ec
-U-Bits: 001100100100101101010101001111010101
-S-Bits: 7f 7f 81 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 7f 81
-Decoded: 7ec
-
-Encoding: 7ed
-U-Bits: 111011000100101101010010000110000000
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7ed
-
-Encoding: 7ed
-U-Bits: 111011000100101101001111001011001001
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 7f 81 7f 7f 81
-Decoded: 7ed
-
-Encoding: 7ed
-U-Bits: 111011000100101101001000000011110111
-S-Bits: 81 81 81 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 81
-Decoded: 7ed
-
-Encoding: 7ee
-U-Bits: 011101011100101101001000011001101011
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81
-Decoded: 7ee
-
-Encoding: 7ee
-U-Bits: 011101011100101101010101010100100010
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f
-Decoded: 7ee
-
-Encoding: 7ee
-U-Bits: 011101011100101101010010011100011100
-S-Bits: 7f 81 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 7f 7f
-Decoded: 7ee
-
-Encoding: 7ef
-U-Bits: 101010111100101101010101010101001001
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81
-Decoded: 7ef
-
-Encoding: 7ef
-U-Bits: 101010111100101101001000011000000000
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f
-Decoded: 7ef
-
-Encoding: 7ef
-U-Bits: 101010111100101101001111010000111110
-S-Bits: 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f
-Decoded: 7ef
-
-Encoding: 7f0
-U-Bits: 000001110100110101010100111011111100
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f
-Decoded: 7f0
-
-Encoding: 7f0
-U-Bits: 000001110100110101001001110110110101
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 7f0
-
-Encoding: 7f0
-U-Bits: 000001110100110101001110111110001011
-S-Bits: 7f 7f 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 7f0
-
-Encoding: 7f1
-U-Bits: 110110010100110101001001110111011110
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 7f
-Decoded: 7f1
-
-Encoding: 7f1
-U-Bits: 110110010100110101010100111010010111
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 7f1
-
-Encoding: 7f1
-U-Bits: 110110010100110101010011110010101001
-S-Bits: 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 7f1
-
-Encoding: 7f2
-U-Bits: 010000001100110101010011101000110101
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81
-Decoded: 7f2
-
-Encoding: 7f2
-U-Bits: 010000001100110101001110100101111100
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 7f2
-
-Encoding: 7f2
-U-Bits: 010000001100110101001001101101000010
-S-Bits: 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 7f2
-
-Encoding: 7f3
-U-Bits: 100111101100110101001110100100010111
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81
-Decoded: 7f3
-
-Encoding: 7f3
-U-Bits: 100111101100110101010011101001011110
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 7f3
-
-Encoding: 7f3
-U-Bits: 100111101100110101010100100001100000
-S-Bits: 81 7f 7f 81 81 81 81 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 7f3
-
-Encoding: 7f4
-U-Bits: 001011101010110101001001101011100100
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 81 7f 7f 81 7f 7f
-Decoded: 7f4
-
-Encoding: 7f4
-U-Bits: 001011101010110101010100100110101101
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 7f4
-
-Encoding: 7f4
-U-Bits: 001011101010110101010011101110010011
-S-Bits: 7f 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 7f4
-
-Encoding: 7f5
-U-Bits: 111100001010110101010100100111000110
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 7f
-Decoded: 7f5
-
-Encoding: 7f5
-U-Bits: 111100001010110101001001101010001111
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 7f5
-
-Encoding: 7f5
-U-Bits: 111100001010110101001110100010110001
-S-Bits: 81 81 81 81 7f 7f 7f 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 7f5
-
-Encoding: 7f6
-U-Bits: 011010010010110101001110111000101101
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 7f 81 81 7f 81
-Decoded: 7f6
-
-Encoding: 7f6
-U-Bits: 011010010010110101010011110101100100
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 7f6
-
-Encoding: 7f6
-U-Bits: 011010010010110101010100111101011010
-S-Bits: 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 7f6
-
-Encoding: 7f7
-U-Bits: 101101110010110101010011110100001111
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 81 81
-Decoded: 7f7
-
-Encoding: 7f7
-U-Bits: 101101110010110101001110111001000110
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 7f7
-
-Encoding: 7f7
-U-Bits: 101101110010110101001001110001111000
-S-Bits: 81 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 7f7
-
-Encoding: 7f8
-U-Bits: 000111010011010101010011101111111000
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f
-Decoded: 7f8
-
-Encoding: 7f8
-U-Bits: 000111010011010101001110100010110001
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 7f 81 81 7f 7f 7f 81
-Decoded: 7f8
-
-Encoding: 7f8
-U-Bits: 000111010011010101001001101010001111
-S-Bits: 7f 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81
-Decoded: 7f8
-
-Encoding: 7f9
-U-Bits: 110000110011010101001110100011011010
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f
-Decoded: 7f9
-
-Encoding: 7f9
-U-Bits: 110000110011010101010011101110010011
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 81 81
-Decoded: 7f9
-
-Encoding: 7f9
-U-Bits: 110000110011010101010100100110101101
-S-Bits: 81 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 7f 81 81 7f 81
-Decoded: 7f9
-
-Encoding: 7fa
-U-Bits: 010110101011010101010100111100110001
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 81
-Decoded: 7fa
-
-Encoding: 7fa
-U-Bits: 010110101011010101001001110001111000
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 81 81 81 81 7f 7f 7f
-Decoded: 7fa
-
-Encoding: 7fa
-U-Bits: 010110101011010101001110111001000110
-S-Bits: 7f 81 7f 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 81 7f
-Decoded: 7fa
-
-Encoding: 7fb
-U-Bits: 100001001011010101001001110000010011
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 81 81
-Decoded: 7fb
-
-Encoding: 7fb
-U-Bits: 100001001011010101010100111101011010
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f
-Decoded: 7fb
-
-Encoding: 7fb
-U-Bits: 100001001011010101010011110101100100
-S-Bits: 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 7f 81 7f 7f
-Decoded: 7fb
-
-Encoding: 7fc
-U-Bits: 001101001101010101001110111111100000
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 7f 7f
-Decoded: 7fc
-
-Encoding: 7fc
-U-Bits: 001101001101010101010011110010101001
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81
-Decoded: 7fc
-
-Encoding: 7fc
-U-Bits: 001101001101010101010100111010010111
-S-Bits: 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81
-Decoded: 7fc
-
-Encoding: 7fd
-U-Bits: 111010101101010101010011110011000010
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 81 7f
-Decoded: 7fd
-
-Encoding: 7fd
-U-Bits: 111010101101010101001110111110001011
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 81 81 81 81 7f 7f 7f 81 7f 81 81
-Decoded: 7fd
-
-Encoding: 7fd
-U-Bits: 111010101101010101001001110110110101
-S-Bits: 81 81 81 7f 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 81 7f 81
-Decoded: 7fd
-
-Encoding: 7fe
-U-Bits: 011100110101010101001001101100101001
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 7f 7f 81
-Decoded: 7fe
-
-Encoding: 7fe
-U-Bits: 011100110101010101010100100001100000
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f
-Decoded: 7fe
-
-Encoding: 7fe
-U-Bits: 011100110101010101010011101001011110
-S-Bits: 7f 81 81 81 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 7f
-Decoded: 7fe
-
-Encoding: 7ff
-U-Bits: 101011010101010101010100100000001011
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81
-Decoded: 7ff
-
-Encoding: 7ff
-U-Bits: 101011010101010101001001101101000010
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 7f 7f 81 81 7f 81 81 7f 81 7f 7f 7f 7f 81 7f
-Decoded: 7ff
-
-Encoding: 7ff
-U-Bits: 101011010101010101001110100101111100
-S-Bits: 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 7f
-Decoded: 7ff
+Decoded RA11: 0x667
+Decoded RA11: 0x666
+Decoded RA11: 0x66f
+Decoded RA11: 0x66c
+Decoded RA11: 0x667
+Decoded RA11: 0x676
Encoding: 03 03 01 00
U-Bits: 111001110011000011100111001100001101001111000000000011010000011010110111111100
@@ -34725,22 +172,22 @@ S-Bits:
Decoded: 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02
-Encoding: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee
+Encoding: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd
U-Bits:
-0E1E0E0E1E0E0E1E1E0E1E0E0E0E0E0E0E0E0E1E1E0E0E1E1E1E1E1E1 23 00 E1E1E1E1E1E0E1E1E1E0E0E1E0E1E1E1E0E0E1E0E0E1E1E1E1E0E0E0E
-0E1E1E0E0E0E0E1E0E1E1E1E0E0E1E1E0E0E1E0E1E0E1E0E0E0E1E0E1 23 00 E0E0E0E1E1E1E1E1E0E1E1E1E0E0E0E0E1E1E1E0E0E1E1E0E1E1E1E0E
-E0E0E0E1E1E1E1E1E0E1E0E0E1E1E0E0E1E0E1E0E0E1E1E0E0E0E0E0E 00 23 0E0E0E1E0E1E0E1E0E0E1E1E0E1E0E0E0E1E1E1E0E0E0E0E1E0E1E0E1
-E1E0E0E1E1E1E1E1E1E0E0E1E1E1E0E0E0E1E1E1E1E1E1E1E1E0E1E1E 00 23 0E0E1E0E1E0E0E0E1E0E0E1E1E0E1E1E1E1E0E1E1E0E1E0E0E1E0E1E1
+0E0E0E0E1E0E0E0E0E0E0E1E1E0E1E0E0E0E1E1E1E0E1E0E0E0E1E1E1 23 00 E1E0E1E0E1E1E1E0E1E1E0E0E0E0E1E0E1E0E0E0E1E1E0E1E1E0E1E0E
+1E0E1E0E0E1E0E0E0E1E1E1E0E0E1E1E1E0E1E1E0E1E0E0E1E0E1E0E0 23 00 E1E1E1E1E1E1E0E1E1E0E0E1E1E0E0E0E0E0E0E0E1E1E1E1E0E0E1E1E
+E0E1E1E1E0E0E0E1E1E1E1E1E1E0E0E1E0E1E1E1E0E0E1E0E0E1E1E1E 00 23 0E1E0E0E1E1E0E1E1E1E1E1E1E1E1E1E0E1E0E0E1E1E1E0E0E0E0E1E1
+E0E0E0E1E0E0E0E1E1E0E1E0E0E0E1E0E1E0E1E0E0E0E0E0E1E0E1E0E 00 23 1E0E1E0E0E1E0E1E0E0E0E0E1E0E1E1E1E1E1E0E1E0E1E1E1E1E0E1E0
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE 23 23 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE 23 23 EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
S-Bits:
-7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81
-7f 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 81 81 81 81 7f 81
-81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 81
-81 81 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 81
+7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81
+81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81 81 81 81
+81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81
+81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f 81 81 81 81 81 81 81 81 81 7f 81 81 81 7f
81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81
81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81
-Decoded: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee
+Decoded: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd
tch_hr_decode: n_errors=10 n_bits_total=211 ber=0.05
Encoding: 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
@@ -34823,34 +270,34 @@ S-Bits:
7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 81
7f 81 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 81 7f 81 7f 7f 7f 81 81 81 7f 81 81 81 7f 7f 7f 81 81 81 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 81 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 81 81 7f 7f 81 7f 81 81 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 7f 7f 81 81
Decoded: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 2f
-pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22
+pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
-Encoding: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 2f 5f c6 36 43 44 03
+Encoding: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 af 5f c6 36 43 44 03
U-Bits:
-100010111000001100000000001101001000000000101100111011001 00 00 101110010011001000110010000011100101000011110001101100001
-001011110100100101010101111111110011111011111110001101001 01 00 011010010110101000111110001100110011101101111000100101110
-110001101010001000010101011111101011000111010110110011110 00 00 101001101100011001101100110110000101000001000001011000110
-011100001010000100100101001000110110100111000000101110000 00 01 111100110000011001001100110100111101011010011110100010011
+100010111000001100000000001101001000000000101100111011001 00 00 101110011011001000110000000011100101000011110001101100001
+001011110100100101010101111111110011111011111110011101001 01 00 011010110110101000110110001100110011101101111000100101100
+110001100000001000010101011111101011000111010111110011110 00 00 100001101100011001101100110110000101000001000001011001110
+011100001010000100100101001000100110100111010000101110000 00 01 111100110000011001001101110100111101001010011110100110011
S-Bits:
-81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81
-7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 81 7f
-81 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 7f 81 81 81 81 7f 7f 7f 81 7f 81 7f 7f 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 81 7f
-7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 7f 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 81
-Decoded: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 2f 5f c6 36 43 44 03
-pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33
+81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f 81 81 7f 7f 81 7f 7f 81 7f 81 81 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81
+7f 7f 81 7f 81 81 81 81 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 81 81 81 7f 7f 81 81 81 81 81 7f 81 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81 7f 81 81 7f 81 7f 81 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 81 7f 81 81 7f 81 81 81 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f
+81 81 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 81 81 81 81 81 7f 81 7f 81 81 7f 7f 7f 81 81 81 7f 81 7f 81 81 81 81 81 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 7f 81 81 81 7f
+7f 81 81 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 81 7f 7f 81 81 81 7f 81 7f 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 81 81 81 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 81 7f 7f 81 81 7f 81 81 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 7f 81 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 81 81
+Decoded: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 af 5f c6 36 43 44 03
+pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
-Encoding: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 2f 5f c6 36 43 44 03 d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 28
+Encoding: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 28
U-Bits:
-100010100101111101110101011001010100000101001001011011000 00 00 001000000000001100000110001011010111110101110101001100010
-010101000000101010101011110000010101000100000011010001101 00 01 010111110101110101010111011111100111010011010000111110111
-010111100101110011111100101110001010100110000010100000100 00 01 010111101110101111110011110101111101110100011000101111111
-011110100110000011110100111111101111111111111010101110000 01 00 001010101010101010101010111010110110000000100000101001011
+100010100101111101110101011001010100000101001001011011000 00 00 001010001000001100000111001011010111110101110101001101010
+010101000000001010101011110000010101000100000011010001101 00 01 010111010101110101010111011111100111010011010000111110111
+010111100101110011111100101111001010100110000011100000100 00 01 010111101110101111110011110101111101110100011000101111111
+011110100110000010110100111111111111111111111010101110000 01 00 001010101010101010101011111010110110010000100000101001011
S-Bits:
-81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 81 7f
-7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 81 81
-7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81
-7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81
-Decoded: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 2f 5f c6 36 43 44 03 d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 28
+81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 81 7f 81 81 7f 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 81 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 81 7f 81 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 7f 81 81 7f 81 7f 81 7f
+7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 81 81 7f 81 7f 81 7f 81 7f 81 81 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 7f 81 7f 7f 81 81 7f 81 7f 7f 7f 7f 81 81 81 81 81 7f 81 81 81
+7f 81 7f 81 81 81 81 7f 7f 81 7f 81 81 81 7f 7f 81 81 81 81 81 81 7f 7f 81 7f 81 81 81 81 7f 7f 81 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 81 81 81 7f 81 81 81 7f 81 7f 81 81 81 81 81 81 7f 7f 81 81 81 81 7f 81 7f 81 81 81 81 81 7f 81 81 81 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 81 81 81 81 81 81 81
+7f 81 81 81 81 7f 81 7f 7f 81 81 7f 7f 7f 7f 7f 81 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 7f 81 81 81 81 81 7f 81 7f 81 81 7f 81 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81
+Decoded: a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8 42 a3 af 5f c6 36 43 44 ab a3 af 5f c6 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 28
pdtch_decode: n_errors=0 n_bits_total=444 ber=0.00
Encoding: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
@@ -34879,7 +326,7 @@ S-Bits:
7f 7f 81 7f 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 7f 81 7f 81 81 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 7f 81 7f 81 81 81 81 7f 81 7f 81 7f 7f 81 81 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 7f 7f 81 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 7f 81 7f 81 81
81 7f 81 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 81 7f 7f 81 81 81 7f 81 7f 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 7f 7f 7f 81 7f 7f 7f 7f 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 81 7f 7f 81 81 7f 7f 81 7f
Decoded: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00
-pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22
+pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
Encoding: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
U-Bits:
@@ -34893,7 +340,7 @@ S-Bits:
81 7f 81 7f 81 81 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 81 7f 7f 81 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 7f
7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 7f 81 7f 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 81 7f 81 7f 81 7f 81 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 7f 81 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 81 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 81 81 81 7f 7f 7f
Decoded: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33
+pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
Encoding: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
U-Bits:
@@ -34909,4 +356,233 @@ S-Bits:
Decoded: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
pdtch_decode: n_errors=0 n_bits_total=444 ber=0.00
+Encoding: 07 40 00 16 10 42 02 56 56 56 86 80 03 56 56 56 56 56 56 56 56 56 56 56 56 56 00
+U-Bits:
+100111000001011111111010101010011100010011111010000110110 00 00 000110110000010000000011000000000110011001100110010000000
+100010010000000000001101001111110010100100001000100111010 00 01 001001010101100010111000000000010111100011100011010001001
+111100001101001011010000110111100001001001000001000100110 00 01 000000110110110100011111011110011001101100000111100010001
+001000110011010110011011001011100100010001101101011100000 01 00 000010000010010110001111111001011101011000000011101100010
+S-Bits:
+81 7f 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 81 81 81 81 81 81 81 81 7f 81 7f 81 7f 81 7f 81 7f 7f 81 81 81 7f 7f 7f 81 7f 7f 81 81 81 81 81 7f 81 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f
+81 7f 7f 7f 81 7f 7f 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 81 81 81 81 81 7f 7f 81 7f 81 7f 7f 81 7f 7f 7f 7f 81 7f 7f 7f 81 7f 7f 81 81 81 7f 81 7f 7f 81 7f 7f 81 7f 7f 81 7f 81 7f 81 7f 81 81 7f 7f 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 81 7f 81 81 81 81 7f 7f 7f 81 81 81 7f 7f 7f 81 81 7f 81 7f 7f 7f 81 7f 7f 81
+81 81 81 81 7f 7f 7f 7f 81 81 7f 81 7f 7f 81 7f 81 81 7f 81 7f 7f 7f 7f 81 81 7f 81 81 81 81 7f 7f 7f 7f 81 7f 7f 81 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 81 7f 7f 81 81 7f 7f 81 7f 7f 7f 7f 7f 7f 81 81 7f 81 81 7f 81 81 7f 81 7f 7f 7f 81 81 81 81 81 7f 81 81 81 81 7f 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 7f 7f 7f 81 81 81 81 7f 7f 7f 81 7f 7f 7f 81
+7f 7f 81 7f 7f 7f 81 81 7f 7f 81 81 7f 81 7f 81 81 7f 7f 81 81 7f 81 81 7f 7f 81 7f 81 81 81 7f 7f 81 7f 7f 7f 81 7f 7f 7f 81 81 7f 81 81 7f 81 7f 81 81 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 7f 7f 7f 81 7f 7f 81 7f 81 81 7f 7f 7f 81 81 81 81 81 81 81 7f 7f 81 7f 81 81 81 7f 81 7f 81 81 7f 7f 7f 7f 7f 7f 7f 81 81 81 7f 81 81 7f 7f 7f 81 7f
+
+Testing FACCH/F codec:
+test_facch(FACCH/F): encoding: 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/F): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/F): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/F): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+test_facch(FACCH/F): encoding: a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/F): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/F): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/F): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+
+test_facch(FACCH/F): encoding: 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/F): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/F): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/F): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+
+
+Testing FACCH/H codec:
+test_facch(FACCH/H): encoding: 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/H): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/H): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+test_facch(FACCH/H): decoded (BER=0/456): 03 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+test_facch(FACCH/H): encoding: a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/H): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/H): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+test_facch(FACCH/H): decoded (BER=0/456): a3 af 5f 00 36 43 44 ab d9 6d 7d 62 24 c9 d2 92 fa 27 5d 71 7a 59 a8
+
+test_facch(FACCH/H): encoding: 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/H): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/H): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+test_facch(FACCH/H): decoded (BER=0/456): 01 02 03 00 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17
+
+
+Testing CSD functions (no FACCH):
+test_csd(TCH/F9.6): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/F9.6): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/F9.6): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111
+
+test_csd(TCH/F4.8): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/F4.8): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/F4.8): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111
+
+test_csd(TCH/H4.8): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/H4.8): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/H4.8): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111
+
+test_csd(TCH/F2.4): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000
+test_csd(TCH/F2.4): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101
+test_csd(TCH/F2.4): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111
+
+test_csd(TCH/H2.4): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000
+test_csd(TCH/H2.4): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101
+test_csd(TCH/H2.4): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111
+
+test_csd(TCH/F14.4): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00
+test_csd(TCH/F14.4): block #1 (pattern 0xaa): n_errors=0 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01
+test_csd(TCH/F14.4): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11
+
+
+Testing CSD functions (with FACCH):
+test_csd(TCH/F9.6): block #0 (pattern 0x00): n_errors=34 / n_bits_total=456
+00000000 00000000 00000000 00000000 00010010 01100000 00000000 00000000
+00000000 00010011 01111000 00000000 00000000 00000000 00100100 10000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/F9.6): block #1 (pattern 0xaa): n_errors=34 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 00101011 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/F9.6): block #2 (pattern 0xff): n_errors=14 / n_bits_total=456
+11111111 11111111 11111111 11110111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111
+test_csd(TCH/F9.6): FACCH/F (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/F4.8): block #0 (pattern 0x00): n_errors=38 / n_bits_total=456
+00000000 00000000 01010000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/F4.8): block #1 (pattern 0xaa): n_errors=37 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/F4.8): block #2 (pattern 0xff): n_errors=13 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111
+test_csd(TCH/F4.8): FACCH/F (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/H4.8): block #0 (pattern 0x00): n_errors=35 / n_bits_total=456
+00000000 00000000 00000000 00000000 00111000 10000000 00000000 00000000
+00000001 00000000 00000000 00000000 00000000 00000000 00011000 10000000
+00000000 00000000 00000000 00000000 00000000 00001000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000
+test_csd(TCH/H4.8): block #1 (pattern 0xaa): n_errors=38 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 00101011 01010101
+01010101 01010101 01010101 00111010 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101
+test_csd(TCH/H4.8): block #2 (pattern 0xff): n_errors=3 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111
+test_csd(TCH/H4.8): FACCH/H (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/F2.4): block #0 (pattern 0x00): n_errors=0 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000
+test_csd(TCH/F2.4): block #1 (pattern 0xaa): n_errors=138 / n_bits_total=456
+11000010 10010010 10010010 10010010 10010010 10010010 10010101 00011000
+01001010
+test_csd(TCH/F2.4): block #2 (pattern 0xff): n_errors=0 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111
+test_csd(TCH/F2.4): FACCH/F (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/H2.4): block #0 (pattern 0x00): n_errors=38 / n_bits_total=456
+00000000 00000000 00000101 00000000 00000000 00000000 00000000 00000000
+00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
+00000000 00000000
+test_csd(TCH/H2.4): block #1 (pattern 0xaa): n_errors=32 / n_bits_total=456
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01010101 01010101 01010101 01010101
+01010101 01010101
+test_csd(TCH/H2.4): block #2 (pattern 0xff): n_errors=2 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111
+test_csd(TCH/H2.4): FACCH/H (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+test_csd(TCH/F14.4): block #0 (pattern 0x00): n_errors=27 / n_bits_total=456
+00000000 00000000 00000000 00000000 00000000 00111100 01000000 00000000
+00000000 00000000 00000000 00111000 10011000 00000000 10000000 00000000
+00000000 00000000 00001111 10001101 01110000 00000000 00000000 00000000
+00000000 00000000 00001001 11000000 00000000 00000000 00000000 00000000
+00000000 00000010 00100010 01000000 00
+test_csd(TCH/F14.4): block #1 (pattern 0xaa): n_errors=35 / n_bits_total=456
+01010101 01010100 10110111 01010101 01010101 00011000 10010001 11000110
+11011100 11011101 11000110 01010101 01010101 01010101 10100100 10010001
+11000110 00001010 01010101 01010101 11101101 11010101 01010100 01010101
+01010101 01010101 00101001 00100101 01010101 01010101 01010101 01010101
+01010101 01010101 01010101 01010101 01
+test_csd(TCH/F14.4): block #2 (pattern 0xff): n_errors=10 / n_bits_total=456
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
+11111111 11111111 11111111 11111111 11
+test_csd(TCH/F14.4): FACCH/F (pattern 0x2b): n_errors=0 / n_bits_total=456
+2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
Success
diff --git a/tests/conv/conv_gsm0503_test.ok b/tests/conv/conv_gsm0503_test.ok
index 39480ca0..dba52bdf 100644
--- a/tests/conv/conv_gsm0503_test.ok
+++ b/tests/conv/conv_gsm0503_test.ok
@@ -6,6 +6,46 @@
[..] Encoding / Decoding cycle : OK
[..] Encoding / Decoding cycle : OK
+[+] Testing: gsm0503_tch_f24
+[.] Input length : ret = 72 exp = 72 -> OK
+[.] Output length : ret = 456 exp = 456 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: gsm0503_tch_h24
+[.] Input length : ret = 72 exp = 72 -> OK
+[.] Output length : ret = 228 exp = 228 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: gsm0503_tch_f48
+[.] Input length : ret = 148 exp = 148 -> OK
+[.] Output length : ret = 456 exp = 456 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: gsm0503_tch_f96
+[.] Input length : ret = 240 exp = 240 -> OK
+[.] Output length : ret = 456 exp = 456 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
+[+] Testing: gsm0503_tch_f144
+[.] Input length : ret = 290 exp = 290 -> OK
+[.] Output length : ret = 456 exp = 456 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
[+] Testing: gsm0503_rach
[.] Input length : ret = 14 exp = 14 -> OK
[.] Output length : ret = 36 exp = 36 -> OK
@@ -190,6 +230,14 @@
[..] Encoding / Decoding cycle : OK
[..] Encoding / Decoding cycle : OK
+[+] Testing: gsm0503_tch_axs_sid_update
+[.] Input length : ret = 49 exp = 49 -> OK
+[.] Output length : ret = 212 exp = 212 -> OK
+[.] Random vector checks:
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+[..] Encoding / Decoding cycle : OK
+
[+] Testing: gsm0503_mcs1_dl_hdr
[.] Input length : ret = 36 exp = 36 -> OK
[.] Output length : ret = 108 exp = 108 -> OK
diff --git a/tests/ctrl/ctrl_test.c b/tests/ctrl/ctrl_test.c
index b46e9ac5..1d4d4d74 100644
--- a/tests/ctrl/ctrl_test.c
+++ b/tests/ctrl/ctrl_test.c
@@ -117,11 +117,14 @@ static void assert_test(struct ctrl_handle *ctrl, struct ctrl_connection *ccon,
} else {
struct msgb *sent_msg = msgb_dequeue(&ccon->write_queue.msg_queue);
OSMO_ASSERT(sent_msg);
- msgb_put_u8(sent_msg, 0);
- printf("replied: '%s'\n", osmo_escape_str((char*)msgb_l2(sent_msg), -1));
+ char *strbuf = talloc_size(sent_msg, msgb_l2len(sent_msg) + 1);
+ memcpy(strbuf, msgb_l2(sent_msg), msgb_l2len(sent_msg));
+ strbuf[msgb_l2len(sent_msg)] = '\0';
+
+ printf("replied: '%s'\n", osmo_escape_str(strbuf, -1));
OSMO_ASSERT(t->reply_str);
- OSMO_ASSERT(!strcmp(t->reply_str, (char*)msgb_l2(sent_msg)));
+ OSMO_ASSERT(!strcmp(t->reply_str, strbuf));
msgb_free(sent_msg);
}
osmo_wqueue_clear(&ccon->write_queue);
@@ -331,7 +334,7 @@ static const struct one_test test_messages_list[] = {
},
};
-static void test_messages()
+static void test_messages(void)
{
struct ctrl_handle *ctrl;
struct ctrl_connection *ccon;
@@ -387,7 +390,7 @@ static int verify_test_defer(struct ctrl_cmd *cmd, const char *value, void *data
return 0;
}
-static void test_deferred_cmd()
+static void test_deferred_cmd(void)
{
struct ctrl_handle *ctrl;
struct ctrl_connection *ccon;
diff --git a/tests/dtx/dtx_gsm0503_test.c b/tests/dtx/dtx_gsm0503_test.c
new file mode 100644
index 00000000..21d1751e
--- /dev/null
+++ b/tests/dtx/dtx_gsm0503_test.c
@@ -0,0 +1,266 @@
+/*
+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Philipp Maier <pmaier@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
+
+/* Length of payload bits in a Normal Burst */
+#define BURST_PLEN (57 * 2 + 2)
+
+char sample_afs_sid_frame[] =
+ {
+"111111110000000011001100101010100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001111011100100010011111111001000100111011110011001001100111100110010011001"
+};
+
+char sample_afs_sid_update_frame[] =
+ {
+"111111110000000011001100101010100000010000001111111100101011011110001001000000110111110000001001011111101111010011001111100000101000011111001001111100110111110011111001001111101100010001001111000000100100011100111001100000111000110000111001010011101111010011111111010000101100011100111001111100110111110011111001110011101000010010001111110000100000011111001001011100110011110010111001101111100011010001111111001100100100011111111001000000110000110000001001"
+};
+
+char sample_afs_onset_frame[] =
+ {
+"111111110000000011001100101010100000111100000000111111000100101000111111100000000111110010001010001111110100000011111100111110100100111111000000110011001011101001001111011100001011110000001010010011111100000000111100111110101000111110110000111111000000101011111111010000001100110000111010111111111000000010111100000010100100111100110000100011001000101000111111101100001011110000111010011111110011000010111100101110101100111111000000010011001111101000001111"
+};
+
+char sample_ahs_sid_update_frame[] =
+ {
+"111100001100101010110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110000110110100011001000011010000000000000001111010010000000000001000000000010110000000011001000000000000000100000101000000000000000001010100000010010000000000010000111110001110110110011001101000000000100100011001000001010000100100000000011"
+};
+
+char sample_ahs_sid_first_p1_frame[] =
+ {
+"111100001100101001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001111001001001011010110001101100101001110001111001110100110010000111101110110110000100100011111001001110000011110110001010010101100001010100000111101110110001010000111110001110110110011001101001111000011101001010011100011000111010110000011"
+};
+
+char sample_ahs_sid_first_p2_frame[] =
+ {
+"111110100100000010100000110111001110101100000100101001011101100011101010010000001010010010001100101010100100010111110101110011011110101000010100111000001001110111101110010101001110000010001101101011110000000011100100110110011111100011001000001101100101001110001111001110100110010000111101110110110000100100011111001001110000011110110001010010101100001010100000111101110110001010000111110001110110110011001101001111000011101001010011100011000111010110000011"
+};
+
+char sample_ahs_onset_frame[] =
+ {
+"111101011000101001010000111001000111011110000000011110001110010011011111100000101101101001101110011111010000000001010010110001101101110100000010011110101100010001011101101010000111100011101100111101011010100011110010110001001111100011001000011010000000000000001010010010000000000001000000000000100000000011001000000000000000100000101000000000000000010010000101010010000000000010101100111110101000110110011001000000000100100011001000001010000100100000001100"
+};
+
+char sample_sid_first_inh_frame[] =
+ {
+"xBxBxBxBxBxBxBxBxBxBxBxBxBxBxBxBx1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0x0x0x0x1x1x0x1x1x0"
+};
+
+char sample_sid_update_inh_frame[] =
+ {
+"xBxBxBxBxBxBxBxBxBxBxBxBxBxBxBxBx0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1x1x1x1x0x0x1x0x0x1"
+};
+
+unsigned int string_to_sbit(sbit_t *sbits, char *string)
+{
+ unsigned int len;
+ unsigned int i;
+
+ len = strlen(string);
+
+ for (i = 0; i < len; i++) {
+ sbits[i] = string[i] == '1' ? -127 : 127;
+ }
+
+ return len;
+}
+
+void test_gsm0503_detect_afs_dtx_frame(char *string)
+{
+ sbit_t sbits[512];
+ uint8_t dtx_frame_type;
+ int n_errors;
+ int n_bits_total;
+ int mode_id = -1;
+
+ string_to_sbit(sbits, string);
+ dtx_frame_type = gsm0503_detect_afs_dtx_frame2(&n_errors, &n_bits_total, &mode_id, sbits);
+ printf(" ==> %s, n_errors=%d, n_bits_total=%d, mode_id=%d\n",
+ gsm0503_amr_dtx_frame_name(dtx_frame_type),
+ n_errors, n_bits_total, mode_id);
+}
+
+void test_gsm0503_detect_ahs_dtx_frame(char *string)
+{
+ sbit_t sbits[512];
+ uint8_t dtx_frame_type;
+ int n_errors;
+ int n_bits_total;
+ int mode_id = -1;
+
+ string_to_sbit(sbits, string);
+ dtx_frame_type = gsm0503_detect_ahs_dtx_frame2(&n_errors, &n_bits_total, &mode_id, sbits);
+ printf(" ==> %s, n_errors=%d, n_bits_total=%d, mode_id=%d\n",
+ gsm0503_amr_dtx_frame_name(dtx_frame_type),
+ n_errors, n_bits_total, mode_id);
+}
+
+static void test_gsm0503_tch_afhs_decode_dtx(const sbit_t *bursts, size_t offset,
+ enum gsm0503_amr_dtx_frames *amr_last_dtx,
+ bool full_rate, const char *test_desc)
+{
+ uint8_t tch_data[128]; /* just to be safe */
+ int n_errors = 0, n_bits_total = 0;
+ int rc;
+
+ printf("Running %s(at offset=%zu): testing %s\n", __func__, offset, test_desc);
+
+ /* Dummy (not really important) values */
+ uint8_t codec[4] = { 0, 1, 2, 3 };
+ int codecs = ARRAY_SIZE(codec);
+ uint8_t ul_cmr = 0;
+ uint8_t ul_ft = 0;
+
+ if (full_rate) {
+ rc = gsm0503_tch_afs_decode_dtx(&tch_data[0], &bursts[offset], false,
+ codec, codecs, &ul_ft, &ul_cmr,
+ &n_errors, &n_bits_total,
+ (uint8_t *)amr_last_dtx);
+ } else {
+ rc = gsm0503_tch_ahs_decode_dtx(&tch_data[0], &bursts[offset], false, false,
+ codec, codecs, &ul_ft, &ul_cmr,
+ &n_errors, &n_bits_total,
+ (uint8_t *)amr_last_dtx);
+ }
+ printf(" ==> gsm0503_tch_a%cs_decode_dtx() yields '%s' (rc=%d, BER %d/%d)\n",
+ full_rate ? 'f' : 'h', gsm0503_amr_dtx_frame_name(*amr_last_dtx),
+ rc, n_errors, n_bits_total);
+ if (rc > 0)
+ printf(" ====> tch_data[] = { %s }\n", osmo_hexdump_nospc(tch_data, rc));
+}
+
+static void test_gsm0503_tch_afhs_decode_dtx_sid_update(void)
+{
+ enum gsm0503_amr_dtx_frames amr_last_dtx = AMR_OTHER;
+ sbit_t bursts[BURST_PLEN * 12]; /* 12 bursts */
+ int rc;
+
+ /* 456 soft-bits containing an AFS_SID_UPDATE frame (captured on the air) */
+ const char *afs_sid_update = \
+ "94 81 83 76 7b 81 6b 7f 76 8c 81 81 81 86 71 7f 75 81 6d 7a 81 6b 7f 78 8a 87 70 75 8e"
+ "81 8d 7f 81 70 72 81 7f 85 86 7f 93 81 8a 74 7f 71 89 8a 75 7f 7f 78 8c 81 8b 7f 81 7f"
+ "7f 7f 70 8a 8b 7f 90 81 81 81 8a 77 7f 7f 70 81 70 71 86 8e 7f 81 7f 81 75 72 87 8c 76"
+ "7f 72 8e 81 81 81 81 92 7f 8c 81 92 7f 8c 89 7f 81 7f 8f 8b 77 76 86 8c 78 73 88 81 8b"
+ "81 7f 8c 85 77 7b 8d 81 81 81 8b 7f 81 7f 8e 81 8e 7f 8a 8a 7f 93 85 6b 7f 7f 72 81 6f"
+ "76 89 81 81 81 8a 73 7f 72 88 87 73 7f 73 81 7f 81 7f 92 87 73 78 81 6f 7f 71 81 76 77"
+ "6f 81 7f 81 71 7f 6e 81 75 77 83 81 81 90 7f 8b 88 76 76 8a 8d 76 74 81 7f 92 81 81 8b"
+ "78 72 81 77 76 81 6c 7c 8b 81 81 8d 7f 8b 81 8e 74 7f 7f 72 81 7f 81 74 7f 71 81 75 7f"
+ "8e 81 81 8c 72 79 85 8c 78 75 8c 8a 7f 90 81 8e 77 77 81 70 7f 7f 71 81 7f 81 7f 8e 89"
+ "7f 8f 81 8f 7f 8c 8d 7f 81 7f 81 6f 7f 71 8a 87 7f 81 6f 77 81 7f 8d 88 73 79 8a 8a 7f"
+ "7f 7f 7f 7f 76 8b 81 8c 77 7c 8a 81 91 7f 81 76 79 81 71 7f 7f 6f 84 8e 78 7f 7f 7f 74"
+ "88 86 7b 77 81 6f 7f 7f 7f 7f 7f 75 81 70 7f 76 89 81 81 81 8d 78 74 84 81 8e 7f 8d 8a"
+ "7f 79 8c 87 7f 81 7f 81 6f 7f 75 8d 8a 7f 81 7f 92 81 81 85 76 7f 6f 8c 88 6c 7f 73 91"
+ "81 8d 71 7f 7f 73 8d 88 7f 81 7f 91 86 6f 7f 73 8e 81 8d 79 78 81 72 74 8c 86 72 7f 77"
+ "6e 81 7f 81 77 76 81 72 74 81 6f 7f 6f 8d 81 91 7f 81 6d 7f 6d 81 6c 7f 6c 81 7f 81 7f"
+ "8c 8b 7f 8e 89 74 74 8c 81 81 81 81 81 92 7f 8e 8b 7f 93 81 8f 7f 90 81 8d 74 7b 8b 89";
+
+ memset(&bursts[0], 0, sizeof(bursts));
+ rc = osmo_hexparse(afs_sid_update, (uint8_t *)&bursts[BURST_PLEN * 4], BURST_PLEN * 8);
+ OSMO_ASSERT(rc == BURST_PLEN * 4);
+
+ /* Test detection of AFS_SID_UPDATE (marker) */
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 0, &amr_last_dtx, true /* AFS */,
+ "detection of AFS_SID_UPDATE");
+
+ /* Test decoding of AFS_SID_UPDATE_CN (actual SID) */
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 4, &amr_last_dtx, true /* AFS */,
+ "decoding of AFS_SID_UPDATE");
+
+ /* 456 soft-bits containing an AHS_SID_UPDATE frame (captured on the air) */
+ const char *ahs_sid_update = \
+ "81 67 7f 7f 7f 71 8f 88 6f 73 81 7e 81 6b 7f 7e 7d 6f 8f 8a 72 76 92 81 82 81 8f 6d 6f"
+ "81 7f 92 8c 7f 97 81 8e 6f 7f 7c 7f 6e 81 7e 81 6e 73 81 7f 93 8d 6f 7f 6c 81 6b 7f 72"
+ "7c 7c 7d 7f 6f 8f 81 94 7f 92 8d 6e 7d 7d 7f 6c 8b 8e 73 71 81 7f 92 90 7f 81 6e 6e 81"
+ "7f 94 8e 70 7f 6e 8c 8d 77 7f 6a 81 7f 81 70 6d 81 6c 71 8c 91 7f 90 8e 73 6e 81 6d 7f"
+ "81 8b 71 6e 81 7f 82 7c 81 7f 81 6d 73 81 6c 6d 81 6d 7f 6e 81 7e 81 6b 7f 7f 7f 6b 81"
+ "6e 6f 81 68 7f 71 91 81 82 81 8e 70 7f 7c 7d 7f 70 81 7f 91 8f 7f 81 6c 7f 71 81 6d 74"
+ "6f 8f 81 92 7f 82 7f 91 8b 7f 81 6b 7f 6d 81 6b 6f 81 6f 6e 90 81 81 92 7f 94 81 95 7f"
+ "96 81 96 70 7f 72 8f 81 95 7f 81 6f 70 81 7f 90 92 7f 81 6c 70 81 6b 7f 6f 8d 8d 7f 81"
+ "77 81 6a 7e 7e 73 92 8c 7f 81 6a 7f 6c 8e 8e 6e 7f 71 8e 8d 7e 81 6d 7f 6c 81 6d 6c 81"
+ "7f 94 81 92 7f 97 81 92 6e 7f 70 8c 8b 73 73 91 81 93 7f 81 70 72 81 7d 81 71 70 81 7f"
+ "7d 7f 6d 90 8d 73 76 92 81 92 6f 7d 7d 70 91 81 8f 73 75 8c 90 7f 94 81 91 70 7f 7d 7e"
+ "70 8d 8d 73 7f 7c 7e 6a 81 7e 81 6d 7f 6a 81 6f 7f 7f 71 8e 81 82 81 81 81 96 72 7e 7d"
+ "81 8d 7f 81 68 7f 7e 7c 7b 7f 6c 81 6a 7f 7f 71 8f 8d 7f 81 6c 72 8e 88 70 70 81 6d 70"
+ "8d 90 7f 81 7e 95 81 94 7f 92 8b 6e 7f 7f 70 8c 8c 73 75 91 81 91 6d 7d 7e 7b 7c 7d 71"
+ "6c 89 91 7f 81 7f 95 81 93 7f 95 90 7f 81 6d 70 81 6f 75 8c 8e 75 71 81 6e 70 8d 8d 7f"
+ "91 92 7f 81 7f 94 8d 70 71 81 6e 6d 81 6e 75 8e 81 93 70 7f 70 8f 8c 7f 81 6d 6f 81 6a";
+
+ memset(&bursts[0], 0, sizeof(bursts));
+ rc = osmo_hexparse(ahs_sid_update, (uint8_t *)&bursts[BURST_PLEN * 2], BURST_PLEN * 10);
+ OSMO_ASSERT(rc == BURST_PLEN * 4);
+
+ /* Test detection and decoding of AHS_SID_UPDATE */
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 0, &amr_last_dtx, false /* AHS */,
+ "detection/decoding of AHS_SID_UPDATE");
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 2, &amr_last_dtx, false /* AHS */,
+ "detection/decoding of AHS_SID_UPDATE");
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 4, &amr_last_dtx, false /* AHS */,
+ "detection/decoding of AHS_SID_UPDATE");
+}
+
+static void test_gsm0503_tch_afhs_decode_dtx_facch(void)
+{
+ enum gsm0503_amr_dtx_frames amr_last_dtx;
+ sbit_t bursts[BURST_PLEN * 8]; /* 8 bursts */
+ unsigned int i;
+
+ /* Set stealing bits to provoke FACCH/[FH] detection */
+ for (i = 0; i < 8; i++) {
+ sbit_t *burst = &bursts[BURST_PLEN * i];
+ memset(&burst[0], 0, BURST_PLEN);
+ burst[i >> 2 ? 57 : 58] = -127;
+ }
+
+ amr_last_dtx = AFS_SID_UPDATE;
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 0,
+ &amr_last_dtx, true /* AFS */,
+ "tagging of FACCH/F");
+ OSMO_ASSERT(amr_last_dtx == AMR_OTHER);
+
+ amr_last_dtx = AHS_SID_UPDATE;
+ test_gsm0503_tch_afhs_decode_dtx(&bursts[0], BURST_PLEN * 0,
+ &amr_last_dtx, false /* AHS */,
+ "tagging of FACCH/H");
+ OSMO_ASSERT(amr_last_dtx == AMR_OTHER);
+}
+
+int main(int argc, char **argv)
+{
+ printf("FR AMR DTX FRAMES:\n");
+ test_gsm0503_detect_afs_dtx_frame(sample_afs_sid_frame);
+ test_gsm0503_detect_afs_dtx_frame(sample_afs_sid_update_frame);
+ test_gsm0503_detect_afs_dtx_frame(sample_afs_onset_frame);
+ printf("HR AMR DTX FRAMES:\n");
+ test_gsm0503_detect_ahs_dtx_frame(sample_ahs_sid_update_frame);
+ test_gsm0503_detect_ahs_dtx_frame(sample_ahs_sid_first_p1_frame);
+ test_gsm0503_detect_ahs_dtx_frame(sample_ahs_sid_first_p2_frame);
+ test_gsm0503_detect_ahs_dtx_frame(sample_ahs_onset_frame);
+ test_gsm0503_detect_ahs_dtx_frame(sample_sid_first_inh_frame);
+ test_gsm0503_detect_ahs_dtx_frame(sample_sid_update_inh_frame);
+
+ test_gsm0503_tch_afhs_decode_dtx_sid_update();
+ test_gsm0503_tch_afhs_decode_dtx_facch();
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/dtx/dtx_gsm0503_test.ok b/tests/dtx/dtx_gsm0503_test.ok
new file mode 100644
index 00000000..477d4312
--- /dev/null
+++ b/tests/dtx/dtx_gsm0503_test.ok
@@ -0,0 +1,27 @@
+FR AMR DTX FRAMES:
+ ==> AFS_SID_FIRST, n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AFS_SID_UPDATE (marker), n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AFS_ONSET, n_errors=0, n_bits_total=228, mode_id=0
+HR AMR DTX FRAMES:
+ ==> AHS_SID_UPDATE (marker), n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AHS_SID_FIRST_P1, n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AHS_SID_FIRST_P2, n_errors=0, n_bits_total=114, mode_id=0
+ ==> AHS_ONSET, n_errors=0, n_bits_total=114, mode_id=0
+ ==> AHS_SID_FIRST_INH, n_errors=0, n_bits_total=212, mode_id=-1
+ ==> AHS_SID_UPDATE_INH, n_errors=0, n_bits_total=212, mode_id=-1
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=0): testing detection of AFS_SID_UPDATE
+ ==> gsm0503_tch_afs_decode_dtx() yields 'AFS_SID_UPDATE (marker)' (rc=0, BER 0/212)
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=464): testing decoding of AFS_SID_UPDATE
+ ==> gsm0503_tch_afs_decode_dtx() yields 'AFS_SID_UPDATE_CN (audio)' (rc=5, BER 0/212)
+ ====> tch_data[] = { 26e9b1d2b0 }
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=0): testing detection/decoding of AHS_SID_UPDATE
+ ==> gsm0503_tch_ahs_decode_dtx() yields 'AMR_OTHER (audio)' (rc=-1, BER 109/212)
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=232): testing detection/decoding of AHS_SID_UPDATE
+ ==> gsm0503_tch_ahs_decode_dtx() yields 'AHS_SID_UPDATE_CN (audio)' (rc=5, BER 0/424)
+ ====> tch_data[] = { 6003ccb270 }
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=464): testing detection/decoding of AHS_SID_UPDATE
+ ==> gsm0503_tch_ahs_decode_dtx() yields 'AMR_OTHER (audio)' (rc=-1, BER 111/212)
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=0): testing tagging of FACCH/F
+ ==> gsm0503_tch_afs_decode_dtx() yields 'AMR_OTHER (audio)' (rc=-1, BER 456/456)
+Running test_gsm0503_tch_afhs_decode_dtx(at offset=0): testing tagging of FACCH/H
+ ==> gsm0503_tch_ahs_decode_dtx() yields 'AMR_OTHER (audio)' (rc=-1, BER 456/456)
diff --git a/tests/exec/exec_test.c b/tests/exec/exec_test.c
new file mode 100644
index 00000000..5f4b460c
--- /dev/null
+++ b/tests/exec/exec_test.c
@@ -0,0 +1,155 @@
+#include <osmocom/core/utils.h>
+#include <osmocom/core/exec.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+static void env_dump(char **env)
+{
+ char **ent;
+
+ for (ent = env; *ent; ent++)
+ printf("\t%s\n", *ent);
+}
+
+static void test_env_filter(void)
+{
+ char *out[256];
+ char *env_in[] = {
+ "FOO=1",
+ "BAR=2",
+ "USER=mahlzeit",
+ "BAZ=3",
+ "SHELL=/bin/sh",
+ NULL
+ };
+ const char *filter[] = {
+ "SHELL",
+ "USER",
+ NULL
+ };
+ int rc;
+
+ printf("\n==== osmo_environment_filter ====\n");
+
+ printf("Input Environment:\n");
+ env_dump(env_in);
+ printf("Input Whitelist:\n");
+ env_dump((char **) filter);
+ rc = osmo_environment_filter(out, ARRAY_SIZE(out), env_in, filter);
+ printf("Output Environment (%d):\n", rc);
+ env_dump(out);
+ OSMO_ASSERT(rc == 3);
+
+ printf("Testing for NULL out\n");
+ rc = osmo_environment_filter(NULL, 123, env_in, filter);
+ OSMO_ASSERT(rc < 0);
+
+ printf("Testing for zero-length out\n");
+ rc = osmo_environment_filter(out, 0, env_in, filter);
+ OSMO_ASSERT(rc < 0);
+
+ printf("Testing for one-length out\n");
+ rc = osmo_environment_filter(out, 1, env_in, filter);
+ OSMO_ASSERT(rc == 1 && out[0] == NULL);
+
+ printf("Testing for no filter\n");
+ rc = osmo_environment_filter(out, ARRAY_SIZE(out), env_in, NULL);
+ OSMO_ASSERT(rc < 0);
+
+ printf("Testing for no input\n");
+ rc = osmo_environment_filter(out, ARRAY_SIZE(out), NULL, filter);
+ OSMO_ASSERT(rc == 1 && out[0] == NULL);
+ printf("Success!\n");
+}
+
+static void test_env_append(void)
+{
+ char *out[256] = {
+ "FOO=a",
+ "BAR=b",
+ "BAZ=c",
+ NULL,
+ };
+ char *add[] = {
+ "MAHL=zeit",
+ "GSM=global",
+ "UMTS=universal",
+ "LTE=evolved",
+ NULL,
+ };
+ int rc;
+
+ printf("\n==== osmo_environment_append ====\n");
+
+ printf("Input Environment:\n");
+ env_dump(out);
+ printf("Input Addition:\n");
+ env_dump(add);
+ rc = osmo_environment_append(out, ARRAY_SIZE(out), add);
+ printf("Output Environment (%d)\n", rc);
+ env_dump(out);
+ OSMO_ASSERT(rc == 8);
+ printf("Success!\n");
+}
+
+static void test_close_fd(void)
+{
+ struct stat st;
+ int fds[2];
+ int rc;
+
+ printf("\n==== osmo_close_all_fds_above ====\n");
+
+ /* create some extra fds */
+ rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ OSMO_ASSERT(rc == 0);
+
+ rc = fstat(fds[0], &st);
+ OSMO_ASSERT(rc == 0);
+
+ osmo_close_all_fds_above(2);
+
+ rc = fstat(fds[0], &st);
+ OSMO_ASSERT(rc == -1 && errno == EBADF);
+ rc = fstat(fds[1], &st);
+ OSMO_ASSERT(rc == -1 && errno == EBADF);
+ printf("Success!\n");
+}
+
+static void test_system_nowait(void)
+{
+ char *addl_env[] = {
+ "MAHLZEIT=spaet",
+ NULL
+ };
+ int rc, pid, i;
+
+ printf("\n==== osmo_system_nowait ====\n");
+
+ pid = osmo_system_nowait("env | grep MAHLZEIT 1>&2", osmo_environment_whitelist, addl_env);
+ OSMO_ASSERT(pid > 0);
+ for (i = 0; i < 10; i++) {
+ sleep(1);
+ rc = waitpid(pid, NULL, WNOHANG);
+ if (rc == pid) {
+ printf("Success!\n");
+ return;
+ }
+ }
+ printf("ERROR: child didn't terminate within 10s\n");
+}
+
+int main(int argc, char **argv)
+{
+ test_env_filter();
+ test_env_append();
+ test_close_fd();
+ test_system_nowait();
+
+ exit(0);
+}
diff --git a/tests/exec/exec_test.err b/tests/exec/exec_test.err
new file mode 100644
index 00000000..4edc61d6
--- /dev/null
+++ b/tests/exec/exec_test.err
@@ -0,0 +1 @@
+MAHLZEIT=spaet
diff --git a/tests/exec/exec_test.ok b/tests/exec/exec_test.ok
new file mode 100644
index 00000000..45a20f07
--- /dev/null
+++ b/tests/exec/exec_test.ok
@@ -0,0 +1,46 @@
+
+==== osmo_environment_filter ====
+Input Environment:
+ FOO=1
+ BAR=2
+ USER=mahlzeit
+ BAZ=3
+ SHELL=/bin/sh
+Input Whitelist:
+ SHELL
+ USER
+Output Environment (3):
+ USER=mahlzeit
+ SHELL=/bin/sh
+Testing for NULL out
+Testing for zero-length out
+Testing for one-length out
+Testing for no filter
+Testing for no input
+Success!
+
+==== osmo_environment_append ====
+Input Environment:
+ FOO=a
+ BAR=b
+ BAZ=c
+Input Addition:
+ MAHL=zeit
+ GSM=global
+ UMTS=universal
+ LTE=evolved
+Output Environment (8)
+ FOO=a
+ BAR=b
+ BAZ=c
+ MAHL=zeit
+ GSM=global
+ UMTS=universal
+ LTE=evolved
+Success!
+
+==== osmo_close_all_fds_above ====
+Success!
+
+==== osmo_system_nowait ====
+Success!
diff --git a/tests/fr/fr_test.c b/tests/fr/fr_test.c
index 4d472b55..cdcdb434 100644
--- a/tests/fr/fr_test.c
+++ b/tests/fr/fr_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#define _GNU_SOURCE
@@ -56,7 +52,7 @@ int socket(int domain, int type, int protocol)
return fd;
}
-void bssgp_prim_cb()
+void bssgp_prim_cb(void)
{
}
diff --git a/tests/fsm/fsm_dealloc_test.c b/tests/fsm/fsm_dealloc_test.c
index 9a6974d9..246ba563 100644
--- a/tests/fsm/fsm_dealloc_test.c
+++ b/tests/fsm/fsm_dealloc_test.c
@@ -290,7 +290,7 @@ void obj_set_other(struct obj *a, struct obj *b)
obj_add_other(b, a);
}
-static struct scene *scene_alloc()
+static struct scene *scene_alloc(void)
{
struct scene *s = talloc_zero(ctx, struct scene);
s->use_count.talloc_object = s;
@@ -419,7 +419,7 @@ static void trigger_tests(void *loop_ctx)
}
}
-void test_osmo_fsm_term_safely()
+void test_osmo_fsm_term_safely(void)
{
fprintf(stderr, "\n\n%s()\n", __func__);
osmo_fsm_term_safely(true);
@@ -428,7 +428,7 @@ void test_osmo_fsm_term_safely()
fprintf(stderr, "\n\n%s() done\n", __func__);
}
-void test_osmo_fsm_set_dealloc_ctx()
+void test_osmo_fsm_set_dealloc_ctx(void)
{
fprintf(stderr, "\n\n%s()\n", __func__);
void *dealloc_ctx = talloc_named_const(ctx, 0, "fsm_dealloc");
@@ -443,7 +443,7 @@ int main(void)
ctx = talloc_named_const(NULL, 0, "main");
osmo_init_logging2(ctx, NULL);
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);
@@ -452,7 +452,7 @@ int main(void)
log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
- osmo_fsm_register(&test_fsm);
+ OSMO_ASSERT(osmo_fsm_register(&test_fsm) == 0);
test_osmo_fsm_term_safely();
test_osmo_fsm_set_dealloc_ctx();
diff --git a/tests/fsm/fsm_test.c b/tests/fsm/fsm_test.c
index f8ebdc7b..3b839412 100644
--- a/tests/fsm/fsm_test.c
+++ b/tests/fsm/fsm_test.c
@@ -41,6 +41,18 @@ static const struct value_string test_fsm_event_names[] = {
{ 0, NULL }
};
+static void test_fsm_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ LOGPFSM(fi, "%s() prev_state=%s\n",
+ __func__, osmo_fsm_state_name(fi->fsm, prev_state));
+}
+
+static void test_fsm_onleave(struct osmo_fsm_inst *fi, uint32_t next_state)
+{
+ LOGPFSM(fi, "%s() next_state=%s\n",
+ __func__, osmo_fsm_state_name(fi->fsm, next_state));
+}
+
static void test_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
@@ -86,17 +98,23 @@ static struct osmo_fsm_state test_fsm_states[] = {
.out_state_mask = (1 << ST_ONE),
.name = "NULL",
.action = test_fsm_null,
+ .onenter = test_fsm_onenter,
+ .onleave = test_fsm_onleave,
},
[ST_ONE]= {
.in_event_mask = (1 << EV_B),
.out_state_mask = (1 << ST_TWO),
.name = "ONE",
.action= test_fsm_one,
+ .onenter = test_fsm_onenter,
+ .onleave = test_fsm_onleave,
},
[ST_TWO]= {
.in_event_mask = 0,
.name = "TWO",
.action = NULL,
+ .onenter = test_fsm_onenter,
+ .onleave = test_fsm_onleave,
},
};
@@ -172,7 +190,7 @@ static struct osmo_fsm_inst *foo(void)
return fi;
}
-static void test_id_api()
+static void test_id_api(void)
{
struct osmo_fsm_inst *fi;
@@ -280,7 +298,7 @@ const struct timeval fake_time_start_time = { 123, 456 };
osmo_timers_update(); \
} while (0)
-void fake_time_start()
+void fake_time_start(void)
{
struct timespec *clock_override;
@@ -301,7 +319,7 @@ static int timer_cb(struct osmo_fsm_inst *fi)
return 0;
}
-static void test_state_chg_keep_timer()
+static void test_state_chg_keep_timer(void)
{
struct osmo_fsm_inst *fi;
@@ -349,7 +367,7 @@ static void test_state_chg_keep_timer()
fprintf(stderr, "--- %s() done\n", __func__);
}
-static void test_state_chg_T()
+static void test_state_chg_T(void)
{
struct osmo_fsm_inst *fi;
@@ -386,6 +404,74 @@ static void test_state_chg_T()
fprintf(stderr, "--- %s() done\n", __func__);
}
+/* Test setting a state timeout with second granularity */
+static void test_state_chg_Ts(void)
+{
+ struct osmo_fsm_inst *fi;
+
+ fprintf(stderr, "\n--- %s()\n", __func__);
+
+ fsm.timer_cb = &timer_cb;
+ timeout_fired = -1;
+ fake_time_start();
+
+ fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL);
+ OSMO_ASSERT(fi);
+
+ osmo_fsm_inst_state_chg(fi, ST_ONE, 8, 4242);
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(3, 0); /* +3s */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(2, 500000); /* +2.5s */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(2, 500000); /* +2.5s */
+ OSMO_ASSERT(timeout_fired == 4242);
+
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL);
+
+ fprintf(stderr, "--- %s() done\n", __func__);
+}
+
+/* Test setting a state timeout with millisecond granularity */
+static void test_state_chg_Tms(void)
+{
+ struct osmo_fsm_inst *fi;
+
+ fprintf(stderr, "\n--- %s()\n", __func__);
+
+ fsm.timer_cb = &timer_cb;
+ timeout_fired = -1;
+ fake_time_start();
+
+ fi = osmo_fsm_inst_alloc(&fsm, g_ctx, NULL, LOGL_DEBUG, NULL);
+ OSMO_ASSERT(fi);
+
+ osmo_fsm_inst_state_chg_ms(fi, ST_ONE, 1337, 4242); /* 1s 337ms */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 500000); /* +500ms, 500ms total */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 250000); /* +250ms, 750ms total */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 350000); /* +350ms, 1s 100ms total */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 200000); /* +200ms, 1s 300ms total */
+ OSMO_ASSERT(timeout_fired == -1);
+
+ fake_time_passes(0, 37000); /* +37ms, 1s 337ms total */
+ OSMO_ASSERT(timeout_fired == 4242);
+
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL);
+
+ fprintf(stderr, "--- %s() done\n", __func__);
+}
+
static const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
@@ -412,12 +498,15 @@ int main(int argc, char **argv)
log_init(&log_info, NULL);
stderr_target = log_target_create_stderr();
log_add_target(stderr_target);
- log_set_print_filename(stderr_target, 0);
+ log_set_print_filename2(stderr_target, LOG_FILENAME_NONE);
+ log_set_use_color(stderr_target, 0);
+ log_set_print_category(stderr_target, 0);
+ log_set_print_category_hex(stderr_target, 0);
g_ctrl = ctrl_handle_alloc(NULL, NULL, NULL);
g_ctx = NULL;
OSMO_ASSERT(osmo_fsm_find_by_name(fsm.name) == NULL);
- osmo_fsm_register(&fsm);
+ OSMO_ASSERT(osmo_fsm_register(&fsm) == 0);
OSMO_ASSERT(osmo_fsm_find_by_name(fsm.name) == &fsm);
OSMO_ASSERT(osmo_fsm_inst_find_by_name(&fsm, "my_id") == NULL);
@@ -431,6 +520,8 @@ int main(int argc, char **argv)
test_id_api();
test_state_chg_keep_timer();
test_state_chg_T();
+ test_state_chg_Ts();
+ test_state_chg_Tms();
osmo_fsm_unregister(&fsm);
exit(0);
diff --git a/tests/fsm/fsm_test.err b/tests/fsm/fsm_test.err
index 13cbacd9..fffb9136 100644
--- a/tests/fsm/fsm_test.err
+++ b/tests/fsm/fsm_test.err
@@ -1,18 +1,22 @@
Checking FSM allocation
-Test_FSM(my_id){NULL}: Allocated
-Test_FSM(my_id){NULL}: Received Event EV_B
-Test_FSM(my_id){NULL}: Event EV_B not permitted
-Test_FSM(my_id){NULL}: Received Event EV_A
-Test_FSM(my_id){NULL}: State change to ONE (no timeout)
-Test_FSM(my_id){ONE}: Received Event EV_B
-Test_FSM(my_id){ONE}: State change to TWO (T2342, 1s)
-Test_FSM(my_id){TWO}: Timeout of T2342
-Timer
-Test_FSM(my_id){TWO}: Deallocated
-
+Test_FSM(my_id){NULL}: Allocated
+Test_FSM(my_id){NULL}: Received Event EV_B
+Test_FSM(my_id){NULL}: Event EV_B not permitted
+Test_FSM(my_id){NULL}: Received Event EV_A
+Test_FSM(my_id){NULL}: test_fsm_onleave() next_state=ONE
+Test_FSM(my_id){NULL}: State change to ONE (no timeout)
+Test_FSM(my_id){ONE}: test_fsm_onenter() prev_state=NULL
+Test_FSM(my_id){ONE}: Received Event EV_B
+Test_FSM(my_id){ONE}: test_fsm_onleave() next_state=TWO
+Test_FSM(my_id){ONE}: State change to TWO (T2342, 1s)
+Test_FSM(my_id){TWO}: test_fsm_onenter() prev_state=ONE
+Test_FSM(my_id){TWO}: Timeout of T2342
+Timer
+Test_FSM(my_id){TWO}: Deallocated
+
--- test_id_api()
Test_FSM{NULL}: Allocated
- osmo_fsm_inst_name() == "Test_FSM"
+ osmo_fsm_inst_name() == "Test_FSM"
osmo_fsm_inst_find_by_name("Test_FSM") == fi
osmo_fsm_inst_update_id("my_id")
rc == 0, ok
@@ -39,23 +43,23 @@ osmo_fsm_inst_update_id("arbitrary_id")
osmo_fsm_inst_find_by_id("arbitrary_id") == fi
osmo_fsm_inst_update_id("")
Attempting to set illegal id for FSM instance of type 'Test_FSM': ""
- rc == -22, ok
+ rc == -22, ok
osmo_fsm_inst_name() == "Test_FSM(arbitrary_id)"
osmo_fsm_inst_find_by_name("Test_FSM(arbitrary_id)") == fi
osmo_fsm_inst_update_id("invalid.id")
Attempting to set illegal id for FSM instance of type 'Test_FSM': "invalid.id"
- rc == -22, ok
+ rc == -22, ok
osmo_fsm_inst_name() == "Test_FSM(arbitrary_id)"
osmo_fsm_inst_find_by_name("Test_FSM(arbitrary_id)") == fi
--- id format tests...
osmo_fsm_inst_update_id_f("format%cid", '.')
Attempting to set illegal id for FSM instance of type 'Test_FSM': "format.id"
- rc == -22, ok
+ rc == -22, ok
osmo_fsm_inst_name() == "Test_FSM(arbitrary_id)"
osmo_fsm_inst_find_by_name("Test_FSM(arbitrary_id)") == fi
osmo_fsm_inst_update_id_f("%s", "")
Attempting to set illegal id for FSM instance of type 'Test_FSM': ""
- rc == -22, ok
+ rc == -22, ok
osmo_fsm_inst_name() == "Test_FSM(arbitrary_id)"
osmo_fsm_inst_find_by_name("Test_FSM(arbitrary_id)") == fi
osmo_fsm_inst_update_id_f("format%xid%d", 0x23, 42)
@@ -78,41 +82,89 @@ osmo_fsm_inst_update_id_f("%s%c%s", "arbitrary", '_', "id")
--- test_id_api() done
Test_FSM(arbitrary_id){NULL}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
-Test_FSM(arbitrary_id){NULL}: Freeing instance
-Test_FSM(arbitrary_id){NULL}: Deallocated
-
+Test_FSM(arbitrary_id){NULL}: Freeing instance
+Test_FSM(arbitrary_id){NULL}: Deallocated
+
--- test_state_chg_keep_timer()
Test_FSM{NULL}: Allocated
-Test_FSM{NULL}: State change to ONE (no timeout)
-Test_FSM{ONE}: State change to TWO (no timeout)
-Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
-Test_FSM{TWO}: Freeing instance
-Test_FSM{TWO}: Deallocated
-Total time passed: 0.000000 s
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
+Test_FSM{NULL}: State change to ONE (no timeout)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Test_FSM{ONE}: test_fsm_onleave() next_state=TWO
+Test_FSM{ONE}: State change to TWO (no timeout)
+Test_FSM{TWO}: test_fsm_onenter() prev_state=ONE
+Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
+Test_FSM{TWO}: Freeing instance
+Test_FSM{TWO}: Deallocated
+Total time passed: 0.000000 s
Test_FSM{NULL}: Allocated
-Test_FSM{NULL}: State change to ONE (T10, 10s)
-Total time passed: 2.000342 s
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
+Test_FSM{NULL}: State change to ONE (T10, 10s)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Total time passed: 2.000342 s
+Test_FSM{ONE}: test_fsm_onleave() next_state=TWO
Test_FSM{ONE}: State change to TWO (keeping T10, 7.999s remaining)
-Total time passed: 2.000342 s
+Test_FSM{TWO}: test_fsm_onenter() prev_state=ONE
+Total time passed: 2.000342 s
Total time passed: 9.999999 s
Total time passed: 10.000000 s
Test_FSM{TWO}: Timeout of T10
-Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
-Test_FSM{TWO}: Freeing instance
-Test_FSM{TWO}: Deallocated
---- test_state_chg_keep_timer() done
+Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
+Test_FSM{TWO}: Freeing instance
+Test_FSM{TWO}: Deallocated
+--- test_state_chg_keep_timer() done
--- test_state_chg_T()
Test_FSM{NULL}: Allocated
-Test_FSM{NULL}: State change to ONE (T42, 23s)
-Test_FSM{ONE}: State change to TWO (no timeout)
-Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
-Test_FSM{TWO}: Freeing instance
-Test_FSM{TWO}: Deallocated
-Test_FSM{NULL}: Allocated
-Test_FSM{NULL}: State change to ONE (T42, 23s)
-Test_FSM{ONE}: State change to TWO (no timeout)
-Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
-Test_FSM{TWO}: Freeing instance
-Test_FSM{TWO}: Deallocated
---- test_state_chg_T() done
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
+Test_FSM{NULL}: State change to ONE (T42, 23s)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Test_FSM{ONE}: test_fsm_onleave() next_state=TWO
+Test_FSM{ONE}: State change to TWO (no timeout)
+Test_FSM{TWO}: test_fsm_onenter() prev_state=ONE
+Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
+Test_FSM{TWO}: Freeing instance
+Test_FSM{TWO}: Deallocated
+Test_FSM{NULL}: Allocated
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
+Test_FSM{NULL}: State change to ONE (T42, 23s)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Test_FSM{ONE}: test_fsm_onleave() next_state=TWO
+Test_FSM{ONE}: State change to TWO (no timeout)
+Test_FSM{TWO}: test_fsm_onenter() prev_state=ONE
+Test_FSM{TWO}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
+Test_FSM{TWO}: Freeing instance
+Test_FSM{TWO}: Deallocated
+--- test_state_chg_T() done
+
+--- test_state_chg_Ts()
+Total time passed: 0.000000 s
+Test_FSM{NULL}: Allocated
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
+Test_FSM{NULL}: State change to ONE (T4242, 8s)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Total time passed: 3.000000 s
+Total time passed: 5.500000 s
+Total time passed: 8.000000 s
+Test_FSM{ONE}: Timeout of T4242
+Test_FSM{ONE}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
+Test_FSM{ONE}: Freeing instance
+Test_FSM{ONE}: Deallocated
+--- test_state_chg_Ts() done
+
+--- test_state_chg_Tms()
+Total time passed: 0.000000 s
+Test_FSM{NULL}: Allocated
+Test_FSM{NULL}: test_fsm_onleave() next_state=ONE
+Test_FSM{NULL}: State change to ONE (T4242, 1337ms)
+Test_FSM{ONE}: test_fsm_onenter() prev_state=NULL
+Total time passed: 0.500000 s
+Total time passed: 0.750000 s
+Total time passed: 1.100000 s
+Total time passed: 1.300000 s
+Total time passed: 1.337000 s
+Test_FSM{ONE}: Timeout of T4242
+Test_FSM{ONE}: Terminating (cause = OSMO_FSM_TERM_REQUEST)
+Test_FSM{ONE}: Freeing instance
+Test_FSM{ONE}: Deallocated
+--- test_state_chg_Tms() done
diff --git a/tests/gad/gad_test.c b/tests/gad/gad_test.c
new file mode 100644
index 00000000..19be6a61
--- /dev/null
+++ b/tests/gad/gad_test.c
@@ -0,0 +1,143 @@
+#include <stdio.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/gad.h>
+
+void test_gad_lat_lon_dec_enc_stability(void)
+{
+ uint32_t lat_enc;
+ uint32_t lon_enc;
+ printf("--- %s\n", __func__);
+ for (lat_enc = 0x0; lat_enc <= 0xffffff; lat_enc++) {
+ int32_t lat_dec = osmo_gad_dec_lat(lat_enc);
+ uint32_t enc2 = osmo_gad_enc_lat(lat_dec);
+ uint32_t want_enc = lat_enc;
+ /* "-0" == 0, because the highest bit is defined as a sign bit. */
+ if (lat_enc == 0x800000)
+ want_enc = 0;
+ if (enc2 != want_enc) {
+ printf("ERR: lat=%u --> %d --> %u\n", lat_enc, lat_dec, enc2);
+ printf("%d -> %u\n", lat_dec + 1, osmo_gad_enc_lat(lat_dec + 1));
+ OSMO_ASSERT(false);
+ }
+ }
+ printf("osmo_gad_dec_lat() -> osmo_gad_enc_lat() of %u values successful\n", lat_enc);
+ for (lon_enc = 0; lon_enc <= 0xffffff; lon_enc++) {
+ int32_t lon_dec = osmo_gad_dec_lon(lon_enc);
+ uint32_t enc2 = osmo_gad_enc_lon(lon_dec);
+ uint32_t want_enc = lon_enc;
+ if (enc2 != want_enc) {
+ printf("ERR: lon=%u 0x%x --> %d --> %u\n", lon_enc, lon_enc, lon_dec, enc2);
+ printf("%d -> %u\n", lon_dec + 1, osmo_gad_enc_lon(lon_dec + 1));
+ printf("%d -> %u\n", lon_dec - 1, osmo_gad_enc_lon(lon_dec - 1));
+ OSMO_ASSERT(false);
+ }
+ }
+ printf("osmo_gad_dec_lon() -> osmo_gad_enc_lon() of %u values successful\n", lon_enc);
+}
+
+struct osmo_gad gad_test_values[] = {
+ {
+ .type = GAD_TYPE_ELL_POINT_UNC_CIRCLE,
+ .ell_point_unc_circle = {
+ /* Values rounded to the nearest encodable value, for test result matching */
+ .lat = 23000006,
+ .lon = 42000002,
+ .unc = 442592,
+ },
+ },
+};
+
+void test_gad_enc_dec(void)
+{
+ int i;
+ printf("--- %s\n", __func__);
+
+ for (i = 0; i < ARRAY_SIZE(gad_test_values); i++) {
+ struct osmo_gad *t = &gad_test_values[i];
+ struct msgb *msg = msgb_alloc(1024, __func__);
+ union gad_raw raw_write;
+ union gad_raw raw_read;
+ struct osmo_gad dec_pdu;
+ int rc;
+ struct osmo_gad_err *err;
+ void *loop_ctx = msg;
+ rc = osmo_gad_enc(&raw_write, t);
+ if (rc <= 0) {
+ printf("[%d] %s: ERROR: osmo_gad_enc() failed\n", i, osmo_gad_type_name(t->type));
+ goto loop_end;
+ }
+ rc = osmo_gad_raw_write(msg, &raw_write);
+ if (rc <= 0) {
+ printf("[%d] %s: ERROR: osmo_gad_raw_write() failed\n", i, osmo_gad_type_name(t->type));
+ goto loop_end;
+ }
+ if (rc != msg->len) {
+ printf("[%d] %s: ERROR: osmo_gad_raw_write() returned length %d but msgb has %d bytes\n",
+ i, osmo_gad_type_name(t->type),
+ rc, msg->len);
+ goto loop_end;
+ }
+
+ memset(&raw_read, 0xff, sizeof(raw_read));
+ rc = osmo_gad_raw_read(&raw_read, &err, loop_ctx, msg->data, msg->len);
+ if (rc) {
+ printf("[%d] ERROR: osmo_gad_raw_read() failed: %s\n", i, err->logmsg);
+ printf(" encoded data: %s\n", osmo_hexdump(msg->data, msg->len));
+ goto loop_end;
+ }
+
+ memset(&dec_pdu, 0xff, sizeof(dec_pdu));
+ rc = osmo_gad_dec(&dec_pdu, &err, loop_ctx, &raw_read);
+ if (rc) {
+ printf("[%d] ERROR: failed to decode pdu: %s\n", i, err->logmsg);
+ printf(" encoded data: %s\n", osmo_hexdump(msg->data, msg->len));
+ goto loop_end;
+ }
+
+ if (memcmp(t, &dec_pdu, sizeof(dec_pdu))) {
+ char strbuf[128];
+ printf("[%d] %s: ERROR: decoded PDU != encoded PDU\n", i,
+ osmo_gad_type_name(t->type));
+ osmo_gad_to_str_buf(strbuf, sizeof(strbuf), t);
+ printf(" original struct: %s\n", strbuf);
+ osmo_gad_to_str_buf(strbuf, sizeof(strbuf), &dec_pdu);
+ printf(" decoded struct: %s\n", strbuf);
+ goto loop_end;
+ }
+
+ printf("[%d] %s: ok\n", i, osmo_gad_type_name(t->type));
+ printf(" encoded data: %s\n", msgb_hexdump(msg));
+
+loop_end:
+ msgb_free(msg);
+ }
+}
+
+void test_gad_to_str(void)
+{
+ int i;
+ printf("--- %s\n", __func__);
+
+ for (i = 0; i < ARRAY_SIZE(gad_test_values); i++) {
+ struct osmo_gad *t = &gad_test_values[i];
+ char buf[1024];
+ int rc;
+ rc = osmo_gad_to_str_buf(buf, sizeof(buf), t);
+
+ printf("[%d] ", i);
+ if (rc <= 0)
+ printf("%s: ERROR: osmo_gad_to_str_buf() failed\n", osmo_gad_type_name(t->type));
+ else
+ printf("%s\n", buf);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ test_gad_lat_lon_dec_enc_stability();
+ test_gad_enc_dec();
+ test_gad_to_str();
+ return 0;
+}
diff --git a/tests/gad/gad_test.ok b/tests/gad/gad_test.ok
new file mode 100644
index 00000000..ec5432a8
--- /dev/null
+++ b/tests/gad/gad_test.ok
@@ -0,0 +1,8 @@
+--- test_gad_lat_lon_dec_enc_stability
+osmo_gad_dec_lat() -> osmo_gad_enc_lat() of 16777216 values successful
+osmo_gad_dec_lon() -> osmo_gad_enc_lon() of 16777216 values successful
+--- test_gad_enc_dec
+[0] Ellipsoid-point-with-uncertainty-circle: ok
+ encoded data: 10 20 b6 0c 1d dd de 28
+--- test_gad_to_str
+[0] Ellipsoid-point-with-uncertainty-circle{lat=23.000006,lon=42.000002,unc=442.592m}
diff --git a/tests/gb/bssgp_fc_test.c b/tests/gb/bssgp_fc_test.c
index cc387771..63e8308a 100644
--- a/tests/gb/bssgp_fc_test.c
+++ b/tests/gb/bssgp_fc_test.c
@@ -148,7 +148,9 @@ int main(int argc, char **argv)
osmo_init_logging2(ctx, &info);
log_set_use_color(osmo_stderr_target, 0);
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
tall_msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
diff --git a/tests/gb/gprs_bssgp_rim_test.c b/tests/gb/gprs_bssgp_rim_test.c
new file mode 100644
index 00000000..bb533f6d
--- /dev/null
+++ b/tests/gb/gprs_bssgp_rim_test.c
@@ -0,0 +1,799 @@
+/* Test routines for the BSSGP implementation in libosmogb
+ *
+ * (C) 2020 by sysmocom - s.f.m.c. GmbH
+ * Author: Philipp Maier <pmaier@sysmocom.de>
+ *
+ * Skeleton based on bssgp_fc_test.c
+ * (C) 2012 by Harald Welte <laforge@gnumonks.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#undef _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gprs/gprs_bssgp.h>
+#include <osmocom/gprs/gprs_ns.h>
+#include <osmocom/gprs/gprs_bssgp_rim.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+void dump_rim_ri(struct bssgp_rim_routing_info *ri)
+{
+ switch (ri->discr) {
+ case BSSGP_RIM_ROUTING_INFO_GERAN:
+ printf("GERAN cell identifier\n");
+ printf(" * mcc: %u\n", ri->geran.raid.mcc);
+ printf(" mnc: %u\n", ri->geran.raid.mnc);
+ printf(" mnc 3 digits: %u\n", ri->geran.raid.mnc_3_digits);
+ printf(" lac: %u\n", ri->geran.raid.lac);
+ printf(" rac: %u\n", ri->geran.raid.rac);
+ printf(" * cell id: %04x\n", ri->geran.cid);
+ break;
+ case BSSGP_RIM_ROUTING_INFO_UTRAN:
+ printf("UTRAN RNC identifier\n");
+ printf(" * mcc: %u\n", ri->utran.raid.mcc);
+ printf(" mnc: %u\n", ri->utran.raid.mnc);
+ printf(" mnc 3 digits: %u\n", ri->utran.raid.mnc_3_digits);
+ printf(" lac: %u\n", ri->utran.raid.lac);
+ printf(" rac: %u\n", ri->utran.raid.rac);
+ printf(" * rnc id: %04x\n", ri->utran.rncid);
+ break;
+ case BSSGP_RIM_ROUTING_INFO_EUTRAN:
+ printf("EUTRAN eNB identifier\n");
+ printf(" * mcc: %u\n", ri->eutran.tai.mcc);
+ printf(" mnc: %u\n", ri->eutran.tai.mnc);
+ printf(" mnc 3 digits: %u\n", ri->eutran.tai.mnc_3_digits);
+ printf(" tac: %u\n", ri->eutran.tai.tac);
+ printf(" * global_enb_id: %s\n",
+ osmo_hexdump_nospc(ri->eutran.global_enb_id,
+ ri->eutran.global_enb_id_len));
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void test_bssgp_parse_rim_ri(void)
+{
+ int rc;
+ struct bssgp_rim_routing_info result;
+ uint8_t testvec_geran[] =
+ { 0x00, 0x62, 0xf2, 0x24, 0x33, 0x90, 0x00, 0x51, 0xe1 };
+ uint8_t testvec_utran[] =
+ { 0x01, 0x62, 0xf2, 0x24, 0x33, 0x90, 0x00, 0x51, 0xe1 };
+ uint8_t testvec_eutran[] =
+ { 0x02, 0x62, 0xf2, 0x24, 0x33, 0x90, 0x00, 0x51, 0xe1 };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_parse_rim_ri(&result, testvec_geran,
+ sizeof(testvec_geran));
+ printf("rc=%d\n", rc);
+ dump_rim_ri(&result);
+ printf("\n");
+
+ rc = bssgp_parse_rim_ri(&result, testvec_utran,
+ sizeof(testvec_utran));
+ printf("rc=%d\n", rc);
+ dump_rim_ri(&result);
+ printf("\n");
+
+ rc = bssgp_parse_rim_ri(&result, testvec_eutran,
+ sizeof(testvec_eutran));
+ printf("rc=%d\n", rc);
+ dump_rim_ri(&result);
+ printf("\n");
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_create_rim_ri(void)
+{
+ int rc;
+ struct bssgp_rim_routing_info ri;
+ uint8_t result[15];
+
+ printf("----- %s START\n", __func__);
+ memset(&ri, 0, sizeof(ri));
+ memset(result, 0, sizeof(result));
+ ri.discr = BSSGP_RIM_ROUTING_INFO_GERAN;
+
+ ri.geran.raid.mcc = 262;
+ ri.geran.raid.mnc = 42;
+ ri.geran.raid.mnc_3_digits = false;
+ ri.geran.raid.lac = 13200;
+ ri.geran.raid.rac = 0;
+ ri.geran.cid = 0x51e1;
+ dump_rim_ri(&ri);
+ rc = bssgp_create_rim_ri(result, &ri);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n\n");
+
+ memset(&ri, 0, sizeof(ri));
+ memset(result, 0, sizeof(result));
+ ri.discr = BSSGP_RIM_ROUTING_INFO_UTRAN;
+ ri.utran.raid.mcc = 262;
+ ri.utran.raid.mnc = 42;
+ ri.utran.raid.mnc_3_digits = 0;
+ ri.utran.raid.lac = 13200;
+ ri.utran.raid.rac = 0;
+ ri.utran.rncid = 0x51e1;
+ dump_rim_ri(&ri);
+ rc = bssgp_create_rim_ri(result, &ri);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n\n");
+
+ memset(&ri, 0, sizeof(ri));
+ memset(result, 0, sizeof(result));
+ ri.discr = BSSGP_RIM_ROUTING_INFO_EUTRAN;
+ ri.eutran.tai.mcc = 262;
+ ri.eutran.tai.mnc = 42;
+ ri.eutran.tai.mnc_3_digits = 0;
+ ri.eutran.tai.tac = 13200;
+ ri.eutran.global_enb_id[0] = 0x00;
+ ri.eutran.global_enb_id[1] = 0x51;
+ ri.eutran.global_enb_id[2] = 0xe1;
+ ri.eutran.global_enb_id_len = 3;
+ dump_rim_ri(&ri);
+ rc = bssgp_create_rim_ri(result, &ri);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n\n");
+
+ printf("----- %s END\n", __func__);
+}
+
+void dump_bssgp_ran_inf_req_app_cont_nacc(struct bssgp_ran_inf_req_app_cont_nacc *app_cont)
+{
+ printf(" app_cont: bssgp_ran_inf_req_app_cont_nacc:\n");
+ printf(" reprt_cell.rai.lac.plmn.mcc = %u\n", app_cont->reprt_cell.rai.lac.plmn.mcc);
+ printf(" reprt_cell.rai.lac.plmn.mnc = %u\n", app_cont->reprt_cell.rai.lac.plmn.mnc);
+ printf(" reprt_cell.rai.lac.plmn.mnc_3_digits = %u\n", app_cont->reprt_cell.rai.lac.plmn.mnc_3_digits);
+ printf(" reprt_cell.rai.lac.lac = %u\n", app_cont->reprt_cell.rai.lac.lac);
+ printf(" reprt_cell.rai.rac = %u\n", app_cont->reprt_cell.rai.rac);
+ printf(" reprt_cell.cell_identity = %04x\n", app_cont->reprt_cell.cell_identity);
+}
+
+void dump_bssgp_ran_inf_req_rim_cont(struct bssgp_ran_inf_req_rim_cont *rim_cont)
+{
+ printf("bssgp_ran_inf_req_rim_cont:\n");
+ printf(" app_id = %02x\n", rim_cont->app_id);
+ printf(" seq_num = %08x\n", rim_cont->seq_num);
+ printf(" pdu_ind.ack_requested = %u\n", rim_cont->pdu_ind.ack_requested);
+ printf(" pdu_ind.pdu_type_ext = %u\n", rim_cont->pdu_ind.pdu_type_ext);
+ printf(" prot_ver = %u\n", rim_cont->prot_ver);
+ switch (rim_cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ dump_bssgp_ran_inf_req_app_cont_nacc(&rim_cont->u.app_cont_nacc);
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ printf(" app_cont: (not implemented yet)\n");
+ break;
+ default:
+ printf(" app_cont: (illegal application identifier)\n");
+ }
+ if (rim_cont->son_trans_app_id) {
+ printf(" son_trans_app_id: %s\n",
+ osmo_hexdump_nospc(rim_cont->son_trans_app_id, rim_cont->son_trans_app_id_len));
+ printf(" son_trans_app_id_len: %zu\n", rim_cont->son_trans_app_id_len);
+ }
+}
+
+static void test_bssgp_dec_ran_inf_req_rim_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_req_rim_cont rim_cont_dec;
+ uint8_t testvec[] =
+ { 0x4b, 0x81, 0x01, 0x4c, 0x84, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x81, 0x02, 0x55, 0x81, 0x01, 0x4d, 0x88,
+ 0x62, 0xf2, 0x24, 0x33, 0x90, 0x00, 0x51, 0xe1 };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_dec_ran_inf_req_rim_cont(&rim_cont_dec, testvec, sizeof(testvec));
+ printf("rc=%d, ", rc);
+ if (rc == 0)
+ dump_bssgp_ran_inf_req_rim_cont(&rim_cont_dec);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_enc_ran_inf_req_rim_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_req_rim_cont rim_cont = { };
+ uint8_t result[256];
+ printf("----- %s START\n", __func__);
+
+ rim_cont.app_id = BSSGP_RAN_INF_APP_ID_NACC;
+ rim_cont.seq_num = 1;
+ rim_cont.pdu_ind.ack_requested = 0;
+ rim_cont.pdu_ind.pdu_type_ext = 1;
+ rim_cont.prot_ver = 1;
+ rim_cont.son_trans_app_id = NULL;
+ rim_cont.son_trans_app_id_len = 0;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.lac.plmn.mcc = 262;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.lac.plmn.mnc = 42;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.lac.plmn.mnc_3_digits = 0;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.lac.lac = 13200;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.rac = 0;
+ rim_cont.u.app_cont_nacc.reprt_cell.cell_identity = 0x51e1;
+
+ dump_bssgp_ran_inf_req_rim_cont(&rim_cont);
+
+ rc = bssgp_enc_ran_inf_req_rim_cont(result, sizeof(result), &rim_cont);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n");
+ printf("----- %s END\n", __func__);
+}
+
+static void dump_bssgp_ran_inf_app_cont_nacc(struct bssgp_ran_inf_app_cont_nacc *app_cont)
+{
+ unsigned int i;
+ unsigned int silen;
+ printf(" app_cont: bssgp_ran_inf_app_cont_nacc:\n");
+ printf(" reprt_cell.rai.lac.plmn.mcc = %u\n", app_cont->reprt_cell.rai.lac.plmn.mcc);
+ printf(" reprt_cell.rai.lac.plmn.mnc = %u\n", app_cont->reprt_cell.rai.lac.plmn.mnc);
+ printf(" reprt_cell.rai.lac.plmn.mnc_3_digits = %u\n", app_cont->reprt_cell.rai.lac.plmn.mnc_3_digits);
+ printf(" reprt_cell.rai.lac.lac = %u\n", app_cont->reprt_cell.rai.lac.lac);
+ printf(" reprt_cell.rai.rac = %u\n", app_cont->reprt_cell.rai.rac);
+ printf(" reprt_cell.cell_identity = %04x\n", app_cont->reprt_cell.cell_identity);
+ printf(" type_psi = %u\n", app_cont->type_psi);
+ printf(" num_si = %u\n", app_cont->num_si);
+
+ if (app_cont->type_psi)
+ silen = 22;
+ else
+ silen = 21;
+
+ for (i = 0; i < app_cont->num_si; i++)
+ printf(" si[%u] = %s\n", i, osmo_hexdump_nospc(app_cont->si[i], silen));
+}
+
+static void dump_bssgp_app_err_cont_nacc(struct bssgp_app_err_cont_nacc *app_cont)
+{
+ printf(" app_err_cont: bssgp_app_err_cont_nacc:\n");
+ printf(" macc_cause = %02x\n", app_cont->nacc_cause);
+ if (app_cont->err_app_cont) {
+ printf(" err_app_cont: %s\n", osmo_hexdump_nospc(app_cont->err_app_cont, app_cont->err_app_cont_len));
+ printf(" err_app_cont_len: %zu\n", app_cont->err_app_cont_len);
+ }
+}
+
+static void dump_bssgp_ran_inf_rim_cont(struct bssgp_ran_inf_rim_cont *rim_cont)
+{
+ printf("bssgp_ran_inf_rim_cont:\n");
+ printf(" app_id = %02x\n", rim_cont->app_id);
+ printf(" seq_num = %08x\n", rim_cont->seq_num);
+ printf(" pdu_ind.ack_requested = %u\n", rim_cont->pdu_ind.ack_requested);
+ printf(" pdu_ind.pdu_type_ext = %u\n", rim_cont->pdu_ind.pdu_type_ext);
+ printf(" prot_ver = %u\n", rim_cont->prot_ver);
+ printf(" app_err = %u\n", rim_cont->app_err);
+ if (rim_cont->app_err) {
+ switch (rim_cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ dump_bssgp_app_err_cont_nacc(&rim_cont->u.app_err_cont_nacc);
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ printf(" app_err_cont: (not implemented yet)\n");
+ break;
+ default:
+ printf(" app_err_cont: (illegal application identifier)\n");
+ }
+ } else {
+ switch (rim_cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ dump_bssgp_ran_inf_app_cont_nacc(&rim_cont->u.app_cont_nacc);
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ printf(" app_cont: (not implemented yet)\n");
+ break;
+ default:
+ printf(" app_cont: (illegal application identifier)\n");
+ }
+ }
+ if (rim_cont->son_trans_app_id) {
+ printf(" son_trans_app_id: %s\n",
+ osmo_hexdump_nospc(rim_cont->son_trans_app_id, rim_cont->son_trans_app_id_len));
+ printf(" son_trans_app_id_len: %zu\n", rim_cont->son_trans_app_id_len);
+ }
+}
+
+static void test_bssgp_dec_ran_inf_rim_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_rim_cont rim_cont_dec;
+ uint8_t testvec[] =
+ { 0x4b, 0x81, 0x01, 0x4c, 0x84, 0x00, 0x00, 0x00, 0x02, 0x4f, 0x81, 0x02, 0x55, 0x81, 0x01, 0x4e, 0xc8,
+ 0x62, 0xf2, 0x24, 0x33, 0x4f, 0x00, 0x51, 0xe0, 0x06, 0x19, 0x8f, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x2b, 0x1b, 0x75, 0x30, 0x00, 0xf1, 0x10,
+ 0x23, 0x6e,
+ 0xc9, 0x03, 0x3c, 0x27, 0x47, 0x40, 0x79, 0x00, 0x00, 0x3c, 0x0b, 0x2b, 0x2b, 0x00, 0x90, 0x00, 0x18,
+ 0x5a, 0x6f,
+ 0xc9, 0xe0, 0x84, 0x10, 0xab, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
+ };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_dec_ran_inf_rim_cont(&rim_cont_dec, testvec, sizeof(testvec));
+ printf("rc=%d, ", rc);
+ if (rc == 0)
+ dump_bssgp_ran_inf_rim_cont(&rim_cont_dec);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_dec_ran_inf_rim_cont_err_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_rim_cont rim_cont_dec;
+ uint8_t testvec[] =
+ { 0x4b, 0x81, 0x01, 0x4c, 0x84, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x81, 0x02, 0x55, 0x81, 0x01, 0x56, 0x86,
+ 0x01, 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_dec_ran_inf_rim_cont(&rim_cont_dec, testvec, sizeof(testvec));
+ printf("rc=%d, ", rc);
+ if (rc == 0)
+ dump_bssgp_ran_inf_rim_cont(&rim_cont_dec);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_enc_ran_inf_rim_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_rim_cont rim_cont = { };
+
+ uint8_t si1[] =
+ { 0x19, 0x8f, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x79, 0x00, 0x00, 0x2b
+ };
+ uint8_t si3[] =
+ { 0x1b, 0x75, 0x30, 0x00, 0xf1, 0x10, 0x23, 0x6e, 0xc9, 0x03, 0x3c, 0x27, 0x47, 0x40, 0x79, 0x00, 0x00,
+ 0x3c, 0x0b, 0x2b, 0x2b
+ };
+ uint8_t si13[] =
+ { 0x00, 0x90, 0x00, 0x18, 0x5a, 0x6f, 0xc9, 0xe0, 0x84, 0x10, 0xab, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b
+ };
+
+ uint8_t result[256];
+ printf("----- %s START\n", __func__);
+
+ rim_cont.app_id = BSSGP_RAN_INF_APP_ID_NACC;
+ rim_cont.seq_num = 1;
+ rim_cont.pdu_ind.ack_requested = 0;
+ rim_cont.pdu_ind.pdu_type_ext = 1;
+ rim_cont.prot_ver = 1;
+ rim_cont.son_trans_app_id = NULL;
+ rim_cont.son_trans_app_id_len = 0;
+ rim_cont.app_err = false;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.lac.plmn.mcc = 262;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.lac.plmn.mnc = 42;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.lac.plmn.mnc_3_digits = 0;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.lac.lac = 13135;
+ rim_cont.u.app_cont_nacc.reprt_cell.rai.rac = 0;
+ rim_cont.u.app_cont_nacc.reprt_cell.cell_identity = 0x51e0;
+ rim_cont.u.app_cont_nacc.type_psi = 0;
+ rim_cont.u.app_cont_nacc.num_si = 3;
+ rim_cont.u.app_cont_nacc.si[0] = si1;
+ rim_cont.u.app_cont_nacc.si[1] = si3;
+ rim_cont.u.app_cont_nacc.si[2] = si13;
+
+ dump_bssgp_ran_inf_rim_cont(&rim_cont);
+
+ rc = bssgp_enc_ran_inf_rim_cont(result, sizeof(result), &rim_cont);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n");
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_enc_ran_inf_rim_cont_err_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_rim_cont rim_cont = { };
+ uint8_t err_app_cont[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
+
+ uint8_t result[256];
+ printf("----- %s START\n", __func__);
+
+ rim_cont.app_id = BSSGP_RAN_INF_APP_ID_NACC;
+ rim_cont.seq_num = 1;
+ rim_cont.pdu_ind.ack_requested = 0;
+ rim_cont.pdu_ind.pdu_type_ext = 1;
+ rim_cont.prot_ver = 1;
+ rim_cont.son_trans_app_id = NULL;
+ rim_cont.son_trans_app_id_len = 0;
+ rim_cont.app_err = true;
+ rim_cont.u.app_err_cont_nacc.nacc_cause = BSSGP_NACC_CAUSE_SYNTAX_ERR;
+ rim_cont.u.app_err_cont_nacc.err_app_cont = err_app_cont;
+ rim_cont.u.app_err_cont_nacc.err_app_cont_len = sizeof(err_app_cont);
+ dump_bssgp_ran_inf_rim_cont(&rim_cont);
+
+ rc = bssgp_enc_ran_inf_rim_cont(result, sizeof(result), &rim_cont);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n");
+ printf("----- %s END\n", __func__);
+}
+
+static void dump_bssgp_ran_inf_ack_rim_cont(struct bssgp_ran_inf_ack_rim_cont *rim_cont)
+{
+ printf("bssgp_ran_inf_ack_rim_cont:\n");
+ printf(" app_id = %02x\n", rim_cont->app_id);
+ printf(" seq_num = %08x\n", rim_cont->seq_num);
+ printf(" prot_ver = %u\n", rim_cont->prot_ver);
+ if (rim_cont->son_trans_app_id) {
+ printf(" son_trans_app_id: %s\n",
+ osmo_hexdump_nospc(rim_cont->son_trans_app_id, rim_cont->son_trans_app_id_len));
+ printf(" son_trans_app_id_len: %zu\n", rim_cont->son_trans_app_id_len);
+ }
+}
+
+static void test_bssgp_dec_ran_inf_ack_rim_cont(void)
+{
+ int rc;
+ struct bssgp_ran_inf_ack_rim_cont rim_cont_dec;
+ uint8_t testvec[] = { 0x4b, 0x81, 0x01, 0x4c, 0x84, 0x00, 0x00, 0x00, 0x01, 0x55, 0x81, 0x01 };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_dec_ran_inf_ack_rim_cont(&rim_cont_dec, testvec, sizeof(testvec));
+ printf("rc=%d, ", rc);
+ if (rc == 0)
+ dump_bssgp_ran_inf_ack_rim_cont(&rim_cont_dec);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_enc_ran_inf_ack_rim_cont(void)
+{
+ int rc;
+ struct bssgp_ran_inf_ack_rim_cont rim_cont = { };
+ uint8_t result[256];
+ printf("----- %s START\n", __func__);
+
+ rim_cont.app_id = BSSGP_RAN_INF_APP_ID_NACC;
+ rim_cont.seq_num = 1;
+ rim_cont.prot_ver = 1;
+ rim_cont.son_trans_app_id = NULL;
+ rim_cont.son_trans_app_id_len = 0;
+ dump_bssgp_ran_inf_ack_rim_cont(&rim_cont);
+
+ rc = bssgp_enc_ran_inf_ack_rim_cont(result, sizeof(result), &rim_cont);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n");
+ printf("----- %s END\n", __func__);
+}
+
+void dump_bssgp_ran_inf_err_rim_cont(struct bssgp_ran_inf_err_rim_cont *rim_cont)
+{
+ printf("bssgp_ran_inf_err_rim_cont:\n");
+ printf(" app_id = %02x\n", rim_cont->app_id);
+ printf(" cause = %02x\n", rim_cont->cause);
+ printf(" prot_ver = %u\n", rim_cont->prot_ver);
+ if (rim_cont->err_pdu) {
+ printf(" err_pdu: %s\n", osmo_hexdump_nospc(rim_cont->err_pdu, rim_cont->err_pdu_len));
+ printf(" err_pdu_len: %zu\n", rim_cont->err_pdu_len);
+ }
+ if (rim_cont->son_trans_app_id) {
+ printf(" son_trans_app_id: %s\n",
+ osmo_hexdump_nospc(rim_cont->son_trans_app_id, rim_cont->son_trans_app_id_len));
+ printf(" son_trans_app_id_len: %zu\n", rim_cont->son_trans_app_id_len);
+ }
+}
+
+static void test_bssgp_dec_ran_inf_err_rim_cont(void)
+{
+ int rc;
+ struct bssgp_ran_inf_err_rim_cont rim_cont_dec;
+ uint8_t testvec[] =
+ { 0x4b, 0x81, 0x17, 0x07, 0x81, 0x2b, 0x55, 0x81, 0x01, 0x15, 0x85, 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_dec_ran_inf_err_rim_cont(&rim_cont_dec, testvec, sizeof(testvec));
+ printf("rc=%d, ", rc);
+ if (rc == 0)
+ dump_bssgp_ran_inf_err_rim_cont(&rim_cont_dec);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_enc_ran_inf_err_rim_cont(void)
+{
+ int rc;
+ struct bssgp_ran_inf_err_rim_cont rim_cont = { };
+ uint8_t err_pdu[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
+ uint8_t result[256];
+ printf("----- %s START\n", __func__);
+
+ rim_cont.app_id = 23;
+ rim_cont.cause = 0x2b;
+ rim_cont.prot_ver = 1;
+ rim_cont.err_pdu = err_pdu;
+ rim_cont.err_pdu_len = sizeof(err_pdu);
+ rim_cont.son_trans_app_id = NULL;
+ rim_cont.son_trans_app_id_len = 0;
+ dump_bssgp_ran_inf_err_rim_cont(&rim_cont);
+
+ rc = bssgp_enc_ran_inf_err_rim_cont(result, sizeof(result), &rim_cont);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n");
+ printf("----- %s END\n", __func__);
+}
+
+void dump_bssgp_ran_inf_app_err_rim_cont(struct bssgp_ran_inf_app_err_rim_cont *rim_cont)
+{
+ printf("bssgp_ran_inf_app_err_rim_cont:\n");
+ printf(" app_id = %02x\n", rim_cont->app_id);
+ printf(" seq_num = %08x\n", rim_cont->seq_num);
+ printf(" pdu_ind.ack_requested = %u\n", rim_cont->pdu_ind.ack_requested);
+ printf(" pdu_ind.pdu_type_ext = %u\n", rim_cont->pdu_ind.pdu_type_ext);
+ printf(" prot_ver = %u\n", rim_cont->prot_ver);
+ switch (rim_cont->app_id) {
+ case BSSGP_RAN_INF_APP_ID_NACC:
+ dump_bssgp_app_err_cont_nacc(&rim_cont->u.app_err_cont_nacc);
+ break;
+ case BSSGP_RAN_INF_APP_ID_SI3:
+ case BSSGP_RAN_INF_APP_ID_MBMS:
+ case BSSGP_RAN_INF_APP_ID_SON:
+ case BSSGP_RAN_INF_APP_ID_UTRA_SI:
+ printf(" app_err_cont: (not implemented yet)\n");
+ break;
+ default:
+ printf(" app_err_cont: (illegal application identifier)\n");
+ }
+}
+
+static void test_bssgp_dec_ran_inf_app_err_rim_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_app_err_rim_cont rim_cont_dec;
+ uint8_t testvec[] =
+ { 0x4b, 0x81, 0x01, 0x4c, 0x84, 0x00, 0x00, 0x00, 0x01, 0x4f, 0x81, 0x02, 0x55, 0x81, 0x01, 0x56, 0x85,
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee
+ };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_dec_ran_inf_app_err_rim_cont(&rim_cont_dec, testvec, sizeof(testvec));
+ printf("rc=%d, ", rc);
+ if (rc == 0)
+ dump_bssgp_ran_inf_app_err_rim_cont(&rim_cont_dec);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_enc_ran_inf_app_err_rim_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_app_err_rim_cont rim_cont = { };
+ uint8_t err_app_cont[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
+ uint8_t result[256];
+
+ printf("----- %s START\n", __func__);
+ rim_cont.app_id = BSSGP_RAN_INF_APP_ID_NACC;
+ rim_cont.seq_num = 1;
+ rim_cont.pdu_ind.ack_requested = 0;
+ rim_cont.pdu_ind.pdu_type_ext = 1;
+ rim_cont.prot_ver = 1;
+ rim_cont.u.app_err_cont_nacc.nacc_cause = BSSGP_NACC_CAUSE_SYNTAX_ERR;
+ rim_cont.u.app_err_cont_nacc.err_app_cont = err_app_cont;
+ rim_cont.u.app_err_cont_nacc.err_app_cont_len = sizeof(err_app_cont);
+ dump_bssgp_ran_inf_app_err_rim_cont(&rim_cont);
+
+ rc = bssgp_enc_ran_inf_app_err_rim_cont(result, sizeof(result), &rim_cont);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n");
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_dec_ran_inf_req_app_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_req_app_cont_nacc app_cont_dec;
+ uint8_t testvec[] = { 0x62, 0xf2, 0x24, 0x33, 0x90, 0x00, 0x51, 0xe1 };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_dec_ran_inf_req_app_cont_nacc(&app_cont_dec, testvec, sizeof(testvec));
+ printf("rc=%d, ", rc);
+ if (rc == 0)
+ dump_bssgp_ran_inf_req_app_cont_nacc(&app_cont_dec);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_enc_ran_inf_req_app_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_req_app_cont_nacc app_cont = { };
+ uint8_t result[256];
+ printf("----- %s START\n", __func__);
+
+ app_cont.reprt_cell.rai.lac.plmn.mcc = 262;
+ app_cont.reprt_cell.rai.lac.plmn.mnc = 42;
+ app_cont.reprt_cell.rai.lac.plmn.mnc_3_digits = 0;
+ app_cont.reprt_cell.rai.lac.lac = 13200;
+ app_cont.reprt_cell.rai.rac = 0;
+ app_cont.reprt_cell.cell_identity = 0x51e1;
+ dump_bssgp_ran_inf_req_app_cont_nacc(&app_cont);
+
+ rc = bssgp_enc_ran_inf_req_app_cont_nacc(result, sizeof(result), &app_cont);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n");
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_dec_ran_inf_app_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_app_cont_nacc app_cont_dec;
+ uint8_t testvec[] =
+ { 0x62, 0xf2, 0x24, 0x33, 0x4f, 0x00, 0x51, 0xe0, 0x06, 0x19, 0x8f, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x2b, 0x1b, 0x75, 0x30, 0x00, 0xf1, 0x10,
+ 0x23, 0x6e, 0xc9, 0x03, 0x3c, 0x27, 0x47, 0x40, 0x79, 0x00, 0x00, 0x3c, 0x0b, 0x2b, 0x2b, 0x00, 0x90, 0x00, 0x18,
+ 0x5a, 0x6f, 0xc9, 0xe0, 0x84, 0x10, 0xab, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_dec_ran_inf_app_cont_nacc(&app_cont_dec, testvec, sizeof(testvec));
+ printf("rc=%d, ", rc);
+ if (rc == 0)
+ dump_bssgp_ran_inf_app_cont_nacc(&app_cont_dec);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_enc_ran_inf_app_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_ran_inf_app_cont_nacc app_cont = { };
+
+ uint8_t si1[] =
+ { 0x19, 0x8f, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x79, 0x00, 0x00, 0x2b };
+ uint8_t si3[] =
+ { 0x1b, 0x75, 0x30, 0x00, 0xf1, 0x10, 0x23, 0x6e, 0xc9, 0x03, 0x3c, 0x27, 0x47, 0x40, 0x79, 0x00, 0x00,
+ 0x3c, 0x0b, 0x2b, 0x2b };
+ uint8_t si13[] =
+ { 0x00, 0x90, 0x00, 0x18, 0x5a, 0x6f, 0xc9, 0xe0, 0x84, 0x10, 0xab, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b };
+
+ uint8_t result[256];
+ printf("----- %s START\n", __func__);
+
+ app_cont.reprt_cell.rai.lac.plmn.mcc = 262;
+ app_cont.reprt_cell.rai.lac.plmn.mnc = 42;
+ app_cont.reprt_cell.rai.lac.plmn.mnc_3_digits = 0;
+ app_cont.reprt_cell.rai.lac.lac = 13135;
+ app_cont.reprt_cell.rai.rac = 0;
+ app_cont.reprt_cell.cell_identity = 0x51e1;
+ app_cont.type_psi = false;
+ app_cont.num_si = 3;
+ app_cont.si[0] = si1;
+ app_cont.si[1] = si3;
+ app_cont.si[2] = si13;
+ dump_bssgp_ran_inf_app_cont_nacc(&app_cont);
+
+ rc = bssgp_enc_ran_inf_app_cont_nacc(result, sizeof(result), &app_cont);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n");
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_dec_app_err_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_app_err_cont_nacc app_cont_dec;
+ uint8_t testvec[] = { 0x01, 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
+
+ printf("----- %s START\n", __func__);
+
+ rc = bssgp_dec_app_err_cont_nacc(&app_cont_dec, testvec, sizeof(testvec));
+ printf("rc=%d, ", rc);
+ if (rc == 0)
+ dump_bssgp_app_err_cont_nacc(&app_cont_dec);
+
+ printf("----- %s END\n", __func__);
+}
+
+static void test_bssgp_enc_app_err_cont_nacc(void)
+{
+ int rc;
+ struct bssgp_app_err_cont_nacc app_cont = { };
+ uint8_t err_app_cont[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
+ uint8_t result[256];
+ printf("----- %s START\n", __func__);
+
+ app_cont.nacc_cause = BSSGP_NACC_CAUSE_SYNTAX_ERR;
+ app_cont.err_app_cont = err_app_cont;
+ app_cont.err_app_cont_len = sizeof(err_app_cont);
+ dump_bssgp_app_err_cont_nacc(&app_cont);
+
+ rc = bssgp_enc_app_err_cont_nacc(result, sizeof(result), &app_cont);
+ printf("rc=%d, ", rc);
+ if (rc > 0)
+ printf("result=%s", osmo_hexdump_nospc(result, rc));
+ printf("\n");
+ printf("----- %s END\n", __func__);
+}
+
+int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ printf("===== BSSGP RIM test START\n");
+
+ /* RIM routing information */
+ test_bssgp_parse_rim_ri();
+ test_bssgp_create_rim_ri();
+
+ /* RIM containers */
+ test_bssgp_dec_ran_inf_req_rim_cont_nacc();
+ test_bssgp_enc_ran_inf_req_rim_cont_nacc();
+ test_bssgp_dec_ran_inf_rim_cont_nacc();
+ test_bssgp_dec_ran_inf_rim_cont_err_nacc();
+ test_bssgp_enc_ran_inf_rim_cont_nacc();
+ test_bssgp_enc_ran_inf_rim_cont_err_nacc();
+ test_bssgp_dec_ran_inf_ack_rim_cont();
+ test_bssgp_enc_ran_inf_ack_rim_cont();
+ test_bssgp_dec_ran_inf_err_rim_cont();
+ test_bssgp_enc_ran_inf_err_rim_cont();
+ test_bssgp_dec_ran_inf_app_err_rim_cont_nacc();
+ test_bssgp_enc_ran_inf_app_err_rim_cont_nacc();
+
+ /* Application containers */
+ test_bssgp_dec_ran_inf_req_app_cont_nacc();
+ test_bssgp_enc_ran_inf_req_app_cont_nacc();
+ test_bssgp_dec_ran_inf_app_cont_nacc();
+ test_bssgp_enc_ran_inf_app_cont_nacc();
+ test_bssgp_dec_app_err_cont_nacc();
+ test_bssgp_enc_app_err_cont_nacc();
+
+ printf("===== BSSGP RIM test END\n\n");
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/tests/gb/gprs_bssgp_rim_test.ok b/tests/gb/gprs_bssgp_rim_test.ok
new file mode 100644
index 00000000..df5a41df
--- /dev/null
+++ b/tests/gb/gprs_bssgp_rim_test.ok
@@ -0,0 +1,276 @@
+===== BSSGP RIM test START
+----- test_bssgp_parse_rim_ri START
+rc=9
+GERAN cell identifier
+ * mcc: 262
+ mnc: 42
+ mnc 3 digits: 0
+ lac: 13200
+ rac: 0
+ * cell id: 51e1
+
+rc=9
+UTRAN RNC identifier
+ * mcc: 262
+ mnc: 42
+ mnc 3 digits: 0
+ lac: 13200
+ rac: 0
+ * rnc id: 51e1
+
+rc=9
+EUTRAN eNB identifier
+ * mcc: 262
+ mnc: 42
+ mnc 3 digits: 0
+ tac: 13200
+ * global_enb_id: 0051e1
+
+----- test_bssgp_parse_rim_ri END
+----- test_bssgp_create_rim_ri START
+GERAN cell identifier
+ * mcc: 262
+ mnc: 42
+ mnc 3 digits: 0
+ lac: 13200
+ rac: 0
+ * cell id: 51e1
+rc=9, result=0062f22433900051e1
+
+UTRAN RNC identifier
+ * mcc: 262
+ mnc: 42
+ mnc 3 digits: 0
+ lac: 13200
+ rac: 0
+ * rnc id: 51e1
+rc=9, result=0162f22433900051e1
+
+EUTRAN eNB identifier
+ * mcc: 262
+ mnc: 42
+ mnc 3 digits: 0
+ tac: 13200
+ * global_enb_id: 0051e1
+rc=9, result=0262f22433900051e1
+
+----- test_bssgp_create_rim_ri END
+----- test_bssgp_dec_ran_inf_req_rim_cont_nacc START
+rc=0, bssgp_ran_inf_req_rim_cont:
+ app_id = 01
+ seq_num = 00000001
+ pdu_ind.ack_requested = 0
+ pdu_ind.pdu_type_ext = 1
+ prot_ver = 1
+ app_cont: bssgp_ran_inf_req_app_cont_nacc:
+ reprt_cell.rai.lac.plmn.mcc = 262
+ reprt_cell.rai.lac.plmn.mnc = 42
+ reprt_cell.rai.lac.plmn.mnc_3_digits = 0
+ reprt_cell.rai.lac.lac = 13200
+ reprt_cell.rai.rac = 0
+ reprt_cell.cell_identity = 51e1
+----- test_bssgp_dec_ran_inf_req_rim_cont_nacc END
+----- test_bssgp_enc_ran_inf_req_rim_cont_nacc START
+bssgp_ran_inf_req_rim_cont:
+ app_id = 01
+ seq_num = 00000001
+ pdu_ind.ack_requested = 0
+ pdu_ind.pdu_type_ext = 1
+ prot_ver = 1
+ app_cont: bssgp_ran_inf_req_app_cont_nacc:
+ reprt_cell.rai.lac.plmn.mcc = 262
+ reprt_cell.rai.lac.plmn.mnc = 42
+ reprt_cell.rai.lac.plmn.mnc_3_digits = 0
+ reprt_cell.rai.lac.lac = 13200
+ reprt_cell.rai.rac = 0
+ reprt_cell.cell_identity = 51e1
+rc=25, result=4b81014c84000000014f81025581014d8862f22433900051e1
+----- test_bssgp_enc_ran_inf_req_rim_cont_nacc END
+----- test_bssgp_dec_ran_inf_rim_cont_nacc START
+rc=0, bssgp_ran_inf_rim_cont:
+ app_id = 01
+ seq_num = 00000002
+ pdu_ind.ack_requested = 0
+ pdu_ind.pdu_type_ext = 1
+ prot_ver = 1
+ app_err = 0
+ app_cont: bssgp_ran_inf_app_cont_nacc:
+ reprt_cell.rai.lac.plmn.mcc = 262
+ reprt_cell.rai.lac.plmn.mnc = 42
+ reprt_cell.rai.lac.plmn.mnc_3_digits = 0
+ reprt_cell.rai.lac.lac = 13135
+ reprt_cell.rai.rac = 0
+ reprt_cell.cell_identity = 51e0
+ type_psi = 0
+ num_si = 3
+ si[0] = 198fb100000000000000000000000000007900002b
+ si[1] = 1b753000f110236ec9033c2747407900003c0b2b2b
+ si[2] = 009000185a6fc9e08410ab2b2b2b2b2b2b2b2b2b2b
+----- test_bssgp_dec_ran_inf_rim_cont_nacc END
+----- test_bssgp_dec_ran_inf_rim_cont_err_nacc START
+rc=0, bssgp_ran_inf_rim_cont:
+ app_id = 01
+ seq_num = 00000001
+ pdu_ind.ack_requested = 0
+ pdu_ind.pdu_type_ext = 1
+ prot_ver = 1
+ app_err = 1
+ app_err_cont: bssgp_app_err_cont_nacc:
+ macc_cause = 01
+ err_app_cont: aabbccddee
+ err_app_cont_len: 5
+----- test_bssgp_dec_ran_inf_rim_cont_err_nacc END
+----- test_bssgp_enc_ran_inf_rim_cont_nacc START
+bssgp_ran_inf_rim_cont:
+ app_id = 01
+ seq_num = 00000001
+ pdu_ind.ack_requested = 0
+ pdu_ind.pdu_type_ext = 1
+ prot_ver = 1
+ app_err = 0
+ app_cont: bssgp_ran_inf_app_cont_nacc:
+ reprt_cell.rai.lac.plmn.mcc = 262
+ reprt_cell.rai.lac.plmn.mnc = 42
+ reprt_cell.rai.lac.plmn.mnc_3_digits = 0
+ reprt_cell.rai.lac.lac = 13135
+ reprt_cell.rai.rac = 0
+ reprt_cell.cell_identity = 51e0
+ type_psi = 0
+ num_si = 3
+ si[0] = 198fb100000000000000000000000000007900002b
+ si[1] = 1b753000f110236ec9033c2747407900003c0b2b2b
+ si[2] = 009000185a6fc9e08410ab2b2b2b2b2b2b2b2b2b2b
+rc=89, result=4b81014c84000000014f81025581014ec862f224334f0051e006198fb100000000000000000000000000007900002b1b753000f110236ec9033c2747407900003c0b2b2b009000185a6fc9e08410ab2b2b2b2b2b2b2b2b2b2b
+----- test_bssgp_enc_ran_inf_rim_cont_nacc END
+----- test_bssgp_enc_ran_inf_rim_cont_err_nacc START
+bssgp_ran_inf_rim_cont:
+ app_id = 01
+ seq_num = 00000001
+ pdu_ind.ack_requested = 0
+ pdu_ind.pdu_type_ext = 1
+ prot_ver = 1
+ app_err = 1
+ app_err_cont: bssgp_app_err_cont_nacc:
+ macc_cause = 01
+ err_app_cont: aabbccddee
+ err_app_cont_len: 5
+rc=23, result=4b81014c84000000014f8102558101568601aabbccddee
+----- test_bssgp_enc_ran_inf_rim_cont_err_nacc END
+----- test_bssgp_dec_ran_inf_ack_rim_cont START
+rc=0, bssgp_ran_inf_ack_rim_cont:
+ app_id = 01
+ seq_num = 00000001
+ prot_ver = 1
+----- test_bssgp_dec_ran_inf_ack_rim_cont END
+----- test_bssgp_enc_ran_inf_ack_rim_cont START
+bssgp_ran_inf_ack_rim_cont:
+ app_id = 01
+ seq_num = 00000001
+ prot_ver = 1
+rc=12, result=4b81014c8400000001558101
+----- test_bssgp_enc_ran_inf_ack_rim_cont END
+----- test_bssgp_dec_ran_inf_err_rim_cont START
+rc=0, bssgp_ran_inf_err_rim_cont:
+ app_id = 17
+ cause = 2b
+ prot_ver = 1
+ err_pdu: aabbccddee
+ err_pdu_len: 5
+----- test_bssgp_dec_ran_inf_err_rim_cont END
+----- test_bssgp_enc_ran_inf_err_rim_cont START
+bssgp_ran_inf_err_rim_cont:
+ app_id = 17
+ cause = 2b
+ prot_ver = 1
+ err_pdu: aabbccddee
+ err_pdu_len: 5
+rc=16, result=4b811707812b5581011585aabbccddee
+----- test_bssgp_enc_ran_inf_err_rim_cont END
+----- test_bssgp_dec_ran_inf_app_err_rim_cont_nacc START
+rc=0, bssgp_ran_inf_app_err_rim_cont:
+ app_id = 01
+ seq_num = 00000001
+ pdu_ind.ack_requested = 0
+ pdu_ind.pdu_type_ext = 1
+ prot_ver = 1
+ app_err_cont: bssgp_app_err_cont_nacc:
+ macc_cause = aa
+ err_app_cont: bbccddee
+ err_app_cont_len: 4
+----- test_bssgp_dec_ran_inf_app_err_rim_cont_nacc END
+----- test_bssgp_enc_ran_inf_app_err_rim_cont_nacc START
+bssgp_ran_inf_app_err_rim_cont:
+ app_id = 01
+ seq_num = 00000001
+ pdu_ind.ack_requested = 0
+ pdu_ind.pdu_type_ext = 1
+ prot_ver = 1
+ app_err_cont: bssgp_app_err_cont_nacc:
+ macc_cause = 01
+ err_app_cont: aabbccddee
+ err_app_cont_len: 5
+rc=23, result=4b81014c84000000014f8102558101568601aabbccddee
+----- test_bssgp_enc_ran_inf_app_err_rim_cont_nacc END
+----- test_bssgp_dec_ran_inf_req_app_cont_nacc START
+rc=0, app_cont: bssgp_ran_inf_req_app_cont_nacc:
+ reprt_cell.rai.lac.plmn.mcc = 262
+ reprt_cell.rai.lac.plmn.mnc = 42
+ reprt_cell.rai.lac.plmn.mnc_3_digits = 0
+ reprt_cell.rai.lac.lac = 13200
+ reprt_cell.rai.rac = 0
+ reprt_cell.cell_identity = 51e1
+----- test_bssgp_dec_ran_inf_req_app_cont_nacc END
+----- test_bssgp_enc_ran_inf_req_app_cont_nacc START
+ app_cont: bssgp_ran_inf_req_app_cont_nacc:
+ reprt_cell.rai.lac.plmn.mcc = 262
+ reprt_cell.rai.lac.plmn.mnc = 42
+ reprt_cell.rai.lac.plmn.mnc_3_digits = 0
+ reprt_cell.rai.lac.lac = 13200
+ reprt_cell.rai.rac = 0
+ reprt_cell.cell_identity = 51e1
+rc=8, result=62f22433900051e1
+----- test_bssgp_enc_ran_inf_req_app_cont_nacc END
+----- test_bssgp_dec_ran_inf_app_cont_nacc START
+rc=0, app_cont: bssgp_ran_inf_app_cont_nacc:
+ reprt_cell.rai.lac.plmn.mcc = 262
+ reprt_cell.rai.lac.plmn.mnc = 42
+ reprt_cell.rai.lac.plmn.mnc_3_digits = 0
+ reprt_cell.rai.lac.lac = 13135
+ reprt_cell.rai.rac = 0
+ reprt_cell.cell_identity = 51e0
+ type_psi = 0
+ num_si = 3
+ si[0] = 198fb100000000000000000000000000007900002b
+ si[1] = 1b753000f110236ec9033c2747407900003c0b2b2b
+ si[2] = 009000185a6fc9e08410ab2b2b2b2b2b2b2b2b2b2b
+----- test_bssgp_dec_ran_inf_app_cont_nacc END
+----- test_bssgp_enc_ran_inf_app_cont_nacc START
+ app_cont: bssgp_ran_inf_app_cont_nacc:
+ reprt_cell.rai.lac.plmn.mcc = 262
+ reprt_cell.rai.lac.plmn.mnc = 42
+ reprt_cell.rai.lac.plmn.mnc_3_digits = 0
+ reprt_cell.rai.lac.lac = 13135
+ reprt_cell.rai.rac = 0
+ reprt_cell.cell_identity = 51e1
+ type_psi = 0
+ num_si = 3
+ si[0] = 198fb100000000000000000000000000007900002b
+ si[1] = 1b753000f110236ec9033c2747407900003c0b2b2b
+ si[2] = 009000185a6fc9e08410ab2b2b2b2b2b2b2b2b2b2b
+rc=72, result=62f224334f0051e106198fb100000000000000000000000000007900002b1b753000f110236ec9033c2747407900003c0b2b2b009000185a6fc9e08410ab2b2b2b2b2b2b2b2b2b2b
+----- test_bssgp_enc_ran_inf_app_cont_nacc END
+----- test_bssgp_dec_app_err_cont_nacc START
+rc=0, app_err_cont: bssgp_app_err_cont_nacc:
+ macc_cause = 01
+ err_app_cont: aabbccddee
+ err_app_cont_len: 5
+----- test_bssgp_dec_app_err_cont_nacc END
+----- test_bssgp_enc_app_err_cont_nacc START
+ app_err_cont: bssgp_app_err_cont_nacc:
+ macc_cause = 01
+ err_app_cont: aabbccddee
+ err_app_cont_len: 5
+rc=6, result=01aabbccddee
+----- test_bssgp_enc_app_err_cont_nacc END
+===== BSSGP RIM test END
+
diff --git a/tests/gb/gprs_bssgp_test.c b/tests/gb/gprs_bssgp_test.c
index 52e986e8..775ae43b 100644
--- a/tests/gb/gprs_bssgp_test.c
+++ b/tests/gb/gprs_bssgp_test.c
@@ -173,7 +173,7 @@ static void test_bssgp_status(void)
printf("----- %s END\n", __func__);
}
-static void test_bssgp_bad_reset()
+static void test_bssgp_bad_reset(void)
{
struct msgb *msg;
uint16_t bvci_be = htons(2);
@@ -256,7 +256,7 @@ static void test_bssgp_flow_control_bvc(void)
printf("----- %s END\n", __func__);
}
-static void test_bssgp_msgb_copy()
+static void test_bssgp_msgb_copy(void)
{
struct msgb *msg, *msg2;
uint16_t bvci_be = htons(2);
@@ -298,7 +298,7 @@ int main(int argc, char **argv)
osmo_init_logging2(ctx, &info);
log_set_use_color(osmo_stderr_target, 0);
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
msgb_talloc_ctx_init(ctx, 0);
diff --git a/tests/gb/gprs_ns2_test.c b/tests/gb/gprs_ns2_test.c
new file mode 100644
index 00000000..0221a8d6
--- /dev/null
+++ b/tests/gb/gprs_ns2_test.c
@@ -0,0 +1,670 @@
+/* test routines for NS connection handling
+ * (C) 2020 sysmocom - s.f.m.c. GmbH
+ * Author: Alexander Couzens <lynxis@fe80.eu>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#undef _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/gprs/gprs_msgb.h>
+#include <osmocom/gprs/gprs_ns2.h>
+#include <osmocom/gprs/gprs_bssgp.h>
+
+#include "../../src/gb/gprs_ns2_internal.h"
+
+int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ return -1;
+}
+
+static struct log_info info = {};
+static struct osmo_wqueue *unitdata = NULL;
+static struct osmo_gprs_ns2_prim last_nse_recovery = {};
+static struct osmo_gprs_ns2_prim last_nse_mtu_change = {};
+
+static int ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_gprs_ns2_prim *nsp;
+ OSMO_ASSERT(oph->sap == SAP_NS);
+ nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
+ if (oph->msg) {
+ if (oph->primitive == GPRS_NS2_PRIM_UNIT_DATA) {
+ osmo_wqueue_enqueue(unitdata, oph->msg);
+ } else {
+ msgb_free(oph->msg);
+ }
+ }
+ if (oph->primitive == GPRS_NS2_PRIM_STATUS) {
+ if (nsp->u.status.cause == GPRS_NS2_AFF_CAUSE_RECOVERY) {
+ last_nse_recovery = *nsp;
+ } else if (nsp->u.status.cause == GPRS_NS2_AFF_CAUSE_MTU_CHANGE) {
+ last_nse_mtu_change = *nsp;
+ }
+ }
+ return 0;
+}
+
+static int gp_send_to_ns(struct gprs_ns2_inst *nsi, struct msgb *msg, uint16_t nsei, uint16_t bvci, uint32_t lsp)
+{
+ struct osmo_gprs_ns2_prim nsp = {};
+ nsp.nsei = nsei;
+ nsp.bvci = bvci;
+ nsp.u.unitdata.link_selector = lsp;
+ osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA,
+ PRIM_OP_REQUEST, msg);
+ return gprs_ns2_recv_prim(nsi, &nsp.oph);
+}
+
+
+static struct msgb *get_pdu(struct gprs_ns2_vc_bind *bind, enum ns_pdu_type pdu_type)
+{
+ struct gprs_ns_hdr *nsh;
+ struct osmo_wqueue *queue = bind->priv;
+
+ while (!llist_empty(&queue->msg_queue)) {
+ struct msgb *msg = msgb_dequeue(&queue->msg_queue);
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ if (nsh->pdu_type == pdu_type)
+ return msg;
+ msgb_free(msg);
+ }
+
+ return NULL;
+}
+
+static bool find_pdu(struct gprs_ns2_vc_bind *bind, enum ns_pdu_type pdu_type)
+{
+ struct msgb *msg;
+ msg = get_pdu(bind, pdu_type);
+ if (msg) {
+ msgb_free(msg);
+ return true;
+ }
+
+ return false;
+}
+
+static unsigned int count_pdus(struct gprs_ns2_vc_bind *bind)
+{
+ struct osmo_wqueue *queue = bind->priv;
+ return llist_count(&queue->msg_queue);
+}
+
+static void clear_pdus(struct gprs_ns2_vc_bind *bind)
+{
+ struct osmo_wqueue *queue = bind->priv;
+ osmo_wqueue_clear(queue);
+}
+
+struct gprs_ns2_vc_driver vc_driver_dummy = {
+ .name = "GB UDP dummy",
+ .free_bind = clear_pdus,
+};
+
+static int vc_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
+{
+ struct gprs_ns2_vc_bind *bind = nsvc->bind;
+ struct osmo_wqueue *queue = bind->priv;
+
+ osmo_wqueue_enqueue(queue, msg);
+ return 0;
+}
+
+static struct gprs_ns2_vc_bind *dummy_bind(struct gprs_ns2_inst *nsi, const char *name)
+{
+ struct gprs_ns2_vc_bind *bind = NULL;
+ OSMO_ASSERT(ns2_bind_alloc(nsi, name, &bind) == 0);
+ OSMO_ASSERT(bind);
+
+ bind->driver = &vc_driver_dummy;
+ bind->ll = GPRS_NS2_LL_UDP;
+ bind->transfer_capability = 42;
+ bind->send_vc = vc_sendmsg;
+ bind->priv = talloc_zero(bind, struct osmo_wqueue);
+ bind->mtu = 123;
+ struct osmo_wqueue *queue = bind->priv;
+
+ osmo_wqueue_init(queue, 100);
+
+ return bind;
+}
+
+static void free_loopback(struct gprs_ns2_vc_bind *bind) {}
+
+struct gprs_ns2_vc_driver vc_driver_loopback = {
+ .name = "loopback dummy",
+ .free_bind = free_loopback,
+};
+
+/* loopback the msg */
+static int loopback_sendmsg(struct gprs_ns2_vc *nsvc, struct msgb *msg)
+{
+ struct gprs_ns2_vc *target = nsvc->priv;
+ return ns2_recv_vc(target, msg);
+}
+
+/* create a loopback nsvc object which can be used with ns2_tx_* functions. it's not fully registered etc. */
+static struct gprs_ns2_vc *loopback_nsvc(struct gprs_ns2_vc_bind *bind, struct gprs_ns2_vc *target)
+{
+ struct gprs_ns2_vc *nsvc = talloc_zero(bind, struct gprs_ns2_vc);
+ memcpy(nsvc, target, sizeof(struct gprs_ns2_vc));
+ nsvc->bind = bind;
+ nsvc->priv = target;
+ return nsvc;
+}
+
+/* a loop back bind to use the tx_ functions from gprs_ns2_message.c */
+static struct gprs_ns2_vc_bind *loopback_bind(struct gprs_ns2_inst *nsi, const char *name)
+{
+ struct gprs_ns2_vc_bind *bind = NULL;
+ OSMO_ASSERT(ns2_bind_alloc(nsi, name, &bind) == 0)
+ OSMO_ASSERT(bind);
+ bind->driver = &vc_driver_loopback;
+ bind->ll = GPRS_NS2_LL_UDP;
+ bind->transfer_capability = 99;
+ bind->send_vc = loopback_sendmsg;
+ bind->mtu = 123;
+ return bind;
+}
+
+void test_nse_transfer_cap(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[2];
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[3];
+
+ /* create a UDP dummy bind[0] with transfer cap 42.
+ * create nse (nsei 1001)
+ * create 2x nsvc with the same bind.
+ * nsvc[0] or nsvc[1] is alive (or both) cap == 42
+ *
+ * create a second bind with transfer cap == 23
+ * create 3rd nsvc with bind[1]
+ * transfer cap should be 42 + 23
+ */
+
+ printf("--- Testing NSE transfer cap\n");
+
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "transfercap1");
+ bind[1] = dummy_bind(nsi, "transfercap2");
+ bind[1]->transfer_capability = 23;
+ nse = gprs_ns2_create_nse(nsi, 1001, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_ALIVE);
+ OSMO_ASSERT(nse);
+
+ printf("---- Test with NSVC[0]\n");
+ nsvc[0] = ns2_vc_alloc(bind[0], nse, false, GPRS_NS2_VC_MODE_ALIVE, NULL);
+ OSMO_ASSERT(nsvc[0]);
+ OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 0);
+ nsvc[0]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
+ ns2_nse_notify_unblocked(nsvc[0], true);
+ OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42);
+
+ printf("---- Test with NSVC[1]\n");
+ nsvc[1] = ns2_vc_alloc(bind[0], nse, false, GPRS_NS2_VC_MODE_ALIVE, NULL);
+ OSMO_ASSERT(nsvc[1]);
+ OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42);
+ nsvc[1]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
+ ns2_nse_notify_unblocked(nsvc[1], true);
+ OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42);
+
+ printf("---- Test with NSVC[2]\n");
+ nsvc[2] = ns2_vc_alloc(bind[1], nse, false, GPRS_NS2_VC_MODE_ALIVE, NULL);
+ OSMO_ASSERT(nsvc[2]);
+ OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42);
+ nsvc[2]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
+ ns2_nse_notify_unblocked(nsvc[2], true);
+ OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42 + 23);
+
+ printf("---- Test with NSVC[1] removed\n");
+ /* reset nsvc[1] to be unconfigured - shouldn't change anything */
+ nsvc[1]->fi->state = 0; /* HACK: 0 = GPRS_NS2_ST_UNCONFIGURED */
+ ns2_nse_notify_unblocked(nsvc[1], false);
+ OSMO_ASSERT(ns2_count_transfer_cap(nse, 0) == 42 + 23);
+
+ gprs_ns2_free(nsi);
+ printf("--- Finish NSE transfer cap\n");
+
+}
+
+/* setup NSE with 2x NSVCs.
+ * block 1x NSVC
+ * unblock 1x NSVC*/
+void test_block_unblock_nsvc(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[2];
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[2];
+ struct gprs_ns_hdr *nsh;
+ struct msgb *msg;
+ char idbuf[32];
+ int i;
+
+ printf("--- Testing NSE block unblock nsvc\n");
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "bblock1");
+ bind[1] = dummy_bind(nsi, "bblock2");
+ nse = gprs_ns2_create_nse(nsi, 1001, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ OSMO_ASSERT(nse);
+
+ for (i=0; i<2; i++) {
+ printf("---- Create NSVC[i]\n");
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
+ nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
+ OSMO_ASSERT(nsvc[i]);
+ nsvc[i]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
+ /* ensure the fi->state works correct */
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
+ ns2_nse_notify_unblocked(nsvc[i], true);
+ }
+
+ /* both nsvcs are unblocked and alive. Let's block it. */
+ OSMO_ASSERT(!find_pdu(bind[0], NS_PDUT_BLOCK));
+ clear_pdus(bind[0]);
+ ns2_vc_block(nsvc[0]);
+ OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_BLOCK));
+ /* state == BLOCKED */
+ clear_pdus(bind[0]);
+
+ /* now unblocking it */
+ ns2_vc_unblock(nsvc[0]);
+ OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_UNBLOCK));
+ clear_pdus(bind[0]);
+
+ msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, "test_unblock");
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_UNBLOCK_ACK;
+ ns2_recv_vc(nsvc[0], msg);
+
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[0]));
+ gprs_ns2_free(nsi);
+ printf("--- Finish NSE block unblock nsvc\n");
+}
+
+/* setup NSE with 2x NSVCs.
+ * block 1st NSVC
+ * block 2nd NSVC
+ * unblock 1st NSVC */
+void test_block_unblock_nsvc2(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[2];
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[2];
+ struct gprs_ns_hdr *nsh;
+ struct msgb *msg;
+ char idbuf[32];
+ int i;
+
+ printf("--- Testing NSE block unblock nsvc2\n");
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "bblock1");
+ bind[1] = dummy_bind(nsi, "bblock2");
+ nse = gprs_ns2_create_nse(nsi, 1001, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ OSMO_ASSERT(nse);
+
+ for (i=0; i<2; i++) {
+ printf("---- Create NSVC[i]\n");
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
+ nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
+ OSMO_ASSERT(nsvc[i]);
+ nsvc[i]->fi->state = 3; /* HACK: 3 = GPRS_NS2_ST_UNBLOCKED */
+ /* ensure the fi->state works correct */
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
+ ns2_nse_notify_unblocked(nsvc[i], true);
+ }
+
+ OSMO_ASSERT(nse->alive);
+ /* both nsvcs are unblocked and alive. Let's block them. */
+ OSMO_ASSERT(!find_pdu(bind[0], NS_PDUT_BLOCK));
+ clear_pdus(bind[0]);
+ ns2_vc_block(nsvc[0]);
+ OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_BLOCK));
+ clear_pdus(bind[0]);
+ OSMO_ASSERT(nse->alive);
+
+ OSMO_ASSERT(!find_pdu(bind[1], NS_PDUT_BLOCK));
+ clear_pdus(bind[1]);
+ ns2_vc_block(nsvc[1]);
+ OSMO_ASSERT(find_pdu(bind[1], NS_PDUT_BLOCK));
+ clear_pdus(bind[1]);
+ OSMO_ASSERT(!nse->alive);
+
+ /* now unblocking the 1st NSVC */
+ ns2_vc_unblock(nsvc[0]);
+ OSMO_ASSERT(find_pdu(bind[0], NS_PDUT_UNBLOCK));
+ clear_pdus(bind[0]);
+ msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, "test_unblock");
+ msg->l2h = msgb_put(msg, sizeof(*nsh));
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_UNBLOCK_ACK;
+ ns2_recv_vc(nsvc[0], msg);
+ OSMO_ASSERT(nse->alive);
+
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[0]));
+ gprs_ns2_free(nsi);
+ printf("--- Finish NSE block unblock nsvc2\n");
+}
+
+static struct msgb *generate_unitdata(const char *msgname)
+{
+ struct gprs_ns_hdr *nsh;
+ struct msgb *msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, msgname);
+ OSMO_ASSERT(msg);
+
+ msg->l2h = msgb_put(msg, sizeof(*nsh) + 6);
+ nsh = (struct gprs_ns_hdr *) msg->l2h;
+ nsh->pdu_type = NS_PDUT_UNITDATA;
+ nsh->data[0] = 0; /* sdu control */
+ nsh->data[1] = 0; /* msb bvci */
+ nsh->data[2] = 12; /* lsb bvci */
+ nsh->data[3] = 0xab; /* first data byte */
+ nsh->data[4] = 0xcd;
+ nsh->data[5] = 0xef;
+
+ return msg;
+}
+
+void test_unitdata(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[2];
+ struct gprs_ns2_vc_bind *loopbind;
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[2];
+ struct gprs_ns2_vc *loop[2];
+
+ struct msgb *msg, *other;
+ char idbuf[32];
+ int i;
+
+ printf("--- Testing unitdata test\n");
+ osmo_wqueue_clear(unitdata);
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "bblock1");
+ bind[1] = dummy_bind(nsi, "bblock2");
+ loopbind = loopback_bind(nsi, "loopback");
+ nse = gprs_ns2_create_nse(nsi, 1004, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ OSMO_ASSERT(nse);
+
+ for (i=0; i<2; i++) {
+ printf("---- Create NSVC[%d]\n", i);
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
+ nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
+ loop[i] = loopback_nsvc(loopbind, nsvc[i]);
+ OSMO_ASSERT(nsvc[i]);
+ ns2_vc_fsm_start(nsvc[i]);
+ OSMO_ASSERT(!ns2_vc_is_unblocked(nsvc[i]));
+ ns2_tx_reset(loop[i], NS_CAUSE_OM_INTERVENTION);
+ ns2_tx_unblock(loop[i]);
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
+ }
+
+ /* both nsvcs are unblocked and alive */
+ printf("---- Send UNITDATA to NSVC[0]\n");
+ msg = generate_unitdata("test_unitdata");
+ ns2_recv_vc(nsvc[0], msg);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(msg == other);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(NULL == other);
+
+ printf("---- Send Block NSVC[0]\n");
+ ns2_vc_block(nsvc[0]);
+ ns2_tx_block_ack(loop[0], NULL);
+
+ /* try to receive a unitdata - this should be dropped & freed by NS */
+ printf("---- Try to receive over blocked NSVC[0]\n");
+ ns2_recv_vc(nsvc[0], msg);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(NULL == other);
+
+ /* nsvc[1] should be still good */
+ printf("---- Receive over NSVC[1]\n");
+ msg = generate_unitdata("test_unitdata2");
+ ns2_recv_vc(nsvc[1], msg);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(msg == other);
+ msgb_free(msg);
+
+ gprs_ns2_free(nsi);
+ printf("--- Finish unitdata test\n");
+}
+
+void test_unitdata_weights(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[3];
+ struct gprs_ns2_vc_bind *loopbind;
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[3];
+ struct gprs_ns2_vc *loop[3];
+
+ struct msgb *msg, *other;
+ char idbuf[32];
+ int i;
+
+ printf("--- Testing unitdata weight test\n");
+ osmo_wqueue_clear(unitdata);
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "bblock1");
+ bind[1] = dummy_bind(nsi, "bblock2");
+ bind[2] = dummy_bind(nsi, "bblock3");
+ loopbind = loopback_bind(nsi, "loopback");
+ nse = gprs_ns2_create_nse(nsi, 1004, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_ALIVE);
+ OSMO_ASSERT(nse);
+
+ /* data weights are
+ * nsvc[0] = 1
+ * nsvc[1] = 2
+ * nsvc[2] = 3
+ */
+ for (i = 0; i < 3; i++) {
+ printf("---- Create NSVC[%d]\n", i);
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
+ nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_ALIVE, idbuf);
+ loop[i] = loopback_nsvc(loopbind, nsvc[i]);
+ OSMO_ASSERT(nsvc[i]);
+ nsvc[i]->data_weight = i + 1;
+ ns2_vc_fsm_start(nsvc[i]);
+ OSMO_ASSERT(!ns2_vc_is_unblocked(nsvc[i]));
+ ns2_tx_alive_ack(loop[i]);
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
+ }
+
+ /* all nsvcs are alive */
+ printf("---- Send UNITDATA to all NSVCs\n");
+ for (i = 0; i < 3; i++) {
+ msg = generate_unitdata("test_unitdata_weight");
+ ns2_recv_vc(nsvc[i], msg);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(msg == other);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(NULL == other);
+ msgb_free(msg);
+ }
+
+ /* nsvc[1] should be still good */
+ printf("---- Send BSSGP data to the NSE to test unitdata over NSVC[1]\n");
+ for (i = 0; i < 3; i++)
+ clear_pdus(bind[i]);
+
+ for (i = 0; i < 12; i++) {
+ msg = generate_unitdata("test_unitdata_weight2");
+ gp_send_to_ns(nsi, msg, 1004, 1, i + 1);
+ }
+
+ for (i = 0; i < 3; i++)
+ fprintf(stderr, "count_pdus(bind[%d]) = %d\n", i, count_pdus(bind[i]));
+
+ for (i = 0; i < 3; i++) {
+ OSMO_ASSERT(count_pdus(bind[i]) == nsvc[i]->data_weight * 2);
+ }
+
+ gprs_ns2_free(nsi);
+ printf("--- Finish unitdata weight test\n");
+}
+
+void test_mtu(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[2];
+ struct gprs_ns2_vc_bind *loopbind;
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[2];
+ struct gprs_ns2_vc *loop[2];
+
+ struct msgb *msg, *other;
+ char idbuf[32];
+ int i;
+
+ printf("--- Testing mtu test\n");
+ osmo_wqueue_clear(unitdata);
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "bblock1");
+ bind[1] = dummy_bind(nsi, "bblock2");
+ loopbind = loopback_bind(nsi, "loopback");
+ nse = gprs_ns2_create_nse(nsi, 1004, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ OSMO_ASSERT(nse);
+
+ for (i=0; i<2; i++) {
+ printf("---- Create NSVC[%d]\n", i);
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
+ nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
+ loop[i] = loopback_nsvc(loopbind, nsvc[i]);
+ OSMO_ASSERT(nsvc[i]);
+ ns2_vc_fsm_start(nsvc[i]);
+ OSMO_ASSERT(!ns2_vc_is_unblocked(nsvc[i]));
+ ns2_tx_reset(loop[i], NS_CAUSE_OM_INTERVENTION);
+ ns2_tx_unblock(loop[i]);
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
+ }
+
+ /* both nsvcs are unblocked and alive */
+ printf("---- Send a small UNITDATA to NSVC[0]\n");
+ msg = generate_unitdata("test_unitdata");
+ ns2_recv_vc(nsvc[0], msg);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(msg == other);
+ other = msgb_dequeue(&unitdata->msg_queue);
+ OSMO_ASSERT(NULL == other);
+ msgb_free(msg);
+
+ printf("---- Check if got mtu reported\n");
+ /* 1b NS PDU type, 1b NS SDU control, 2b BVCI */
+ OSMO_ASSERT(last_nse_recovery.u.status.mtu == 123 - 4);
+
+ bind[0]->mtu = 100;
+ ns2_nse_update_mtu(nse);
+ OSMO_ASSERT(last_nse_mtu_change.u.status.mtu == 100 - 4);
+
+ gprs_ns2_free(nsi);
+ printf("--- Finish unitdata test\n");
+}
+
+void test_unconfigured(void *ctx)
+{
+ struct gprs_ns2_inst *nsi;
+ struct gprs_ns2_vc_bind *bind[2];
+ struct gprs_ns2_vc_bind *loopbind;
+ struct gprs_ns2_nse *nse;
+ struct gprs_ns2_vc *nsvc[2];
+ struct gprs_ns2_vc *loop[2];
+
+ char idbuf[32];
+ int i;
+
+ printf("--- Testing force unconfigured\n");
+ osmo_wqueue_clear(unitdata);
+ printf("---- Create NSE + Binds\n");
+ nsi = gprs_ns2_instantiate(ctx, ns_prim_cb, NULL);
+ bind[0] = dummy_bind(nsi, "bblock1");
+ bind[1] = dummy_bind(nsi, "bblock2");
+ loopbind = loopback_bind(nsi, "loopback");
+ nse = gprs_ns2_create_nse(nsi, 1004, GPRS_NS2_LL_UDP, GPRS_NS2_DIALECT_STATIC_RESETBLOCK);
+ OSMO_ASSERT(nse);
+
+ for (i=0; i<2; i++) {
+ printf("---- Create NSVC[%d]\n", i);
+ snprintf(idbuf, sizeof(idbuf), "NSE%05u-dummy-%i", nse->nsei, i);
+ nsvc[i] = ns2_vc_alloc(bind[i], nse, false, GPRS_NS2_VC_MODE_BLOCKRESET, idbuf);
+ loop[i] = loopback_nsvc(loopbind, nsvc[i]);
+ OSMO_ASSERT(nsvc[i]);
+ ns2_vc_fsm_start(nsvc[i]);
+ OSMO_ASSERT(!ns2_vc_is_unblocked(nsvc[i]));
+ ns2_tx_reset(loop[i], NS_CAUSE_OM_INTERVENTION);
+ ns2_tx_unblock(loop[i]);
+ OSMO_ASSERT(ns2_vc_is_unblocked(nsvc[i]));
+ }
+
+ /* both nsvcs are unblocked and alive */
+ printf("---- Check if NSE is alive\n");
+ OSMO_ASSERT(nse->alive);
+
+ ns2_vc_force_unconfigured(nsvc[0]);
+ OSMO_ASSERT(nse->alive);
+
+ ns2_vc_force_unconfigured(nsvc[1]);
+ printf("---- Check if NSE is dead\n");
+ OSMO_ASSERT(!nse->alive);
+
+ gprs_ns2_free(nsi);
+ printf("--- Finish force unconfigured test\n");
+}
+
+int main(int argc, char **argv)
+{
+ void *ctx = talloc_named_const(NULL, 0, "gprs_ns2_test");
+ osmo_init_logging2(ctx, &info);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_log_level(osmo_stderr_target, LOGL_INFO);
+ unitdata = talloc_zero(ctx, struct osmo_wqueue);
+ osmo_wqueue_init(unitdata, 100);
+ setlinebuf(stdout);
+
+ printf("===== NS2 protocol test START\n");
+ test_nse_transfer_cap(ctx);
+ test_block_unblock_nsvc(ctx);
+ test_block_unblock_nsvc2(ctx);
+ test_unitdata(ctx);
+ test_unitdata_weights(ctx);
+ test_unconfigured(ctx);
+ test_mtu(ctx);
+ printf("===== NS2 protocol test END\n\n");
+
+ talloc_free(ctx);
+ exit(EXIT_SUCCESS);
+}
diff --git a/tests/gb/gprs_ns2_test.err b/tests/gb/gprs_ns2_test.err
new file mode 100644
index 00000000..5ed62dba
--- /dev/null
+++ b/tests/gb/gprs_ns2_test.err
@@ -0,0 +1,139 @@
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01001)-NSVC(none) Tx NS-BLOCK cause=O&M intervention
+NSE(01001)-NSVC(none) Tx NS-ALIVE
+NSE(01001)-NSVC(none) Tx NS-UNBLOCK
+NSE(01001)-NSVC(none) Rx NS-UNBLOCK-ACK
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01001)-NSVC(none) Tx NS-BLOCK cause=O&M intervention
+NSE(01001)-NSVC(none) Tx NS-ALIVE
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) Tx NS-BLOCK cause=O&M intervention
+NSE(01001)-NSVC(none) Tx NS-ALIVE
+NSE(01001)-NSVC(none) Tx NS-UNBLOCK
+NSE(01001)-NSVC(none) Rx NS-UNBLOCK-ACK
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01001) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01001)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-BLOCK cause=O&M intervention
+NSE(01004)-NSVC(none) Tx NS-BLOCK-ACK
+NSE(01004)-NSVC(none) Rx NS-BLOCK-ACK
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-STATUS cause=NS-VC blocked
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) Rx NS-ALIVE-ACK
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) Rx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) Rx NS-ALIVE-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=126, first=0, mtu=119
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+NSE(01004)-NSVC(none) Tx NS-UNITDATA
+count_pdus(bind[0]) = 2
+count_pdus(bind[1]) = 4
+count_pdus(bind[2]) = 6
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE recovery, transfer=42, first=1, mtu=119
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=42, first=0, mtu=119
+NSE(01004)-NSVC(none) Tx NS-RESET cause=O&M intervention
+NSE(01004)-NSVC(none) Rx NS-RESET
+NSE(01004)-NSVC(none) Tx NS-RESET-ACK
+NSE(01004)-NSVC(none) Tx NS-ALIVE
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK
+NSE(01004)-NSVC(none) Rx NS-UNBLOCK
+NSE(01004)-NSVC(none) Tx NS-UNBLOCK-ACK
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC recovery, transfer=84, first=0, mtu=119
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004)-NSVC(none) Rx NS-UNITDATA
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE MTU changed, transfer=84, first=0, mtu=96
+NSE(01004) NS-STATUS.ind(bvci=00000): cause=NSE failure, transfer=0, first=0, mtu=96
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=96
+NSE(01004)-NSVC(none) NS-STATUS.ind(bvci=00000): cause=NSVC failure, transfer=0, first=0, mtu=96
diff --git a/tests/gb/gprs_ns2_test.ok b/tests/gb/gprs_ns2_test.ok
new file mode 100644
index 00000000..16e66bb8
--- /dev/null
+++ b/tests/gb/gprs_ns2_test.ok
@@ -0,0 +1,51 @@
+===== NS2 protocol test START
+--- Testing NSE transfer cap
+---- Create NSE + Binds
+---- Test with NSVC[0]
+---- Test with NSVC[1]
+---- Test with NSVC[2]
+---- Test with NSVC[1] removed
+--- Finish NSE transfer cap
+--- Testing NSE block unblock nsvc
+---- Create NSE + Binds
+---- Create NSVC[i]
+---- Create NSVC[i]
+--- Finish NSE block unblock nsvc
+--- Testing NSE block unblock nsvc2
+---- Create NSE + Binds
+---- Create NSVC[i]
+---- Create NSVC[i]
+--- Finish NSE block unblock nsvc2
+--- Testing unitdata test
+---- Create NSE + Binds
+---- Create NSVC[0]
+---- Create NSVC[1]
+---- Send UNITDATA to NSVC[0]
+---- Send Block NSVC[0]
+---- Try to receive over blocked NSVC[0]
+---- Receive over NSVC[1]
+--- Finish unitdata test
+--- Testing unitdata weight test
+---- Create NSE + Binds
+---- Create NSVC[0]
+---- Create NSVC[1]
+---- Create NSVC[2]
+---- Send UNITDATA to all NSVCs
+---- Send BSSGP data to the NSE to test unitdata over NSVC[1]
+--- Finish unitdata weight test
+--- Testing force unconfigured
+---- Create NSE + Binds
+---- Create NSVC[0]
+---- Create NSVC[1]
+---- Check if NSE is alive
+---- Check if NSE is dead
+--- Finish force unconfigured test
+--- Testing mtu test
+---- Create NSE + Binds
+---- Create NSVC[0]
+---- Create NSVC[1]
+---- Send a small UNITDATA to NSVC[0]
+---- Check if got mtu reported
+--- Finish unitdata test
+===== NS2 protocol test END
+
diff --git a/tests/gb/gprs_ns2_vty.vty b/tests/gb/gprs_ns2_vty.vty
new file mode 100644
index 00000000..8db5fb50
--- /dev/null
+++ b/tests/gb/gprs_ns2_vty.vty
@@ -0,0 +1,89 @@
+OsmoNSdummy> list
+...
+ show ns binds [stats]
+ show ns entities [stats]
+ show ns persistent
+ show ns (nsei|nsvc) <0-65535> [stats]
+...
+ logging filter nse nsei <0-65535>
+ logging filter nsvc nsvci <0-65535>
+...
+OsmoNSdummy> enable
+OsmoNSdummy# configure terminal
+OsmoNSdummy(config)# list
+...
+ ns
+...
+OsmoNSdummy(config)# ns
+OsmoNSdummy(config-ns)# list
+...
+ timer (tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries|tsns-prov|tsns-size-retries|tsns-config-retries|tsns-procedures-retries) <0-65535>
+ nse <0-65535> [ip-sns-role-sgsn]
+ no nse <0-65535>
+ bind (fr|udp) ID
+ no bind ID
+...
+OsmoNSdummy(config-ns)# bind udp abc
+OsmoNSdummy(config-ns-bind)# fr eta0 frnet
+fr can be only used with frame relay bind
+OsmoNSdummy(config-ns-bind)# listen 127.0.0.14 42999
+OsmoNSdummy(config-ns-bind)# end
+OsmoNSdummy# show ns
+UDP bind: 127.0.0.14:42999 DSCP: 0 Priority: 0
+ IP-SNS signalling weight: 1 data weight: 1
+ 0 NS-VC:
+OsmoNSdummy# configure terminal
+OsmoNSdummy(config)# ns
+OsmoNSdummy(config-ns)# nse 1234
+OsmoNSdummy(config-ns-nse)# nsvc udp abc 127.0.0.15 9496
+OsmoNSdummy(config-ns-nse)# end
+OsmoNSdummy# show ns
+NSEI 01234: UDP, DEAD since 0d 0h 0m 0s
+ 1 NS-VC:
+ RECOVERING PERSIST sig_weight=1 data_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+UDP bind: 127.0.0.14:42999 DSCP: 0 Priority: 0
+ IP-SNS signalling weight: 1 data weight: 1
+ 1 NS-VC:
+ RECOVERING PERSIST sig_weight=1 data_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+OsmoNSdummy# configure terminal
+OsmoNSdummy(config)# ns
+OsmoNSdummy(config-ns)# nse 1234
+OsmoNSdummy(config-ns-nse)# nsvc udp abc 127.0.0.16 9496 signalling-weight 0 data-weight 9
+OsmoNSdummy(config-ns-nse)# nsvc udp abc 127.0.0.17 9496 signalling-weight 0 data-weight 0
+OsmoNSdummy(config-ns-nse)# end
+OsmoNSdummy# show ns
+NSEI 01234: UDP, DEAD since 0d 0h 0m 0s
+ 3 NS-VC:
+ RECOVERING PERSIST sig_weight=1 data_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+ RECOVERING PERSIST sig_weight=0 data_weight=9 udp)[127.0.0.14]:42999<>[127.0.0.16]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+ RECOVERING PERSIST sig_weight=0 data_weight=0 udp)[127.0.0.14]:42999<>[127.0.0.17]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+UDP bind: 127.0.0.14:42999 DSCP: 0 Priority: 0
+ IP-SNS signalling weight: 1 data weight: 1
+ 3 NS-VC:
+ RECOVERING PERSIST sig_weight=1 data_weight=1 udp)[127.0.0.14]:42999<>[127.0.0.15]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+ RECOVERING PERSIST sig_weight=0 data_weight=9 udp)[127.0.0.14]:42999<>[127.0.0.16]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+ RECOVERING PERSIST sig_weight=0 data_weight=0 udp)[127.0.0.14]:42999<>[127.0.0.17]:9496 DEAD (cause: remote) since 0d 0h 0m 0s
+OsmoNSdummy# configure terminal
+OsmoNSdummy(config)# ns
+OsmoNSdummy(config-ns)# nse 1234
+OsmoNSdummy(config-ns-nse)# nsvc udp abc 127.0.0.16 9496 signalling-weight 2 data-weight 2
+Specified NSVC is already present in this NSE.
+OsmoNSdummy(config-ns-nse)# exit
+OsmoNSdummy(config-ns)# nse 1235
+OsmoNSdummy(config-ns-nse)# nsvc udp abc 127.0.0.16 9496 signalling-weight 2 data-weight 2
+Specified NSVC is already present in another NSE01234.
+OsmoNSdummy(config-ns-nse)# exit
+OsmoNSdummy(config-ns)# nse 2342
+OsmoNSdummy(config-ns-nse)# ip-sns-bind abc
+OsmoNSdummy(config-ns-nse)# ip-sns-bind abc
+Failed to add ip-sns-bind abc already present
+OsmoNSdummy(config-ns-nse)# ip-sns-bind abc2
+Can not find the given bind 'abc2'
+OsmoNSdummy(config-ns-nse)# ip-sns-remote 127.0.0.1 22222
+OsmoNSdummy(config-ns-nse)# ip-sns-remote 127.0.0.1 22222
+Specified SNS endpoint already part of the NSE.
+OsmoNSdummy(config-ns-nse)# exit
+OsmoNSdummy(config-ns)# no bind abc
+OsmoNSdummy(config-ns)# no bind abc77
+bind abc77 does not exist!
+OsmoNSdummy(config-ns)# exit
diff --git a/tests/gb/gprs_ns_test.c b/tests/gb/gprs_ns_test.c
index f70e4937..fc1ec605 100644
--- a/tests/gb/gprs_ns_test.c
+++ b/tests/gb/gprs_ns_test.c
@@ -262,7 +262,7 @@ static void dump_rate_ctr_group(FILE *stream, const char *prefix,
unsigned int i;
for (i = 0; i < ctrg->desc->num_ctr; i++) {
- struct rate_ctr *ctr = &ctrg->ctr[i];
+ struct rate_ctr *ctr = rate_ctr_group_get_ctr(ctrg, i);
if (ctr->current && !strchr(ctrg->desc->ctr_desc[i].name, ':'))
fprintf(stream, " %s%s: %llu%s",
prefix, ctrg->desc->ctr_desc[i].description,
@@ -420,7 +420,7 @@ static int expire_nsvc_timer(struct gprs_nsvc *nsvc)
return rc;
}
-static void test_nsvc()
+static void test_nsvc(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in peer[1] = {{0},};
@@ -459,7 +459,7 @@ static void test_nsvc()
alarm(0);
}
-static void test_ignored_messages()
+static void test_ignored_messages(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in peer[1] = {{0},};
@@ -486,7 +486,7 @@ static void test_ignored_messages()
nsi = NULL;
}
-static void test_bss_port_changes()
+static void test_bss_port_changes(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in peer[4] = {{0},};
@@ -540,7 +540,7 @@ static void test_bss_port_changes()
nsi = NULL;
}
-static void test_bss_reset_ack()
+static void test_bss_reset_ack(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in peer[4] = {{0},};
@@ -687,7 +687,7 @@ static void test_bss_reset_ack()
}
-static void test_sgsn_reset()
+static void test_sgsn_reset(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in sgsn_peer= {0};
@@ -765,7 +765,7 @@ static void test_sgsn_reset()
nsi = NULL;
}
-static void test_sgsn_reset_invalid_state()
+static void test_sgsn_reset_invalid_state(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in sgsn_peer= {0};
@@ -833,7 +833,7 @@ static void test_sgsn_reset_invalid_state()
nsi = NULL;
}
-static void test_sgsn_output()
+static void test_sgsn_output(void)
{
struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL);
struct sockaddr_in sgsn_peer= {0};
@@ -904,11 +904,11 @@ int main(int argc, char **argv)
void *ctx = talloc_named_const(NULL, 0, "gprs_ns_test");
osmo_init_logging2(ctx, &info);
log_set_use_color(osmo_stderr_target, 0);
- log_set_print_filename(osmo_stderr_target, 0);
- osmo_signal_register_handler(SS_L_NS, &test_signal, NULL);
-
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_log_level(osmo_stderr_target, LOGL_INFO);
+ osmo_signal_register_handler(SS_L_NS, &test_signal, NULL);
setlinebuf(stdout);
diff --git a/tests/gb/gprs_ns_test.err b/tests/gb/gprs_ns_test.err
new file mode 100644
index 00000000..266fb4e5
--- /dev/null
+++ b/tests/gb/gprs_ns_test.err
@@ -0,0 +1,147 @@
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+Ignoring NS STATUS from 1.2.3.4:1111 for non-existing NS-VC
+Ignoring NS ALIVE ACK from 1.2.3.4:1111 for non-existing NS-VC
+Ignoring NS RESET ACK from 1.2.3.4:1111 for non-existing NS-VC
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4386, NSVCI=4386, cause=O&M intervention)
+NSEI=4386 Tx NS RESET ACK (NSVCI=4386)
+NSEI=4386 Rx NS UNBLOCK
+NSVCI=4386 Rx NS RESET (NSEI=4386, NSVCI=4386, cause=O&M intervention)
+NSEI=4386 Tx NS RESET ACK (NSVCI=4386)
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:3333
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4386, NSVCI=13124, cause=O&M intervention)
+NSEI=4386 Tx NS RESET ACK (NSVCI=13124)
+NS-VC changed NSEI (NSVCI=4386) from 4386 to 13124
+NSVCI=4386 Rx NS RESET (NSEI=13124, NSVCI=4386, cause=O&M intervention)
+NSEI=13124 Tx NS RESET ACK (NSVCI=4386)
+NSVCI=13124 Rx NS RESET (NSEI=4386, NSVCI=4386, cause=O&M intervention)
+NS-VC changed link (NSVCI=4386) from 1.2.3.4:3333 to 1.2.3.4:4444
+NSEI=4386 Tx NS RESET ACK (NSVCI=4386)
+NSVCI=4386 Rx NS RESET (NSEI=4386, NSVCI=4386, cause=O&M intervention)
+NSEI=4386 Tx NS RESET ACK (NSVCI=4386)
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:1111
+NSVCI=65535(invalid) Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSEI=4096 Rx NS UNBLOCK
+NSVCI=65535 Creating NS-VC with Signal weight 1, Data weight 1
+Creating NS-VC for BSS at 1.2.3.4:2222
+NSVCI=65535(invalid) Rx NS RESET (NSEI=8192, NSVCI=8193, cause=O&M intervention)
+NSEI=8192 Tx NS RESET ACK (NSVCI=8193)
+NSEI=8192 Rx NS UNBLOCK
+NSEI=4096 RESET procedure based on API request
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NSEI=8192 RESET procedure based on API request
+NSEI=8192 Tx NS RESET (NSVCI=8193, cause=O&M intervention)
+NSVCI=8193 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NS-VC changed link (NSVCI=4097) from 1.2.3.4:2222 to 1.2.3.4:1111
+NSEI=8192 RESET procedure based on API request
+NSEI=8192 Tx NS RESET (NSVCI=8193, cause=O&M intervention)
+failed to send NS message via UDP: Operation not permitted
+NSEI=8192, error resetting NS-VC
+NSEI=4096 RESET procedure based on API request
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NSVCI=8193 Rx NS RESET (NSEI=8192, NSVCI=8193, cause=O&M intervention)
+NSEI=8192 Tx NS RESET ACK (NSVCI=8193)
+NSEI=8192 Rx NS UNBLOCK
+NSVCI=4097 Rx NS RESET (NSEI=61440, NSVCI=4097, cause=O&M intervention)
+NSEI=61440 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=4097 Rx NS RESET (NSEI=4096, NSVCI=61441, cause=O&M intervention)
+Creating NS-VC 61441 replacing 4097 at 1.2.3.4:2222
+NSVCI=61441 Creating NS-VC with Signal weight 1, Data weight 1
+NSEI=4096 Tx NS RESET ACK (NSVCI=61441)
+NSVCI=61441 Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NS-VC changed link (NSVCI=4097) from 1.2.3.4:2222 to 0.0.0.0:0
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NS RESET ACK Discarding unexpected message for NS-VCI 4097 from SGSN NSEI=4096
+NSEI=4096 RESET procedure based on API request
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=61440, NSVCI=4097)
+NSEI=61440 RESET procedure based on API request
+NSEI=61440 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=61441)
+NS-VC changed link (NSVCI=61441) from 1.2.3.4:2222 to 0.0.0.0:0
+NSVCI=61441 Rx NS RESET (NSEI=4096, NSVCI=4097, cause=O&M intervention)
+NS-VC changed link (NSVCI=4097) from 1.2.3.4:2222 to 0.0.0.0:0
+NSEI=4096 Tx NS RESET ACK (NSVCI=4097)
+NSEI=4096 Rx NS UNBLOCK
+NSEI=4096 RESET procedure based on API request
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSEI=4096 Rx NS UNBLOCK ACK
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NS RESET ACK Discarding unexpected message for NS-VCI 4097 from SGSN NSEI=4096
+NSEI=4096 Reset timed out but RESET flag is not set
+NSEI=4096 Tx NS RESET (NSVCI=4097, cause=O&M intervention)
+NSVCI=4097 Rx NS RESET ACK (NSEI=4096, NSVCI=4097)
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=257 Creating NS-VC with Signal weight 1, Data weight 1
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NSEI=256 Tx NS UNBLOCK (NSVCI=257)
+NSEI=256 Rx NS UNBLOCK ACK
+NSVCI=257 Rx NS RESET (NSEI=256, NSVCI=257, cause=O&M intervention)
+NSEI=256 Tx NS RESET ACK (NSVCI=257)
+NSVCI=257 Rx NS RESET (NSEI=61440, NSVCI=257, cause=O&M intervention)
+NSEI=256 Tx NS RESET ACK (NSVCI=257)
+NSVCI=257 Rx NS RESET (NSEI=256, NSVCI=61441, cause=O&M intervention)
+NSEI=256 Tx NS RESET ACK (NSVCI=257)
+NSVCI=257 Rx NS RESET (NSEI=256, NSVCI=257, cause=O&M intervention)
+NSEI=256 Tx NS RESET ACK (NSVCI=257)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NS RESET ACK Discarding unexpected message for NS-VCI 257 from SGSN NSEI=256
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+NSVCI=257 Rx NS RESET ACK (NSEI=57344, NSVCI=257)
+NS RESET ACK Unknown NSEI 57344 (NS-VCI=257) from 5.6.7.8:32000
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=57345)
+NS RESET ACK Unknown NS-VCI 57345 (SGSN NSEI=256) from 5.6.7.8:32000
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+NSVCI=257 Creating NS-VC with Signal weight 1, Data weight 1
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NSEI=256 Tx NS UNBLOCK (NSVCI=257)
+NSEI=256 Rx NS UNBLOCK ACK
+NSEI=256 Tns-alive expired more then 10 times, blocking NS-VC
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=PDU not compatible with protocol state)
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NSEI=256 Tx NS UNBLOCK (NSVCI=257)
+NSEI=256 Rx NS UNBLOCK ACK
+NSVCI=65534 Creating NS-VC with Signal weight 1, Data weight 1
+Unable to resolve NSEI 256 to NS-VC!
+NSVCI=257 Creating NS-VC with Signal weight 1, Data weight 1
+NSEI=256 RESET procedure based on API request
+NSEI=256 Tx NS RESET (NSVCI=257, cause=O&M intervention)
+All NS-VCs for NSEI 256 are either dead or blocked!
+NSVCI=257 Rx NS RESET ACK (NSEI=256, NSVCI=257)
+NSEI=256 Tx NS UNBLOCK (NSVCI=257)
+All NS-VCs for NSEI 256 are either dead or blocked!
+NSEI=256 Rx NS UNBLOCK ACK
diff --git a/tests/gb/osmo-ns-dummy.cfg b/tests/gb/osmo-ns-dummy.cfg
new file mode 100644
index 00000000..72696189
--- /dev/null
+++ b/tests/gb/osmo-ns-dummy.cfg
@@ -0,0 +1,25 @@
+!
+! OsmoNSdummy (1.4.0.495-64db) configuration saved from vty
+!!
+ns
+ timer tns-block 3
+ timer tns-block-retries 3
+ timer tns-reset 3
+ timer tns-reset-retries 3
+ timer tns-test 30
+ timer tns-alive 3
+ timer tns-alive-retries 10
+ timer tsns-prov 3
+ timer tsns-size-retries 3
+ timer tsns-config-retries 3
+ bind udp local
+ listen 127.0.0.1 2158
+ accept-ipaccess
+ ip-sns signalling-weight 1 data-weight 1
+ nse 1236
+ nsvc ipa local 127.0.0.4 23000 nsvci 101
+ nse 1235
+ nsvc udp local 127.0.0.3 23000
+ nse 1234
+ ip-sns-bind local
+ ip-sns-remote 127.0.0.2 2158
diff --git a/tests/gb/osmoappdesc.py b/tests/gb/osmoappdesc.py
new file mode 100644
index 00000000..323b94ef
--- /dev/null
+++ b/tests/gb/osmoappdesc.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+# (C) 2021 by sysmocom - s.f.m.c. GmbH
+# Author: Alexander Couzens <lynxis@fe80.eu>
+# 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/>
+
+app_configs = {
+ "osmo-ns-dummy": ["osmo-ns-dummy.cfg"],
+}
+
+apps = [(45999, "../../utils/osmo-ns-dummy -p 45999", "OsmoNSdummy", "osmo-ns-dummy")
+ ]
+
+vty_command = ["../../utils/osmo-ns-dummy", "-p", "45999", "-c", "osmo-ns-dummy.cfg"]
+
+vty_app = apps[0]
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index 96178237..8a89357c 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -12,12 +12,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -33,40 +31,96 @@
#include <osmocom/core/msgb.h>
-static const uint8_t csd_9600_v110_lv[] = { 0x07, 0xa1, 0xb8, 0x89, 0x21, 0x15, 0x63, 0x80 };
+static const uint8_t csd_9600_v110_lv[] = { 0x07, 0xa1, 0x88, 0x89, 0x21, 0x15, 0x63, 0x80 };
static const struct gsm_mncc_bearer_cap bcap_csd_9600_v110 = {
- .transfer = GSM48_BCAP_ITCAP_UNR_DIG_INF,
- .mode = GSM48_BCAP_TMOD_CIRCUIT,
- .coding = GSM48_BCAP_CODING_GSM_STD,
- .radio = GSM48_BCAP_RRQ_FR_ONLY,
- .speech_ver[0]= -1,
+ .transfer = GSM48_BCAP_ITCAP_UNR_DIG_INF,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_FR_ONLY,
+ .speech_ver[0] = -1,
+ .data = {
+ .rate_adaption = GSM48_BCAP_RA_V110_X30,
+ .sig_access = GSM48_BCAP_SA_I440_I450,
+ .async = 1,
+ .nr_stop_bits = 1,
+ .nr_data_bits = 8,
+ .user_rate = GSM48_BCAP_UR_9600,
+ .parity = GSM48_BCAP_PAR_NONE,
+ .interm_rate = GSM48_BCAP_IR_16k,
+ .transp = GSM48_BCAP_TR_TRANSP,
+ .modem_type = GSM48_BCAP_MT_NONE,
+ },
+};
+
+static const uint8_t csd_4800_rlp_lv[] = { 0x07, 0xa1, 0x88, 0x89, 0x21, 0x14, 0x63, 0xa0 };
+
+static const struct gsm_mncc_bearer_cap bcap_csd_4800_rlp = {
+ .transfer = GSM48_BCAP_ITCAP_UNR_DIG_INF,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_FR_ONLY,
+ .speech_ver[0] = -1,
+ .data = {
+ .rate_adaption = GSM48_BCAP_RA_V110_X30,
+ .sig_access = GSM48_BCAP_SA_I440_I450,
+ .async = 1,
+ .nr_stop_bits = 1,
+ .nr_data_bits = 8,
+ .user_rate = GSM48_BCAP_UR_4800,
+ .parity = GSM48_BCAP_PAR_NONE,
+ .interm_rate = GSM48_BCAP_IR_16k,
+ .transp = GSM48_BCAP_TR_RLP,
+ .modem_type = GSM48_BCAP_MT_NONE,
+ },
+};
+
+static const uint8_t csd_2400_v22bis_lv[] = { 0x07, 0xa2, 0xb8, 0x81, 0x21, 0x13, 0x43, 0x83 };
+
+static const struct gsm_mncc_bearer_cap bcap_csd_2400_v22bis = {
+ .transfer = GSM48_BCAP_ITCAP_3k1_AUDIO,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_FR_ONLY,
+ .speech_ver[0] = -1,
.data = {
- .rate_adaption = GSM48_BCAP_RA_V110_X30,
- .sig_access = GSM48_BCAP_SA_I440_I450,
- .async = 1,
- .nr_stop_bits = 1,
- .nr_data_bits = 8,
- .user_rate = GSM48_BCAP_UR_9600,
- .parity = GSM48_BCAP_PAR_NONE,
- .interm_rate = GSM48_BCAP_IR_16k,
- .transp = GSM48_BCAP_TR_TRANSP,
- .modem_type = GSM48_BCAP_MT_NONE,
+ .rate_adaption = GSM48_BCAP_RA_NONE,
+ .sig_access = GSM48_BCAP_SA_I440_I450,
+ .async = 1,
+ .nr_stop_bits = 1,
+ .nr_data_bits = 8,
+ .user_rate = GSM48_BCAP_UR_2400,
+ .parity = GSM48_BCAP_PAR_NONE,
+ .interm_rate = GSM48_BCAP_IR_8k,
+ .transp = GSM48_BCAP_TR_TRANSP,
+ .modem_type = GSM48_BCAP_MT_V22bis,
},
};
static const uint8_t speech_all_lv[] = { 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81 };
static const struct gsm_mncc_bearer_cap bcap_speech_all = {
- .transfer = GSM48_BCAP_ITCAP_SPEECH,
- .mode = GSM48_BCAP_TMOD_CIRCUIT,
- .coding = GSM48_BCAP_CODING_GSM_STD,
- .radio = GSM48_BCAP_RRQ_DUAL_FR,
+ .transfer = GSM48_BCAP_ITCAP_SPEECH,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_DUAL_FR,
.speech_ver = {
4, 2, 0, 5, 1, -1,
},
};
+static const uint8_t speech_no3a_lv[] = { 0x01, 0xa0 };
+
+static const struct gsm_mncc_bearer_cap bcap_speech_no3a = {
+ .transfer = GSM48_BCAP_ITCAP_SPEECH,
+ .mode = GSM48_BCAP_TMOD_CIRCUIT,
+ .coding = GSM48_BCAP_CODING_GSM_STD,
+ .radio = GSM48_BCAP_RRQ_FR_ONLY,
+ .speech_ver = {
+ 0, -1,
+ },
+};
+
struct bcap_test {
const uint8_t *lv;
@@ -76,16 +130,22 @@ struct bcap_test {
static const struct bcap_test bcap_tests[] = {
{ csd_9600_v110_lv, &bcap_csd_9600_v110, "CSD 9600/V.110/transparent" },
+ { csd_4800_rlp_lv, &bcap_csd_4800_rlp, "CSD 4800/RLP/non-transparent" },
+ { /* XXX: this testcase is expected to fail because octet 4 is not represented in
+ * 'struct gsm_mncc_bearer_cap' and the encoder unconditionally hard-codes it to 0x88. */
+ csd_2400_v22bis_lv, &bcap_csd_2400_v22bis, "CSD 2400/V.22bis/transparent" },
{ speech_all_lv, &bcap_speech_all, "Speech, all codecs" },
+ { speech_no3a_lv, &bcap_speech_no3a, "Speech, without octet 3a" },
};
-static int test_bearer_cap()
+static int test_bearer_cap(void)
{
struct gsm_mncc_bearer_cap bc;
int i, rc;
for (i = 0; i < ARRAY_SIZE(bcap_tests); i++) {
struct msgb *msg = msgb_alloc(100, "test");
+ bool pass = false;
int lv_len;
memset(&bc, 0, sizeof(bc));
@@ -95,7 +155,7 @@ static int test_bearer_cap()
if (rc < 0) {
fprintf(stderr, "Error decoding %s\n",
bcap_tests[i].name);
- return rc;
+ goto verdict;
}
if (memcmp(&bc, bcap_tests[i].bc, sizeof(bc))) {
fprintf(stderr, "Incorrect decoded result of %s:\n",
@@ -104,7 +164,7 @@ static int test_bearer_cap()
osmo_hexdump((uint8_t *) bcap_tests[i].bc, sizeof(bc)));
fprintf(stderr, " is: %s\n",
osmo_hexdump((uint8_t *) &bc, sizeof(bc)));
- return -1;
+ goto verdict;
}
/* also test re-encode? */
@@ -112,7 +172,7 @@ static int test_bearer_cap()
if (rc < 0) {
fprintf(stderr, "Error encoding %s\n",
bcap_tests[i].name);
- return rc;
+ goto verdict;
}
lv_len = bcap_tests[i].lv[0]+1;
if (memcmp(msg->data, bcap_tests[i].lv, lv_len)) {
@@ -122,10 +182,14 @@ static int test_bearer_cap()
osmo_hexdump(bcap_tests[i].lv, lv_len));
fprintf(stderr, " is: %s\n",
osmo_hexdump(msg->data, msg->len));
- return -1;
+ goto verdict;
}
- printf("Test `%s' passed\n", bcap_tests[i].name);
+ /* all checks passed */
+ pass = true;
+
+verdict:
+ printf("Test `%s' %sed\n", bcap_tests[i].name, pass ? "pass" : "fail");
msgb_free(msg);
}
@@ -325,6 +389,368 @@ static void test_lai_encode_decode(void)
}
}
+static struct osmo_routing_area_id test_osmo_routing_area_id_items[] = {
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 77,
+ .mnc = 121,
+ },
+ .lac = 666,
+ },
+ .rac = 5,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 84,
+ .mnc = 98,
+ },
+ .lac = 11,
+ },
+ .rac = 89,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 0,
+ .mnc = 0,
+ .mnc_3_digits = false,
+ /* expecting 000-00, BCD = 00 f0 00 */
+ },
+ .lac = 0,
+ },
+ .rac = 0,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 0,
+ .mnc = 0,
+ .mnc_3_digits = true,
+ /* expecting 000-000, BCD = 00 00 00 */
+ },
+ .lac = 0,
+ },
+ .rac = 0,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 999,
+ .mnc = 999,
+ },
+ .lac = 65535,
+ },
+ .rac = 255,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 1,
+ .mnc = 2,
+ .mnc_3_digits = false,
+ /* expecting 001-02, BCD = 00 f1 20 */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 1,
+ .mnc = 2,
+ .mnc_3_digits = true,
+ /* expecting 001-002, BCD = 00 21 00 */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 12,
+ .mnc = 34,
+ .mnc_3_digits = false,
+ /* expecting 012-34, BCD = 10 f2 43 */
+ },
+ .lac = 56,
+ },
+ .rac = 78,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 12,
+ .mnc = 34,
+ .mnc_3_digits = true,
+ /* expecting 012-034, BCD = 10 42 30 */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 123,
+ .mnc = 456,
+ .mnc_3_digits = false,
+ /* expecting 123-456, BCD = 21 63 54 (false flag has no effect) */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+ {
+ .lac = {
+ .plmn = {
+ .mcc = 123,
+ .mnc = 456,
+ .mnc_3_digits = true,
+ /* expecting 123-456, BCD = 21 63 54 (same) */
+ },
+ .lac = 23,
+ },
+ .rac = 42,
+ },
+};
+
+static inline void dump_osmo_routing_area_id(const struct osmo_routing_area_id *raid)
+{
+ printf("%s%s", osmo_rai_name2(raid), raid->lac.plmn.mnc_3_digits ? " (3-digit MNC)" : "");
+}
+
+static inline void check_osmo_routing_area_id(const struct osmo_routing_area_id *raid)
+{
+ uint8_t buf[sizeof(struct gsm48_ra_id)] = {};
+ struct osmo_routing_area_id raid0 = {};
+ int rc;
+
+ printf("RA ID: ");
+ dump_osmo_routing_area_id(raid);
+
+ rc = osmo_routing_area_id_encode_buf(buf, sizeof(buf), raid);
+ printf("osmo_routing_area_id_encode_buf(): %src=%d\n", osmo_hexdump(buf, sizeof(buf)), rc);
+
+ rc = osmo_routing_area_id_decode(&raid0, buf, sizeof(buf));
+ printf("osmo_routing_area_id_decode(): ");
+ dump_osmo_routing_area_id(&raid0);
+ printf(" rc=%d\n", rc);
+
+ if (osmo_rai_cmp(raid, &raid0))
+ printf("FAIL\n");
+ else
+ printf("ok\n");
+}
+
+static void test_osmo_routing_area_id(void)
+{
+ int i;
+ printf("==%s()==\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(test_osmo_routing_area_id_items); i++)
+ check_osmo_routing_area_id(&test_osmo_routing_area_id_items[i]);
+}
+
+static void dump_cm3(struct gsm48_classmark3 *cm3)
+{
+ printf("mult_band_supp=%02x\n", cm3->mult_band_supp);
+ printf("a5_bits=%02x\n", cm3->a5_bits);
+ printf("assoc_radio_cap_1=%02x\n", cm3->assoc_radio_cap_1);
+ printf("assoc_radio_cap_2=%02x\n", cm3->assoc_radio_cap_2);
+ printf("\n");
+ printf("r_support.present=%u\n", cm3->r_support.present);
+ printf("r_support.r_gsm_assoc_radio_cap=%02x\n",
+ cm3->r_support.r_gsm_assoc_radio_cap);
+ printf("\n");
+ printf("hscsd_mult_slot_cap.present=%u\n",
+ cm3->hscsd_mult_slot_cap.present);
+ printf("hscsd_mult_slot_cap.mslot_class=%02x\n",
+ cm3->hscsd_mult_slot_cap.mslot_class);
+ printf("\n");
+ printf("ucs2_treatment=%u\n", cm3->ucs2_treatment);
+ printf("extended_meas_cap=%u\n", cm3->extended_meas_cap);
+ printf("\n");
+ printf("ms_meas_cap.present=%u\n", cm3->ms_meas_cap.present);
+ printf("ms_meas_cap.sms_value=%02x\n", cm3->ms_meas_cap.sms_value);
+ printf("ms_meas_cap.sm_value=%02x\n", cm3->ms_meas_cap.sm_value);
+ printf("\n");
+ printf("ms_pos_method_cap.present=%u\n",
+ cm3->ms_pos_method_cap.present);
+ printf("ms_pos_method_cap.method=%02x\n",
+ cm3->ms_pos_method_cap.method);
+ printf("\n");
+ printf("ecsd_multislot_cap.present=%u\n",
+ cm3->ecsd_multislot_cap.present);
+ printf("ecsd_multislot_cap.mslot_class=%02x\n",
+ cm3->ecsd_multislot_cap.mslot_class);
+ printf("\n");
+ printf("psk8_struct.present=%u\n", cm3->psk8_struct.present);
+ printf("psk8_struct.mod_cap=%u\n", cm3->psk8_struct.mod_cap);
+ printf("psk8_struct.rf_pwr_cap_1.present=%u\n",
+ cm3->psk8_struct.rf_pwr_cap_1.present);
+ printf("psk8_struct.rf_pwr_cap_1.value=%02x\n",
+ cm3->psk8_struct.rf_pwr_cap_1.value);
+ printf("psk8_struct.rf_pwr_cap_2.present=%u\n",
+ cm3->psk8_struct.rf_pwr_cap_2.present);
+ printf("psk8_struct.rf_pwr_cap_2.value=%02x\n",
+ cm3->psk8_struct.rf_pwr_cap_2.value);
+ printf("\n");
+ printf("gsm_400_bands_supp.present=%u\n",
+ cm3->gsm_400_bands_supp.present);
+ printf("gsm_400_bands_supp.value=%02x\n",
+ cm3->gsm_400_bands_supp.value);
+ printf("gsm_400_bands_supp.assoc_radio_cap=%02x\n",
+ cm3->gsm_400_bands_supp.assoc_radio_cap);
+ printf("\n");
+ printf("gsm_850_assoc_radio_cap.present=%u\n",
+ cm3->gsm_850_assoc_radio_cap.present);
+ printf("gsm_850_assoc_radio_cap.value=%02x\n",
+ cm3->gsm_850_assoc_radio_cap.value);
+ printf("\n");
+ printf("gsm_1900_assoc_radio_cap.present=%u\n",
+ cm3->gsm_1900_assoc_radio_cap.present);
+ printf("gsm_1900_assoc_radio_cap.value=%02x\n",
+ cm3->gsm_1900_assoc_radio_cap.value);
+ printf("\n");
+ printf("umts_fdd_rat_cap=%u\n", cm3->umts_fdd_rat_cap);
+ printf("umts_tdd_rat_cap=%u\n", cm3->umts_tdd_rat_cap);
+ printf("cdma200_rat_cap=%u\n", cm3->cdma200_rat_cap);
+ printf("\n");
+ printf("dtm_gprs_multislot_cap.present=%u\n",
+ cm3->dtm_gprs_multislot_cap.present);
+ printf("dtm_gprs_multislot_cap.mslot_class=%02x\n",
+ cm3->dtm_gprs_multislot_cap.mslot_class);
+ printf("dtm_gprs_multislot_cap.single_slot_dtm=%u\n",
+ cm3->dtm_gprs_multislot_cap.single_slot_dtm);
+ printf("dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=%u\n",
+ cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present);
+ printf("dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=%02x\n",
+ cm3->dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class);
+ printf("\n");
+ printf("single_band_supp.present=%u\n", cm3->single_band_supp.present);
+ printf("single_band_supp.value=%u\n", cm3->single_band_supp.value);
+ printf("\n");
+ printf("gsm_750_assoc_radio_cap.present=%u\n",
+ cm3->gsm_750_assoc_radio_cap.present);
+ printf("gsm_750_assoc_radio_cap.value=%02x\n",
+ cm3->gsm_750_assoc_radio_cap.value);
+ printf("\n");
+ printf("umts_1_28_mcps_tdd_rat_cap=%u\n",
+ cm3->umts_1_28_mcps_tdd_rat_cap);
+ printf("geran_feature_package=%u\n", cm3->geran_feature_package);
+ printf("\n");
+ printf("extended_dtm_gprs_multislot_cap.present=%u\n",
+ cm3->extended_dtm_gprs_multislot_cap.present);
+ printf("extended_dtm_gprs_multislot_cap.mslot_class=%02x\n",
+ cm3->extended_dtm_gprs_multislot_cap.mslot_class);
+ printf
+ ("extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=%u\n",
+ cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.present);
+ printf
+ ("extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=%02x\n",
+ cm3->extended_dtm_gprs_multislot_cap.
+ extended_dtm_egprs_multislot_cap.mslot_class);
+ printf("\n");
+ printf("high_multislot_cap.present=%u\n",
+ cm3->high_multislot_cap.present);
+ printf("high_multislot_cap.value=%02x\n",
+ cm3->high_multislot_cap.value);
+ printf("\n");
+ printf("geran_feature_package_2=%u\n", cm3->geran_feature_package_2);
+ printf("gmsk_multislot_power_prof=%02x\n",
+ cm3->gmsk_multislot_power_prof);
+ printf("psk8_multislot_power_prof=%02x\n",
+ cm3->psk8_multislot_power_prof);
+ printf("\n");
+ printf("t_gsm_400_bands_supp.present=%u\n",
+ cm3->t_gsm_400_bands_supp.present);
+ printf("t_gsm_400_bands_supp.value=%02x\n",
+ cm3->t_gsm_400_bands_supp.value);
+ printf("t_gsm_400_bands_supp.assoc_radio_cap=%02x\n",
+ cm3->t_gsm_400_bands_supp.assoc_radio_cap);
+ printf("\n");
+ printf("dl_advanced_rx_perf=%02x\n", cm3->dl_advanced_rx_perf);
+ printf("dtm_enhancements_cap=%u\n", cm3->dtm_enhancements_cap);
+ printf("\n");
+ printf("dtm_gprs_high_multislot_cap.present=%u\n",
+ cm3->dtm_gprs_high_multislot_cap.present);
+ printf("dtm_gprs_high_multislot_cap.mslot_class=%02x\n",
+ cm3->dtm_gprs_high_multislot_cap.mslot_class);
+ printf("dtm_gprs_high_multislot_cap.offset_required=%u\n",
+ cm3->dtm_gprs_high_multislot_cap.offset_required);
+ printf
+ ("dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.present=%u\n",
+ cm3->dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.
+ present);
+ printf
+ ("dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.mslot_class=%02x\n",
+ cm3->dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.
+ mslot_class);
+ printf("\n");
+ printf("repeated_acch_capability=%u\n", cm3->repeated_acch_capability);
+ printf("\n");
+ printf("gsm_710_assoc_radio_cap.present=%u\n",
+ cm3->gsm_710_assoc_radio_cap.present);
+ printf("gsm_710_assoc_radio_cap.value=%02x\n",
+ cm3->gsm_710_assoc_radio_cap.value);
+ printf("\n");
+ printf("t_gsm_810_assoc_radio_cap.present=%u\n",
+ cm3->t_gsm_810_assoc_radio_cap.present);
+ printf("t_gsm_810_assoc_radio_cap.value=%02x\n",
+ cm3->t_gsm_810_assoc_radio_cap.value);
+ printf("\n");
+ printf("ciphering_mode_setting_cap=%u\n",
+ cm3->ciphering_mode_setting_cap);
+ printf("add_pos_cap=%u\n", cm3->add_pos_cap);
+ printf("e_utra_fdd_supp=%u\n", cm3->e_utra_fdd_supp);
+ printf("e_utra_tdd_supp=%u\n", cm3->e_utra_tdd_supp);
+ printf("e_utra_meas_rep_supp=%u\n", cm3->e_utra_meas_rep_supp);
+ printf("prio_resel_supp=%u\n", cm3->prio_resel_supp);
+ printf("utra_csg_cells_rep=%u\n", cm3->utra_csg_cells_rep);
+ printf("vamos_level=%02x\n", cm3->vamos_level);
+ printf("tighter_capability=%02x\n", cm3->tighter_capability);
+ printf("sel_ciph_dl_sacch=%u\n", cm3->sel_ciph_dl_sacch);
+ printf("cs_ps_srvcc_geran_utra=%02x\n", cm3->cs_ps_srvcc_geran_utra);
+ printf("cs_ps_srvcc_geran_eutra=%02x\n", cm3->cs_ps_srvcc_geran_eutra);
+ printf("geran_net_sharing=%u\n", cm3->geran_net_sharing);
+ printf("e_utra_wb_rsrq_meas_supp=%u\n", cm3->e_utra_wb_rsrq_meas_supp);
+ printf("er_band_support=%u\n", cm3->er_band_support);
+ printf("utra_mult_band_ind_supp=%u\n", cm3->utra_mult_band_ind_supp);
+ printf("e_utra_mult_band_ind_supp=%u\n",
+ cm3->e_utra_mult_band_ind_supp);
+ printf("extended_tsc_set_cap_supp=%u\n",
+ cm3->extended_tsc_set_cap_supp);
+ printf("extended_earfcn_val_range=%u\n",
+ cm3->extended_earfcn_val_range);
+}
+
+static void test_decode_classmark3(void)
+{
+ struct gsm48_classmark3 cm3;
+ const uint8_t cm3_1[] = { 0x60, 0x14, 0x04, 0x2f, 0x65, 0x00, 0x20, 0x03, 0x40, 0x4a };
+ const uint8_t cm3_2[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
+ const uint8_t cm3_3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa};
+
+ printf("=====cm3_1=====\n");
+ gsm48_decode_classmark3(&cm3, cm3_1, sizeof(cm3_1));
+ dump_cm3(&cm3);
+ printf("\n");
+
+ printf("=====cm3_2=====\n");
+ gsm48_decode_classmark3(&cm3, cm3_2, sizeof(cm3_2));
+ dump_cm3(&cm3);
+ printf("\n");
+
+ printf("=====cm3_3=====\n");
+ gsm48_decode_classmark3(&cm3, cm3_3, sizeof(cm3_3));
+ dump_cm3(&cm3);
+ printf("\n");
+}
+
static void test_mid_from_tmsi(void)
{
static const uint8_t res[] = { 0x17, 0x05, 0xf4, 0xaa, 0xbb, 0xcc, 0xdd };
@@ -611,6 +1037,368 @@ static void test_mid_decode_zero_length(void)
printf("\n");
}
+struct msgb *msgb_from_hex(const char *label, uint16_t size, const char *hex)
+{
+ struct msgb *msg = msgb_alloc_headroom(size, 4, label);
+ OSMO_ASSERT(msg);
+ msg->l3h = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
+ return msg;
+}
+
+struct mobile_identity_tc {
+ const char *label;
+ const char *compl_l3_msg;
+ int expect_rc;
+ struct osmo_mobile_identity expect_mi;
+};
+
+/* Some Complete Layer 3 messages copied from real GSM network traces. */
+struct mobile_identity_tc mobile_identity_tests[] = {
+ {
+ .label = "LU with IMSI 901700000004620",
+ .compl_l3_msg = "050802008168000130" "089910070000006402",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "901700000004620",
+ },
+ },
+ {
+ .label = "LU with TMSI 0x0980ad8a",
+ .compl_l3_msg = "05084262f224002a50" "05f40980ad8a",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_TMSI,
+ .tmsi = 0x0980ad8a,
+ },
+ },
+ {
+ .label = "LU with invalid MI type",
+ .compl_l3_msg = "050802008168000130" "089d10070000006402",
+ .expect_rc = -EINVAL,
+ },
+ {
+ .label = "LU with truncated IMSI MI",
+ .compl_l3_msg = "050802008168000130" "0899100700000064",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "LU with too short IMSI MI (12345)",
+ .compl_l3_msg = "050802008168000130" "03193254",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "LU with just long enough IMSI MI 123456",
+ .compl_l3_msg = "050802008168000130" "04113254f6",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "123456",
+ },
+ },
+ {
+ .label = "LU with max length IMSI MI 123456789012345",
+ .compl_l3_msg = "050802008168000130" "081932547698103254",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "123456789012345",
+ },
+ },
+ {
+ .label = "LU with just too long IMSI MI 1234567890123456",
+ .compl_l3_msg = "050802008168000130" "091132547698103254f6",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "LU with truncated TMSI MI",
+ .compl_l3_msg = "05084262f224002a50" "05f40980ad",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "LU with odd length TMSI",
+ .compl_l3_msg = "05084262f224002a50" "05fc0980ad8a",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "LU with too long TMSI MI",
+ .compl_l3_msg = "05084262f224002a50" "06f40980ad23",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "LU with too short TMSI",
+ .compl_l3_msg = "05084262f224002a50" "04f480ad8a",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "CM Service Request with IMSI 123456",
+ .compl_l3_msg = "052401035058a6" "04113254f6",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "123456",
+ },
+ },
+ {
+ .label = "CM Service Request with TMSI 0x5a42e404",
+ .compl_l3_msg = "052401035058a6" "05f45a42e404",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_TMSI,
+ .tmsi = 0x5a42e404,
+ },
+ },
+ {
+ .label = "CM Service Request with shorter CM2, with IMSI 123456",
+ .compl_l3_msg = "052401025058" "04113254f6",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "123456",
+ },
+ },
+ {
+ .label = "CM Service Request with longer CM2, with IMSI 123456",
+ .compl_l3_msg = "052401055058a62342" "04113254f6",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "123456",
+ },
+ },
+ {
+ .label = "CM Service Request with shorter CM2, with TMSI 0x00000000",
+ .compl_l3_msg = "052401025058" "05f400000000",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_TMSI,
+ .tmsi = 0,
+ },
+ },
+ {
+ .label = "CM Service Request with invalid MI type",
+ .compl_l3_msg = "052401035058a6" "089d10070000006402",
+ .expect_rc = -EINVAL,
+ },
+ {
+ .label = "CM Service Request with truncated IMSI MI",
+ .compl_l3_msg = "052401035058a6" "0899100700000064",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "CM Service Request with truncated TMSI MI",
+ .compl_l3_msg = "0524010150" "05f40980ad",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "CM Service Request with odd length TMSI",
+ .compl_l3_msg = "052401045058a623" "05fc0980ad8a",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "CM Service Request with too long TMSI MI",
+ .compl_l3_msg = "052401035058a6" "06f40980ad23",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "CM Service Request with too short TMSI",
+ .compl_l3_msg = "052401035058a6" "04f480ad8a",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "CM Service Reestablish Request with TMSI 0x5a42e404",
+ .compl_l3_msg = "052801035058a6" "05f45a42e404",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_TMSI,
+ .tmsi = 0x5a42e404,
+ },
+ },
+ {
+ .label = "Paging Response with IMSI 1234567",
+ .compl_l3_msg = "06270003505886" "0419325476",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "1234567",
+ },
+ },
+ {
+ .label = "Paging Response with TMSI 0xb48883de",
+ .compl_l3_msg = "06270003505886" "05f4b48883de",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_TMSI,
+ .tmsi = 0xb48883de,
+ },
+ },
+ {
+ .label = "Paging Response with TMSI, with unused nibble not 0xf",
+ .compl_l3_msg = "06270003505886" "0504b48883de",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "Paging Response with too short IMEI (1234567)",
+ .compl_l3_msg = "06270003505886" "041a325476",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "Paging Response with IMEI 123456789012345",
+ .compl_l3_msg = "06270003505886" "081a32547698103254",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMEI,
+ .imei = "123456789012345",
+ },
+ },
+ {
+ .label = "Paging Response with IMEI 12345678901234 (no Luhn checksum)",
+ .compl_l3_msg = "06270003505886" "0812325476981032f4",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMEI,
+ .imei = "12345678901234",
+ },
+ },
+ {
+ .label = "Paging Response with IMEISV 1234567890123456",
+ .compl_l3_msg = "06270003505886" "091332547698103254f6",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMEISV,
+ .imeisv = "1234567890123456",
+ },
+ },
+ {
+ .label = "Paging Response with too short IMEISV 123456789012345",
+ .compl_l3_msg = "06270003505886" "081b32547698103254",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "Paging Response with too long IMEISV 12345678901234567",
+ .compl_l3_msg = "06270003505886" "091b3254769810325476",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "Paging Response with IMSI 123456789012345 and flipped ODD bit",
+ .compl_l3_msg = "06270003505886" "081132547698103254",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "IMSI-Detach with IMSI 901700000004620",
+ .compl_l3_msg = "050130" "089910070000006402",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "901700000004620",
+ },
+ },
+ {
+ .label = "IMSI-Detach with TMSI 0x0980ad8a",
+ .compl_l3_msg = "050130" "05f40980ad8a",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_TMSI,
+ .tmsi = 0x0980ad8a,
+ },
+ },
+ {
+ .label = "IMSI-Detach with invalid MI type",
+ .compl_l3_msg = "050130" "089d10070000006402",
+ .expect_rc = -EINVAL,
+ },
+ {
+ .label = "IMSI-Detach with truncated IMSI MI",
+ .compl_l3_msg = "050130" "0899100700000064",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "IMSI-Detach with too short IMSI MI (12345)",
+ .compl_l3_msg = "050130" "03193254",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "IMSI-Detach with just long enough IMSI MI 123456",
+ .compl_l3_msg = "050130" "04113254f6",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "123456",
+ },
+ },
+ {
+ .label = "IMSI-Detach with max length IMSI MI 123456789012345",
+ .compl_l3_msg = "050130" "081932547698103254",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "123456789012345",
+ },
+ },
+ {
+ .label = "IMSI-Detach with just too long IMSI MI 1234567890123456",
+ .compl_l3_msg = "050130" "091132547698103254f6",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "IMSI-Detach with truncated TMSI MI",
+ .compl_l3_msg = "050130" "05f40980ad",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "IMSI-Detach with odd length TMSI",
+ .compl_l3_msg = "050130" "05fc0980ad8a",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "IMSI-Detach with too long TMSI MI",
+ .compl_l3_msg = "050130" "06f40980ad23",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "IMSI-Detach with too short TMSI",
+ .compl_l3_msg = "050130" "04f480ad8a",
+ .expect_rc = -EBADMSG,
+ },
+ {
+ .label = "Identity Response with IMSI 901700000004620",
+ .compl_l3_msg = "0519" "089910070000006402",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMSI,
+ .imsi = "901700000004620",
+ },
+ },
+ {
+ .label = "Identity Response with IMEI 123456789012345",
+ .compl_l3_msg = "0519" "081a32547698103254",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMEI,
+ .imei = "123456789012345",
+ },
+ },
+ {
+ .label = "Identity Response with IMEISV 9876543210987654",
+ .compl_l3_msg = "0519" "099378563412907856f4",
+ .expect_mi = {
+ .type = GSM_MI_TYPE_IMEISV,
+ .imeisv = "9876543210987654",
+ },
+ },
+};
+
+void test_struct_mobile_identity(void)
+{
+ struct mobile_identity_tc *t;
+ printf("%s()\n", __func__);
+ for (t = mobile_identity_tests; (t - mobile_identity_tests) < ARRAY_SIZE(mobile_identity_tests); t++) {
+ struct osmo_mobile_identity mi;
+ struct msgb *msg;
+ int rc;
+ memset(&mi, 0xff, sizeof(mi));
+
+ msg = msgb_from_hex(t->label, 1024, t->compl_l3_msg);
+ rc = osmo_mobile_identity_decode_from_l3(&mi, msg, false);
+ msgb_free(msg);
+
+ printf("%s: %s", t->label, rc ? "rc != 0" : "rc == 0");
+ if (!rc) {
+ printf(", mi = %s", osmo_mobile_identity_to_str_c(OTC_SELECT, &mi));
+ }
+
+ if (rc == t->expect_rc
+ && ((rc != 0) || !osmo_mobile_identity_cmp(&mi, &t->expect_mi))) {
+ printf(" ok");
+ } else {
+ printf(" ERROR: Got rc = %d, expected rc = %d", rc, t->expect_rc);
+ if (!t->expect_rc)
+ printf(", mi = %s", osmo_mobile_identity_to_str_c(OTC_SELECT, &t->expect_mi));
+ }
+ printf("\n");
+ }
+ printf("\n");
+}
+
static const struct bcd_number_test {
/* Human-readable test name */
const char *test_name;
@@ -741,7 +1529,7 @@ static const struct bcd_number_test {
},
};
-static void test_bcd_number_encode_decode()
+static void test_bcd_number_encode_decode(void)
{
const struct bcd_number_test *test;
uint8_t buf_enc[0xff] = { 0xff };
@@ -975,7 +1763,7 @@ static void test_random_range_encoding(int range, int max_arfcn_num)
}
}
-static void test_range_encoding()
+static void test_range_encoding(void)
{
int *arfcns;
int arfcns_num = 0;
@@ -1036,7 +1824,7 @@ static int range512[] = {
__FILE__, __LINE__, (int) res, # cmp, (int) wanted); \
}
-static void test_arfcn_filter()
+static void test_arfcn_filter(void)
{
int arfcns[50], i, res, f0_included;
for (i = 0; i < ARRAY_SIZE(arfcns); ++i)
@@ -1069,7 +1857,7 @@ static void test_arfcn_filter()
VERIFY(arfcns[i], ==, ((i + 1) * 2) - 1);
}
-static void test_print_encoding()
+static void test_print_encoding(void)
{
int rc;
int w[17];
@@ -1094,7 +1882,7 @@ static void test_print_encoding()
printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list)));
}
-static void test_si_range_helpers()
+static void test_si_range_helpers(void)
{
int ws[(sizeof(freqs1)/sizeof(freqs1[0]))];
int i, f0 = 0xFFFFFF;
@@ -1133,7 +1921,7 @@ static void test_si_range_helpers()
VERIFY(f0, ==, 1);
}
-static void test_power_ctrl()
+static void test_power_ctrl(void)
{
int8_t rc8;
int rc;
@@ -1175,6 +1963,62 @@ static void test_power_ctrl()
VERIFY(rc, <, 0);
}
+static void test_rach_tx_integer_raw2val(void)
+{
+ unsigned int raw;
+ for (raw = 0; raw <= 0x0f; raw++) {
+ unsigned int val = rach_tx_integer_raw2val(raw);
+ printf("rach_tx_integer_raw2val(0x0%x): %u slots used to spread transmission\n",
+ raw, val);
+ }
+}
+
+static void test_gsm_gsmtime2fn(void)
+{
+ struct gsm_time gsm_time;
+ uint32_t fn;
+ uint32_t fn_recovered;
+
+ for (fn = 0; fn < 42432; fn++) {
+ gsm_time.t1 = (fn / 1326) % 32;
+ gsm_time.t2 = fn % 26;
+ gsm_time.t3 = fn % 51;
+
+ fn_recovered = gsm_gsmtime2fn(&gsm_time);
+
+ if (fn_recovered != fn) {
+ printf(" Wrong frame number computed! t1=%d, t2=%d, t3=%d ==> fn=%d, expected fn=%d\n",
+ gsm_time.t1, gsm_time.t2, gsm_time.t3, fn_recovered, fn);
+ OSMO_ASSERT(false);
+ }
+ }
+}
+
+static void test_gsm_rfn2fn(void)
+{
+ unsigned int i;
+ struct {
+ uint32_t curr_fn;
+ uint16_t rfn;
+ uint32_t exp_fn;
+ } input[] = {
+ { .curr_fn = 0, .rfn = 0, .exp_fn = 0 },
+ { .curr_fn = 0, .rfn = 4, .exp_fn = 4 },
+ { .curr_fn = 2229729, .rfn = 23322, .exp_fn = 2229786 },
+ { .curr_fn = 2229777, .rfn = 23322, .exp_fn = 2229786 },
+ { .curr_fn = 1320458, .rfn = 5070, .exp_fn = 1320462 },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(input); i++) {
+ uint32_t fn = gsm_rfn2fn(input[i].rfn, input[i].curr_fn);
+ if (fn != input[i].exp_fn) {
+ printf("Wrong frame number computed! curr_fn=%u, rfn=%u ==> fn=%u, expected fn=%u\n",
+ input[i].curr_fn, input[i].rfn, fn, input[i].exp_fn);
+ OSMO_ASSERT(false);
+ }
+ }
+}
+
int main(int argc, char **argv)
{
test_bearer_cap();
@@ -1182,15 +2026,21 @@ int main(int argc, char **argv)
test_mid_from_imsi();
test_mid_encode_decode();
test_mid_decode_zero_length();
+ test_struct_mobile_identity();
test_bcd_number_encode_decode();
test_ra_cap();
test_lai_encode_decode();
+ test_osmo_routing_area_id();
+ test_decode_classmark3();
test_si_range_helpers();
test_arfcn_filter();
test_print_encoding();
test_range_encoding();
test_power_ctrl();
+ test_rach_tx_integer_raw2val();
+ test_gsm_gsmtime2fn();
+ test_gsm_rfn2fn();
return EXIT_SUCCESS;
}
diff --git a/tests/gsm0408/gsm0408_test.err b/tests/gsm0408/gsm0408_test.err
new file mode 100644
index 00000000..0102de01
--- /dev/null
+++ b/tests/gsm0408/gsm0408_test.err
@@ -0,0 +1,6 @@
+Incorrect encoded result of CSD 2400/V.22bis/transparent:
+ should: 07 a2 b8 81 21 13 43 83
+ is: 07 a2 88 81 21 13 43 83
+Incorrect encoded result of Speech, without octet 3a:
+ should: 01 a0
+ is: 02 20 80
diff --git a/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok
index d343869f..721e2c53 100644
--- a/tests/gsm0408/gsm0408_test.ok
+++ b/tests/gsm0408/gsm0408_test.ok
@@ -1,5 +1,8 @@
Test `CSD 9600/V.110/transparent' passed
+Test `CSD 4800/RLP/non-transparent' passed
+Test `CSD 2400/V.22bis/transparent' failed
Test `Speech, all codecs' passed
+Test `Speech, without octet 3a' failed
Simple TMSI encoding test....passed
Simple IMSI encoding test....passed: [10] 17 08 99 10 07 00 00 00 64 02
@@ -139,6 +142,57 @@ Decoding zero length Mobile Identities
rc=1
returned empty string
+test_struct_mobile_identity()
+LU with IMSI 901700000004620: rc == 0, mi = IMSI-901700000004620 ok
+LU with TMSI 0x0980ad8a: rc == 0, mi = TMSI-0x0980AD8A ok
+LU with invalid MI type: rc != 0 ok
+LU with truncated IMSI MI: rc != 0 ok
+LU with too short IMSI MI (12345): rc != 0 ok
+LU with just long enough IMSI MI 123456: rc == 0, mi = IMSI-123456 ok
+LU with max length IMSI MI 123456789012345: rc == 0, mi = IMSI-123456789012345 ok
+LU with just too long IMSI MI 1234567890123456: rc != 0 ok
+LU with truncated TMSI MI: rc != 0 ok
+LU with odd length TMSI: rc != 0 ok
+LU with too long TMSI MI: rc != 0 ok
+LU with too short TMSI: rc != 0 ok
+CM Service Request with IMSI 123456: rc == 0, mi = IMSI-123456 ok
+CM Service Request with TMSI 0x5a42e404: rc == 0, mi = TMSI-0x5A42E404 ok
+CM Service Request with shorter CM2, with IMSI 123456: rc == 0, mi = IMSI-123456 ok
+CM Service Request with longer CM2, with IMSI 123456: rc == 0, mi = IMSI-123456 ok
+CM Service Request with shorter CM2, with TMSI 0x00000000: rc == 0, mi = TMSI-0x00000000 ok
+CM Service Request with invalid MI type: rc != 0 ok
+CM Service Request with truncated IMSI MI: rc != 0 ok
+CM Service Request with truncated TMSI MI: rc != 0 ok
+CM Service Request with odd length TMSI: rc != 0 ok
+CM Service Request with too long TMSI MI: rc != 0 ok
+CM Service Request with too short TMSI: rc != 0 ok
+CM Service Reestablish Request with TMSI 0x5a42e404: rc == 0, mi = TMSI-0x5A42E404 ok
+Paging Response with IMSI 1234567: rc == 0, mi = IMSI-1234567 ok
+Paging Response with TMSI 0xb48883de: rc == 0, mi = TMSI-0xB48883DE ok
+Paging Response with TMSI, with unused nibble not 0xf: rc != 0 ok
+Paging Response with too short IMEI (1234567): rc != 0 ok
+Paging Response with IMEI 123456789012345: rc == 0, mi = IMEI-123456789012345 ok
+Paging Response with IMEI 12345678901234 (no Luhn checksum): rc == 0, mi = IMEI-12345678901234 ok
+Paging Response with IMEISV 1234567890123456: rc == 0, mi = IMEI-SV-1234567890123456 ok
+Paging Response with too short IMEISV 123456789012345: rc != 0 ok
+Paging Response with too long IMEISV 12345678901234567: rc != 0 ok
+Paging Response with IMSI 123456789012345 and flipped ODD bit: rc != 0 ok
+IMSI-Detach with IMSI 901700000004620: rc == 0, mi = IMSI-901700000004620 ok
+IMSI-Detach with TMSI 0x0980ad8a: rc == 0, mi = TMSI-0x0980AD8A ok
+IMSI-Detach with invalid MI type: rc != 0 ok
+IMSI-Detach with truncated IMSI MI: rc != 0 ok
+IMSI-Detach with too short IMSI MI (12345): rc != 0 ok
+IMSI-Detach with just long enough IMSI MI 123456: rc == 0, mi = IMSI-123456 ok
+IMSI-Detach with max length IMSI MI 123456789012345: rc == 0, mi = IMSI-123456789012345 ok
+IMSI-Detach with just too long IMSI MI 1234567890123456: rc != 0 ok
+IMSI-Detach with truncated TMSI MI: rc != 0 ok
+IMSI-Detach with odd length TMSI: rc != 0 ok
+IMSI-Detach with too long TMSI MI: rc != 0 ok
+IMSI-Detach with too short TMSI: rc != 0 ok
+Identity Response with IMSI 901700000004620: rc == 0, mi = IMSI-901700000004620 ok
+Identity Response with IMEI 123456789012345: rc == 0, mi = IMEI-123456789012345 ok
+Identity Response with IMEISV 9876543210987654: rc == 0, mi = IMEI-SV-9876543210987654 ok
+
BSD number encoding / decoding test
- Running test: regular 9-digit MSISDN
- Encoding ASCII (buffer limit=0) '123456789'...
@@ -334,6 +388,382 @@ RA test...passed
Encoded 21 63 54 00 17
gsm48_decode_lai2() gives 123-456-23 (3-digit MNC)
passed
+==test_osmo_routing_area_id()==
+RA ID: 077-121-666-5osmo_routing_area_id_encode_buf(): 70 17 21 02 9a 05 rc=6
+osmo_routing_area_id_decode(): 077-121-666-5 (3-digit MNC) rc=6
+ok
+RA ID: 084-98-11-89osmo_routing_area_id_encode_buf(): 80 f4 89 00 0b 59 rc=6
+osmo_routing_area_id_decode(): 084-98-11-89 rc=6
+ok
+RA ID: 000-00-0-0osmo_routing_area_id_encode_buf(): 00 f0 00 00 00 00 rc=6
+osmo_routing_area_id_decode(): 000-00-0-0 rc=6
+ok
+RA ID: 000-000-0-0 (3-digit MNC)osmo_routing_area_id_encode_buf(): 00 00 00 00 00 00 rc=6
+osmo_routing_area_id_decode(): 000-000-0-0 (3-digit MNC) rc=6
+ok
+RA ID: 999-999-65535-255osmo_routing_area_id_encode_buf(): 99 99 99 ff ff ff rc=6
+osmo_routing_area_id_decode(): 999-999-65535-255 (3-digit MNC) rc=6
+ok
+RA ID: 001-02-23-42osmo_routing_area_id_encode_buf(): 00 f1 20 00 17 2a rc=6
+osmo_routing_area_id_decode(): 001-02-23-42 rc=6
+ok
+RA ID: 001-002-23-42 (3-digit MNC)osmo_routing_area_id_encode_buf(): 00 21 00 00 17 2a rc=6
+osmo_routing_area_id_decode(): 001-002-23-42 (3-digit MNC) rc=6
+ok
+RA ID: 012-34-56-78osmo_routing_area_id_encode_buf(): 10 f2 43 00 38 4e rc=6
+osmo_routing_area_id_decode(): 012-34-56-78 rc=6
+ok
+RA ID: 012-034-23-42 (3-digit MNC)osmo_routing_area_id_encode_buf(): 10 42 30 00 17 2a rc=6
+osmo_routing_area_id_decode(): 012-034-23-42 (3-digit MNC) rc=6
+ok
+RA ID: 123-456-23-42osmo_routing_area_id_encode_buf(): 21 63 54 00 17 2a rc=6
+osmo_routing_area_id_decode(): 123-456-23-42 (3-digit MNC) rc=6
+ok
+RA ID: 123-456-23-42 (3-digit MNC)osmo_routing_area_id_encode_buf(): 21 63 54 00 17 2a rc=6
+osmo_routing_area_id_decode(): 123-456-23-42 (3-digit MNC) rc=6
+ok
+=====cm3_1=====
+mult_band_supp=06
+a5_bits=00
+assoc_radio_cap_1=04
+assoc_radio_cap_2=01
+
+r_support.present=0
+r_support.r_gsm_assoc_radio_cap=00
+
+hscsd_mult_slot_cap.present=0
+hscsd_mult_slot_cap.mslot_class=00
+
+ucs2_treatment=0
+extended_meas_cap=0
+
+ms_meas_cap.present=0
+ms_meas_cap.sms_value=00
+ms_meas_cap.sm_value=00
+
+ms_pos_method_cap.present=1
+ms_pos_method_cap.method=01
+
+ecsd_multislot_cap.present=0
+ecsd_multislot_cap.mslot_class=00
+
+psk8_struct.present=1
+psk8_struct.mod_cap=1
+psk8_struct.rf_pwr_cap_1.present=1
+psk8_struct.rf_pwr_cap_1.value=02
+psk8_struct.rf_pwr_cap_2.present=1
+psk8_struct.rf_pwr_cap_2.value=02
+
+gsm_400_bands_supp.present=0
+gsm_400_bands_supp.value=00
+gsm_400_bands_supp.assoc_radio_cap=00
+
+gsm_850_assoc_radio_cap.present=1
+gsm_850_assoc_radio_cap.value=04
+
+gsm_1900_assoc_radio_cap.present=0
+gsm_1900_assoc_radio_cap.value=00
+
+umts_fdd_rat_cap=0
+umts_tdd_rat_cap=0
+cdma200_rat_cap=0
+
+dtm_gprs_multislot_cap.present=0
+dtm_gprs_multislot_cap.mslot_class=00
+dtm_gprs_multislot_cap.single_slot_dtm=0
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=0
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=00
+
+single_band_supp.present=0
+single_band_supp.value=0
+
+gsm_750_assoc_radio_cap.present=0
+gsm_750_assoc_radio_cap.value=00
+
+umts_1_28_mcps_tdd_rat_cap=0
+geran_feature_package=1
+
+extended_dtm_gprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.mslot_class=00
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=00
+
+high_multislot_cap.present=0
+high_multislot_cap.value=00
+
+geran_feature_package_2=0
+gmsk_multislot_power_prof=00
+psk8_multislot_power_prof=00
+
+t_gsm_400_bands_supp.present=0
+t_gsm_400_bands_supp.value=00
+t_gsm_400_bands_supp.assoc_radio_cap=00
+
+dl_advanced_rx_perf=01
+dtm_enhancements_cap=1
+
+dtm_gprs_high_multislot_cap.present=0
+dtm_gprs_high_multislot_cap.mslot_class=00
+dtm_gprs_high_multislot_cap.offset_required=0
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.present=0
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.mslot_class=00
+
+repeated_acch_capability=1
+
+gsm_710_assoc_radio_cap.present=0
+gsm_710_assoc_radio_cap.value=00
+
+t_gsm_810_assoc_radio_cap.present=0
+t_gsm_810_assoc_radio_cap.value=00
+
+ciphering_mode_setting_cap=0
+add_pos_cap=0
+e_utra_fdd_supp=0
+e_utra_tdd_supp=0
+e_utra_meas_rep_supp=0
+prio_resel_supp=1
+utra_csg_cells_rep=0
+vamos_level=01
+tighter_capability=01
+sel_ciph_dl_sacch=0
+cs_ps_srvcc_geran_utra=00
+cs_ps_srvcc_geran_eutra=00
+geran_net_sharing=0
+e_utra_wb_rsrq_meas_supp=0
+er_band_support=0
+utra_mult_band_ind_supp=0
+e_utra_mult_band_ind_supp=0
+extended_tsc_set_cap_supp=0
+extended_earfcn_val_range=0
+
+=====cm3_2=====
+mult_band_supp=05
+a5_bits=05
+assoc_radio_cap_1=05
+assoc_radio_cap_2=05
+
+r_support.present=0
+r_support.r_gsm_assoc_radio_cap=00
+
+hscsd_mult_slot_cap.present=1
+hscsd_mult_slot_cap.mslot_class=0a
+
+ucs2_treatment=1
+extended_meas_cap=0
+
+ms_meas_cap.present=1
+ms_meas_cap.sms_value=05
+ms_meas_cap.sm_value=05
+
+ms_pos_method_cap.present=0
+ms_pos_method_cap.method=00
+
+ecsd_multislot_cap.present=1
+ecsd_multislot_cap.mslot_class=0a
+
+psk8_struct.present=1
+psk8_struct.mod_cap=0
+psk8_struct.rf_pwr_cap_1.present=1
+psk8_struct.rf_pwr_cap_1.value=01
+psk8_struct.rf_pwr_cap_2.present=0
+psk8_struct.rf_pwr_cap_2.value=00
+
+gsm_400_bands_supp.present=1
+gsm_400_bands_supp.value=01
+gsm_400_bands_supp.assoc_radio_cap=05
+
+gsm_850_assoc_radio_cap.present=0
+gsm_850_assoc_radio_cap.value=00
+
+gsm_1900_assoc_radio_cap.present=1
+gsm_1900_assoc_radio_cap.value=05
+
+umts_fdd_rat_cap=0
+umts_tdd_rat_cap=1
+cdma200_rat_cap=0
+
+dtm_gprs_multislot_cap.present=1
+dtm_gprs_multislot_cap.mslot_class=01
+dtm_gprs_multislot_cap.single_slot_dtm=0
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=1
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=01
+
+single_band_supp.present=0
+single_band_supp.value=0
+
+gsm_750_assoc_radio_cap.present=1
+gsm_750_assoc_radio_cap.value=05
+
+umts_1_28_mcps_tdd_rat_cap=0
+geran_feature_package=1
+
+extended_dtm_gprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.mslot_class=00
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=00
+
+high_multislot_cap.present=1
+high_multislot_cap.value=01
+
+geran_feature_package_2=1
+gmsk_multislot_power_prof=01
+psk8_multislot_power_prof=01
+
+t_gsm_400_bands_supp.present=0
+t_gsm_400_bands_supp.value=00
+t_gsm_400_bands_supp.assoc_radio_cap=00
+
+dl_advanced_rx_perf=01
+dtm_enhancements_cap=0
+
+dtm_gprs_high_multislot_cap.present=1
+dtm_gprs_high_multislot_cap.mslot_class=02
+dtm_gprs_high_multislot_cap.offset_required=1
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.present=0
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.mslot_class=00
+
+repeated_acch_capability=1
+
+gsm_710_assoc_radio_cap.present=0
+gsm_710_assoc_radio_cap.value=00
+
+t_gsm_810_assoc_radio_cap.present=1
+t_gsm_810_assoc_radio_cap.value=05
+
+ciphering_mode_setting_cap=0
+add_pos_cap=1
+e_utra_fdd_supp=0
+e_utra_tdd_supp=0
+e_utra_meas_rep_supp=0
+prio_resel_supp=0
+utra_csg_cells_rep=0
+vamos_level=00
+tighter_capability=00
+sel_ciph_dl_sacch=0
+cs_ps_srvcc_geran_utra=00
+cs_ps_srvcc_geran_eutra=00
+geran_net_sharing=0
+e_utra_wb_rsrq_meas_supp=0
+er_band_support=0
+utra_mult_band_ind_supp=0
+e_utra_mult_band_ind_supp=0
+extended_tsc_set_cap_supp=0
+extended_earfcn_val_range=0
+
+=====cm3_3=====
+mult_band_supp=02
+a5_bits=0a
+assoc_radio_cap_1=0a
+assoc_radio_cap_2=00
+
+r_support.present=1
+r_support.r_gsm_assoc_radio_cap=02
+
+hscsd_mult_slot_cap.present=1
+hscsd_mult_slot_cap.mslot_class=0a
+
+ucs2_treatment=1
+extended_meas_cap=0
+
+ms_meas_cap.present=1
+ms_meas_cap.sms_value=05
+ms_meas_cap.sm_value=05
+
+ms_pos_method_cap.present=0
+ms_pos_method_cap.method=00
+
+ecsd_multislot_cap.present=1
+ecsd_multislot_cap.mslot_class=0a
+
+psk8_struct.present=1
+psk8_struct.mod_cap=0
+psk8_struct.rf_pwr_cap_1.present=1
+psk8_struct.rf_pwr_cap_1.value=01
+psk8_struct.rf_pwr_cap_2.present=0
+psk8_struct.rf_pwr_cap_2.value=00
+
+gsm_400_bands_supp.present=1
+gsm_400_bands_supp.value=01
+gsm_400_bands_supp.assoc_radio_cap=05
+
+gsm_850_assoc_radio_cap.present=0
+gsm_850_assoc_radio_cap.value=00
+
+gsm_1900_assoc_radio_cap.present=1
+gsm_1900_assoc_radio_cap.value=05
+
+umts_fdd_rat_cap=0
+umts_tdd_rat_cap=1
+cdma200_rat_cap=0
+
+dtm_gprs_multislot_cap.present=1
+dtm_gprs_multislot_cap.mslot_class=01
+dtm_gprs_multislot_cap.single_slot_dtm=0
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=1
+dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=01
+
+single_band_supp.present=0
+single_band_supp.value=0
+
+gsm_750_assoc_radio_cap.present=1
+gsm_750_assoc_radio_cap.value=05
+
+umts_1_28_mcps_tdd_rat_cap=0
+geran_feature_package=1
+
+extended_dtm_gprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.mslot_class=00
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.present=0
+extended_dtm_gprs_multislot_cap.dtm_egprs_multislot_cap.mslot_class=00
+
+high_multislot_cap.present=1
+high_multislot_cap.value=01
+
+geran_feature_package_2=1
+gmsk_multislot_power_prof=01
+psk8_multislot_power_prof=01
+
+t_gsm_400_bands_supp.present=0
+t_gsm_400_bands_supp.value=00
+t_gsm_400_bands_supp.assoc_radio_cap=00
+
+dl_advanced_rx_perf=01
+dtm_enhancements_cap=0
+
+dtm_gprs_high_multislot_cap.present=1
+dtm_gprs_high_multislot_cap.mslot_class=02
+dtm_gprs_high_multislot_cap.offset_required=1
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.present=0
+dtm_gprs_high_multislot_cap.dtm_egprs_high_multislot_cap.mslot_class=00
+
+repeated_acch_capability=1
+
+gsm_710_assoc_radio_cap.present=0
+gsm_710_assoc_radio_cap.value=00
+
+t_gsm_810_assoc_radio_cap.present=1
+t_gsm_810_assoc_radio_cap.value=04
+
+ciphering_mode_setting_cap=0
+add_pos_cap=0
+e_utra_fdd_supp=0
+e_utra_tdd_supp=0
+e_utra_meas_rep_supp=0
+prio_resel_supp=0
+utra_csg_cells_rep=0
+vamos_level=00
+tighter_capability=00
+sel_ciph_dl_sacch=0
+cs_ps_srvcc_geran_utra=00
+cs_ps_srvcc_geran_eutra=00
+geran_net_sharing=0
+e_utra_wb_rsrq_meas_supp=0
+er_band_support=0
+utra_mult_band_ind_supp=0
+e_utra_mult_band_ind_supp=0
+extended_tsc_set_cap_supp=0
+extended_earfcn_val_range=0
+
Element is: 2 => freqs[i] = 121
Element is: 2 => freqs[i] = 1
Element is: 0 => freqs[i] = 68
@@ -398,3 +828,19 @@ Random range test: range 127, max num ARFCNs 29
Random range test: range 255, max num ARFCNs 22
Random range test: range 511, max num ARFCNs 18
Random range test: range 1023, max num ARFCNs 16
+rach_tx_integer_raw2val(0x00): 3 slots used to spread transmission
+rach_tx_integer_raw2val(0x01): 4 slots used to spread transmission
+rach_tx_integer_raw2val(0x02): 5 slots used to spread transmission
+rach_tx_integer_raw2val(0x03): 6 slots used to spread transmission
+rach_tx_integer_raw2val(0x04): 7 slots used to spread transmission
+rach_tx_integer_raw2val(0x05): 8 slots used to spread transmission
+rach_tx_integer_raw2val(0x06): 9 slots used to spread transmission
+rach_tx_integer_raw2val(0x07): 10 slots used to spread transmission
+rach_tx_integer_raw2val(0x08): 11 slots used to spread transmission
+rach_tx_integer_raw2val(0x09): 12 slots used to spread transmission
+rach_tx_integer_raw2val(0x0a): 14 slots used to spread transmission
+rach_tx_integer_raw2val(0x0b): 16 slots used to spread transmission
+rach_tx_integer_raw2val(0x0c): 20 slots used to spread transmission
+rach_tx_integer_raw2val(0x0d): 25 slots used to spread transmission
+rach_tx_integer_raw2val(0x0e): 32 slots used to spread transmission
+rach_tx_integer_raw2val(0x0f): 50 slots used to spread transmission
diff --git a/tests/gsm0502/gsm0502_test.c b/tests/gsm0502/gsm0502_test.c
index a950c6c1..7768abb0 100644
--- a/tests/gsm0502/gsm0502_test.c
+++ b/tests/gsm0502/gsm0502_test.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
@@ -90,7 +86,7 @@ uint32_t facch_h1_fn_samples[] = { 500728, 500771, 500797, 500841, 500875, 50091
502782, 502825, 502869, 502903, 502955, 502999
};
-static void test_gsm0502_fn_remap()
+static void test_gsm0502_fn_remap(void)
{
unsigned int i;
uint32_t fn_begin;
@@ -152,8 +148,28 @@ static void test_gsm0502_fn_remap()
printf("\n");
}
+static void test_gsm0502_fncmp(void)
+{
+ OSMO_ASSERT(gsm0502_fncmp(1337, 1337) == 0);
+ OSMO_ASSERT(gsm0502_fncmp(42, 1337) == -1);
+ OSMO_ASSERT(gsm0502_fncmp(1337, 42) == 1);
+ OSMO_ASSERT(gsm0502_fncmp(42, 0) == 1);
+
+ /* 2715642 is very close to the Fn period (GSM_TDMA_HYPERFRAME) */
+ OSMO_ASSERT(gsm0502_fncmp(2715642, 42) == -1);
+ OSMO_ASSERT(gsm0502_fncmp(42, 2715642) == 1);
+ OSMO_ASSERT(gsm0502_fncmp(0, 2715642) == 1);
+
+ /* 1357824 is half of the Fn period (GSM_TDMA_HYPERFRAME) */
+ OSMO_ASSERT(gsm0502_fncmp(1357820, 1357824) == -1);
+ OSMO_ASSERT(gsm0502_fncmp(1357820, 1357825) == -1);
+ OSMO_ASSERT(gsm0502_fncmp(1357824, 1357820) == 1);
+ OSMO_ASSERT(gsm0502_fncmp(1357825, 1357820) == 1);
+}
+
int main(int argc, char **argv)
{
test_gsm0502_fn_remap();
+ test_gsm0502_fncmp();
return EXIT_SUCCESS;
}
diff --git a/tests/gsm0808/gsm0808_test.c b/tests/gsm0808/gsm0808_test.c
index 2389218a..ed99245c 100644
--- a/tests/gsm0808/gsm0808_test.c
+++ b/tests/gsm0808/gsm0808_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/gsm0808.h>
@@ -115,7 +111,7 @@ static void test_create_layer3(void)
msgb_free(in_msg);
}
-static void test_create_layer3_aoip()
+static void test_create_layer3_aoip(void)
{
static const uint8_t res[] = {
0x00, 0x17, 0x57, 0x05, 0x08, 0x00, 0x77, 0x62,
@@ -152,7 +148,7 @@ static void test_create_layer3_aoip()
msgb_free(in_msg);
}
-static void test_create_reset()
+static void test_create_reset(void)
{
static const uint8_t res[] = { 0x00, 0x04, 0x30, 0x04, 0x01, 0x20 };
struct msgb *msg;
@@ -163,7 +159,7 @@ static void test_create_reset()
msgb_free(msg);
}
-static void test_create_reset_ack()
+static void test_create_reset_ack(void)
{
static const uint8_t res[] = { 0x00, 0x01, 0x31 };
struct msgb *msg;
@@ -175,7 +171,7 @@ static void test_create_reset_ack()
}
-static void test_create_clear_command()
+static void test_create_clear_command(void)
{
static const uint8_t res[] = { 0x20, 0x04, 0x01, 0x23 };
struct msgb *msg;
@@ -186,7 +182,7 @@ static void test_create_clear_command()
msgb_free(msg);
}
-static void test_create_clear_command2()
+static void test_create_clear_command2(void)
{
static const uint8_t res[] = { 0x00, 0x04, 0x20, 0x04, 0x01, 0x23 };
struct msgb *msg;
@@ -197,7 +193,7 @@ static void test_create_clear_command2()
msgb_free(msg);
}
-static void test_create_clear_command2_csfb()
+static void test_create_clear_command2_csfb(void)
{
static const uint8_t res[] = { 0x00, 0x05, 0x20, 0x04, 0x01, 0x23, 0x8F };
struct msgb *msg;
@@ -208,7 +204,7 @@ static void test_create_clear_command2_csfb()
msgb_free(msg);
}
-static void test_create_clear_complete()
+static void test_create_clear_complete(void)
{
static const uint8_t res[] = { 0x00, 0x01, 0x21 };
struct msgb *msg;
@@ -219,7 +215,7 @@ static void test_create_clear_complete()
msgb_free(msg);
}
-static void test_create_cipher()
+static void test_create_cipher(void)
{
static const uint8_t res[] =
{ 0x00, 0x0c, 0x53, 0x0a, 0x09, 0x03, 0xaa,
@@ -259,7 +255,7 @@ static void test_create_cipher()
msgb_free(msg);
}
-static void test_create_cipher_complete()
+static void test_create_cipher_complete(void)
{
static const uint8_t res1[] = {
0x00, 0x08, 0x55, 0x20, 0x03, 0x23, 0x42, 0x21, 0x2c, 0x04 };
@@ -306,7 +302,7 @@ static inline void parse_cipher_reject(struct msgb *msg, uint8_t exp)
if (rc < 0)
printf("FIXME: failed (%d) to parse created message %s\n", rc, msgb_hexdump(msg));
- rc = gsm0808_get_cipher_reject_cause(&tp);
+ rc = gsm0808_get_cause(&tp);
if (rc < 0)
printf("FIXME: failed (%s) to extract Cause from created message %s\n",
strerror(-rc), msgb_hexdump(msg));
@@ -316,7 +312,7 @@ static inline void parse_cipher_reject(struct msgb *msg, uint8_t exp)
rc, exp, OSMO_BIT_PRINT(exp), msgb_hexdump(msg));
}
-static void test_create_cipher_reject()
+static void test_create_cipher_reject(void)
{
static const uint8_t res[] = { 0x00, 0x04, 0x59, 0x04, 0x01, 0x23 };
enum gsm0808_cause cause = GSM0808_CAUSE_CCCH_OVERLOAD;
@@ -331,7 +327,7 @@ static void test_create_cipher_reject()
msgb_free(msg);
}
-static void test_create_cipher_reject_ext()
+static void test_create_cipher_reject_ext(void)
{
static const uint8_t res[] = { 0x00, 0x05, 0x59, 0x04, 0x02, 0xd0, 0xFA };
uint8_t cause = 0xFA;
@@ -346,7 +342,7 @@ static void test_create_cipher_reject_ext()
msgb_free(msg);
}
-static void test_create_cm_u()
+static void test_create_cm_u(void)
{
static const uint8_t res[] = {
0x00, 0x07, 0x54, 0x12, 0x01, 0x23, 0x13, 0x01, 0x42 };
@@ -368,18 +364,120 @@ static void test_create_cm_u()
msgb_free(msg);
}
-static void test_create_sapi_reject()
+static void test_create_sapi_reject(void)
{
- static const uint8_t res[] = { 0x00, 0x03, 0x25, 0x03, 0x25 };
+ static const uint8_t res[] = { 0x00, 0x06, 0x25, 0x18, 0x03, 0x04, 0x01, 0x25 };
struct msgb *msg;
printf("Testing creating SAPI Reject\n");
- msg = gsm0808_create_sapi_reject(3);
+ msg = gsm0808_create_sapi_reject_cause(3, GSM0808_CAUSE_BSS_NOT_EQUIPPED);
VERIFY(msg, res, ARRAY_SIZE(res));
msgb_free(msg);
}
-static void test_create_ass()
+static void test_dec_confusion(void)
+{
+ static const uint8_t hex[] =
+ { 0x26, 0x04, 0x01, 0x52, 0x1f, 0x07, 0x00, 0xff, 0x00, 0x03, 0x25, 0x03, 0x25 };
+ struct tlv_parsed tp;
+ int diag_len;
+ enum gsm0808_cause cause;
+ enum gsm0808_cause_class cause_class;
+ struct gsm0808_diagnostics *diag;
+
+ printf("Testing decoding CONFUSION\n");
+
+ tlv_parse(&tp, gsm0808_att_tlvdef(), hex+1, sizeof(hex)-1, 0, 0);
+
+ /* Check for the Cause and Diagnostic mandatory elements */
+ if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE) || !TLVP_PRESENT(&tp, GSM0808_IE_DIAGNOSTIC)) {
+ printf("Either Cause or Diagnostic mandatory IE are not detected\n");
+ return;
+ }
+
+ diag_len = TLVP_LEN(&tp, GSM0808_IE_DIAGNOSTIC);
+ if (diag_len < 5) {
+ printf("Diagnostic length is too short: %d (expected > 5)\n",
+ diag_len);
+ return;
+ }
+
+ cause = gsm0808_get_cause(&tp);
+ if ((int)cause < 0) {
+ printf("ERROR: failed (%s) to extract Cause, aborting\n", strerror(-(int)cause));
+ return;
+ }
+ cause_class = gsm0808_cause_class(cause);
+ printf(" Cause class %d/0x%x (%s)\n",
+ cause_class, cause_class, gsm0808_cause_class_name(cause_class));
+ printf(" Cause %d/0x%x (%s)\n",
+ cause, cause, gsm0808_cause_name(cause));
+
+ diag = (struct gsm0808_diagnostics *)TLVP_VAL(&tp, GSM0808_IE_DIAGNOSTIC);
+ printf(" Diagnostics error octet location %d (%s)\n",
+ diag->error_pointer_octet,
+ gsm0808_diagnostics_octet_location_str(diag->error_pointer_octet));
+ printf(" Diagnostics error bit location %d (%s)\n",
+ diag->error_pointer_bit,
+ gsm0808_diagnostics_bit_location_str(diag->error_pointer_bit));
+ printf(" Diagnostics message that provoked the error: %s\n",
+ osmo_hexdump(diag->msg, diag_len-2));
+}
+
+/* Test Perform Location Report SYS#5891 */
+static void test_dec_perform_location_report_sys5891(void)
+{
+/* Message Type Perform Location Request
+ Location Type
+ Element ID: 0x44
+ Length: 1
+ Location Information: current geographic location (0x00)
+ Cell Identifier/CI (25911)
+ Element ID: 0x05
+ Length: 8
+ 0000 .... = Spare bit(s): 0x00
+ .... 0000 = Cell identification discriminator: The whole Cell Global Identification, CGI, is used to identify the cells. (0)
+ Mobile Country Code (MCC): (removed))
+ Mobile Network Code (MNC): (removed))
+ Cell LAC: 0x001e (30)
+ Cell CI: 0x6537 (25911)
+ LCS Client Type
+ Element ID: 0x48
+ Length: 1
+ 0011 .... = Client Category: Emergency Services (0x03)
+ .... 0000 = Client Subtype: unspecified (0x00)
+ LCS Priority
+ Element ID: 0x43
+ Length: 1
+ Periodicity: highest (0)
+ LCS QoS
+ Element ID: 0x3e
+ Length: 4
+ 0000 00.. = Spare: 0x00
+ .... ..0. = Velocity Requested: do not report velocity (0x00)
+ .... ...0 = Vertical Coordinate Indicator: vertical coordinate not requested (0x00)
+ 1... .... = Horizontal Accuracy Indicator: horizontal accuracy is specified (0x01)
+ .001 0010 = Horizontal Accuracy: 0x12
+ 0... .... = Vertical Accuracy Indicator: vertical accuracy is not specified (0x00)
+ .000 0000 = Spare: 0x00
+ 00.. .... = Response Time Category: Response Time is not specified (0x00)
+*/
+ const uint8_t hex[] = {
+ 0x2b, 0x44, 0x01, 0x00, 0x05, 0x08, 0x00, 0xab, 0xbc, 0xcd, 0x00, 0x1e,
+ 0x65, 0x37, 0x48, 0x01, 0x30, 0x43, 0x01, 0x00, 0x3e, 0x04, 0x00, 0x92,
+ 0x00, 0x00
+ };
+
+ struct tlv_parsed tp;
+ int rc;
+
+ printf("Testing decoding Perform Location Report SYS#5891\n");
+
+ rc = tlv_parse(&tp, gsm0808_att_tlvdef(), hex+1, sizeof(hex)-1, 0, 0);
+ OSMO_ASSERT(rc == 5);
+}
+
+static void test_create_ass(void)
{
static const uint8_t res1[] =
{ 0x00, 0x0a, 0x01, 0x0b, 0x04, 0x01, 0x0b, 0xa1, 0x25, 0x01, 0x00,
@@ -389,8 +487,8 @@ static void test_create_ass()
0x04, GSM0808_IE_AOIP_TRASP_ADDR, 0x06, 0xc0, 0xa8, 0x64, 0x17,
0x04, 0xd2, GSM0808_IE_SPEECH_CODEC_LIST, 0x07,
GSM0808_SCT_FR3 | 0x50, 0xef, 0xcd, GSM0808_SCT_FR2 | 0xa0, 0x9f,
- GSM0808_SCT_CSD | 0x90, 0xc0, GSM0808_IE_CALL_ID, 0xaa, 0xbb,
- 0xcc, 0xdd };
+ GSM0808_SCT_CSD | 0x90, 0xc0, GSM0808_IE_CALL_ID, 0xdd, 0xcc,
+ 0xbb, 0xaa };
struct msgb *msg;
struct gsm0808_channel_type ct;
@@ -429,7 +527,7 @@ static void test_create_ass()
msgb_free(msg);
}
-static void test_create_ass2()
+static void test_create_ass2(void)
{
static const uint8_t res[] = {
BSSAP_MSG_BSS_MANAGEMENT,
@@ -450,7 +548,7 @@ static void test_create_ass2()
GSM0808_SCT_CSD | 0x90,
0xc0,
GSM0808_IE_CALL_ID,
- 0xde, 0xad, 0xfa, 0xce, /* CallID */
+ 0xce, 0xfa, 0xad, 0xde, /* CallID */
0x83, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, /* Kc */
GSM0808_IE_GLOBAL_CALL_REF, 0x0d, /* GCR, length */
0x03, 0x44, 0x44, 0x44, /* GCR, Net ID */
@@ -506,7 +604,7 @@ static void test_create_ass2()
msgb_free(msg);
}
-static void test_create_ass_compl()
+static void test_create_ass_compl(void)
{
static const uint8_t res1[] = {
0x00, 0x09, 0x02, 0x15, 0x23, 0x21, 0x42, 0x2c,
@@ -525,7 +623,7 @@ static void test_create_ass_compl()
msgb_free(msg);
}
-static void test_create_ass_compl_aoip()
+static void test_create_ass_compl_aoip(void)
{
struct sockaddr_storage ss;
struct sockaddr_in sin;
@@ -562,7 +660,7 @@ static void test_create_ass_compl_aoip()
msgb_free(msg);
}
-static void test_create_ass_fail()
+static void test_create_ass_fail(void)
{
static const uint8_t res1[] = { 0x00, 0x04, 0x03, 0x04, 0x01, 0x23 };
static const uint8_t res2[] = {
@@ -580,7 +678,7 @@ static void test_create_ass_fail()
msgb_free(msg);
}
-static void test_create_ass_fail_aoip()
+static void test_create_ass_fail_aoip(void)
{
static const uint8_t res1[] =
{ 0x00, 0x0d, 0x03, 0x04, 0x01, 0x23, GSM0808_IE_SPEECH_CODEC_LIST,
@@ -606,7 +704,7 @@ static void test_create_ass_fail_aoip()
msgb_free(msg);
}
-static void test_create_clear_rqst()
+static void test_create_clear_rqst(void)
{
static const uint8_t res[] = { 0x00, 0x04, 0x22, 0x04, 0x01, 0x23 };
struct msgb *msg;
@@ -617,7 +715,7 @@ static void test_create_clear_rqst()
msgb_free(msg);
}
-static void test_create_paging()
+static void test_create_paging(void)
{
static const uint8_t res[] =
{ 0x00, 0x10, 0x52, 0x08, 0x08, 0x09, 0x10, 0x10, 0x00, 0x00, 0x00,
@@ -657,7 +755,7 @@ static void test_create_paging()
msgb_free(msg);
}
-static void test_create_dtap()
+static void test_create_dtap(void)
{
static const uint8_t res[] = { 0x01, 0x03, 0x02, 0x23, 0x42 };
struct msgb *msg, *l3;
@@ -674,7 +772,7 @@ static void test_create_dtap()
msgb_free(l3);
}
-static void test_prepend_dtap()
+static void test_prepend_dtap(void)
{
static const uint8_t res[] = { 0x01, 0x03, 0x02, 0x23, 0x42 };
struct msgb *in_msg;
@@ -691,7 +789,7 @@ static void test_prepend_dtap()
msgb_free(in_msg);
}
-static void test_enc_dec_lcls()
+static void test_enc_dec_lcls(void)
{
static const uint8_t res[] = {
GSM0808_IE_GLOBAL_CALL_REF,
@@ -769,7 +867,7 @@ static void test_enc_dec_lcls()
msgb_free(msg);
}
-static void test_enc_dec_aoip_trasp_addr_v4()
+static void test_enc_dec_aoip_trasp_addr_v4(void)
{
struct sockaddr_storage enc_addr;
struct sockaddr_storage dec_addr;
@@ -797,7 +895,7 @@ static void test_enc_dec_aoip_trasp_addr_v4()
msgb_free(msg);
}
-static void test_enc_dec_aoip_trasp_addr_v6()
+static void test_enc_dec_aoip_trasp_addr_v6(void)
{
struct sockaddr_storage enc_addr;
struct sockaddr_storage dec_addr;
@@ -826,7 +924,29 @@ static void test_enc_dec_aoip_trasp_addr_v6()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_speech_codec()
+static void test_enc_aoip_trasp_addr_msg_too_small(void)
+{
+ struct msgb *msg;
+ struct sockaddr_storage enc_addr;
+ struct sockaddr_in enc_addr_in;
+ uint8_t rc_enc;
+
+ memset(&enc_addr_in, 0, sizeof(enc_addr_in));
+ enc_addr_in.sin_family = AF_INET;
+ enc_addr_in.sin_port = htons(1234);
+ inet_aton("255.0.255.255", &enc_addr_in.sin_addr);
+
+ memset(&enc_addr, 0, sizeof(enc_addr));
+ memcpy(&enc_addr, &enc_addr_in, sizeof(enc_addr_in));
+
+ msg = msgb_alloc(7, "output buffer");
+ rc_enc = gsm0808_enc_aoip_trasp_addr(msg, &enc_addr);
+ OSMO_ASSERT(rc_enc == 0);
+
+ msgb_free(msg);
+}
+
+static void test_gsm0808_enc_dec_speech_codec(void)
{
struct gsm0808_speech_codec enc_sc = {
.pi = true,
@@ -839,7 +959,7 @@ static void test_gsm0808_enc_dec_speech_codec()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec(msg, &enc_sc);
+ rc_enc = gsm0808_enc_speech_codec2(msg, &enc_sc);
OSMO_ASSERT(rc_enc == 3);
rc_dec = gsm0808_dec_speech_codec(&dec_sc, msg->data + 2, msg->len - 2);
@@ -851,7 +971,7 @@ static void test_gsm0808_enc_dec_speech_codec()
}
-static void test_gsm0808_enc_dec_speech_codec_with_cfg()
+static void test_gsm0808_enc_dec_speech_codec_with_cfg(void)
{
struct gsm0808_speech_codec enc_sc = {
.pi = true,
@@ -865,7 +985,7 @@ static void test_gsm0808_enc_dec_speech_codec_with_cfg()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec(msg, &enc_sc);
+ rc_enc = gsm0808_enc_speech_codec2(msg, &enc_sc);
OSMO_ASSERT(rc_enc == 5);
rc_dec = gsm0808_dec_speech_codec(&dec_sc, msg->data + 2, msg->len - 2);
@@ -876,7 +996,7 @@ static void test_gsm0808_enc_dec_speech_codec_with_cfg()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg()
+static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg(void)
{
struct gsm0808_speech_codec enc_sc = {
.pi = true,
@@ -890,7 +1010,7 @@ static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec(msg, &enc_sc);
+ rc_enc = gsm0808_enc_speech_codec2(msg, &enc_sc);
OSMO_ASSERT(rc_enc == 5);
rc_dec = gsm0808_dec_speech_codec(&dec_sc, msg->data + 2, msg->len - 2);
@@ -901,7 +1021,7 @@ static void test_gsm0808_enc_dec_speech_codec_ext_with_cfg()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_speech_codec_list()
+static void test_gsm0808_enc_dec_speech_codec_list(void)
{
struct gsm0808_speech_codec_list enc_scl = {
.codec = {
@@ -933,7 +1053,7 @@ static void test_gsm0808_enc_dec_speech_codec_list()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec_list(msg, &enc_scl);
+ rc_enc = gsm0808_enc_speech_codec_list2(msg, &enc_scl);
OSMO_ASSERT(rc_enc == 9);
rc_dec = gsm0808_dec_speech_codec_list(&dec_scl, msg->data + 2, msg->len - 2);
@@ -944,7 +1064,7 @@ static void test_gsm0808_enc_dec_speech_codec_list()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_empty_speech_codec_list()
+static void test_gsm0808_enc_dec_empty_speech_codec_list(void)
{
struct gsm0808_speech_codec_list enc_scl = {
.len = 0,
@@ -955,7 +1075,7 @@ static void test_gsm0808_enc_dec_empty_speech_codec_list()
int rc_dec;
msg = msgb_alloc(1024, "output buffer");
- rc_enc = gsm0808_enc_speech_codec_list(msg, &enc_scl);
+ rc_enc = gsm0808_enc_speech_codec_list2(msg, &enc_scl);
OSMO_ASSERT(rc_enc == 2);
rc_dec = gsm0808_dec_speech_codec_list(&dec_scl, msg->data + 2, msg->len - 2);
@@ -966,7 +1086,89 @@ static void test_gsm0808_enc_dec_empty_speech_codec_list()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_channel_type()
+static void test_gsm0808_enc_dec_channel_type_data(void)
+{
+ struct gsm0808_channel_type enc_ct = {
+ .ch_indctr = GSM0808_CHAN_DATA,
+ .ch_rate_type = GSM0808_DATA_HALF_PREF,
+
+ .data_transparent = true,
+ .data_rate = GSM0808_DATA_RATE_TRANSP_4k8,
+ };
+ struct gsm0808_channel_type dec_ct = {};
+ struct msgb *msg;
+ uint8_t ct_enc_expected[] = { GSM0808_IE_CHANNEL_TYPE,
+ 0x03, 0x02, 0x0b, 0x11,
+ };
+ uint8_t rc_enc;
+ int rc_dec;
+
+ msg = msgb_alloc(1024, "output buffer");
+ rc_enc = gsm0808_enc_channel_type(msg, &enc_ct);
+ OSMO_ASSERT(rc_enc == 5);
+ if (memcmp(ct_enc_expected, msg->data, msg->len)) {
+ printf(" got: %s\n", osmo_hexdump(msg->data, msg->len));
+ printf("expect: %s\n", osmo_hexdump(ct_enc_expected, sizeof(ct_enc_expected)));
+ OSMO_ASSERT(false);
+ }
+
+ rc_dec = gsm0808_dec_channel_type(&dec_ct, msg->data + 2, msg->len - 2);
+ OSMO_ASSERT(rc_dec == 3);
+ OSMO_ASSERT(dec_ct.ch_indctr == enc_ct.ch_indctr);
+ OSMO_ASSERT(dec_ct.ch_rate_type == enc_ct.ch_rate_type);
+ OSMO_ASSERT(dec_ct.data_transparent == enc_ct.data_transparent);
+ OSMO_ASSERT(dec_ct.data_rate == enc_ct.data_rate);
+ OSMO_ASSERT(dec_ct.data_rate_allowed_is_set == enc_ct.data_rate_allowed_is_set);
+ OSMO_ASSERT(dec_ct.data_asym_pref_is_set == enc_ct.data_asym_pref_is_set);
+
+ msgb_free(msg);
+}
+
+static void test_gsm0808_enc_dec_channel_type_data_asym_pref(void)
+{
+ struct gsm0808_channel_type enc_ct = {
+ .ch_indctr = GSM0808_CHAN_DATA,
+ .ch_rate_type = GSM0808_DATA_HALF_PREF,
+
+ .data_transparent = false,
+ .data_rate = GSM0808_DATA_RATE_NON_TRANSP_6k0,
+ .data_rate_allowed_is_set = true,
+ .data_rate_allowed = GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_6k0
+ | GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_12k0
+ | GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_14k5,
+ .data_asym_pref_is_set = true,
+ .data_asym_pref = GSM0808_CT_ASYM_PREF_UL,
+ };
+ struct gsm0808_channel_type dec_ct = {};
+ struct msgb *msg;
+ uint8_t ct_enc_expected[] = { GSM0808_IE_CHANNEL_TYPE,
+ 0x05, 0x02, 0x0b, 0xd1, 0x8b, 0x20,
+ };
+ uint8_t rc_enc;
+ int rc_dec;
+
+ msg = msgb_alloc(1024, "output buffer");
+ rc_enc = gsm0808_enc_channel_type(msg, &enc_ct);
+ OSMO_ASSERT(rc_enc == 7);
+ if (memcmp(ct_enc_expected, msg->data, msg->len)) {
+ printf(" got: %s\n", osmo_hexdump(msg->data, msg->len));
+ printf("expect: %s\n", osmo_hexdump(ct_enc_expected, sizeof(ct_enc_expected)));
+ OSMO_ASSERT(false);
+ }
+
+ rc_dec = gsm0808_dec_channel_type(&dec_ct, msg->data + 2, msg->len - 2);
+ OSMO_ASSERT(rc_dec == 5);
+ OSMO_ASSERT(dec_ct.ch_indctr == enc_ct.ch_indctr);
+ OSMO_ASSERT(dec_ct.ch_rate_type == enc_ct.ch_rate_type);
+ OSMO_ASSERT(dec_ct.data_transparent == enc_ct.data_transparent);
+ OSMO_ASSERT(dec_ct.data_rate == enc_ct.data_rate);
+ OSMO_ASSERT(dec_ct.data_rate_allowed_is_set == enc_ct.data_rate_allowed_is_set);
+ OSMO_ASSERT(dec_ct.data_asym_pref_is_set == enc_ct.data_asym_pref_is_set);
+
+ msgb_free(msg);
+}
+
+static void test_gsm0808_enc_dec_channel_type_speech(void)
{
struct gsm0808_channel_type enc_ct = {
.ch_indctr = GSM0808_CHAN_SPEECH,
@@ -997,7 +1199,65 @@ static void test_gsm0808_enc_dec_channel_type()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_encrypt_info()
+static void test_gsm0808_enc_dec_channel_type_sign(void)
+{
+ struct gsm0808_channel_type enc_ct = {
+ .ch_indctr = GSM0808_CHAN_SIGN,
+ .ch_rate_type = GSM0808_SIGN_FULL_PREF_NO_CHANGE,
+ };
+ struct gsm0808_channel_type dec_ct = {};
+ struct msgb *msg;
+ uint8_t ct_enc_expected[] = { GSM0808_IE_CHANNEL_TYPE,
+ 0x03, 0x03, 0x1a, 0x00
+ };
+ uint8_t rc_enc;
+ int rc_dec;
+
+ msg = msgb_alloc(1024, "output buffer");
+ rc_enc = gsm0808_enc_channel_type(msg, &enc_ct);
+ OSMO_ASSERT(rc_enc == 5);
+ OSMO_ASSERT(memcmp(ct_enc_expected, msg->data, msg->len) == 0);
+
+ rc_dec = gsm0808_dec_channel_type(&dec_ct, msg->data + 2, msg->len - 2);
+ OSMO_ASSERT(rc_dec == 2);
+ OSMO_ASSERT(enc_ct.ch_indctr == dec_ct.ch_indctr);
+ OSMO_ASSERT(enc_ct.ch_rate_type == dec_ct.ch_rate_type);
+
+ msgb_free(msg);
+}
+
+static void test_gsm0808_dec_channel_type_err(void)
+{
+ struct gsm0808_channel_type ct;
+ int rc;
+
+ /* Unknown channel indicator */
+ const uint8_t hex1[] = { 0x05, 0x0b, 0xa1, 0x25 };
+ rc = gsm0808_dec_channel_type(&ct, hex1, sizeof(hex1));
+ OSMO_ASSERT(rc == -ENOTSUP);
+
+ /* Data: ext in Octet 5 with transparent service */
+ const uint8_t hex2[] = { 0x02, 0x0b, 0x80, 0x00 };
+ rc = gsm0808_dec_channel_type(&ct, hex2, sizeof(hex2));
+ OSMO_ASSERT(rc == -EINVAL);
+
+ /* Data: ext in Octet 5, but too short */
+ const uint8_t hex3[] = { 0x02, 0x0b, 0xc0 };
+ rc = gsm0808_dec_channel_type(&ct, hex3, sizeof(hex3));
+ OSMO_ASSERT(rc == -EOVERFLOW);
+
+ /* Data: ext in Octet 5a, but too short */
+ const uint8_t hex4[] = { 0x02, 0x0b, 0xc0, 0x80 };
+ rc = gsm0808_dec_channel_type(&ct, hex4, sizeof(hex4));
+ OSMO_ASSERT(rc == -EOVERFLOW);
+
+ /* Speech: extension bit is set in last byte */
+ const uint8_t hex5[] = { 0x01, 0x0b, 0xa1, 0xa5 };
+ rc = gsm0808_dec_channel_type(&ct, hex5, sizeof(hex5));
+ OSMO_ASSERT(rc == -EOVERFLOW);
+}
+
+static void test_gsm0808_enc_dec_encrypt_info(void)
{
struct gsm0808_encrypt_info enc_ei = {
.perm_algo = { GSM0808_ALG_ID_A5_0, GSM0808_ALG_ID_A5_1 },
@@ -1027,7 +1287,20 @@ static void test_gsm0808_enc_dec_encrypt_info()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_lac()
+static void test_gsm0808_dec_cell_id_list_srvcc(void)
+{
+ /* taken from a pcap file of a real-world 3rd party MSC (SYS#5838) */
+ const uint8_t enc_cil[] = { 0x0b, 0x2, 0xf2, 0x10, 0x4e, 0x20, 0x15, 0xbe};
+ struct gsm0808_cell_id_list2 dec_cil;
+ int rc;
+
+ rc = gsm0808_dec_cell_id_list2(&dec_cil, enc_cil, sizeof(enc_cil));
+ OSMO_ASSERT(rc == sizeof(enc_cil));
+ OSMO_ASSERT(dec_cil.id_discr == CELL_IDENT_SAI);
+ OSMO_ASSERT(dec_cil.id_list_len == 1);
+}
+
+static void test_gsm0808_enc_dec_cell_id_list_lac(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1054,7 +1327,7 @@ static void test_gsm0808_enc_dec_cell_id_list_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_single_lac()
+static void test_gsm0808_enc_dec_cell_id_list_single_lac(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1083,7 +1356,7 @@ static void test_gsm0808_enc_dec_cell_id_list_single_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_lac()
+static void test_gsm0808_enc_dec_cell_id_list_multi_lac(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1119,7 +1392,7 @@ static void test_gsm0808_enc_dec_cell_id_list_multi_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_bss()
+static void test_gsm0808_enc_dec_cell_id_list_bss(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1142,7 +1415,7 @@ static void test_gsm0808_enc_dec_cell_id_list_bss()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_lai_and_lac()
+static void test_gsm0808_enc_dec_cell_id_list_multi_lai_and_lac(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1198,7 +1471,7 @@ static void test_gsm0808_enc_dec_cell_id_list_multi_lai_and_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_ci()
+static void test_gsm0808_enc_dec_cell_id_list_multi_ci(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1232,7 +1505,7 @@ static void test_gsm0808_enc_dec_cell_id_list_multi_ci()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci()
+static void test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1273,7 +1546,7 @@ static void test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_list_multi_global()
+static void test_gsm0808_enc_dec_cell_id_list_multi_global(void)
{
struct gsm0808_cell_id_list2 enc_cil;
struct gsm0808_cell_id_list2 dec_cil;
@@ -1346,7 +1619,7 @@ static void print_cil(const struct gsm0808_cell_id_list2 *cil)
printf(" cell_id_list == %s\n", gsm0808_cell_id_list_name(cil));
}
-void test_cell_id_list_add() {
+void test_cell_id_list_add(void) {
size_t zu;
const struct gsm0808_cell_id_list2 cgi1 = {
@@ -1528,7 +1801,7 @@ void test_cell_id_list_add() {
printf("------- %s done\n", __func__);
}
-static void test_gsm0808_enc_dec_cell_id_lac()
+static void test_gsm0808_enc_dec_cell_id_lac(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_LAC,
@@ -1554,7 +1827,7 @@ static void test_gsm0808_enc_dec_cell_id_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_bss()
+static void test_gsm0808_enc_dec_cell_id_bss(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_BSS,
@@ -1576,7 +1849,7 @@ static void test_gsm0808_enc_dec_cell_id_bss()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_no_cell()
+static void test_gsm0808_enc_dec_cell_id_no_cell(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_NO_CELL,
@@ -1598,7 +1871,7 @@ static void test_gsm0808_enc_dec_cell_id_no_cell()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_lai_and_lac()
+static void test_gsm0808_enc_dec_cell_id_lai_and_lac(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_LAI_AND_LAC,
@@ -1629,7 +1902,7 @@ static void test_gsm0808_enc_dec_cell_id_lai_and_lac()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_ci()
+static void test_gsm0808_enc_dec_cell_id_ci(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_CI,
@@ -1652,7 +1925,7 @@ static void test_gsm0808_enc_dec_cell_id_ci()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_lac_and_ci()
+static void test_gsm0808_enc_dec_cell_id_lac_and_ci(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_LAC_AND_CI,
@@ -1679,7 +1952,7 @@ static void test_gsm0808_enc_dec_cell_id_lac_and_ci()
msgb_free(msg);
}
-static void test_gsm0808_enc_dec_cell_id_global()
+static void test_gsm0808_enc_dec_cell_id_global(void)
{
struct gsm0808_cell_id enc_ci = {
.id_discr = CELL_IDENT_WHOLE_GLOBAL,
@@ -1711,29 +1984,123 @@ static void test_gsm0808_enc_dec_cell_id_global()
msgb_free(msg);
}
+static void test_gsm0808_enc_dec_cell_id_global_ps(void)
+{
+ struct gsm0808_cell_id enc_cgi = {
+ .id_discr = CELL_IDENT_WHOLE_GLOBAL,
+ .id.global = {
+ .lai = {
+ .plmn = { .mcc = 123, .mnc = 456 },
+ .lac = 0x2342
+ },
+ .cell_identity = 0x423,
+ }
+ };
+ struct gsm0808_cell_id enc_cgi_ps = {
+ .id_discr = CELL_IDENT_WHOLE_GLOBAL_PS,
+ .id.global_ps = {
+ .rai = {
+ .lac = {
+ .plmn = { .mcc = 123, .mnc = 456 },
+ .lac = 0x2342
+ },
+ .rac = 0xcc,
+ },
+ .cell_identity = 0x423,
+ }
+ };
+ struct msgb *msg_cgi, *msg_cgi_ps;
+ uint8_t rc_enc;
+
+ msg_cgi = msgb_alloc(1024, "output buffer (CGI)");
+ rc_enc = gsm0808_enc_cell_id(msg_cgi, &enc_cgi);
+ OSMO_ASSERT(rc_enc > 0);
+
+ msg_cgi_ps = msgb_alloc(1024, "output buffer (CGI-PS)");
+ rc_enc = gsm0808_enc_cell_id(msg_cgi_ps, &enc_cgi_ps);
+ OSMO_ASSERT(rc_enc > 0);
+
+ OSMO_ASSERT(msgb_eq(msg_cgi, msg_cgi_ps));
+
+ msgb_free(msg_cgi);
+ msgb_free(msg_cgi_ps);
+}
+
+static void print_s15_s0(uint16_t s15_s0, bool full_rate)
+{
+ int i;
+ printf(" S15-S0 = 0x%04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0,
+ OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0));
+ for (i = 0; i < 16; i++) {
+ uint8_t modes;
+ int m;
+ int space;
+
+ if (!(s15_s0 & (1 << i)))
+ continue;
+
+ space = 6;
+ if (i < 10)
+ space++;
+
+ printf(" S%d", i);
+
+ modes = gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][i];
+ if (!modes) {
+ printf(" (empty)\n");
+ continue;
+ }
+
+ for (m = 0; m < 8; m++) {
+ if (!(modes & (1 << m))) {
+ /* avoid whitespace at line ends -- accumulate whitespace width until there is
+ * non-whitespace to actually be printed.*/
+ space += 8;
+ continue;
+ }
+ printf("%*s", space, gsm0808_amr_mode_name(m));
+ space = 8;
+ }
+ printf("\n");
+ }
+}
+
+static void print_mr_cfg(const struct gsm48_multi_rate_conf *cfg)
+{
+ printf(" cfg.smod=%u spare=%u icmi=%u nscb=%u ver=%u\n",
+ cfg->smod, cfg->spare, cfg->icmi, cfg->nscb, cfg->ver);
+ printf(" ");
+#define PRINT_MODE_BIT(NAME) do { \
+ if (cfg->NAME) \
+ printf(" " #NAME "=1"); \
+ else \
+ printf(" -------"); \
+ } while (0)
+ PRINT_MODE_BIT(m4_75);
+ PRINT_MODE_BIT(m5_15);
+ PRINT_MODE_BIT(m5_90);
+ PRINT_MODE_BIT(m6_70);
+ PRINT_MODE_BIT(m7_40);
+ PRINT_MODE_BIT(m7_95);
+ PRINT_MODE_BIT(m10_2);
+ PRINT_MODE_BIT(m12_2);
+ printf("\n");
+}
+
static void test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(struct gsm48_multi_rate_conf *cfg)
{
uint16_t s15_s0;
printf("Input:\n");
- printf(" m4_75= %u smod= %u\n", cfg->m4_75, cfg->smod);
- printf(" m5_15= %u spare= %u\n", cfg->m5_15, cfg->spare);
- printf(" m5_90= %u icmi= %u\n", cfg->m5_90, cfg->icmi);
- printf(" m6_70= %u nscb= %u\n", cfg->m6_70, cfg->nscb);
- printf(" m7_40= %u ver= %u\n", cfg->m7_40, cfg->ver);
- printf(" m7_95= %u\n", cfg->m7_95);
- printf(" m10_2= %u\n", cfg->m10_2);
- printf(" m12_2= %u\n", cfg->m12_2);
+ print_mr_cfg(cfg);
s15_s0 = gsm0808_sc_cfg_from_gsm48_mr_cfg(cfg, true);
printf("Result (fr):\n");
- printf(" S15-S0 = %04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0,
- OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0));
+ print_s15_s0(s15_s0, true);
s15_s0 = gsm0808_sc_cfg_from_gsm48_mr_cfg(cfg, false);
printf("Result (hr):\n");
- printf(" S15-S0 = %04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0,
- OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0));
+ print_s15_s0(s15_s0, false);
printf("\n");
}
@@ -1925,7 +2292,7 @@ static void test_gsm0808_sc_cfg_from_gsm48_mr_cfg(void)
cfg.m10_2 = 0;
cfg.m12_2 = 0;
test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg);
-
+
}
static void test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single(uint16_t s15_s0)
@@ -1934,20 +2301,12 @@ static void test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single(uint16_t s15_s0)
int rc;
printf("Input:\n");
- printf(" S15-S0 = %04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0,
- OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0));
+ print_s15_s0(s15_s0, true);
rc = gsm48_mr_cfg_from_gsm0808_sc_cfg(&cfg, s15_s0);
printf("Output:\n");
- printf(" m4_75= %u smod= %u\n", cfg.m4_75, cfg.smod);
- printf(" m5_15= %u spare= %u\n", cfg.m5_15, cfg.spare);
- printf(" m5_90= %u icmi= %u\n", cfg.m5_90, cfg.icmi);
- printf(" m6_70= %u nscb= %u\n", cfg.m6_70, cfg.nscb);
- printf(" m7_40= %u ver= %u\n", cfg.m7_40, cfg.ver);
- printf(" m7_95= %u\n", cfg.m7_95);
- printf(" m10_2= %u\n", cfg.m10_2);
- printf(" m12_2= %u\n", cfg.m12_2);
+ print_mr_cfg(&cfg);
if (rc != 0)
printf(" Result invalid!\n");
@@ -1955,7 +2314,7 @@ static void test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single(uint16_t s15_s0)
printf("\n");
}
-void test_gsm48_mr_cfg_from_gsm0808_sc_cfg()
+void test_gsm48_mr_cfg_from_gsm0808_sc_cfg(void)
{
printf("Testing gsm48_mr_cfg_from_gsm0808_sc_cfg():\n");
@@ -2140,7 +2499,7 @@ static const struct test_cell_id_matching_data test_cell_id_matching_tests[] = {
{ .id = cgi_23_042_23_5, .match_id = cgi_23_99_23_5, .expect_match = false, .expect_exact_match = false },
};
-static void test_cell_id_matching()
+static void test_cell_id_matching(void)
{
int i;
bool ok = true;
@@ -2303,7 +2662,7 @@ static const struct gsm0808_cell_id test_gsm0808_cell_id_to_from_cgi_data[] = {
{ .id_discr = 423 },
};
-static void test_gsm0808_cell_id_to_from_cgi()
+static void test_gsm0808_cell_id_to_from_cgi(void)
{
int i;
int j;
@@ -2387,12 +2746,17 @@ int main(int argc, char **argv)
test_enc_dec_aoip_trasp_addr_v4();
test_enc_dec_aoip_trasp_addr_v6();
+ test_enc_aoip_trasp_addr_msg_too_small();
test_gsm0808_enc_dec_speech_codec();
test_gsm0808_enc_dec_speech_codec_ext_with_cfg();
test_gsm0808_enc_dec_speech_codec_with_cfg();
test_gsm0808_enc_dec_speech_codec_list();
test_gsm0808_enc_dec_empty_speech_codec_list();
- test_gsm0808_enc_dec_channel_type();
+ test_gsm0808_enc_dec_channel_type_data();
+ test_gsm0808_enc_dec_channel_type_data_asym_pref();
+ test_gsm0808_enc_dec_channel_type_speech();
+ test_gsm0808_enc_dec_channel_type_sign();
+ test_gsm0808_dec_channel_type_err();
test_gsm0808_enc_dec_encrypt_info();
test_gsm0808_enc_dec_cell_id_list_lac();
@@ -2403,6 +2767,7 @@ int main(int argc, char **argv)
test_gsm0808_enc_dec_cell_id_list_multi_ci();
test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci();
test_gsm0808_enc_dec_cell_id_list_multi_global();
+ test_gsm0808_dec_cell_id_list_srvcc();
test_cell_id_list_add();
@@ -2413,6 +2778,7 @@ int main(int argc, char **argv)
test_gsm0808_enc_dec_cell_id_ci();
test_gsm0808_enc_dec_cell_id_lac_and_ci();
test_gsm0808_enc_dec_cell_id_global();
+ test_gsm0808_enc_dec_cell_id_global_ps();
test_gsm0808_sc_cfg_from_gsm48_mr_cfg();
test_gsm48_mr_cfg_from_gsm0808_sc_cfg();
@@ -2422,6 +2788,9 @@ int main(int argc, char **argv)
test_gsm0808_cell_id_to_from_cgi();
+ test_dec_confusion();
+ test_dec_perform_location_report_sys5891();
+
printf("Done\n");
return EXIT_SUCCESS;
}
diff --git a/tests/gsm0808/gsm0808_test.ok b/tests/gsm0808/gsm0808_test.ok
index b620e369..2329fcdc 100644
--- a/tests/gsm0808/gsm0808_test.ok
+++ b/tests/gsm0808/gsm0808_test.ok
@@ -87,549 +87,549 @@ test_gsm0808_enc_dec_cell_id_lac_and_ci: encoded: 05 05 01 04 23 02 35 (rc = 7)
test_gsm0808_enc_dec_cell_id_global: encoded: 05 08 00 21 63 54 23 42 04 23 (rc = 10)
Testing gsm0808_sc_cfg_from_gsm48_mr_cfg():
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- ------- ------- ------- -------
Result (fr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Result (hr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Input:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 ------- ------- ------- ------- ------- ------- -------
Result (fr):
- S15-S0 = 5701 = 0b0101011100000001
+ S15-S0 = 0x5701 = 0b0101011100000001
+ S0 4.75
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0701 = 0b0000011100000001
+ S15-S0 = 0x0701 = 0b0000011100000001
+ S0 4.75
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 1 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- m5_15=1 ------- ------- ------- ------- ------- -------
Result (fr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Result (hr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- m5_90=1 ------- ------- ------- ------- -------
Result (fr):
- S15-S0 = 5704 = 0b0101011100000100
+ S15-S0 = 0x5704 = 0b0101011100000100
+ S2 5.90
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0704 = 0b0000011100000100
+ S15-S0 = 0x0704 = 0b0000011100000100
+ S2 5.90
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- m6_70=1 ------- ------- ------- -------
Result (fr):
- S15-S0 = 1608 = 0b0001011000001000
+ S15-S0 = 0x1608 = 0b0001011000001000
+ S3 6.70
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
Result (hr):
- S15-S0 = 0608 = 0b0000011000001000
+ S15-S0 = 0x0608 = 0b0000011000001000
+ S3 6.70
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- m7_40=1 ------- ------- -------
Result (fr):
- S15-S0 = 0410 = 0b0000010000010000
+ S15-S0 = 0x0410 = 0b0000010000010000
+ S4 7.40
+ S10 4.75 5.90 6.70 7.40
Result (hr):
- S15-S0 = 0410 = 0b0000010000010000
+ S15-S0 = 0x0410 = 0b0000010000010000
+ S4 7.40
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- ------- m7_95=1 ------- -------
Result (fr):
- S15-S0 = 4020 = 0b0100000000100000
+ S15-S0 = 0x4020 = 0b0100000000100000
+ S5 7.95
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0020 = 0b0000000000100000
+ S15-S0 = 0x0020 = 0b0000000000100000
+ S5 7.95
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- ------- ------- m10_2=1 -------
Result (fr):
- S15-S0 = 1040 = 0b0001000001000000
+ S15-S0 = 0x1040 = 0b0001000001000000
+ S6 10.2
+ S12 4.75 5.90 6.70 10.2
Result (hr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- ------- ------- ------- m12_2=1
Result (fr):
- S15-S0 = 4080 = 0b0100000010000000
+ S15-S0 = 0x4080 = 0b0100000010000000
+ S7 12.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Input:
- m4_75= 1 smod= 0
- m5_15= 1 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 m5_15=1 m5_90=1 m6_70=1 ------- ------- ------- -------
Result (fr):
- S15-S0 = 570d = 0b0101011100001101
+ S15-S0 = 0x570d = 0b0101011100001101
+ S0 4.75
+ S2 5.90
+ S3 6.70
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 070d = 0b0000011100001101
+ S15-S0 = 0x070d = 0b0000011100001101
+ S0 4.75
+ S2 5.90
+ S3 6.70
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 1
- m10_2= 1
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- ------- ------- m7_40=1 m7_95=1 m10_2=1 m12_2=1
Result (fr):
- S15-S0 = 54f0 = 0b0101010011110000
+ S15-S0 = 0x54f0 = 0b0101010011110000
+ S4 7.40
+ S5 7.95
+ S6 10.2
+ S7 12.2
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0430 = 0b0000010000110000
+ S15-S0 = 0x0430 = 0b0000010000110000
+ S4 7.40
+ S5 7.95
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 0
- m10_2= 1
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- ------- m5_90=1 m6_70=1 ------- ------- m10_2=1 m12_2=1
Result (fr):
- S15-S0 = 57cc = 0b0101011111001100
+ S15-S0 = 0x57cc = 0b0101011111001100
+ S2 5.90
+ S3 6.70
+ S6 10.2
+ S7 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 070c = 0b0000011100001100
+ S15-S0 = 0x070c = 0b0000011100001100
+ S2 5.90
+ S3 6.70
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 1 smod= 0
- m5_15= 1 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 m5_15=1 ------- ------- m7_40=1 m7_95=1 ------- -------
Result (fr):
- S15-S0 = 5731 = 0b0101011100110001
+ S15-S0 = 0x5731 = 0b0101011100110001
+ S0 4.75
+ S4 7.40
+ S5 7.95
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0731 = 0b0000011100110001
+ S15-S0 = 0x0731 = 0b0000011100110001
+ S0 4.75
+ S4 7.40
+ S5 7.95
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 0 smod= 0
- m5_15= 1 spare= 0
- m5_90= 0 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 0
- m7_95= 1
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ ------- m5_15=1 ------- m6_70=1 ------- m7_95=1 ------- m12_2=1
Result (fr):
- S15-S0 = 56a8 = 0b0101011010101000
+ S15-S0 = 0x56a8 = 0b0101011010101000
+ S3 6.70
+ S5 7.95
+ S7 12.2
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0628 = 0b0000011000101000
+ S15-S0 = 0x0628 = 0b0000011000101000
+ S3 6.70
+ S5 7.95
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Input:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- m10_2=1 -------
Result (fr):
- S15-S0 = 5755 = 0b0101011101010101
+ S15-S0 = 0x5755 = 0b0101011101010101
+ S0 4.75
+ S2 5.90
+ S4 7.40
+ S6 10.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0717 = 0b0000011100010111
-
-Input:
- m4_75= 1 smod= 0
- m5_15= 1 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 1 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 1
- m10_2= 1
- m12_2= 1
+ S15-S0 = 0x0717 = 0b0000011100010111
+ S0 4.75
+ S1 4.75 5.90 7.40
+ S2 5.90
+ S4 7.40
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+
+Input:
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 m5_15=1 m5_90=1 m6_70=1 m7_40=1 m7_95=1 m10_2=1 m12_2=1
Result (fr):
- S15-S0 = 57ff = 0b0101011111111111
+ S15-S0 = 0x57ff = 0b0101011111111111
+ S0 4.75
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S3 6.70
+ S4 7.40
+ S5 7.95
+ S6 10.2
+ S7 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 073f = 0b0000011100111111
-
-Input:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ S15-S0 = 0x073f = 0b0000011100111111
+ S0 4.75
+ S1 4.75 5.90 7.40
+ S2 5.90
+ S3 6.70
+ S4 7.40
+ S5 7.95
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+
+Input:
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Result (fr):
- S15-S0 = 5797 = 0b0101011110010111
+ S15-S0 = 0x5797 = 0b0101011110010111
+ S0 4.75
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S4 7.40
+ S7 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0717 = 0b0000011100010111
-
-Input:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 0
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 0
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ S15-S0 = 0x0717 = 0b0000011100010111
+ S0 4.75
+ S1 4.75 5.90 7.40
+ S2 5.90
+ S4 7.40
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+
+Input:
+ cfg.smod=0 spare=0 icmi=0 nscb=0 ver=0
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- -------
Result (fr):
- S15-S0 = 5715 = 0b0101011100010101
+ S15-S0 = 0x5715 = 0b0101011100010101
+ S0 4.75
+ S2 5.90
+ S4 7.40
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S12 4.75 5.90 6.70 10.2
+ S14 4.75 5.90 7.95 12.2
Result (hr):
- S15-S0 = 0717 = 0b0000011100010111
+ S15-S0 = 0x0717 = 0b0000011100010111
+ S0 4.75
+ S1 4.75 5.90 7.40
+ S2 5.90
+ S4 7.40
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
Testing gsm48_mr_cfg_from_gsm0808_sc_cfg():
Input:
- S15-S0 = ff03 = 0b1111111100000011
+ S15-S0 = 0xff03 = 0b1111111100000011
+ S0 4.75
+ S1 4.75 5.90 7.40 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- ------- -------
Result invalid!
Input:
- S15-S0 = ff06 = 0b1111111100000110
+ S15-S0 = 0xff06 = 0b1111111100000110
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 3e08 = 0b0011111000001000
+ S15-S0 = 0x3e08 = 0b0011111000001000
+ S3 6.70
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- m6_70=1 ------- ------- ------- -------
Input:
- S15-S0 = 0c12 = 0b0000110000010010
+ S15-S0 = 0x0c12 = 0b0000110000010010
+ S1 4.75 5.90 7.40 12.2
+ S4 7.40
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = c020 = 0b1100000000100000
+ S15-S0 = 0xc020 = 0b1100000000100000
+ S5 7.95
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- m7_95=1 ------- -------
Input:
- S15-S0 = 3040 = 0b0011000001000000
+ S15-S0 = 0x3040 = 0b0011000001000000
+ S6 10.2
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- m10_2=1 -------
Input:
- S15-S0 = c082 = 0b1100000010000010
+ S15-S0 = 0xc082 = 0b1100000010000010
+ S1 4.75 5.90 7.40 12.2
+ S7 12.2
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 0001 = 0b0000000000000001
+ S15-S0 = 0x0001 = 0b0000000000000001
+ S0 4.75
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- ------- ------- ------- ------- ------- -------
Input:
- S15-S0 = 0002 = 0b0000000000000010
+ S15-S0 = 0x0002 = 0b0000000000000010
+ S1 4.75 5.90 7.40 12.2
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 0004 = 0b0000000000000100
+ S15-S0 = 0x0004 = 0b0000000000000100
+ S2 5.90
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- m5_90=1 ------- ------- ------- ------- -------
Input:
- S15-S0 = 0008 = 0b0000000000001000
+ S15-S0 = 0x0008 = 0b0000000000001000
+ S3 6.70
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 1 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- m6_70=1 ------- ------- ------- -------
Input:
- S15-S0 = 0010 = 0b0000000000010000
+ S15-S0 = 0x0010 = 0b0000000000010000
+ S4 7.40
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- m7_40=1 ------- ------- -------
Input:
- S15-S0 = 0020 = 0b0000000000100000
+ S15-S0 = 0x0020 = 0b0000000000100000
+ S5 7.95
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- m7_95=1 ------- -------
Input:
- S15-S0 = 0040 = 0b0000000001000000
+ S15-S0 = 0x0040 = 0b0000000001000000
+ S6 10.2
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- m10_2=1 -------
Input:
- S15-S0 = 0080 = 0b0000000010000000
+ S15-S0 = 0x0080 = 0b0000000010000000
+ S7 12.2
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- ------- m12_2=1
Input:
- S15-S0 = 0058 = 0b0000000001011000
+ S15-S0 = 0x0058 = 0b0000000001011000
+ S3 6.70
+ S4 7.40
+ S6 10.2
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 1 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 1
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- m6_70=1 m7_40=1 ------- m10_2=1 -------
Input:
- S15-S0 = 0021 = 0b0000000000100001
+ S15-S0 = 0x0021 = 0b0000000000100001
+ S0 4.75
+ S5 7.95
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- ------- ------- ------- m7_95=1 ------- -------
Input:
- S15-S0 = 0084 = 0b0000000010000100
+ S15-S0 = 0x0084 = 0b0000000010000100
+ S2 5.90
+ S7 12.2
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- m5_90=1 ------- ------- ------- ------- m12_2=1
Input:
- S15-S0 = 0086 = 0b0000000010000110
+ S15-S0 = 0x0086 = 0b0000000010000110
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S7 12.2
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Input:
- S15-S0 = 000a = 0b0000000000001010
+ S15-S0 = 0x000a = 0b0000000000001010
+ S1 4.75 5.90 7.40 12.2
+ S3 6.70
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Result invalid!
Input:
- S15-S0 = 0079 = 0b0000000001111001
+ S15-S0 = 0x0079 = 0b0000000001111001
+ S0 4.75
+ S3 6.70
+ S4 7.40
+ S5 7.95
+ S6 10.2
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 1 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 1
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- ------- m6_70=1 m7_40=1 m7_95=1 ------- -------
Result invalid!
Input:
- S15-S0 = 0000 = 0b0000000000000000
+ S15-S0 = 0x0000 = 0b0000000000000000
Output:
- m4_75= 0 smod= 0
- m5_15= 0 spare= 0
- m5_90= 0 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 0 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 0
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ ------- ------- ------- ------- ------- ------- ------- -------
Result invalid!
Input:
- S15-S0 = ffff = 0b1111111111111111
+ S15-S0 = 0xffff = 0b1111111111111111
+ S0 4.75
+ S1 4.75 5.90 7.40 12.2
+ S2 5.90
+ S3 6.70
+ S4 7.40
+ S5 7.95
+ S6 10.2
+ S7 12.2
+ S8 4.75 5.90
+ S9 4.75 5.90 6.70
+ S10 4.75 5.90 6.70 7.40
+ S11 (empty)
+ S12 4.75 5.90 6.70 10.2
+ S13 (empty)
+ S14 4.75 5.90 7.95 12.2
+ S15 (empty)
Output:
- m4_75= 1 smod= 0
- m5_15= 0 spare= 0
- m5_90= 1 icmi= 1
- m6_70= 0 nscb= 0
- m7_40= 1 ver= 1
- m7_95= 0
- m10_2= 0
- m12_2= 1
+ cfg.smod=0 spare=0 icmi=1 nscb=0 ver=1
+ m4_75=1 ------- m5_90=1 ------- m7_40=1 ------- ------- m12_2=1
Result invalid!
@@ -910,4 +910,11 @@ cid unknown 0x1a7:unknown 0x1a7 -> cgi 777-007-7777-7777 -> cid unknown 0x1a7:un
--> gsm0808_cell_id{LAC-CI} = LAC-CI:7777-7777
--> gsm0808_cell_id{LAI} = LAI:777-007-7777
--> gsm0808_cell_id{CGI} = CGI:777-007-7777-7777
+Testing decoding CONFUSION
+ Cause class 5/0x5 (Invalid message)
+ Cause 82/0x52 (INFORMATION ELEMENT OR FIELD MISSING)
+ Diagnostics error octet location 0 (Error location not determined)
+ Diagnostics error bit location 15 (Reserved value)
+ Diagnostics message that provoked the error: 00 03 25 03 25
+Testing decoding Perform Location Report SYS#5891
Done
diff --git a/tests/gsm23003/gsm23003_test.c b/tests/gsm23003/gsm23003_test.c
index eac5a115..77d3173e 100644
--- a/tests/gsm23003/gsm23003_test.c
+++ b/tests/gsm23003/gsm23003_test.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -58,7 +54,7 @@ static struct {
{ NULL, false },
};
-bool test_valid_imsi()
+bool test_valid_imsi(void)
{
int i;
bool pass = true;
@@ -101,7 +97,7 @@ static struct {
{ NULL, false },
};
-bool test_valid_msisdn()
+bool test_valid_msisdn(void)
{
int i;
bool pass = true;
@@ -142,7 +138,7 @@ static struct {
{ NULL, false, false },
};
-bool test_valid_imei()
+bool test_valid_imei(void)
{
int i;
bool pass = true;
@@ -189,7 +185,7 @@ static struct test_mnc_from_str test_mnc_from_strs[] = {
{ "023 ", { -EINVAL, 0, false } },
};
-static bool test_mnc_from_str()
+static bool test_mnc_from_str(void)
{
int i;
bool pass = true;
@@ -213,7 +209,7 @@ static bool test_mnc_from_str()
return pass;
}
-static bool test_gummei_name()
+static bool test_gummei_name(void)
{
static const struct osmo_gummei gummei = {
.plmn = { .mcc = 901, .mnc = 70 },
@@ -230,7 +226,7 @@ static bool test_gummei_name()
return pass;
}
-static bool test_domain_gen()
+static bool test_domain_gen(void)
{
static const struct osmo_gummei gummei = {
.plmn = { .mcc = 901, .mnc = 70 },
@@ -256,7 +252,7 @@ static bool test_domain_gen()
}
-static bool test_domain_parse()
+static bool test_domain_parse(void)
{
static const char *mme_dom_valid = "mmec01.mmegiA001.mme.epc.mnc070.mcc901.3gppnetwork.org";
static const char *home_dom_valid = "epc.mnc070.mcc901.3gppnetwork.org";
diff --git a/tests/gsm23236/gsm23236_test.c b/tests/gsm23236/gsm23236_test.c
new file mode 100644
index 00000000..6cbdbeb2
--- /dev/null
+++ b/tests/gsm23236/gsm23236_test.c
@@ -0,0 +1,616 @@
+/*
+ * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <strings.h>
+#include <string.h>
+
+#include <osmocom/gsm/gsm23236.h>
+#include <osmocom/core/utils.h>
+
+void *ctx;
+bool ok = true;
+
+void bitdump(uint8_t count, uint32_t val)
+{
+ uint32_t bit;
+ if (count < 1)
+ return;
+ for (bit = ((uint32_t)1) << (count - 1); bit; bit >>= 1)
+ printf("%c", (val & bit)? '1' : '0');
+}
+
+struct nri_v_get_set_test {
+ uint32_t tmsi;
+ uint8_t nri_bitlen;
+ int16_t expect_get_nri;
+ int expect_get_rc;
+ int16_t set_nri_v;
+ uint32_t expect_tmsi;
+ int expect_set_rc;
+};
+
+struct nri_v_get_set_test nri_v_get_set_tests[] = {
+ {
+ .tmsi = 0,
+ .nri_bitlen = 10,
+ .expect_get_nri = 0,
+ .set_nri_v = 0,
+ .expect_tmsi = 0,
+ },
+ {
+ .tmsi = 0,
+ .nri_bitlen = 10,
+ .expect_get_nri = 0,
+ .set_nri_v = 0x7fff,
+ .expect_tmsi = 0x00ffc000
+ },
+ {
+ .tmsi = 0xffffffff,
+ .nri_bitlen = 10,
+ .expect_get_nri = 0x3ff,
+ .set_nri_v = 0,
+ .expect_tmsi = 0xff003fff
+ },
+ {
+ .tmsi = 0xffffffff,
+ .nri_bitlen = 10,
+ .expect_get_nri = 0x3ff,
+ .set_nri_v = 0x7fff,
+ .expect_tmsi = 0xffffffff
+ },
+ {
+ .tmsi = 0,
+ .nri_bitlen = 5,
+ .expect_get_nri = 0,
+ .set_nri_v = 0,
+ .expect_tmsi = 0,
+ },
+ {
+ .tmsi = 0,
+ .nri_bitlen = 5,
+ .expect_get_nri = 0,
+ .set_nri_v = 0x7fff,
+ .expect_tmsi = 0x00f80000
+ },
+ {
+ .tmsi = 0xffffffff,
+ .nri_bitlen = 5,
+ .expect_get_nri = 0x1f,
+ .set_nri_v = 0,
+ .expect_tmsi = 0xff07ffff
+ },
+ {
+ .tmsi = 0xffffffff,
+ .nri_bitlen = 5,
+ .expect_get_nri = 0x1f,
+ .set_nri_v = 0x7fff,
+ .expect_tmsi = 0xffffffff
+ },
+ {
+ .tmsi = 0x01234567,
+ .nri_bitlen = 8,
+ .expect_get_nri = 0x23,
+ .set_nri_v = 0x42,
+ .expect_tmsi = 0x01424567
+ },
+ {
+ .tmsi = 0x01234567,
+ .nri_bitlen = 15,
+ .expect_get_nri = 0x2345 >> 1,
+ .set_nri_v = 0x7fff,
+ .expect_tmsi = 0x01ffff67
+ },
+ {
+ .tmsi = 0x01234567,
+ .nri_bitlen = 16,
+ .expect_get_rc = -1,
+ .expect_get_nri = -1,
+ .set_nri_v = 0x7fff,
+ .expect_set_rc = -1,
+ .expect_tmsi = 0x01234567,
+ },
+ {
+ .tmsi = 0x01234567,
+ .nri_bitlen = 0,
+ .expect_get_rc = -1,
+ .expect_get_nri = -1,
+ .set_nri_v = 0x7fff,
+ .expect_set_rc = -1,
+ .expect_tmsi = 0x01234567,
+ },
+};
+
+void test_nri_v_get_set(void)
+{
+ struct nri_v_get_set_test *t;
+
+ for (t = nri_v_get_set_tests; t < &nri_v_get_set_tests[ARRAY_SIZE(nri_v_get_set_tests)]; t++) {
+ int16_t nri_v = 0;
+ uint32_t tmsi2;
+ int rc;
+
+ rc = osmo_tmsi_nri_v_get(&nri_v, t->tmsi, t->nri_bitlen);
+ printf("\nosmo_tmsi_nri_v_get(0x%08x, %u) -> nri_v=0x%x rc=%d\n", t->tmsi, t->nri_bitlen, nri_v, rc);
+ if (!rc) {
+ printf("........|NRI->..................\n");
+ bitdump(32, t->tmsi);
+ printf(" tmsi nri_bitlen=%u\n", t->nri_bitlen);
+ printf(" ");
+ bitdump(t->nri_bitlen, nri_v);
+ printf(" = 0x%x", nri_v);
+ }
+ if (nri_v == t->expect_get_nri && rc == t->expect_get_rc) {
+ printf(" ok\n");
+ } else {
+ printf(" ERROR: expected nri_v=0x%x rc=%d\n", t->expect_get_nri, t->expect_get_rc);
+ ok = false;
+ }
+
+ tmsi2 = t->tmsi;
+ rc = osmo_tmsi_nri_v_set(&tmsi2, t->set_nri_v, t->nri_bitlen);
+ printf("osmo_tmsi_nri_v_set(0x%08x, 0x%x, %u) -> tmsi=0x%08x rc=%d\n", t->tmsi, t->set_nri_v, t->nri_bitlen,
+ tmsi2, rc);
+ if (!rc) {
+ printf(" ");
+ bitdump(t->nri_bitlen, t->set_nri_v);
+ printf("\n");
+ bitdump(32, tmsi2);
+ }
+ if (tmsi2 == t->expect_tmsi && rc == t->expect_set_rc) {
+ printf(" ok\n");
+ } else {
+ printf(" ERROR: expected tmsi=0x%08x rc=%d\n", t->expect_tmsi, t->expect_set_rc);
+ ok = false;
+ }
+ }
+}
+
+struct nri_validate_tc {
+ int16_t nri;
+ uint8_t nri_bitlen;
+ int expect_rc;
+};
+
+struct nri_validate_tc nri_validate_tests[] = {
+ { .nri = INT16_MIN, .nri_bitlen = 10, .expect_rc = -1 },
+ { .nri = -23, .nri_bitlen = 10, .expect_rc = -1 },
+ { .nri = -1, .nri_bitlen = 10, .expect_rc = -1 },
+ { .nri = 0, .nri_bitlen = 10, .expect_rc = 0 },
+ { .nri = (1 << 10) - 1, .nri_bitlen = 10, .expect_rc = 0 },
+ { .nri = (1 << 10), .nri_bitlen = 10, .expect_rc = 1 },
+ { .nri = INT16_MAX, .nri_bitlen = 10, .expect_rc = 1 },
+
+ { .nri = INT16_MIN, .nri_bitlen = 5, .expect_rc = -1 },
+ { .nri = -23, .nri_bitlen = 5, .expect_rc = -1 },
+ { .nri = -1, .nri_bitlen = 5, .expect_rc = -1 },
+ { .nri = 0, .nri_bitlen = 5, .expect_rc = 0 },
+ { .nri = (1 << 5) - 1, .nri_bitlen = 5, .expect_rc = 0 },
+ { .nri = (1 << 5), .nri_bitlen = 5, .expect_rc = 1 },
+ { .nri = INT16_MAX, .nri_bitlen = 5, .expect_rc = 1 },
+
+ { .nri = INT16_MIN, .nri_bitlen = 1, .expect_rc = -1 },
+ { .nri = -23, .nri_bitlen = 1, .expect_rc = -1 },
+ { .nri = -1, .nri_bitlen = 1, .expect_rc = -1 },
+ { .nri = 0, .nri_bitlen = 1, .expect_rc = 0 },
+ { .nri = 1, .nri_bitlen = 1, .expect_rc = 0 },
+ { .nri = 2, .nri_bitlen = 1, .expect_rc = 1 },
+ { .nri = INT16_MAX, .nri_bitlen = 1, .expect_rc = 1 },
+
+ { .nri = INT16_MIN, .nri_bitlen = 0, .expect_rc = -1 },
+ { .nri = -23, .nri_bitlen = 0, .expect_rc = -1 },
+ { .nri = -1, .nri_bitlen = 0, .expect_rc = -1 },
+ { .nri = 0, .nri_bitlen = 0, .expect_rc = 1 },
+ { .nri = 1, .nri_bitlen = 0, .expect_rc = 1 },
+ { .nri = INT16_MAX, .nri_bitlen = 0, .expect_rc = 1 },
+};
+
+void test_nri_validate(void)
+{
+ struct nri_validate_tc *t;
+ printf("\n%s()\n", __func__);
+ for (t = nri_validate_tests; (t - nri_validate_tests) < ARRAY_SIZE(nri_validate_tests); t++) {
+ int rc = osmo_nri_v_validate(t->nri, t->nri_bitlen);
+ printf("osmo_nri_v_validate(%d, %u) = %d ", t->nri, t->nri_bitlen, rc);
+ if (rc == t->expect_rc) {
+ printf("ok\n");
+ } else {
+ printf("ERROR, expected rc = %d\n", t->expect_rc);
+ ok = false;
+ }
+ }
+}
+
+struct nri_range_validate_tc {
+ struct osmo_nri_range range;
+ uint8_t nri_bitlen;
+ int expect_rc;
+};
+
+struct nri_range_validate_tc nri_range_validate_tests[] = {
+ { .range = { .first = INT16_MIN, .last = INT16_MIN }, .nri_bitlen = 10, .expect_rc = -1 },
+ { .range = { .first = -23, .last = -23 }, .nri_bitlen = 10, .expect_rc = -1 },
+ { .range = { .first = -1, .last = -1 }, .nri_bitlen = 10, .expect_rc = -1 },
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 10, .expect_rc = 0 },
+ { .range = { .first = (1 << 10) - 1, .last = (1 << 10) - 1 }, .nri_bitlen = 10, .expect_rc = 0 },
+ { .range = { .first = (1 << 10), .last = (1 << 10) }, .nri_bitlen = 10, .expect_rc = 1 },
+ { .range = { .first = INT16_MAX, .last = INT16_MAX }, .nri_bitlen = 10, .expect_rc = 1 },
+
+ { .range = { .first = INT16_MIN, .last = INT16_MIN }, .nri_bitlen = 5, .expect_rc = -1 },
+ { .range = { .first = -23, .last = -23 }, .nri_bitlen = 5, .expect_rc = -1 },
+ { .range = { .first = -1, .last = -1 }, .nri_bitlen = 5, .expect_rc = -1 },
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 5, .expect_rc = 0 },
+ { .range = { .first = (1 << 5) - 1, .last = (1 << 5) - 1 }, .nri_bitlen = 5, .expect_rc = 0 },
+ { .range = { .first = (1 << 5), .last = (1 << 5) }, .nri_bitlen = 5, .expect_rc = 1 },
+ { .range = { .first = INT16_MAX, .last = INT16_MAX }, .nri_bitlen = 5, .expect_rc = 1 },
+
+ { .range = { .first = INT16_MIN, .last = INT16_MIN }, .nri_bitlen = 1, .expect_rc = -1 },
+ { .range = { .first = -23, .last = -23 }, .nri_bitlen = 1, .expect_rc = -1 },
+ { .range = { .first = -1, .last = -1 }, .nri_bitlen = 1, .expect_rc = -1 },
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 1, .expect_rc = 0 },
+ { .range = { .first = 1, .last = 1 }, .nri_bitlen = 1, .expect_rc = 0 },
+ { .range = { .first = 2, .last = 2 }, .nri_bitlen = 1, .expect_rc = 1 },
+ { .range = { .first = INT16_MAX, .last = INT16_MAX }, .nri_bitlen = 1, .expect_rc = 1 },
+
+ { .range = { .first = INT16_MIN, .last = INT16_MIN }, .nri_bitlen = 0, .expect_rc = -1 },
+ { .range = { .first = -23, .last = -23 }, .nri_bitlen = 0, .expect_rc = -1 },
+ { .range = { .first = -1, .last = -1 }, .nri_bitlen = 0, .expect_rc = -1 },
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 0, .expect_rc = 1 },
+ { .range = { .first = 1, .last = 1 }, .nri_bitlen = 0, .expect_rc = 1 },
+ { .range = { .first = INT16_MAX, .last = INT16_MAX }, .nri_bitlen = 0, .expect_rc = 1 },
+
+
+ { .range = { .first = 0, .last = INT16_MIN }, .nri_bitlen = 10, .expect_rc = -2 },
+ { .range = { .first = 0, .last = -23 }, .nri_bitlen = 10, .expect_rc = -2 },
+ { .range = { .first = 0, .last = -1 }, .nri_bitlen = 10, .expect_rc = -2 },
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 10, .expect_rc = 0 },
+ { .range = { .first = 0, .last = (1 << 10) - 1 }, .nri_bitlen = 10, .expect_rc = 0 },
+ { .range = { .first = 0, .last = (1 << 10) }, .nri_bitlen = 10, .expect_rc = 2 },
+ { .range = { .first = 0, .last = INT16_MAX }, .nri_bitlen = 10, .expect_rc = 2 },
+
+ { .range = { .first = 0, .last = INT16_MIN }, .nri_bitlen = 5, .expect_rc = -2 },
+ { .range = { .first = 0, .last = -23 }, .nri_bitlen = 5, .expect_rc = -2 },
+ { .range = { .first = 0, .last = -1 }, .nri_bitlen = 5, .expect_rc = -2 },
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 5, .expect_rc = 0 },
+ { .range = { .first = 0, .last = (1 << 5) - 1 }, .nri_bitlen = 5, .expect_rc = 0 },
+ { .range = { .first = 0, .last = (1 << 5) }, .nri_bitlen = 5, .expect_rc = 2 },
+ { .range = { .first = 0, .last = INT16_MAX }, .nri_bitlen = 5, .expect_rc = 2 },
+
+ { .range = { .first = 0, .last = INT16_MIN }, .nri_bitlen = 1, .expect_rc = -2 },
+ { .range = { .first = 0, .last = -23 }, .nri_bitlen = 1, .expect_rc = -2 },
+ { .range = { .first = 0, .last = -1 }, .nri_bitlen = 1, .expect_rc = -2 },
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 1, .expect_rc = 0 },
+ { .range = { .first = 0, .last = 1 }, .nri_bitlen = 1, .expect_rc = 0 },
+ { .range = { .first = 0, .last = 2 }, .nri_bitlen = 1, .expect_rc = 2 },
+ { .range = { .first = 0, .last = INT16_MAX }, .nri_bitlen = 1, .expect_rc = 2 },
+
+ { .range = { .first = 0, .last = INT16_MIN }, .nri_bitlen = 0, .expect_rc = 1 },
+ { .range = { .first = 0, .last = -23 }, .nri_bitlen = 0, .expect_rc = 1 },
+ { .range = { .first = 0, .last = -1 }, .nri_bitlen = 0, .expect_rc = 1 },
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 0, .expect_rc = 1 },
+ { .range = { .first = 0, .last = 1 }, .nri_bitlen = 0, .expect_rc = 1 },
+ { .range = { .first = 0, .last = INT16_MAX }, .nri_bitlen = 0, .expect_rc = 1 },
+
+
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 10, .expect_rc = 0 },
+ { .range = { .first = 1, .last = 0 }, .nri_bitlen = 10, .expect_rc = -3 },
+ { .range = { .first = (1 << 10) - 1, .last = (1 << 10) - 1 }, .nri_bitlen = 10, .expect_rc = 0 },
+ { .range = { .first = (1 << 10) - 1, .last = (1 << 10) - 2 }, .nri_bitlen = 10, .expect_rc = -3 },
+ { .range = { .first = (1 << 10) - 1, .last = 0 }, .nri_bitlen = 10, .expect_rc = -3 },
+
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 5, .expect_rc = 0 },
+ { .range = { .first = 1, .last = 0 }, .nri_bitlen = 5, .expect_rc = -3 },
+ { .range = { .first = (1 << 5) - 1, .last = (1 << 5) - 1 }, .nri_bitlen = 5, .expect_rc = 0 },
+ { .range = { .first = (1 << 5) - 1, .last = (1 << 5) - 2 }, .nri_bitlen = 5, .expect_rc = -3 },
+ { .range = { .first = (1 << 5) - 1, .last = 0 }, .nri_bitlen = 5, .expect_rc = -3 },
+
+ { .range = { .first = 0, .last = 0 }, .nri_bitlen = 1, .expect_rc = 0 },
+ { .range = { .first = 1, .last = 1 }, .nri_bitlen = 1, .expect_rc = 0 },
+ { .range = { .first = 1, .last = 0 }, .nri_bitlen = 1, .expect_rc = -3 },
+
+};
+
+void test_nri_range_validate(void)
+{
+ struct nri_range_validate_tc *t;
+ printf("\n%s()\n", __func__);
+ for (t = nri_range_validate_tests; (t - nri_range_validate_tests) < ARRAY_SIZE(nri_range_validate_tests); t++) {
+ int rc = osmo_nri_range_validate(&t->range, t->nri_bitlen);
+ printf("osmo_nri_range_validate({%d,%d}, %u) = %d ", t->range.first, t->range.last, t->nri_bitlen, rc);
+ if (rc == t->expect_rc) {
+ printf("ok\n");
+ } else {
+ printf("ERROR, expected rc = %d\n", t->expect_rc);
+ ok = false;
+ }
+ }
+}
+
+void dump_list(const struct osmo_nri_ranges *nri_ranges)
+{
+ struct osmo_nri_range *r;
+ printf("nri_ranges = {\n");
+ llist_for_each_entry(r, &nri_ranges->entries, entry) {
+ printf(" { %d, %d },\n", r->first, r->last);
+ if (osmo_nri_range_validate(r, 255)) {
+ ok = false;
+ printf(" ^^^^^ ERROR: invalid range\n");
+ }
+ }
+ printf("};\n");
+}
+
+void test_nri_list(void)
+{
+ struct osmo_nri_ranges *nri_ranges = osmo_nri_ranges_alloc(ctx);
+ printf("\n%s()\n", __func__);
+
+#define ADD(FIRST, LAST) do { \
+ struct osmo_nri_range r = { .first = FIRST, .last = LAST }; \
+ int rc; \
+ rc = osmo_nri_ranges_add(nri_ranges, &r); \
+ printf("osmo_nri_ranges_add(%d, %d) -> %d\n", r.first, r.last, rc); \
+ dump_list(nri_ranges); \
+ } while(0)
+
+#define DEL(FIRST, LAST) do { \
+ struct osmo_nri_range r = { .first = FIRST, .last = LAST }; \
+ int rc; \
+ rc = osmo_nri_ranges_del(nri_ranges, &r); \
+ printf("osmo_nri_ranges_del(%d, %d) -> %d\n", r.first, r.last, rc); \
+ dump_list(nri_ranges); \
+ } while(0)
+
+#define MATCHES(NRI, EXPECT_MATCH) do { \
+ bool matches = osmo_nri_v_matches_ranges(NRI, nri_ranges); \
+ printf("osmo_nri_v_matches_ranges(%d) -> %s\n", NRI, matches ? "true" : "false"); \
+ if (matches != EXPECT_MATCH) { \
+ ok = false; \
+ printf(" ^ ERROR: expected " #EXPECT_MATCH "\n"); \
+ } \
+ } while(0)
+
+#define OVERLAPS(FIRST, LAST, EXPECT_OVERLAP) do { \
+ struct osmo_nri_range r = { .first = FIRST, .last = LAST }; \
+ bool overlaps = osmo_nri_range_overlaps_ranges(&r, nri_ranges); \
+ printf("osmo_nri_range_overlaps_ranges(%d, %d) -> %s\n", r.first, r.last, overlaps ? "true" : "false"); \
+ if (overlaps != EXPECT_OVERLAP) { \
+ ok = false; \
+ printf(" ^ ERROR: expected " #EXPECT_OVERLAP "\n"); \
+ } \
+ } while(0)
+
+ dump_list(nri_ranges);
+ MATCHES(INT16_MIN, false);
+ MATCHES(-1, false);
+ MATCHES(0, false);
+ MATCHES(INT16_MAX, false);
+ MATCHES(100, false);
+ OVERLAPS(INT16_MIN, -1, false);
+ OVERLAPS(-100, 100, false);
+ OVERLAPS(10, 20, false);
+
+ ADD(100, 200);
+ MATCHES(INT16_MIN, false);
+ MATCHES(-1, false);
+ MATCHES(0, false);
+ MATCHES(INT16_MAX, false);
+ MATCHES(99, false);
+ MATCHES(100, true);
+ MATCHES(101, true);
+ MATCHES(199, true);
+ MATCHES(200, true);
+ MATCHES(201, false);
+ OVERLAPS(INT16_MIN, -1, false);
+ OVERLAPS(-100, 100, true);
+ OVERLAPS(10, 20, false);
+ OVERLAPS(10, 99, false);
+ OVERLAPS(10, 100, true);
+ OVERLAPS(10, 150, true);
+ OVERLAPS(99, 99, false);
+ OVERLAPS(100, 100, true);
+ OVERLAPS(150, 300, true);
+ OVERLAPS(200, 300, true);
+ OVERLAPS(201, 300, false);
+
+ printf("\ndel from start:\n");
+ DEL(0, 110);
+ DEL(111, 111);
+ DEL(112, 199);
+ MATCHES(INT16_MIN, false);
+ MATCHES(-1, false);
+ MATCHES(0, false);
+ MATCHES(INT16_MAX, false);
+ MATCHES(199, false);
+ MATCHES(200, true);
+ MATCHES(201, false);
+ OVERLAPS(INT16_MIN, -1, false);
+ OVERLAPS(-1000, 1000, true);
+ OVERLAPS(0, 199, false);
+ OVERLAPS(0, 200, true);
+ OVERLAPS(0, 201, true);
+ OVERLAPS(0, 1000, true);
+ OVERLAPS(199, 199, false);
+ OVERLAPS(200, 200, true);
+ OVERLAPS(201, 201, false);
+
+ printf("\ndel from end:\n");
+ ADD(100, 200);
+ DEL(190, INT16_MAX);
+ DEL(189, 189);
+ DEL(101, 188);
+ MATCHES(INT16_MIN, false);
+ MATCHES(-1, false);
+ MATCHES(0, false);
+ MATCHES(INT16_MAX, false);
+ MATCHES(99, false);
+ MATCHES(100, true);
+ MATCHES(101, false);
+
+ printf("\ndel from middle:\n");
+ ADD(100, 200);
+ DEL(150, 160);
+ DEL(110, 120);
+ DEL(130, 130);
+ DEL(180, 190);
+ MATCHES(INT16_MIN, false);
+ MATCHES(-1, false);
+ MATCHES(0, false);
+ MATCHES(INT16_MAX, false);
+ MATCHES(99, false);
+ MATCHES(100, true);
+ MATCHES(109, true);
+ MATCHES(110, false);
+ MATCHES(120, false);
+ MATCHES(121, true);
+ MATCHES(129, true);
+ MATCHES(130, false);
+ MATCHES(131, true);
+ MATCHES(148, true);
+ MATCHES(149, true);
+ MATCHES(150, false);
+ MATCHES(160, false);
+ MATCHES(161, true);
+ MATCHES(170, true);
+ MATCHES(179, true);
+ MATCHES(180, false);
+ MATCHES(185, false);
+ MATCHES(190, false);
+ MATCHES(191, true);
+ MATCHES(195, true);
+ MATCHES(200, true);
+ MATCHES(201, false);
+ MATCHES(1000, false);
+ OVERLAPS(110, 120, false);
+ OVERLAPS(110, 130, true);
+ OVERLAPS(100, 200, true);
+
+ printf("\ndel across whole chunks:\n");
+ DEL(115, 185);
+ DEL(105, 195);
+ DEL(0, 1000);
+
+ printf("\nadd to join chunks:\n");
+ ADD(0, 100);
+ DEL(11, 19);
+ DEL(23, 23);
+ DEL(30, 41);
+ ADD(23, 23);
+ ADD(11, 41);
+ MATCHES(0, true);
+ MATCHES(10, true);
+ MATCHES(11, true);
+ MATCHES(24, true);
+ MATCHES(41, true);
+ MATCHES(42, true);
+ MATCHES(100, true);
+ MATCHES(101, false);
+
+ printf("\nborder cases:\n");
+ ADD(0, 0);
+ ADD(INT16_MAX, INT16_MAX);
+ ADD(1, INT16_MAX - 1);
+ MATCHES(INT16_MIN, false);
+ MATCHES(-1, false);
+ MATCHES(0, true);
+ MATCHES(INT16_MAX, true);
+ DEL(0, 0);
+ DEL(INT16_MAX, INT16_MAX);
+ DEL(1, INT16_MAX - 1);
+
+ printf("\nrange errors:\n");
+ ADD(-1, -1);
+ ADD(-20, -10);
+ ADD(100, 1);
+ ADD(0, INT16_MAX);
+ DEL(-1, -1);
+ DEL(-20, -10);
+ DEL(100, 1);
+}
+
+void test_nri_limit_by_ranges(void)
+{
+ const uint8_t nri_bitlen = 8;
+ const int16_t expect_nri_vals[] = { 10, 20, 21, 30, 31, 32 };
+ int i;
+ struct osmo_nri_ranges *nri_ranges = osmo_nri_ranges_alloc(ctx);
+ printf("\n%s()\n", __func__);
+
+ ADD(10, 10);
+ ADD(20, 21);
+ ADD(30, 32);
+
+ for (i = 0; i < 19; i++) {
+ int rc;
+ int16_t nri_v;
+ int16_t expect_nri_v = expect_nri_vals[i % ARRAY_SIZE(expect_nri_vals)];
+
+ nri_v = i;
+ rc = osmo_nri_v_limit_by_ranges(&nri_v, nri_ranges, nri_bitlen);
+ printf("osmo_nri_v_limit_by_ranges(%d) -> nri_v=%d rc=%d", i, nri_v, rc);
+ if (!rc && nri_v == expect_nri_v) {
+ printf(" ok\n");
+ } else {
+ printf(" ERROR: expected nri_v=%d rc=0\n", expect_nri_v);
+ ok = false;
+ }
+ }
+ for (i = 0; i < 19; i++) {
+ int rc;
+ int16_t nri_v;
+ uint32_t tmsi, tmsi2;
+ int16_t expect_nri_v = expect_nri_vals[i % ARRAY_SIZE(expect_nri_vals)];
+
+ tmsi = 0;
+ osmo_tmsi_nri_v_set(&tmsi, i, nri_bitlen);
+ tmsi2 = tmsi;
+ rc = osmo_tmsi_nri_v_limit_by_ranges(&tmsi2, nri_ranges, nri_bitlen);
+ osmo_tmsi_nri_v_get(&nri_v, tmsi2, nri_bitlen);
+ printf("osmo_tmsi_nri_v_limit_by_ranges(0x%08x, %u) -> tmsi=0x%08x nri_v=%d rc=%d",
+ tmsi, nri_bitlen, tmsi2, nri_v, rc);
+ if (!rc && nri_v == expect_nri_v) {
+ printf(" ok\n");
+ } else {
+ printf(" ERROR: expected nri_v=%d rc=0\n", expect_nri_v);
+ ok = false;
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ ctx = talloc_named_const(NULL, 0, "nri_test");
+
+ test_nri_v_get_set();
+ test_nri_validate();
+ test_nri_range_validate();
+ test_nri_list();
+ test_nri_limit_by_ranges();
+
+ talloc_free(ctx);
+ if (!ok) {
+ printf("\nFAIL\n");
+ return -1;
+ }
+
+ printf("\npass\n");
+ return 0;
+}
+
diff --git a/tests/gsm23236/gsm23236_test.ok b/tests/gsm23236/gsm23236_test.ok
new file mode 100644
index 00000000..22630bfe
--- /dev/null
+++ b/tests/gsm23236/gsm23236_test.ok
@@ -0,0 +1,514 @@
+
+osmo_tmsi_nri_v_get(0x00000000, 10) -> nri_v=0x0 rc=0
+........|NRI->..................
+00000000000000000000000000000000 tmsi nri_bitlen=10
+ 0000000000 = 0x0 ok
+osmo_tmsi_nri_v_set(0x00000000, 0x0, 10) -> tmsi=0x00000000 rc=0
+ 0000000000
+00000000000000000000000000000000 ok
+
+osmo_tmsi_nri_v_get(0x00000000, 10) -> nri_v=0x0 rc=0
+........|NRI->..................
+00000000000000000000000000000000 tmsi nri_bitlen=10
+ 0000000000 = 0x0 ok
+osmo_tmsi_nri_v_set(0x00000000, 0x7fff, 10) -> tmsi=0x00ffc000 rc=0
+ 1111111111
+00000000111111111100000000000000 ok
+
+osmo_tmsi_nri_v_get(0xffffffff, 10) -> nri_v=0x3ff rc=0
+........|NRI->..................
+11111111111111111111111111111111 tmsi nri_bitlen=10
+ 1111111111 = 0x3ff ok
+osmo_tmsi_nri_v_set(0xffffffff, 0x0, 10) -> tmsi=0xff003fff rc=0
+ 0000000000
+11111111000000000011111111111111 ok
+
+osmo_tmsi_nri_v_get(0xffffffff, 10) -> nri_v=0x3ff rc=0
+........|NRI->..................
+11111111111111111111111111111111 tmsi nri_bitlen=10
+ 1111111111 = 0x3ff ok
+osmo_tmsi_nri_v_set(0xffffffff, 0x7fff, 10) -> tmsi=0xffffffff rc=0
+ 1111111111
+11111111111111111111111111111111 ok
+
+osmo_tmsi_nri_v_get(0x00000000, 5) -> nri_v=0x0 rc=0
+........|NRI->..................
+00000000000000000000000000000000 tmsi nri_bitlen=5
+ 00000 = 0x0 ok
+osmo_tmsi_nri_v_set(0x00000000, 0x0, 5) -> tmsi=0x00000000 rc=0
+ 00000
+00000000000000000000000000000000 ok
+
+osmo_tmsi_nri_v_get(0x00000000, 5) -> nri_v=0x0 rc=0
+........|NRI->..................
+00000000000000000000000000000000 tmsi nri_bitlen=5
+ 00000 = 0x0 ok
+osmo_tmsi_nri_v_set(0x00000000, 0x7fff, 5) -> tmsi=0x00f80000 rc=0
+ 11111
+00000000111110000000000000000000 ok
+
+osmo_tmsi_nri_v_get(0xffffffff, 5) -> nri_v=0x1f rc=0
+........|NRI->..................
+11111111111111111111111111111111 tmsi nri_bitlen=5
+ 11111 = 0x1f ok
+osmo_tmsi_nri_v_set(0xffffffff, 0x0, 5) -> tmsi=0xff07ffff rc=0
+ 00000
+11111111000001111111111111111111 ok
+
+osmo_tmsi_nri_v_get(0xffffffff, 5) -> nri_v=0x1f rc=0
+........|NRI->..................
+11111111111111111111111111111111 tmsi nri_bitlen=5
+ 11111 = 0x1f ok
+osmo_tmsi_nri_v_set(0xffffffff, 0x7fff, 5) -> tmsi=0xffffffff rc=0
+ 11111
+11111111111111111111111111111111 ok
+
+osmo_tmsi_nri_v_get(0x01234567, 8) -> nri_v=0x23 rc=0
+........|NRI->..................
+00000001001000110100010101100111 tmsi nri_bitlen=8
+ 00100011 = 0x23 ok
+osmo_tmsi_nri_v_set(0x01234567, 0x42, 8) -> tmsi=0x01424567 rc=0
+ 01000010
+00000001010000100100010101100111 ok
+
+osmo_tmsi_nri_v_get(0x01234567, 15) -> nri_v=0x11a2 rc=0
+........|NRI->..................
+00000001001000110100010101100111 tmsi nri_bitlen=15
+ 001000110100010 = 0x11a2 ok
+osmo_tmsi_nri_v_set(0x01234567, 0x7fff, 15) -> tmsi=0x01ffff67 rc=0
+ 111111111111111
+00000001111111111111111101100111 ok
+
+osmo_tmsi_nri_v_get(0x01234567, 16) -> nri_v=0xffffffff rc=-1
+ ok
+osmo_tmsi_nri_v_set(0x01234567, 0x7fff, 16) -> tmsi=0x01234567 rc=-1
+ ok
+
+osmo_tmsi_nri_v_get(0x01234567, 0) -> nri_v=0xffffffff rc=-1
+ ok
+osmo_tmsi_nri_v_set(0x01234567, 0x7fff, 0) -> tmsi=0x01234567 rc=-1
+ ok
+
+test_nri_validate()
+osmo_nri_v_validate(-32768, 10) = -1 ok
+osmo_nri_v_validate(-23, 10) = -1 ok
+osmo_nri_v_validate(-1, 10) = -1 ok
+osmo_nri_v_validate(0, 10) = 0 ok
+osmo_nri_v_validate(1023, 10) = 0 ok
+osmo_nri_v_validate(1024, 10) = 1 ok
+osmo_nri_v_validate(32767, 10) = 1 ok
+osmo_nri_v_validate(-32768, 5) = -1 ok
+osmo_nri_v_validate(-23, 5) = -1 ok
+osmo_nri_v_validate(-1, 5) = -1 ok
+osmo_nri_v_validate(0, 5) = 0 ok
+osmo_nri_v_validate(31, 5) = 0 ok
+osmo_nri_v_validate(32, 5) = 1 ok
+osmo_nri_v_validate(32767, 5) = 1 ok
+osmo_nri_v_validate(-32768, 1) = -1 ok
+osmo_nri_v_validate(-23, 1) = -1 ok
+osmo_nri_v_validate(-1, 1) = -1 ok
+osmo_nri_v_validate(0, 1) = 0 ok
+osmo_nri_v_validate(1, 1) = 0 ok
+osmo_nri_v_validate(2, 1) = 1 ok
+osmo_nri_v_validate(32767, 1) = 1 ok
+osmo_nri_v_validate(-32768, 0) = -1 ok
+osmo_nri_v_validate(-23, 0) = -1 ok
+osmo_nri_v_validate(-1, 0) = -1 ok
+osmo_nri_v_validate(0, 0) = 1 ok
+osmo_nri_v_validate(1, 0) = 1 ok
+osmo_nri_v_validate(32767, 0) = 1 ok
+
+test_nri_range_validate()
+osmo_nri_range_validate({-32768,-32768}, 10) = -1 ok
+osmo_nri_range_validate({-23,-23}, 10) = -1 ok
+osmo_nri_range_validate({-1,-1}, 10) = -1 ok
+osmo_nri_range_validate({0,0}, 10) = 0 ok
+osmo_nri_range_validate({1023,1023}, 10) = 0 ok
+osmo_nri_range_validate({1024,1024}, 10) = 1 ok
+osmo_nri_range_validate({32767,32767}, 10) = 1 ok
+osmo_nri_range_validate({-32768,-32768}, 5) = -1 ok
+osmo_nri_range_validate({-23,-23}, 5) = -1 ok
+osmo_nri_range_validate({-1,-1}, 5) = -1 ok
+osmo_nri_range_validate({0,0}, 5) = 0 ok
+osmo_nri_range_validate({31,31}, 5) = 0 ok
+osmo_nri_range_validate({32,32}, 5) = 1 ok
+osmo_nri_range_validate({32767,32767}, 5) = 1 ok
+osmo_nri_range_validate({-32768,-32768}, 1) = -1 ok
+osmo_nri_range_validate({-23,-23}, 1) = -1 ok
+osmo_nri_range_validate({-1,-1}, 1) = -1 ok
+osmo_nri_range_validate({0,0}, 1) = 0 ok
+osmo_nri_range_validate({1,1}, 1) = 0 ok
+osmo_nri_range_validate({2,2}, 1) = 1 ok
+osmo_nri_range_validate({32767,32767}, 1) = 1 ok
+osmo_nri_range_validate({-32768,-32768}, 0) = -1 ok
+osmo_nri_range_validate({-23,-23}, 0) = -1 ok
+osmo_nri_range_validate({-1,-1}, 0) = -1 ok
+osmo_nri_range_validate({0,0}, 0) = 1 ok
+osmo_nri_range_validate({1,1}, 0) = 1 ok
+osmo_nri_range_validate({32767,32767}, 0) = 1 ok
+osmo_nri_range_validate({0,-32768}, 10) = -2 ok
+osmo_nri_range_validate({0,-23}, 10) = -2 ok
+osmo_nri_range_validate({0,-1}, 10) = -2 ok
+osmo_nri_range_validate({0,0}, 10) = 0 ok
+osmo_nri_range_validate({0,1023}, 10) = 0 ok
+osmo_nri_range_validate({0,1024}, 10) = 2 ok
+osmo_nri_range_validate({0,32767}, 10) = 2 ok
+osmo_nri_range_validate({0,-32768}, 5) = -2 ok
+osmo_nri_range_validate({0,-23}, 5) = -2 ok
+osmo_nri_range_validate({0,-1}, 5) = -2 ok
+osmo_nri_range_validate({0,0}, 5) = 0 ok
+osmo_nri_range_validate({0,31}, 5) = 0 ok
+osmo_nri_range_validate({0,32}, 5) = 2 ok
+osmo_nri_range_validate({0,32767}, 5) = 2 ok
+osmo_nri_range_validate({0,-32768}, 1) = -2 ok
+osmo_nri_range_validate({0,-23}, 1) = -2 ok
+osmo_nri_range_validate({0,-1}, 1) = -2 ok
+osmo_nri_range_validate({0,0}, 1) = 0 ok
+osmo_nri_range_validate({0,1}, 1) = 0 ok
+osmo_nri_range_validate({0,2}, 1) = 2 ok
+osmo_nri_range_validate({0,32767}, 1) = 2 ok
+osmo_nri_range_validate({0,-32768}, 0) = 1 ok
+osmo_nri_range_validate({0,-23}, 0) = 1 ok
+osmo_nri_range_validate({0,-1}, 0) = 1 ok
+osmo_nri_range_validate({0,0}, 0) = 1 ok
+osmo_nri_range_validate({0,1}, 0) = 1 ok
+osmo_nri_range_validate({0,32767}, 0) = 1 ok
+osmo_nri_range_validate({0,0}, 10) = 0 ok
+osmo_nri_range_validate({1,0}, 10) = -3 ok
+osmo_nri_range_validate({1023,1023}, 10) = 0 ok
+osmo_nri_range_validate({1023,1022}, 10) = -3 ok
+osmo_nri_range_validate({1023,0}, 10) = -3 ok
+osmo_nri_range_validate({0,0}, 5) = 0 ok
+osmo_nri_range_validate({1,0}, 5) = -3 ok
+osmo_nri_range_validate({31,31}, 5) = 0 ok
+osmo_nri_range_validate({31,30}, 5) = -3 ok
+osmo_nri_range_validate({31,0}, 5) = -3 ok
+osmo_nri_range_validate({0,0}, 1) = 0 ok
+osmo_nri_range_validate({1,1}, 1) = 0 ok
+osmo_nri_range_validate({1,0}, 1) = -3 ok
+
+test_nri_list()
+nri_ranges = {
+};
+osmo_nri_v_matches_ranges(-32768) -> false
+osmo_nri_v_matches_ranges(-1) -> false
+osmo_nri_v_matches_ranges(0) -> false
+osmo_nri_v_matches_ranges(32767) -> false
+osmo_nri_v_matches_ranges(100) -> false
+osmo_nri_range_overlaps_ranges(-32768, -1) -> false
+osmo_nri_range_overlaps_ranges(-100, 100) -> false
+osmo_nri_range_overlaps_ranges(10, 20) -> false
+osmo_nri_ranges_add(100, 200) -> 0
+nri_ranges = {
+ { 100, 200 },
+};
+osmo_nri_v_matches_ranges(-32768) -> false
+osmo_nri_v_matches_ranges(-1) -> false
+osmo_nri_v_matches_ranges(0) -> false
+osmo_nri_v_matches_ranges(32767) -> false
+osmo_nri_v_matches_ranges(99) -> false
+osmo_nri_v_matches_ranges(100) -> true
+osmo_nri_v_matches_ranges(101) -> true
+osmo_nri_v_matches_ranges(199) -> true
+osmo_nri_v_matches_ranges(200) -> true
+osmo_nri_v_matches_ranges(201) -> false
+osmo_nri_range_overlaps_ranges(-32768, -1) -> false
+osmo_nri_range_overlaps_ranges(-100, 100) -> true
+osmo_nri_range_overlaps_ranges(10, 20) -> false
+osmo_nri_range_overlaps_ranges(10, 99) -> false
+osmo_nri_range_overlaps_ranges(10, 100) -> true
+osmo_nri_range_overlaps_ranges(10, 150) -> true
+osmo_nri_range_overlaps_ranges(99, 99) -> false
+osmo_nri_range_overlaps_ranges(100, 100) -> true
+osmo_nri_range_overlaps_ranges(150, 300) -> true
+osmo_nri_range_overlaps_ranges(200, 300) -> true
+osmo_nri_range_overlaps_ranges(201, 300) -> false
+
+del from start:
+osmo_nri_ranges_del(0, 110) -> 0
+nri_ranges = {
+ { 111, 200 },
+};
+osmo_nri_ranges_del(111, 111) -> 0
+nri_ranges = {
+ { 112, 200 },
+};
+osmo_nri_ranges_del(112, 199) -> 0
+nri_ranges = {
+ { 200, 200 },
+};
+osmo_nri_v_matches_ranges(-32768) -> false
+osmo_nri_v_matches_ranges(-1) -> false
+osmo_nri_v_matches_ranges(0) -> false
+osmo_nri_v_matches_ranges(32767) -> false
+osmo_nri_v_matches_ranges(199) -> false
+osmo_nri_v_matches_ranges(200) -> true
+osmo_nri_v_matches_ranges(201) -> false
+osmo_nri_range_overlaps_ranges(-32768, -1) -> false
+osmo_nri_range_overlaps_ranges(-1000, 1000) -> true
+osmo_nri_range_overlaps_ranges(0, 199) -> false
+osmo_nri_range_overlaps_ranges(0, 200) -> true
+osmo_nri_range_overlaps_ranges(0, 201) -> true
+osmo_nri_range_overlaps_ranges(0, 1000) -> true
+osmo_nri_range_overlaps_ranges(199, 199) -> false
+osmo_nri_range_overlaps_ranges(200, 200) -> true
+osmo_nri_range_overlaps_ranges(201, 201) -> false
+
+del from end:
+osmo_nri_ranges_add(100, 200) -> 0
+nri_ranges = {
+ { 100, 200 },
+};
+osmo_nri_ranges_del(190, 32767) -> 0
+nri_ranges = {
+ { 100, 189 },
+};
+osmo_nri_ranges_del(189, 189) -> 0
+nri_ranges = {
+ { 100, 188 },
+};
+osmo_nri_ranges_del(101, 188) -> 0
+nri_ranges = {
+ { 100, 100 },
+};
+osmo_nri_v_matches_ranges(-32768) -> false
+osmo_nri_v_matches_ranges(-1) -> false
+osmo_nri_v_matches_ranges(0) -> false
+osmo_nri_v_matches_ranges(32767) -> false
+osmo_nri_v_matches_ranges(99) -> false
+osmo_nri_v_matches_ranges(100) -> true
+osmo_nri_v_matches_ranges(101) -> false
+
+del from middle:
+osmo_nri_ranges_add(100, 200) -> 0
+nri_ranges = {
+ { 100, 200 },
+};
+osmo_nri_ranges_del(150, 160) -> 0
+nri_ranges = {
+ { 100, 149 },
+ { 161, 200 },
+};
+osmo_nri_ranges_del(110, 120) -> 0
+nri_ranges = {
+ { 100, 109 },
+ { 121, 149 },
+ { 161, 200 },
+};
+osmo_nri_ranges_del(130, 130) -> 0
+nri_ranges = {
+ { 100, 109 },
+ { 121, 129 },
+ { 131, 149 },
+ { 161, 200 },
+};
+osmo_nri_ranges_del(180, 190) -> 0
+nri_ranges = {
+ { 100, 109 },
+ { 121, 129 },
+ { 131, 149 },
+ { 161, 179 },
+ { 191, 200 },
+};
+osmo_nri_v_matches_ranges(-32768) -> false
+osmo_nri_v_matches_ranges(-1) -> false
+osmo_nri_v_matches_ranges(0) -> false
+osmo_nri_v_matches_ranges(32767) -> false
+osmo_nri_v_matches_ranges(99) -> false
+osmo_nri_v_matches_ranges(100) -> true
+osmo_nri_v_matches_ranges(109) -> true
+osmo_nri_v_matches_ranges(110) -> false
+osmo_nri_v_matches_ranges(120) -> false
+osmo_nri_v_matches_ranges(121) -> true
+osmo_nri_v_matches_ranges(129) -> true
+osmo_nri_v_matches_ranges(130) -> false
+osmo_nri_v_matches_ranges(131) -> true
+osmo_nri_v_matches_ranges(148) -> true
+osmo_nri_v_matches_ranges(149) -> true
+osmo_nri_v_matches_ranges(150) -> false
+osmo_nri_v_matches_ranges(160) -> false
+osmo_nri_v_matches_ranges(161) -> true
+osmo_nri_v_matches_ranges(170) -> true
+osmo_nri_v_matches_ranges(179) -> true
+osmo_nri_v_matches_ranges(180) -> false
+osmo_nri_v_matches_ranges(185) -> false
+osmo_nri_v_matches_ranges(190) -> false
+osmo_nri_v_matches_ranges(191) -> true
+osmo_nri_v_matches_ranges(195) -> true
+osmo_nri_v_matches_ranges(200) -> true
+osmo_nri_v_matches_ranges(201) -> false
+osmo_nri_v_matches_ranges(1000) -> false
+osmo_nri_range_overlaps_ranges(110, 120) -> false
+osmo_nri_range_overlaps_ranges(110, 130) -> true
+osmo_nri_range_overlaps_ranges(100, 200) -> true
+
+del across whole chunks:
+osmo_nri_ranges_del(115, 185) -> 0
+nri_ranges = {
+ { 100, 109 },
+ { 191, 200 },
+};
+osmo_nri_ranges_del(105, 195) -> 0
+nri_ranges = {
+ { 100, 104 },
+ { 196, 200 },
+};
+osmo_nri_ranges_del(0, 1000) -> 0
+nri_ranges = {
+};
+
+add to join chunks:
+osmo_nri_ranges_add(0, 100) -> 0
+nri_ranges = {
+ { 0, 100 },
+};
+osmo_nri_ranges_del(11, 19) -> 0
+nri_ranges = {
+ { 0, 10 },
+ { 20, 100 },
+};
+osmo_nri_ranges_del(23, 23) -> 0
+nri_ranges = {
+ { 0, 10 },
+ { 20, 22 },
+ { 24, 100 },
+};
+osmo_nri_ranges_del(30, 41) -> 0
+nri_ranges = {
+ { 0, 10 },
+ { 20, 22 },
+ { 24, 29 },
+ { 42, 100 },
+};
+osmo_nri_ranges_add(23, 23) -> 0
+nri_ranges = {
+ { 0, 10 },
+ { 20, 29 },
+ { 42, 100 },
+};
+osmo_nri_ranges_add(11, 41) -> 0
+nri_ranges = {
+ { 0, 100 },
+};
+osmo_nri_v_matches_ranges(0) -> true
+osmo_nri_v_matches_ranges(10) -> true
+osmo_nri_v_matches_ranges(11) -> true
+osmo_nri_v_matches_ranges(24) -> true
+osmo_nri_v_matches_ranges(41) -> true
+osmo_nri_v_matches_ranges(42) -> true
+osmo_nri_v_matches_ranges(100) -> true
+osmo_nri_v_matches_ranges(101) -> false
+
+border cases:
+osmo_nri_ranges_add(0, 0) -> 0
+nri_ranges = {
+ { 0, 100 },
+};
+osmo_nri_ranges_add(32767, 32767) -> 0
+nri_ranges = {
+ { 0, 100 },
+ { 32767, 32767 },
+};
+osmo_nri_ranges_add(1, 32766) -> 0
+nri_ranges = {
+ { 0, 32767 },
+};
+osmo_nri_v_matches_ranges(-32768) -> false
+osmo_nri_v_matches_ranges(-1) -> false
+osmo_nri_v_matches_ranges(0) -> true
+osmo_nri_v_matches_ranges(32767) -> true
+osmo_nri_ranges_del(0, 0) -> 0
+nri_ranges = {
+ { 1, 32767 },
+};
+osmo_nri_ranges_del(32767, 32767) -> 0
+nri_ranges = {
+ { 1, 32766 },
+};
+osmo_nri_ranges_del(1, 32766) -> 0
+nri_ranges = {
+};
+
+range errors:
+osmo_nri_ranges_add(-1, -1) -> -1
+nri_ranges = {
+};
+osmo_nri_ranges_add(-20, -10) -> -1
+nri_ranges = {
+};
+osmo_nri_ranges_add(100, 1) -> -1
+nri_ranges = {
+};
+osmo_nri_ranges_add(0, 32767) -> 0
+nri_ranges = {
+ { 0, 32767 },
+};
+osmo_nri_ranges_del(-1, -1) -> -1
+nri_ranges = {
+ { 0, 32767 },
+};
+osmo_nri_ranges_del(-20, -10) -> -1
+nri_ranges = {
+ { 0, 32767 },
+};
+osmo_nri_ranges_del(100, 1) -> -1
+nri_ranges = {
+ { 0, 32767 },
+};
+
+test_nri_limit_by_ranges()
+osmo_nri_ranges_add(10, 10) -> 0
+nri_ranges = {
+ { 10, 10 },
+};
+osmo_nri_ranges_add(20, 21) -> 0
+nri_ranges = {
+ { 10, 10 },
+ { 20, 21 },
+};
+osmo_nri_ranges_add(30, 32) -> 0
+nri_ranges = {
+ { 10, 10 },
+ { 20, 21 },
+ { 30, 32 },
+};
+osmo_nri_v_limit_by_ranges(0) -> nri_v=10 rc=0 ok
+osmo_nri_v_limit_by_ranges(1) -> nri_v=20 rc=0 ok
+osmo_nri_v_limit_by_ranges(2) -> nri_v=21 rc=0 ok
+osmo_nri_v_limit_by_ranges(3) -> nri_v=30 rc=0 ok
+osmo_nri_v_limit_by_ranges(4) -> nri_v=31 rc=0 ok
+osmo_nri_v_limit_by_ranges(5) -> nri_v=32 rc=0 ok
+osmo_nri_v_limit_by_ranges(6) -> nri_v=10 rc=0 ok
+osmo_nri_v_limit_by_ranges(7) -> nri_v=20 rc=0 ok
+osmo_nri_v_limit_by_ranges(8) -> nri_v=21 rc=0 ok
+osmo_nri_v_limit_by_ranges(9) -> nri_v=30 rc=0 ok
+osmo_nri_v_limit_by_ranges(10) -> nri_v=31 rc=0 ok
+osmo_nri_v_limit_by_ranges(11) -> nri_v=32 rc=0 ok
+osmo_nri_v_limit_by_ranges(12) -> nri_v=10 rc=0 ok
+osmo_nri_v_limit_by_ranges(13) -> nri_v=20 rc=0 ok
+osmo_nri_v_limit_by_ranges(14) -> nri_v=21 rc=0 ok
+osmo_nri_v_limit_by_ranges(15) -> nri_v=30 rc=0 ok
+osmo_nri_v_limit_by_ranges(16) -> nri_v=31 rc=0 ok
+osmo_nri_v_limit_by_ranges(17) -> nri_v=32 rc=0 ok
+osmo_nri_v_limit_by_ranges(18) -> nri_v=10 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00000000, 8) -> tmsi=0x000a0000 nri_v=10 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00010000, 8) -> tmsi=0x00140000 nri_v=20 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00020000, 8) -> tmsi=0x00150000 nri_v=21 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00030000, 8) -> tmsi=0x001e0000 nri_v=30 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00040000, 8) -> tmsi=0x001f0000 nri_v=31 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00050000, 8) -> tmsi=0x00200000 nri_v=32 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00060000, 8) -> tmsi=0x000a0000 nri_v=10 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00070000, 8) -> tmsi=0x00140000 nri_v=20 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00080000, 8) -> tmsi=0x00150000 nri_v=21 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00090000, 8) -> tmsi=0x001e0000 nri_v=30 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x000a0000, 8) -> tmsi=0x001f0000 nri_v=31 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x000b0000, 8) -> tmsi=0x00200000 nri_v=32 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x000c0000, 8) -> tmsi=0x000a0000 nri_v=10 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x000d0000, 8) -> tmsi=0x00140000 nri_v=20 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x000e0000, 8) -> tmsi=0x00150000 nri_v=21 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x000f0000, 8) -> tmsi=0x001e0000 nri_v=30 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00100000, 8) -> tmsi=0x001f0000 nri_v=31 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00110000, 8) -> tmsi=0x00200000 nri_v=32 rc=0 ok
+osmo_tmsi_nri_v_limit_by_ranges(0x00120000, 8) -> tmsi=0x000a0000 nri_v=10 rc=0 ok
+
+pass
diff --git a/tests/gsm29205/gsm29205_test.c b/tests/gsm29205/gsm29205_test.c
index 44c3453c..6598f894 100644
--- a/tests/gsm29205/gsm29205_test.c
+++ b/tests/gsm29205/gsm29205_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/gsm29205.h>
@@ -32,7 +28,7 @@
#include <string.h>
#include <errno.h>
-static void test_gcr()
+static void test_gcr(void)
{
static const uint8_t res[] = {
0x03, /* .net_len */
diff --git a/tests/gsm44021/frame_csd_test.c b/tests/gsm44021/frame_csd_test.c
new file mode 100644
index 00000000..98efdacb
--- /dev/null
+++ b/tests/gsm44021/frame_csd_test.c
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+#include <osmocom/gsm/gsm44021.h>
+
+
+static void fill_v110_frame(struct osmo_v110_decoded_frame *fr)
+{
+ unsigned int i;
+
+ memset(fr, 0, sizeof(*fr));
+
+ /* we abuse the fact that ubit_t is 8bit so we can actually
+ * store integer values to clearly identify which bit ends up where */
+
+ /* D1..D48: 101..148 */
+ for (i = 0; i < ARRAY_SIZE(fr->d_bits); i++)
+ fr->d_bits[i] = 101 + i;
+ /* E1..E7: 201..207 */
+ for (i = 0; i < ARRAY_SIZE(fr->e_bits); i++)
+ fr->e_bits[i] = 201 + i;
+ /* S1..S9: 211..219 */
+ for (i = 0; i < ARRAY_SIZE(fr->s_bits); i++)
+ fr->s_bits[i] = 211 + i;
+ /* X1..X2: 221..222 */
+ for (i = 0; i < ARRAY_SIZE(fr->x_bits); i++)
+ fr->x_bits[i] = 221 + i;
+}
+
+
+static void test_frame_enc_12k_6k(void)
+{
+ struct osmo_v110_decoded_frame fr;
+ ubit_t bits[60];
+
+ printf("Testing Frame Encoding for 12k/6k radio interface rate\n");
+
+ fill_v110_frame(&fr);
+
+ /* run encoder and dump to stdout */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_csd_12k_6k_encode_frame(bits, sizeof(bits), &fr);
+ osmo_csd_ubit_dump(stdout, bits, sizeof(bits));
+
+ /* run decoder on what we just encoded */
+ memset(&fr, 0, sizeof(fr));
+ osmo_csd_12k_6k_decode_frame(&fr, bits, sizeof(bits));
+
+ /* re-encode and dump again 'expout' will match it. */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_csd_12k_6k_encode_frame(bits, sizeof(bits), &fr);
+ osmo_csd_ubit_dump(stdout, bits, sizeof(bits));
+}
+
+static void test_frame_enc_3k6(void)
+{
+ struct osmo_v110_decoded_frame fr;
+ ubit_t bits[36];
+
+ printf("Testing Frame Encoding for 3.6k radio interface rate\n");
+
+ fill_v110_frame(&fr);
+ /* different D-bit numbering for 3k6, see TS 44.021 Section 8.1.4 */
+ for (unsigned int i = 0; i < ARRAY_SIZE(fr.d_bits); i++)
+ fr.d_bits[i] = 101 + i/2;
+
+ /* run encoder and dump to stdout */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_csd_3k6_encode_frame(bits, sizeof(bits), &fr);
+ osmo_csd_ubit_dump(stdout, bits, sizeof(bits));
+
+ /* run decoder on what we just encoded */
+ memset(&fr, 0, sizeof(fr));
+ osmo_csd_3k6_decode_frame(&fr, bits, sizeof(bits));
+
+ /* re-encode and dump again 'expout' will match it. */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_csd_3k6_encode_frame(bits, sizeof(bits), &fr);
+ osmo_csd_ubit_dump(stdout, bits, sizeof(bits));
+}
+
+
+int main(int argc, char **argv)
+{
+ test_frame_enc_12k_6k();
+ printf("\n");
+ test_frame_enc_3k6();
+}
+
diff --git a/tests/gsm44021/frame_csd_test.ok b/tests/gsm44021/frame_csd_test.ok
new file mode 100644
index 00000000..1a5d3f25
--- /dev/null
+++ b/tests/gsm44021/frame_csd_test.ok
@@ -0,0 +1,31 @@
+Testing Frame Encoding for 12k/6k radio interface rate
+101 102 103 104 105 106 211
+107 108 109 110 111 112 221
+113 114 115 116 117 118 213
+119 120 121 122 123 124 214
+204 205 206 207 125 126 127
+128 129 130 216 131 132 133
+134 135 136 222 137 138 139
+140 141 142 218 143 144 145
+146 147 148 219
+101 102 103 104 105 106 211
+107 108 109 110 111 112 221
+113 114 115 116 117 118 213
+119 120 121 122 123 124 214
+204 205 206 207 125 126 127
+128 129 130 216 131 132 133
+134 135 136 222 137 138 139
+140 141 142 218 143 144 145
+146 147 148 219
+
+Testing Frame Encoding for 3.6k radio interface rate
+101 102 103 211 104 105 106 221
+107 108 109 213 110 111 112 214
+204 205 206 207 113 114 115 216
+116 117 118 222 119 120 121 218
+122 123 124 219
+101 102 103 211 104 105 106 221
+107 108 109 213 110 111 112 214
+204 205 206 207 113 114 115 216
+116 117 118 222 119 120 121 218
+122 123 124 219
diff --git a/tests/gsm48/rest_octets_test.c b/tests/gsm48/rest_octets_test.c
new file mode 100644
index 00000000..6e11f941
--- /dev/null
+++ b/tests/gsm48/rest_octets_test.c
@@ -0,0 +1,116 @@
+/*
+ * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm48_rest_octets.h>
+
+struct si13_test {
+ const char *name;
+ const struct osmo_gsm48_si13_info si;
+ int enc_rc;
+ int dec_rc;
+ void (*post_dec_cb)(const struct si13_test *test, const struct osmo_gsm48_si13_info* dec);
+};
+
+void post_dec_cb_test_alpha(const struct si13_test *test, const struct osmo_gsm48_si13_info* dec)
+{
+ OSMO_ASSERT(test->si.pwr_ctrl_pars.alpha == dec->pwr_ctrl_pars.alpha);
+}
+
+static const struct si13_test test_si13_arr[] = {
+ {
+ .name = "test alpha",
+ .si = {
+ .cell_opts = {
+ .nmo = GPRS_NMO_II,
+ .t3168 = 2000,
+ .t3192 = 1500,
+ .drx_timer_max = 3,
+ .bs_cv_max = 15,
+ .ctrl_ack_type_use_block = true,
+ .ext_info_present = 0,
+ .ext_info = {
+ .egprs_supported = 1,
+ .use_egprs_p_ch_req = 1,
+ .bep_period = 5,
+ .pfc_supported = 0,
+ .dtm_supported = 0,
+ .bss_paging_coordination = 0,
+ .ccn_active = true,
+ },
+ },
+ .pwr_ctrl_pars = {
+ .alpha = 5,
+ .t_avg_w = 16,
+ .t_avg_t = 16,
+ .pc_meas_chan = 0,
+ .n_avg_i = 8,
+ },
+ .bcch_change_mark = 1,
+ .si_change_field = 0,
+ .rac = 0x03,
+ .spgc_ccch_sup = 0,
+ .net_ctrl_ord = 0,
+ .prio_acc_thr = 6,
+ },
+ .enc_rc = 20,
+ .dec_rc = 71,
+ .post_dec_cb = post_dec_cb_test_alpha,
+ },
+};
+
+static void test_si13(void)
+{
+ int i, rc;
+ uint8_t data[GSM_MACBLOCK_LEN];
+ struct osmo_gsm48_si13_info si13;
+
+ for (i = 0; i < ARRAY_SIZE(test_si13_arr); i++) {
+ memset(data, 0, sizeof(data));
+ rc = osmo_gsm48_rest_octets_si13_encode(data, &test_si13_arr[i].si);
+ if (rc >= 0) {
+ printf("si13_encode (%d): %s\n", rc, osmo_hexdump(data, rc));
+ } else {
+ printf("si13_encode failed (%d)\n", rc);
+ }
+ OSMO_ASSERT(rc == test_si13_arr[i].enc_rc);
+ if (rc <= 0)
+ continue;
+ memset(&si13, 0 , sizeof(si13));
+ rc = osmo_gsm48_rest_octets_si13_decode(&si13, data);
+ if (rc >= 0) {
+ printf("si13_decode (%d)\n", rc);
+ } else {
+ printf("si13_decode failed (%d)\n", rc);
+ }
+ OSMO_ASSERT(rc == test_si13_arr[i].dec_rc);
+ if (test_si13_arr[i].post_dec_cb) {
+ test_si13_arr[i].post_dec_cb(&test_si13_arr[i], &si13);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ test_si13();
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/gsm48/rest_octets_test.ok b/tests/gsm48/rest_octets_test.ok
new file mode 100644
index 00000000..54204f24
--- /dev/null
+++ b/tests/gsm48/rest_octets_test.ok
@@ -0,0 +1,2 @@
+si13_encode (20): 90 00 d8 5a 6f c9 e5 84 10 ab 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+si13_decode (71)
diff --git a/tests/gsup/gsup_test.c b/tests/gsup/gsup_test.c
index b84c88f0..fb3708ac 100644
--- a/tests/gsup/gsup_test.c
+++ b/tests/gsup/gsup_test.c
@@ -19,6 +19,7 @@
#define TEST_AN_APDU_IE 0x62, 0x05, 0x01, 0x42, 0x42, 0x42, 0x42
#define TEST_SOURCE_NAME_IE 0x60, 0x05, 'M', 'S', 'C', '-', 'A'
#define TEST_DESTINATION_NAME_IE 0x61, 0x05, 'M', 'S', 'C', '-', 'B'
+#define TEST_NUM_VEC_IE(x) 0x52, 1, x
static void test_gsup_messages_dec_enc(void)
{
@@ -32,6 +33,13 @@ static void test_gsup_messages_dec_enc(void)
TEST_CLASS_SUBSCR_IE
};
+ static const uint8_t send_auth_info_req10[] = {
+ 0x08,
+ TEST_IMSI_IE,
+ TEST_NUM_VEC_IE(10),
+ TEST_CLASS_SUBSCR_IE
+ };
+
static const uint8_t send_auth_info_err[] = {
0x09,
TEST_IMSI_IE,
@@ -446,32 +454,32 @@ static void test_gsup_messages_dec_enc(void)
0x51, /* GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS */
};
- static const uint8_t send_e_process_access_signalling_req[] = {
- 0x40, /* OSMO_GSUP_MSGT_E_PROCESS_ACCESS_SIGNALLING_REQUEST */
+ static const uint8_t send_e_send_end_signal_res[] = {
+ 0x3E, /* OSMO_GSUP_MSGT_E_SEND_END_SIGNAL_RESULT */
TEST_IMSI_IE,
- /* Session ID and state (continue) */
+ /* Session ID and state (end) */
0x30, 0x04, 0xde, 0xad, 0xbe, 0xef,
- 0x31, 0x01, 0x02,
+ 0x31, 0x01, 0x03,
TEST_CLASS_INTER_MSC_IE,
TEST_SOURCE_NAME_IE,
TEST_DESTINATION_NAME_IE,
- TEST_AN_APDU_IE, /* (Handover Detect) */
+ TEST_AN_APDU_IE, /* (Handover Complete) */
};
- static const uint8_t send_e_send_end_signal_res[] = {
- 0x3E, /* OSMO_GSUP_MSGT_E_SEND_END_SIGNAL_RESULT */
+ static const uint8_t send_e_process_access_signalling_req[] = {
+ 0x40, /* OSMO_GSUP_MSGT_E_PROCESS_ACCESS_SIGNALLING_REQUEST */
TEST_IMSI_IE,
- /* Session ID and state (end) */
+ /* Session ID and state (continue) */
0x30, 0x04, 0xde, 0xad, 0xbe, 0xef,
- 0x31, 0x01, 0x03,
+ 0x31, 0x01, 0x02,
TEST_CLASS_INTER_MSC_IE,
TEST_SOURCE_NAME_IE,
TEST_DESTINATION_NAME_IE,
- TEST_AN_APDU_IE, /* (Handover Complete) */
+ TEST_AN_APDU_IE, /* (Handover Detect) */
};
static const uint8_t send_e_forward_access_signalling_req [] = {
@@ -612,6 +620,8 @@ static void test_gsup_messages_dec_enc(void)
send_e_abort, sizeof(send_e_abort)},
{"E Routing Error",
send_e_routing_error, sizeof(send_e_routing_error)},
+ {"Send Authentication Info Request (10 Vectors)",
+ send_auth_info_req10, sizeof(send_auth_info_req10)},
};
printf("Test GSUP message decoding/encoding\n");
@@ -739,9 +749,10 @@ int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "gsup_test");
osmo_init_logging2(ctx, &info);
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_timestamp(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
test_gsup_messages_dec_enc();
diff --git a/tests/gsup/gsup_test.err b/tests/gsup/gsup_test.err
index 1da39649..73a0776b 100644
--- a/tests/gsup/gsup_test.err
+++ b/tests/gsup/gsup_test.err
@@ -115,6 +115,9 @@
generated message: 4e 01 08 21 43 65 87 09 21 43 f5 30 04 de ad be ef 31 01 03 0a 01 04 60 05 4d 53 43 2d 41 61 05 4d 53 43 2d 42
original message: 4e 01 08 21 43 65 87 09 21 43 f5 30 04 de ad be ef 31 01 03 0a 01 04 60 05 4d 53 43 2d 41 61 05 4d 53 43 2d 42
IMSI: 123456789012345
+ generated message: 08 01 08 21 43 65 87 09 21 43 f5 52 01 0a 0a 01 01
+ original message: 08 01 08 21 43 65 87 09 21 43 f5 52 01 0a 0a 01 01
+ IMSI: 123456789012345
message 0: tested 14 truncations, 13 parse failures
message 1: tested 14 truncations, 13 parse failures
message 2: tested 83 truncations, 81 parse failures
@@ -154,13 +157,14 @@
message 36: tested 37 truncations, 32 parse failures
message 37: tested 26 truncations, 22 parse failures
message 38: tested 37 truncations, 32 parse failures
+ message 39: tested 17 truncations, 15 parse failures
DLGSUP Stopping DLGSUP logging
message 0: tested 3584 modifications, 771 parse failures
message 1: tested 3584 modifications, 770 parse failures
message 2: tested 21248 modifications, 2577 parse failures
message 3: tested 2816 modifications, 510 parse failures
message 4: tested 3584 modifications, 770 parse failures
- message 5: tested 20736 modifications, 4025 parse failures
+ message 5: tested 20736 modifications, 4517 parse failures
message 6: tested 3584 modifications, 771 parse failures
message 7: tested 3584 modifications, 770 parse failures
message 8: tested 2816 modifications, 510 parse failures
@@ -194,3 +198,4 @@ DLGSUP Stopping DLGSUP logging
message 36: tested 9472 modifications, 1803 parse failures
message 37: tested 6656 modifications, 1546 parse failures
message 38: tested 9472 modifications, 1803 parse failures
+ message 39: tested 4352 modifications, 1030 parse failures
diff --git a/tests/gsup/gsup_test.ok b/tests/gsup/gsup_test.ok
index db8bc2f6..ef2aec67 100644
--- a/tests/gsup/gsup_test.ok
+++ b/tests/gsup/gsup_test.ok
@@ -77,4 +77,6 @@ Test GSUP message decoding/encoding
E Abort OK
Testing E Routing Error
E Routing Error OK
+ Testing Send Authentication Info Request (10 Vectors)
+ Send Authentication Info Request (10 Vectors) OK
Done.
diff --git a/tests/i460_mux/i460_mux_test.c b/tests/i460_mux/i460_mux_test.c
new file mode 100644
index 00000000..1be35e7d
--- /dev/null
+++ b/tests/i460_mux/i460_mux_test.c
@@ -0,0 +1,399 @@
+
+#include <osmocom/core/utils.h>
+
+#include <osmocom/isdn/i460_mux.h>
+
+static void bits_cb(struct osmo_i460_subchan *schan, void *user_data,
+ const ubit_t *bits, unsigned int num_bits)
+{
+ char *str = user_data;
+ printf("demux_bits_cb '%s': %s\n", str, osmo_ubit_dump(bits, num_bits));
+}
+
+
+const struct osmo_i460_schan_desc scd64 = {
+ .rate = OSMO_I460_RATE_64k,
+ .bit_offset = 0,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "64k",
+ },
+};
+
+const struct osmo_i460_schan_desc scd32_0 = {
+ .rate = OSMO_I460_RATE_32k,
+ .bit_offset = 0,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "32k_0",
+ },
+};
+const struct osmo_i460_schan_desc scd32_4 = {
+ .rate = OSMO_I460_RATE_32k,
+ .bit_offset = 4,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "32k_4",
+ },
+};
+
+const struct osmo_i460_schan_desc scd16_0 = {
+ .rate = OSMO_I460_RATE_16k,
+ .bit_offset = 0,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "16k_0",
+ },
+};
+const struct osmo_i460_schan_desc scd16_2 = {
+ .rate = OSMO_I460_RATE_16k,
+ .bit_offset = 2,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "16k_2",
+ },
+};
+const struct osmo_i460_schan_desc scd16_4 = {
+ .rate = OSMO_I460_RATE_16k,
+ .bit_offset = 4,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "16k_4",
+ },
+};
+const struct osmo_i460_schan_desc scd16_6 = {
+ .rate = OSMO_I460_RATE_16k,
+ .bit_offset = 6,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "16k_6",
+ },
+};
+
+const struct osmo_i460_schan_desc scd8_0 = {
+ .rate = OSMO_I460_RATE_8k,
+ .bit_offset = 0,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "8k_0",
+ },
+};
+const struct osmo_i460_schan_desc scd8_1 = {
+ .rate = OSMO_I460_RATE_8k,
+ .bit_offset = 1,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "8k_1",
+ },
+};
+const struct osmo_i460_schan_desc scd8_2 = {
+ .rate = OSMO_I460_RATE_8k,
+ .bit_offset = 2,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "8k_2",
+ },
+};
+const struct osmo_i460_schan_desc scd8_3 = {
+ .rate = OSMO_I460_RATE_8k,
+ .bit_offset = 3,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "8k_3",
+ },
+};
+const struct osmo_i460_schan_desc scd8_4 = {
+ .rate = OSMO_I460_RATE_8k,
+ .bit_offset = 4,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "8k_4",
+ },
+};
+const struct osmo_i460_schan_desc scd8_5 = {
+ .rate = OSMO_I460_RATE_8k,
+ .bit_offset = 5,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "8k_5",
+ },
+};
+const struct osmo_i460_schan_desc scd8_6 = {
+ .rate = OSMO_I460_RATE_8k,
+ .bit_offset = 6,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "8k_6",
+ },
+};
+const struct osmo_i460_schan_desc scd8_7 = {
+ .rate = OSMO_I460_RATE_8k,
+ .bit_offset = 7,
+ .demux = {
+ .num_bits = 40,
+ .out_cb_bits = bits_cb,
+ .out_cb_bytes = NULL,
+ .user_data = "8k_7",
+ },
+};
+
+static void test_no_subchan(void)
+{
+ struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+ /* Initialization */
+ printf("\n==> %s\n", __func__);
+ osmo_i460_ts_init(ts);
+
+ /* feed in some data; expect nothing to happen */
+ const uint8_t nothing[128] = { 0, };
+ osmo_i460_demux_in(ts, nothing, sizeof(nothing));
+
+ /* pull bytes out of mux (should be all 0xff) */
+ uint8_t buf[128];
+ osmo_i460_mux_out(ts, buf, sizeof(buf));
+ printf("out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+}
+
+static struct msgb *gen_alternating_bitmsg(unsigned int num_bits)
+{
+ struct msgb *msg = msgb_alloc(num_bits, "mux-in");
+ int i;
+ for (i = 0; i < num_bits; i++)
+ msgb_put_u8(msg, i & 1);
+ return msg;
+}
+
+static void test_64k_subchan(void)
+{
+ struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+ /* Initialization */
+ printf("\n==> %s\n", __func__);
+ osmo_i460_ts_init(ts);
+ osmo_i460_subchan_add(NULL, ts, &scd64);
+
+ /* demux */
+ uint8_t sequence[128];
+ int i;
+ for (i = 0; i < sizeof(sequence); i++)
+ sequence[i] = i;
+ osmo_i460_demux_in(ts, sequence, sizeof(sequence));
+
+ /* mux */
+ struct msgb *msg = gen_alternating_bitmsg(128);
+ osmo_i460_mux_enqueue(&ts->schan[0], msg);
+
+ uint8_t buf[16];
+ osmo_i460_mux_out(ts, buf, sizeof(buf));
+ printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+
+ osmo_i460_subchan_del(&ts->schan[0]);
+}
+
+static void test_32k_subchan(void)
+{
+ struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+ /* Initialization */
+ printf("\n==> %s\n", __func__);
+ osmo_i460_ts_init(ts);
+ osmo_i460_subchan_add(NULL, ts, &scd32_0);
+ osmo_i460_subchan_add(NULL, ts, &scd32_4);
+
+ /* demux */
+ uint8_t sequence[10];
+ int i;
+ for (i = 0; i < sizeof(sequence); i++)
+ sequence[i] = 0;
+ sequence[1] = 0xf0;
+ sequence[0] = 0x0f;
+ sequence[2] = 0xff;
+ osmo_i460_demux_in(ts, sequence, sizeof(sequence));
+
+ /* mux */
+
+ /* test with only a single channel active */
+ for (i = 0; i < 2; i++) {
+ struct msgb *msg = gen_alternating_bitmsg(128);
+ osmo_i460_mux_enqueue(&ts->schan[i], msg);
+ printf("%s-single-%u\n", __func__, i);
+
+ uint8_t buf[16];
+ int j;
+ for (j = 0; j < 3; j++) {
+ osmo_i460_mux_out(ts, buf, sizeof(buf));
+ printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ osmo_i460_subchan_del(&ts->schan[i]);
+}
+
+
+
+static void test_16k_subchan(void)
+{
+ struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+ /* Initialization */
+ printf("\n==> %s\n", __func__);
+ osmo_i460_ts_init(ts);
+ osmo_i460_subchan_add(NULL, ts, &scd16_0);
+ osmo_i460_subchan_add(NULL, ts, &scd16_2);
+ osmo_i460_subchan_add(NULL, ts, &scd16_4);
+ osmo_i460_subchan_add(NULL, ts, &scd16_6);
+
+ /* demux */
+ uint8_t sequence[20];
+ int i;
+ for (i = 0; i < sizeof(sequence); i++)
+ sequence[i] = 0;
+ sequence[0] = 0xC0;
+ sequence[1] = 0x30;
+ sequence[2] = 0x0c;
+ sequence[3] = 0x03;
+ sequence[4] = 0xff;
+ osmo_i460_demux_in(ts, sequence, sizeof(sequence));
+
+ /* mux */
+
+ /* test with only a single channel active */
+ for (i = 0; i < 4; i++) {
+ struct msgb *msg = gen_alternating_bitmsg(128);
+ osmo_i460_mux_enqueue(&ts->schan[i], msg);
+ printf("%s-single-%u\n", __func__, i);
+
+ uint8_t buf[16];
+ int j;
+ for (j = 0; j < 5; j++) {
+ osmo_i460_mux_out(ts, buf, sizeof(buf));
+ printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ osmo_i460_subchan_del(&ts->schan[i]);
+}
+
+
+static void test_8k_subchan(void)
+{
+ struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+ /* Initialization */
+ printf("\n==> %s\n", __func__);
+ osmo_i460_ts_init(ts);
+ osmo_i460_subchan_add(NULL, ts, &scd8_0);
+ osmo_i460_subchan_add(NULL, ts, &scd8_1);
+ osmo_i460_subchan_add(NULL, ts, &scd8_2);
+ osmo_i460_subchan_add(NULL, ts, &scd8_3);
+ osmo_i460_subchan_add(NULL, ts, &scd8_4);
+ osmo_i460_subchan_add(NULL, ts, &scd8_5);
+ osmo_i460_subchan_add(NULL, ts, &scd8_6);
+ osmo_i460_subchan_add(NULL, ts, &scd8_7);
+
+ /* demux */
+ uint8_t sequence[40];
+ int i;
+ for (i = 0; i < sizeof(sequence); i++)
+ sequence[i] = 0;
+ i = 0;
+ sequence[i++] = 0x80;
+ sequence[i++] = 0x40;
+ sequence[i++] = 0x20;
+ sequence[i++] = 0x10;
+ sequence[i++] = 0xf0;
+ sequence[i++] = 0x08;
+ sequence[i++] = 0x04;
+ sequence[i++] = 0x02;
+ sequence[i++] = 0x01;
+ sequence[i++] = 0x0f;
+ sequence[i++] = 0xff;
+ osmo_i460_demux_in(ts, sequence, sizeof(sequence));
+
+ /* mux */
+
+ /* test with only a single channel active */
+ for (i = 0; i < 8; i++) {
+ struct msgb *msg = gen_alternating_bitmsg(64);
+ osmo_i460_mux_enqueue(&ts->schan[i], msg);
+ printf("%s-single-%u\n", __func__, i);
+
+ uint8_t buf[16];
+ int j;
+ for (j = 0; j < 5; j++) {
+ osmo_i460_mux_out(ts, buf, sizeof(buf));
+ printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+ }
+ }
+
+ for (i = 0; i < 8; i++)
+ osmo_i460_subchan_del(&ts->schan[i]);
+}
+
+/* activate only one sub-channel; expect unused bits to be '1' */
+static void test_unused_subchan(void)
+{
+ struct osmo_i460_timeslot _ts, *ts = &_ts;
+
+ /* Initialization */
+ printf("\n==> %s\n", __func__);
+ osmo_i460_ts_init(ts);
+ osmo_i460_subchan_add(NULL, ts, &scd16_0);
+
+ /* mux */
+ struct msgb *msg = gen_alternating_bitmsg(128);
+ memset(msgb_data(msg), 0, msgb_length(msg));
+ osmo_i460_mux_enqueue(&ts->schan[0], msg);
+ printf("%s-single\n", __func__);
+
+ uint8_t buf[16];
+ int j;
+ for (j = 0; j < 5; j++) {
+ osmo_i460_mux_out(ts, buf, sizeof(buf));
+ printf("mux_out: %s\n", osmo_hexdump(buf, sizeof(buf)));
+ }
+
+ osmo_i460_subchan_del(&ts->schan[0]);
+}
+
+int main(int argc, char **argv)
+{
+ test_no_subchan();
+ test_64k_subchan();
+ test_32k_subchan();
+ test_16k_subchan();
+ test_8k_subchan();
+ test_unused_subchan();
+ return 0;
+}
diff --git a/tests/i460_mux/i460_mux_test.ok b/tests/i460_mux/i460_mux_test.ok
new file mode 100644
index 00000000..79c55ed0
--- /dev/null
+++ b/tests/i460_mux/i460_mux_test.ok
@@ -0,0 +1,115 @@
+
+==> test_no_subchan
+out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+
+==> test_64k_subchan
+demux_bits_cb '64k': 0000000000000001000000100000001100000100000001010000011000000111000010000000100100001010000010110000110000001101000011100000111100010000000100010001001000010011000101000001010100010110000101110001100000011001000110100001101100011100000111010001111000011111001000000010000100100010001000110010010000100101001001100010011100101000001010010010101000101011001011000010110100101110001011110011000000110001001100100011001100110100001101010011011000110111001110000011100100111010001110110011110000111101001111100011111101000000010000010100001001000011010001000100010101000110010001110100100001001001010010100100101101001100010011010100111001001111010100000101000101010010010100110101010001010101010101100101011101011000010110010101101001011011010111000101110101011110010111110110000001100001011000100110001101100100011001010110011001100111011010000110100101101010011010110110110001101101011011100110111101110000011100010111001001110011011101000111010101110110011101110111100001111001011110100111101101111100011111010111111001111111
+mux_out: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55
+
+==> test_32k_subchan
+demux_bits_cb '32k_0': 0000111111110000000000000000000000000000
+demux_bits_cb '32k_4': 1111000011110000000000000000000000000000
+test_32k_subchan-single-0
+mux_out: 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f
+mux_out: 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_32k_subchan-single-1
+mux_out: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
+mux_out: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+
+==> test_16k_subchan
+demux_bits_cb '16k_0': 1100000011000000000000000000000000000000
+demux_bits_cb '16k_2': 0011000011000000000000000000000000000000
+demux_bits_cb '16k_4': 0000110011000000000000000000000000000000
+demux_bits_cb '16k_6': 0000001111000000000000000000000000000000
+test_16k_subchan-single-0
+mux_out: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
+mux_out: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
+mux_out: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
+mux_out: 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f 7f
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_16k_subchan-single-1
+mux_out: df df df df df df df df df df df df df df df df
+mux_out: df df df df df df df df df df df df df df df df
+mux_out: df df df df df df df df df df df df df df df df
+mux_out: df df df df df df df df df df df df df df df df
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_16k_subchan-single-2
+mux_out: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
+mux_out: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
+mux_out: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
+mux_out: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_16k_subchan-single-3
+mux_out: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
+mux_out: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
+mux_out: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
+mux_out: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+
+==> test_8k_subchan
+demux_bits_cb '8k_0': 1000100000100000000000000000000000000000
+demux_bits_cb '8k_1': 0100100000100000000000000000000000000000
+demux_bits_cb '8k_2': 0010100000100000000000000000000000000000
+demux_bits_cb '8k_3': 0001100000100000000000000000000000000000
+demux_bits_cb '8k_4': 0000010001100000000000000000000000000000
+demux_bits_cb '8k_5': 0000001001100000000000000000000000000000
+demux_bits_cb '8k_6': 0000000101100000000000000000000000000000
+demux_bits_cb '8k_7': 0000000011100000000000000000000000000000
+test_8k_subchan-single-0
+mux_out: 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff
+mux_out: 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff
+mux_out: 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff
+mux_out: 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff 7f ff
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_8k_subchan-single-1
+mux_out: bf ff bf ff bf ff bf ff bf ff bf ff bf ff bf ff
+mux_out: bf ff bf ff bf ff bf ff bf ff bf ff bf ff bf ff
+mux_out: bf ff bf ff bf ff bf ff bf ff bf ff bf ff bf ff
+mux_out: bf ff bf ff bf ff bf ff bf ff bf ff bf ff bf ff
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_8k_subchan-single-2
+mux_out: df ff df ff df ff df ff df ff df ff df ff df ff
+mux_out: df ff df ff df ff df ff df ff df ff df ff df ff
+mux_out: df ff df ff df ff df ff df ff df ff df ff df ff
+mux_out: df ff df ff df ff df ff df ff df ff df ff df ff
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_8k_subchan-single-3
+mux_out: ef ff ef ff ef ff ef ff ef ff ef ff ef ff ef ff
+mux_out: ef ff ef ff ef ff ef ff ef ff ef ff ef ff ef ff
+mux_out: ef ff ef ff ef ff ef ff ef ff ef ff ef ff ef ff
+mux_out: ef ff ef ff ef ff ef ff ef ff ef ff ef ff ef ff
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_8k_subchan-single-4
+mux_out: f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff
+mux_out: f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff
+mux_out: f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff
+mux_out: f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff f7 ff
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_8k_subchan-single-5
+mux_out: fb ff fb ff fb ff fb ff fb ff fb ff fb ff fb ff
+mux_out: fb ff fb ff fb ff fb ff fb ff fb ff fb ff fb ff
+mux_out: fb ff fb ff fb ff fb ff fb ff fb ff fb ff fb ff
+mux_out: fb ff fb ff fb ff fb ff fb ff fb ff fb ff fb ff
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_8k_subchan-single-6
+mux_out: fd ff fd ff fd ff fd ff fd ff fd ff fd ff fd ff
+mux_out: fd ff fd ff fd ff fd ff fd ff fd ff fd ff fd ff
+mux_out: fd ff fd ff fd ff fd ff fd ff fd ff fd ff fd ff
+mux_out: fd ff fd ff fd ff fd ff fd ff fd ff fd ff fd ff
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+test_8k_subchan-single-7
+mux_out: fe ff fe ff fe ff fe ff fe ff fe ff fe ff fe ff
+mux_out: fe ff fe ff fe ff fe ff fe ff fe ff fe ff fe ff
+mux_out: fe ff fe ff fe ff fe ff fe ff fe ff fe ff fe ff
+mux_out: fe ff fe ff fe ff fe ff fe ff fe ff fe ff fe ff
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
+
+==> test_unused_subchan
+test_unused_subchan-single
+mux_out: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f
+mux_out: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f
+mux_out: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f
+mux_out: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f
+mux_out: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
diff --git a/tests/it_q/it_q_test.c b/tests/it_q/it_q_test.c
new file mode 100644
index 00000000..6025e392
--- /dev/null
+++ b/tests/it_q/it_q_test.c
@@ -0,0 +1,159 @@
+#include <stdio.h>
+#include <errno.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/it_q.h>
+
+struct it_q_test1 {
+ struct llist_head list;
+ int *foo;
+};
+
+struct it_q_test2 {
+ int foo;
+ struct llist_head list;
+};
+
+#define ENTER_TC printf("\n== Entering test case %s\n", __func__)
+
+static void tc_alloc(void)
+{
+ struct osmo_it_q *q1, *q2;
+
+ ENTER_TC;
+
+ printf("allocating q1\n");
+ q1 = osmo_it_q_alloc(OTC_GLOBAL, "q1", 3, NULL, NULL);
+ OSMO_ASSERT(q1);
+
+ /* ensure that no duplicate allocation for the */
+ printf("attempting duplicate allocation of qa\n");
+ q2 = osmo_it_q_alloc(OTC_GLOBAL, "q1", 3, NULL, NULL);
+ OSMO_ASSERT(!q2);
+
+ /* ensure that same name can be re-created after destroying old one */
+ osmo_it_q_destroy(q1);
+ printf("re-allocating q1\n");
+ q1 = osmo_it_q_alloc(OTC_GLOBAL, "q1", 3, NULL, NULL);
+ OSMO_ASSERT(q1);
+
+ osmo_it_q_destroy(q1);
+}
+
+static void tc_queue_length(void)
+{
+ struct osmo_it_q *q1;
+ unsigned int qlen = 3;
+ struct it_q_test1 *item;
+ int i, rc;
+
+ ENTER_TC;
+
+ printf("allocating q1\n");
+ q1 = osmo_it_q_alloc(OTC_GLOBAL, "q1", qlen, NULL, NULL);
+ OSMO_ASSERT(q1);
+
+ printf("adding queue entries up to the limit\n");
+ for (i = 0; i < qlen; i++) {
+ item = talloc_zero(OTC_GLOBAL, struct it_q_test1);
+ rc = osmo_it_q_enqueue(q1, item, list);
+ OSMO_ASSERT(rc == 0);
+ }
+ printf("attempting to add more than the limit\n");
+ item = talloc_zero(OTC_GLOBAL, struct it_q_test1);
+ rc = osmo_it_q_enqueue(q1, item, list);
+ OSMO_ASSERT(rc == -ENOSPC);
+
+ osmo_it_q_destroy(q1);
+}
+
+static void tc_enqueue_dequeue(void)
+{
+ const unsigned int qlen = 12;
+ struct it_q_test1 *item;
+ struct osmo_it_q *q1;
+ int rc;
+
+ ENTER_TC;
+
+ printf("allocating q1\n");
+ q1 = osmo_it_q_alloc(OTC_GLOBAL, "q1", 12, NULL, NULL);
+ OSMO_ASSERT(q1);
+
+ printf("try dequeueing from an empty queue\n");
+ osmo_it_q_dequeue(q1, &item, list);
+ OSMO_ASSERT(item == NULL);
+
+ printf("adding queue entries up to the limit\n");
+ for (unsigned int i = 0; i < qlen; i++) {
+ item = talloc_zero(OTC_GLOBAL, struct it_q_test1);
+ rc = osmo_it_q_enqueue(q1, item, list);
+ OSMO_ASSERT(rc == 0);
+ }
+
+ printf("removing queue entries up to the limit\n");
+ for (unsigned int i = 0; i < qlen; i++) {
+ osmo_it_q_dequeue(q1, &item, list);
+ OSMO_ASSERT(item != NULL);
+ talloc_free(item);
+ }
+
+ printf("try dequeueing from an empty queue\n");
+ osmo_it_q_dequeue(q1, &item, list);
+ OSMO_ASSERT(item == NULL);
+
+ osmo_it_q_destroy(q1);
+}
+
+static int g_read_cb_count;
+
+static void q_read_cb(struct osmo_it_q *q, struct llist_head *item)
+{
+ struct it_q_test1 *it = container_of(item, struct it_q_test1, list);
+ *it->foo += 1;
+ talloc_free(item);
+}
+
+static void tc_eventfd(void)
+{
+ struct osmo_it_q *q1;
+ unsigned int qlen = 30;
+ struct it_q_test1 *item;
+ int i, rc;
+
+ ENTER_TC;
+
+ printf("allocating q1\n");
+ q1 = osmo_it_q_alloc(OTC_GLOBAL, "q1", qlen, q_read_cb, NULL);
+ OSMO_ASSERT(q1);
+ osmo_fd_register(&q1->event_ofd);
+
+ /* ensure read-cb isn't called unless we enqueue something */
+ osmo_select_main(1);
+ OSMO_ASSERT(g_read_cb_count == 0);
+
+ /* ensure read-cb is called for each enqueued msg once */
+ printf("adding %u queue entries up to the limit\n", qlen);
+ for (i = 0; i < qlen; i++) {
+ item = talloc_zero(OTC_GLOBAL, struct it_q_test1);
+ item->foo = &g_read_cb_count;
+ rc = osmo_it_q_enqueue(q1, item, list);
+ OSMO_ASSERT(rc == 0);
+ }
+
+ osmo_select_main(1);
+ printf("%u entries were dequeued\n", qlen);
+ OSMO_ASSERT(g_read_cb_count == qlen);
+
+ osmo_it_q_destroy(q1);
+}
+
+int main(int argc, char **argv)
+{
+ tc_alloc();
+ tc_queue_length();
+ tc_enqueue_dequeue();
+ tc_eventfd();
+ return 0;
+}
diff --git a/tests/it_q/it_q_test.ok b/tests/it_q/it_q_test.ok
new file mode 100644
index 00000000..e89b78d1
--- /dev/null
+++ b/tests/it_q/it_q_test.ok
@@ -0,0 +1,22 @@
+
+== Entering test case tc_alloc
+allocating q1
+attempting duplicate allocation of qa
+re-allocating q1
+
+== Entering test case tc_queue_length
+allocating q1
+adding queue entries up to the limit
+attempting to add more than the limit
+
+== Entering test case tc_enqueue_dequeue
+allocating q1
+try dequeueing from an empty queue
+adding queue entries up to the limit
+removing queue entries up to the limit
+try dequeueing from an empty queue
+
+== Entering test case tc_eventfd
+allocating q1
+adding 30 queue entries up to the limit
+30 entries were dequeued
diff --git a/tests/iuup/iuup_test.c b/tests/iuup/iuup_test.c
new file mode 100644
index 00000000..7798aa25
--- /dev/null
+++ b/tests/iuup/iuup_test.c
@@ -0,0 +1,767 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/select.h>
+
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gsm/iuup.h>
+
+static void *iuup_test_ctx;
+
+static struct osmo_iuup_rnl_config def_configure_req = {
+ .transparent = false,
+ .active = true,
+ .supported_versions_mask = 0x0001,
+ .num_rfci = 3,
+ .num_subflows = 3,
+ .IPTIs_present = true,
+ .rfci = {
+ {.used = 1, .id = 0, .IPTI = 1, .subflow_sizes = {81, 103, 60} },
+ {.used = 1, .id = 1, .IPTI = 7, .subflow_sizes = {39, 0, 0} },
+ {.used = 1, .id = 2, .IPTI = 1, .subflow_sizes = {0, 0, 0} },
+ },
+ /* .delivery_err_sdu = All set to 0 (YES) by default, */
+ .t_init = { .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT },
+ .t_ta = { .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT },
+ .t_rc = { .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT },
+};
+
+/* Frame 33, "Initialization", OS#4744 3g_call_23112021.pcapng
+IuUP
+ 1110 .... = PDU Type: Control Procedure (14)
+ .... 00.. = Ack/Nack: Procedure (0)
+ .... ..00 = Frame Number: 0
+ 0000 .... = Mode Version: 0x0
+ .... 0000 = Procedure: Initialization (0)
+ 1101 11.. = Header CRC: 0x37 [correct]
+ .... ..11 1001 1001 = Payload CRC: 0x399
+ 000. .... = Spare: 0x0
+ ...1 .... = TI: IPTIs present in frame (1)
+ .... 011. = Subflows: 3
+ .... ...0 = Chain Indicator: this frame is the last frame for the procedure (0)
+ RFCI 0 Initialization
+ 0... .... = RFCI 0 LRI: Not last RFCI (0x0)
+ .0.. .... = RFCI 0 LI: one octet used (0x0)
+ ..00 0000 = RFCI 0: 0
+ RFCI 0 Flow 0 Len: 81
+ RFCI 0 Flow 1 Len: 103
+ RFCI 0 Flow 2 Len: 60
+ RFCI 1 Initialization
+ 0... .... = RFCI 1 LRI: Not last RFCI (0x0)
+ .0.. .... = RFCI 1 LI: one octet used (0x0)
+ ..00 0001 = RFCI 1: 1
+ RFCI 1 Flow 0 Len: 39
+ RFCI 1 Flow 1 Len: 0
+ RFCI 1 Flow 2 Len: 0
+ RFCI 2 Initialization
+ 1... .... = RFCI 2 LRI: Last RFCI in current frame (0x1)
+ .0.. .... = RFCI 2 LI: one octet used (0x0)
+ ..00 0010 = RFCI 2: 2
+ RFCI 2 Flow 0 Len: 0
+ RFCI 2 Flow 1 Len: 0
+ RFCI 2 Flow 2 Len: 0
+ IPTIs
+ 0001 .... = RFCI 0 IPTI: 0x1
+ .... 0111 = RFCI 1 IPTI: 0x7
+ 0001 .... = RFCI 2 IPTI: 0x1
+ Iu UP Mode Versions Supported: 0x0001
+ 0... .... .... .... = Version 16: not supported (0x0)
+ .0.. .... .... .... = Version 15: not supported (0x0)
+ ..0. .... .... .... = Version 14: not supported (0x0)
+ ...0 .... .... .... = Version 13: not supported (0x0)
+ .... 0... .... .... = Version 12: not supported (0x0)
+ .... .0.. .... .... = Version 11: not supported (0x0)
+ .... ..0. .... .... = Version 10: not supported (0x0)
+ .... ...0 .... .... = Version 9: not supported (0x0)
+ .... .... 0... .... = Version 8: not supported (0x0)
+ .... .... .0.. .... = Version 7: not supported (0x0)
+ .... .... ..0. .... = Version 6: not supported (0x0)
+ .... .... ...0 .... = Version 5: not supported (0x0)
+ .... .... .... 0... = Version 4: not supported (0x0)
+ .... .... .... .0.. = Version 3: not supported (0x0)
+ .... .... .... ..0. = Version 2: not supported (0x0)
+ .... .... .... ...1 = Version 1: supported (0x1)
+ 0000 .... = RFCI Data Pdu Type: PDU type 0 (0x0)
+*/
+static const uint8_t iuup_initialization[] = {
+ 0xe0, 0x00, 0xdf, 0x99, 0x16, 0x00, 0x51, 0x67, 0x3c, 0x01, 0x27, 0x00,
+ 0x00, 0x82, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x00
+};
+
+/* Frame 87, "Data RFCI=0 FN = 1", OS#4744 3g_call_23112021.pcapng
+IuUP
+ 0000 .... = PDU Type: Data with CRC (0)
+ .... 0001 = Frame Number: 1
+ 00.. .... = FQC: Frame Good (0)
+ ..00 0000 = RFCI: 0x00
+ 1110 00.. = Header CRC: 0x38 [correct]
+ .... ..11 1111 1111 = Payload CRC: 0x3ff
+ Payload Data: 08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740
+*/
+static const uint8_t iuup_data[] = {
+ 0x01, 0x00, 0xe3, 0xff, /*payload starts here: */ 0x08, 0x55, 0x6d, 0x94, 0x4c, 0x71, 0xa1, 0xa0,
+ 0x81, 0xe7, 0xea, 0xd2, 0x04, 0x24, 0x44, 0x80, 0x00, 0x0e, 0xcd, 0x82,
+ 0xb8, 0x11, 0x18, 0x00, 0x00, 0x97, 0xc4, 0x79, 0x4e, 0x77, 0x40
+};
+
+#define IUUP_MSGB_SIZE 4096
+
+static struct osmo_iuup_tnl_prim *itp_ctrl_nack_alloc(enum iuup_procedure proc_ind, enum iuup_error_cause error_cause, uint8_t fn)
+{
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_ctrl_nack *nack;
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(struct iuup_ctrl_nack));
+ nack = (struct iuup_ctrl_nack *) msgb_l2(tnp->oph.msg);
+ *nack = (struct iuup_ctrl_nack){
+ .hdr = {
+ .frame_nr = fn,
+ .ack_nack = IUUP_AN_NACK,
+ .pdu_type = IUUP_PDU_T_CONTROL,
+ .proc_ind = proc_ind,
+ .mode_version = 0,
+ .payload_crc_hi = 0,
+ .header_crc = 0,
+ .payload_crc_lo = 0,
+ },
+ .spare = 0,
+ .error_cause = error_cause,
+ };
+ nack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(tnp->oph.msg), msgb_l2len(tnp->oph.msg));
+ return tnp;
+}
+
+static struct osmo_iuup_tnl_prim *itp_ctrl_ack_alloc(enum iuup_procedure proc_ind, uint8_t fn)
+{
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_ctrl_ack *ack;
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(struct iuup_ctrl_ack));
+ ack = (struct iuup_ctrl_ack *) msgb_l2(tnp->oph.msg);
+ *ack = (struct iuup_ctrl_ack){
+ .hdr = {
+ .frame_nr = fn,
+ .ack_nack = IUUP_AN_ACK,
+ .pdu_type = IUUP_PDU_T_CONTROL,
+ .proc_ind = proc_ind,
+ .mode_version = 0,
+ .payload_crc_hi = 0,
+ .header_crc = 0,
+ .payload_crc_lo = 0,
+ },
+ };
+ ack->hdr.header_crc = osmo_iuup_compute_header_crc(msgb_l2(tnp->oph.msg), msgb_l2len(tnp->oph.msg));
+ return tnp;
+}
+
+static void clock_override_set(long sec, long usec)
+{
+ osmo_gettimeofday_override_time.tv_sec = sec + usec / (1000*1000);
+ osmo_gettimeofday_override_time.tv_usec = usec % (1000*1000);
+ printf("sys={%lu.%06lu}, %s\n", osmo_gettimeofday_override_time.tv_sec,
+ osmo_gettimeofday_override_time.tv_usec, __func__);
+}
+
+void test_crc(void)
+{
+ int rc;
+
+ /* Frame 34, "Initialization ACK", OS#4744 3g_call_23112021.pcapng */
+ static const uint8_t iuup_initialization_ack[] = {
+ 0xe4, 0x00, 0xdf, 0x99, 0x16, 0x00, 0x51, 0x67, 0x3c, 0x01, 0x27, 0x00,
+ 0x00, 0x82, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x00
+ };
+
+ printf("=== start: %s ===\n", __func__);
+
+ rc = osmo_iuup_compute_header_crc(iuup_initialization, sizeof(iuup_initialization));
+ printf("iuup_initialization: Header CRC = 0x%02x\n", rc);
+ rc = osmo_iuup_compute_payload_crc(iuup_initialization, sizeof(iuup_initialization));
+ printf("iuup_initialization: Payload CRC = 0x%03x\n", rc);
+
+ rc = osmo_iuup_compute_header_crc(iuup_initialization_ack, sizeof(iuup_initialization_ack));
+ printf("iuup_initialization_ack: Header CRC = 0x%02x\n", rc);
+ rc = osmo_iuup_compute_payload_crc(iuup_initialization_ack, sizeof(iuup_initialization_ack));
+ printf("iuup_initialization_ack: Payload CRC = 0x%03x\n", rc);
+
+ printf("=== end: %s ===\n", __func__);
+}
+
+
+/****************************
+ * test_tinit_timeout_retrans
+ ****************************/
+static unsigned int _tinit_timeout_retrans_user_rx_prim = 0;
+static int _tinit_timeout_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ printf("%s()\n", __func__);
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_ERR_EVENT);
+ OSMO_ASSERT(irp->u.status.u.error_event.cause == IUUP_ERR_CAUSE_INIT_FAILURE_NET_TMR);
+ OSMO_ASSERT(irp->u.status.u.error_event.distance == IUUP_ERR_DIST_LOCAL);
+ _tinit_timeout_retrans_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+static unsigned int _tinit_timeout_retrans_transport_rx_prim = 0;
+static int _tinit_timeout_retrans_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ _tinit_timeout_retrans_transport_rx_prim++;
+
+ msgb_free(msg);
+ return 0;
+}
+void test_tinit_timeout_retrans(void)
+{
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ int rc, i;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _tinit_timeout_retrans_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _tinit_timeout_retrans_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is transmitted automatically: */
+ OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == 1);
+
+ /* After one sec, INITIALIZATION msg is retransmitted */
+ for (i = 1; i < IUUP_TIMER_INIT_N_DEFAULT + 1; i++) {
+ clock_override_set(0, IUUP_TIMER_INIT_T_DEFAULT*1000 * i);
+ osmo_select_main(0);
+ OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i + 1);
+ }
+ /* Last one should send an error event: */
+ OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 0);
+ clock_override_set(0, IUUP_TIMER_INIT_T_DEFAULT*1000 * i);
+ osmo_select_main(0);
+ OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i);
+ OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 1);
+
+ /* Nothing else is received afterwards. osmo_select_main() will block forever. */
+ /*clock_override_set(i + 1, 0);
+ osmo_select_main(0);
+ OSMO_ASSERT(_tinit_timeout_retrans_transport_rx_prim == i);
+ OSMO_ASSERT(_tinit_timeout_retrans_user_rx_prim == 1);*/
+
+ osmo_iuup_instance_free(iui);
+}
+
+/****************************
+ * test_tinit_nack
+ ****************************/
+static unsigned int _init_nack_retrans_user_rx_prim = 0;
+static int _init_nack_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+
+ printf("%s()\n", __func__);
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_ERR_EVENT);
+ OSMO_ASSERT(irp->u.status.u.error_event.cause == IUUP_ERR_CAUSE_INIT_FAILURE_REP_NACK);
+ OSMO_ASSERT(irp->u.status.u.error_event.distance == IUUP_ERR_DIST_SECOND_FWD);
+ _init_nack_retrans_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+static int _init_nack_retrans_transport_rx_prim = 0;
+static int _init_nack_retrans_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ _init_nack_retrans_transport_rx_prim++;
+
+ msgb_free(msg);
+ return 0;
+}
+void test_init_nack_retrans(void)
+{
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ int rc, i;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _init_nack_retrans_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _init_nack_retrans_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is transmitted automatically: */
+ OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == 1);
+
+ /* After one sec, INITIALIZATION msg is retransmitted */
+ for (i = 1; i < IUUP_TIMER_INIT_N_DEFAULT + 1; i++) {
+ /* Send NACK: */
+ tnp = itp_ctrl_nack_alloc(IUUP_PROC_INIT, IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED, 0);
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ /* A new INIT is retransmitted: */
+ OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == i + 1);
+ }
+ /* Last one should send an error event: */
+ OSMO_ASSERT(_init_nack_retrans_user_rx_prim == 0);
+ tnp = itp_ctrl_nack_alloc(IUUP_PROC_INIT, IUUP_ERR_CAUSE_MODE_VERSION_NOT_SUPPORTED, 0);
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_init_nack_retrans_transport_rx_prim == i);
+ OSMO_ASSERT(_init_nack_retrans_user_rx_prim == 1);
+
+ /* Nothing else is received afterwards. osmo_select_main() will block forever. */
+
+ osmo_iuup_instance_free(iui);
+}
+
+
+/****************************
+ * test_init_ack
+ ****************************/
+static unsigned int _init_ack_user_rx_prim = 0;
+static int _init_ack_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION));
+ printf("User: UL len=%u: %s\n", msgb_l3len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg)));
+
+ _init_ack_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+static int _init_ack_transport_rx_prim = 0;
+static int _init_ack_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ _init_ack_transport_rx_prim++;
+
+ msgb_free(msg);
+ return 0;
+}
+void test_init_ack(void)
+{
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_pdutype0_hdr *hdr0;
+ int rc;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _init_ack_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _init_ack_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is transmitted automatically: */
+ OSMO_ASSERT(_init_ack_transport_rx_prim == 1);
+
+ /* Send ACK: */
+ tnp = itp_ctrl_ack_alloc(IUUP_PROC_INIT, 0);
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_init_ack_transport_rx_prim == 1); /* Make sure there's no retrans */
+ OSMO_ASSERT(_init_ack_user_rx_prim == 0); /* Make sure there's no error event */
+
+ /* Send IuUP incoming data to the implementation: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data));
+ hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr0, iuup_data, sizeof(iuup_data));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ /* We receive it in RNL: */
+ OSMO_ASSERT(_init_ack_user_rx_prim == 1);
+
+ /* Now in opposite direction, RNL->[IuuP]->TNL: */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.data.rfci = 0;
+ rnp->u.data.frame_nr = 1;
+ rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
+ rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4);
+ memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4);
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ OSMO_ASSERT(_init_ack_transport_rx_prim == 2); /* We receive data in TNL */
+
+ osmo_iuup_instance_free(iui);
+}
+
+/****************************
+ * test_passive_init
+ ****************************/
+static unsigned int _passive_init_user_rx_prim = 0;
+static int _passive_init_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+
+ switch (_passive_init_user_rx_prim) {
+ case 0:
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT);
+ break;
+ case 1:
+ default:
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION));
+ printf("User: UL len=%u: %s\n", msgb_l3len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg)));
+ }
+
+ _passive_init_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+static int _passive_init_transport_rx_prim = 0;
+static int _passive_init_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg;
+
+ printf("%s()\n", __func__);
+ msg = oph->msg;
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ _passive_init_transport_rx_prim++;
+
+ msgb_free(msg);
+ return 0;
+}
+void test_passive_init(void)
+{
+ /* Here we check the passive INIT code path, aka receiving INIT and returning INIT_ACK/NACK */
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_pdutype14_hdr *hdr14;
+ struct iuup_pdutype0_hdr *hdr0;
+ int rc;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _passive_init_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _passive_init_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ rnp->u.config.active = false;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is NOT transmitted automatically: */
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 0);
+
+ /* Send Init: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization));
+ hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 1); /* We receive an Init ACK */
+ OSMO_ASSERT(_passive_init_user_rx_prim == 1); /* We receive the Status-Init.ind */
+
+ /* Send IuUP incoming data to the implementation: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data));
+ hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr0, iuup_data, sizeof(iuup_data));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ /* We receive it in RNL: */
+ OSMO_ASSERT(_passive_init_user_rx_prim == 2);
+
+ /* Now in opposite direction, RNL->[IuuP]->TNL: */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.data.rfci = 0;
+ rnp->u.data.frame_nr = 1;
+ rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
+ rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4);
+ memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4);
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 2); /* We receive data in TNL */
+
+ osmo_iuup_instance_free(iui);
+}
+
+/****************************
+ * test_passive_init_retrans
+ ****************************/
+static unsigned int _passive_init_retrans_user_rx_prim = 0;
+static int _passive_init_retrans_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ struct msgb *msg = oph->msg;
+
+ printf("%s()\n", __func__);
+
+ switch (_passive_init_retrans_user_rx_prim) {
+ case 0:
+ case 1:
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT);
+ break;
+ case 2:
+ default:
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION));
+ printf("User: UL len=%u: %s\n", msgb_l3len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l3(msg), msgb_l3len(msg)));
+ }
+
+ _passive_init_retrans_user_rx_prim++;
+ msgb_free(oph->msg);
+ return 0;
+}
+void test_passive_init_retrans(void)
+{
+ /* Here we check the passive INIT code path, aka receiving INIT and
+ * returning INIT_ACK/NACK. We emulate the peer not receiving the INIT
+ * ACK and hence retransmitting the INIT. The IuUP stack should then
+ * push the new INIT info up the stack and ACK it. */
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_pdutype14_hdr *hdr14;
+ struct iuup_pdutype0_hdr *hdr0;
+ int rc;
+
+ /* reset global var, we reuse it together wth callback from test_passive_init(): */
+ _passive_init_transport_rx_prim = 0;
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _passive_init_retrans_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _passive_init_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ rnp->u.config.active = false;
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ /* STATUS-INIT.req is NOT transmitted automatically: */
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 0);
+
+ /* Send Init: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization));
+ hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 1); /* We receive an Init ACK */
+ OSMO_ASSERT(_passive_init_retrans_user_rx_prim == 1); /* We receive the Status-Init.ind */
+
+ /* Send Init (retrans): */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_initialization));
+ hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr14, iuup_initialization, sizeof(iuup_initialization));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 2); /* We receive another Init ACK */
+ OSMO_ASSERT(_passive_init_retrans_user_rx_prim == 2); /* We receive another Status-Init.ind */
+
+ /* Send IuUP incoming data to the implementation: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_data));
+ hdr0 = (struct iuup_pdutype0_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr0, iuup_data, sizeof(iuup_data));
+ OSMO_ASSERT((rc = osmo_iuup_tnl_prim_up(iui, tnp)) == 0);
+ /* We receive it in RNL: */
+ OSMO_ASSERT(_passive_init_retrans_user_rx_prim == 3);
+
+ /* Now in opposite direction, RNL->[IuuP]->TNL: */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.data.rfci = 0;
+ rnp->u.data.frame_nr = 1;
+ rnp->u.data.fqc = IUUP_FQC_FRAME_GOOD;
+ rnp->oph.msg->l3h = msgb_put(rnp->oph.msg, sizeof(iuup_data) - 4);
+ memcpy(rnp->oph.msg->l3h, iuup_data + 4, sizeof(iuup_data) - 4);
+ OSMO_ASSERT((rc = osmo_iuup_rnl_prim_down(iui, rnp)) == 0);
+ OSMO_ASSERT(_passive_init_transport_rx_prim == 3); /* We receive data in TNL */
+
+ osmo_iuup_instance_free(iui);
+}
+
+static int _decode_passive_init_2_rfci_no_iptis_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
+ printf("%s(): Initialization decoded fine!\n", __func__);
+ OSMO_ASSERT(OSMO_PRIM_HDR(&irp->oph) == OSMO_PRIM(OSMO_IUUP_RNL_STATUS, PRIM_OP_INDICATION));
+ OSMO_ASSERT(irp->u.status.procedure == IUUP_PROC_INIT);
+ OSMO_ASSERT(irp->u.status.u.initialization.num_rfci == 2);
+ OSMO_ASSERT(irp->u.status.u.initialization.num_subflows == 3);
+ OSMO_ASSERT(irp->u.status.u.initialization.data_pdu_type == 0);
+ OSMO_ASSERT(irp->u.status.u.initialization.IPTIs_present == false);
+ msgb_free(oph->msg);
+ return 0;
+}
+static int _decode_passive_init_2_rfci_no_iptis_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph;
+ struct msgb *msg;
+ struct iuup_pdutype14_hdr *hdr;
+
+ printf("%s()\n", __func__);
+ msg = oph->msg;
+ OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST));
+ printf("Transport: DL len=%u: %s\n", msgb_l2len(msg),
+ osmo_hexdump((const unsigned char *) msgb_l2(msg), msgb_l2len(msg)));
+ hdr = msgb_l2(msg);
+ OSMO_ASSERT(hdr->pdu_type == IUUP_PDU_T_CONTROL);
+ OSMO_ASSERT(hdr->ack_nack == IUUP_AN_ACK);
+ msgb_free(msg);
+ return 0;
+}
+void test_decode_passive_init_2_rfci_no_iptis(void)
+{
+ /* Here we check the passive INIT code path, aka receiving INIT and returning INIT_ACK/NACK */
+ struct osmo_iuup_instance *iui;
+ struct osmo_iuup_rnl_prim *rnp;
+ struct osmo_iuup_tnl_prim *tnp;
+ struct iuup_pdutype14_hdr *hdr14;
+ int rc;
+
+ /* Frame 46, "Initialization", SYS#5969 call4_Iu_Iuh.pcap
+ 1110 .... = PDU Type: Control Procedure (14)
+ .... 00.. = Ack/Nack: Procedure (0)
+ .... ..00 = Frame Number: 0
+ 0000 .... = Mode Version: 0x0
+ .... 0000 = Procedure: Initialization (0)
+ 1101 11.. = Header CRC: 0x37 [correct]
+ .... ..01 1011 0100 = Payload CRC: 0x1b4
+ 000. .... = Spare: 0x0
+ ...0 .... = TI: IPTIs not present (0)
+ .... 011. = Subflows: 3
+ .... ...0 = Chain Indicator: this frame is the last frame for the procedure (0)
+ RFCI 1 Initialization
+ 0... .... = RFCI 0 LRI: Not last RFCI (0x0)
+ .0.. .... = RFCI 0 LI: one octet used (0x0)
+ ..00 0001 = RFCI 0: 1
+ RFCI 0 Flow 0 Len: 81
+ RFCI 0 Flow 1 Len: 103
+ RFCI 0 Flow 2 Len: 60
+ RFCI 6 Initialization
+ 1... .... = RFCI 1 LRI: Last RFCI in current frame (0x1)
+ .0.. .... = RFCI 1 LI: one octet used (0x0)
+ ..00 0110 = RFCI 1: 6
+ RFCI 1 Flow 0 Len: 39
+ RFCI 1 Flow 1 Len: 0
+ RFCI 1 Flow 2 Len: 0
+ Iu UP Mode Versions Supported: 0x0001
+ 0... .... .... .... = Version 16: not supported (0x0)
+ .0.. .... .... .... = Version 15: not supported (0x0)
+ ..0. .... .... .... = Version 14: not supported (0x0)
+ ...0 .... .... .... = Version 13: not supported (0x0)
+ .... 0... .... .... = Version 12: not supported (0x0)
+ .... .0.. .... .... = Version 11: not supported (0x0)
+ .... ..0. .... .... = Version 10: not supported (0x0)
+ .... ...0 .... .... = Version 9: not supported (0x0)
+ .... .... 0... .... = Version 8: not supported (0x0)
+ .... .... .0.. .... = Version 7: not supported (0x0)
+ .... .... ..0. .... = Version 6: not supported (0x0)
+ .... .... ...0 .... = Version 5: not supported (0x0)
+ .... .... .... 0... = Version 4: not supported (0x0)
+ .... .... .... .0.. = Version 3: not supported (0x0)
+ .... .... .... ..0. = Version 2: not supported (0x0)
+ .... .... .... ...1 = Version 1: supported (0x1)
+ 0000 .... = RFCI Data Pdu Type: PDU type 0 (0x0)
+ */
+ const uint8_t iuup_init[] = {
+ 0xe0, 0x00, 0xdd, 0xb4, 0x06, 0x01, 0x51, 0x67, 0x3c, 0x86, 0x27,
+ 0x00, 0x00, 0x00, 0x01, 0x00
+ };
+
+ iui = osmo_iuup_instance_alloc(iuup_test_ctx, __func__);
+ OSMO_ASSERT(iui);
+ osmo_iuup_instance_set_user_prim_cb(iui, _decode_passive_init_2_rfci_no_iptis_user_prim_cb, NULL);
+ osmo_iuup_instance_set_transport_prim_cb(iui, _decode_passive_init_2_rfci_no_iptis_transport_prim_cb, NULL);
+
+ clock_override_set(0, 0);
+
+ /* Tx CONFIG.req */
+ rnp = osmo_iuup_rnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, IUUP_MSGB_SIZE);
+ rnp->u.config = def_configure_req;
+ rnp->u.config.active = false;
+
+ rc = osmo_iuup_rnl_prim_down(iui, rnp);
+ OSMO_ASSERT(rc == 0);
+
+ /* Send Init: */
+ tnp = osmo_iuup_tnl_prim_alloc(iuup_test_ctx, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, IUUP_MSGB_SIZE);
+ tnp->oph.msg->l2h = msgb_put(tnp->oph.msg, sizeof(iuup_init));
+ hdr14 = (struct iuup_pdutype14_hdr *)msgb_l2(tnp->oph.msg);
+ memcpy(hdr14, iuup_init, sizeof(iuup_init));
+
+ rc = osmo_iuup_tnl_prim_up(iui, tnp);
+ OSMO_ASSERT(rc == 0);
+
+ osmo_iuup_instance_free(iui);
+}
+
+int main(int argc, char **argv)
+{
+ iuup_test_ctx = talloc_named_const(NULL, 0, "iuup_test");
+ osmo_init_logging2(iuup_test_ctx, NULL);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_category_filter(osmo_stderr_target, DLIUUP, 1, LOGL_DEBUG);
+ osmo_fsm_log_addr(false);
+
+ osmo_gettimeofday_override = true;
+
+ test_crc();
+ test_tinit_timeout_retrans();
+ test_init_nack_retrans();
+ test_init_ack();
+ test_passive_init();
+ test_passive_init_retrans();
+ test_decode_passive_init_2_rfci_no_iptis();
+
+ printf("OK.\n");
+}
diff --git a/tests/iuup/iuup_test.err b/tests/iuup/iuup_test.err
new file mode 100644
index 00000000..fe1ed397
--- /dev/null
+++ b/tests/iuup/iuup_test.err
@@ -0,0 +1,55 @@
+DLIUUP IuUP(test_tinit_timeout_retrans){NULL}: Allocated
+DLIUUP IuUP(test_tinit_timeout_retrans){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_tinit_timeout_retrans){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Timeout of T1
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Timeout of T1
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Timeout of T1
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Timeout of T1
+DLIUUP IuUP(test_tinit_timeout_retrans){Initialisation}: Deallocated
+DLIUUP IuUP(test_init_nack_retrans){NULL}: Allocated
+DLIUUP IuUP(test_init_nack_retrans){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_init_nack_retrans){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Received Event INIT_NACK
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Rx Initialization NACK N=0/3
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Received Event INIT_NACK
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Rx Initialization NACK N=1/3
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Received Event INIT_NACK
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Rx Initialization NACK N=2/3
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Received Event INIT_NACK
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Rx Initialization NACK N=3/3
+DLIUUP IuUP(test_init_nack_retrans){Initialisation}: Deallocated
+DLIUUP IuUP(test_init_ack){NULL}: Allocated
+DLIUUP IuUP(test_init_ack){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_init_ack){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_init_ack){Initialisation}: Received Event LAST_INIT_ACK
+DLIUUP IuUP(test_init_ack){Initialisation}: state_chg to SMpSDU_Data_Transfer_Ready
+DLIUUP IuUP(test_init_ack){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-ind
+DLIUUP IuUP(test_init_ack){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-req
+DLIUUP IuUP(test_init_ack){SMpSDU_Data_Transfer_Ready}: Deallocated
+DLIUUP IuUP(test_passive_init){NULL}: Allocated
+DLIUUP IuUP(test_passive_init){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_passive_init){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_passive_init){Initialisation}: Received Event INIT
+DLIUUP IuUP(test_passive_init){Initialisation}: Tx Initialization ACK
+DLIUUP IuUP(test_passive_init){Initialisation}: state_chg to SMpSDU_Data_Transfer_Ready
+DLIUUP IuUP(test_passive_init){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-ind
+DLIUUP IuUP(test_passive_init){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-req
+DLIUUP IuUP(test_passive_init){SMpSDU_Data_Transfer_Ready}: Deallocated
+DLIUUP IuUP(test_passive_init_retrans){NULL}: Allocated
+DLIUUP IuUP(test_passive_init_retrans){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_passive_init_retrans){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_passive_init_retrans){Initialisation}: Received Event INIT
+DLIUUP IuUP(test_passive_init_retrans){Initialisation}: Tx Initialization ACK
+DLIUUP IuUP(test_passive_init_retrans){Initialisation}: state_chg to SMpSDU_Data_Transfer_Ready
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Received Event INIT
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Tx Initialization ACK
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-ind
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Received Event IuUP-DATA-req
+DLIUUP IuUP(test_passive_init_retrans){SMpSDU_Data_Transfer_Ready}: Deallocated
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){NULL}: Allocated
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){NULL}: Received Event IuUP-CONFIG-req
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){NULL}: state_chg to Initialisation
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){Initialisation}: Received Event INIT
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){Initialisation}: Tx Initialization ACK
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){Initialisation}: state_chg to SMpSDU_Data_Transfer_Ready
+DLIUUP IuUP(test_decode_passive_init_2_rfci_no_iptis){SMpSDU_Data_Transfer_Ready}: Deallocated
diff --git a/tests/iuup/iuup_test.ok b/tests/iuup/iuup_test.ok
new file mode 100644
index 00000000..57baba9c
--- /dev/null
+++ b/tests/iuup/iuup_test.ok
@@ -0,0 +1,61 @@
+=== start: test_crc ===
+iuup_initialization: Header CRC = 0x37
+iuup_initialization: Payload CRC = 0x399
+iuup_initialization_ack: Header CRC = 0x09
+iuup_initialization_ack: Payload CRC = 0x399
+=== end: test_crc ===
+sys={0.000000}, clock_override_set
+_tinit_timeout_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+sys={1.000000}, clock_override_set
+_tinit_timeout_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+sys={2.000000}, clock_override_set
+_tinit_timeout_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+sys={3.000000}, clock_override_set
+_tinit_timeout_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+sys={4.000000}, clock_override_set
+_tinit_timeout_retrans_user_prim_cb()
+sys={0.000000}, clock_override_set
+_init_nack_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_nack_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_nack_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_nack_retrans_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_nack_retrans_user_prim_cb()
+sys={0.000000}, clock_override_set
+_init_ack_transport_prim_cb()
+Transport: DL len=22: e0 00 df 99 16 00 51 67 3c 01 27 00 00 82 00 00 00 17 10 00 01 00
+_init_ack_user_prim_cb()
+User: UL len=31: 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+_init_ack_transport_prim_cb()
+Transport: DL len=35: 01 00 e3 ff 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+sys={0.000000}, clock_override_set
+_passive_init_user_prim_cb()
+_passive_init_transport_prim_cb()
+Transport: DL len=4: e4 00 24 00
+_passive_init_user_prim_cb()
+User: UL len=31: 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+_passive_init_transport_prim_cb()
+Transport: DL len=35: 01 00 e3 ff 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+sys={0.000000}, clock_override_set
+_passive_init_retrans_user_prim_cb()
+_passive_init_transport_prim_cb()
+Transport: DL len=4: e4 00 24 00
+_passive_init_retrans_user_prim_cb()
+_passive_init_transport_prim_cb()
+Transport: DL len=4: e4 00 24 00
+_passive_init_retrans_user_prim_cb()
+User: UL len=31: 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+_passive_init_transport_prim_cb()
+Transport: DL len=35: 01 00 e3 ff 08 55 6d 94 4c 71 a1 a0 81 e7 ea d2 04 24 44 80 00 0e cd 82 b8 11 18 00 00 97 c4 79 4e 77 40
+sys={0.000000}, clock_override_set
+_decode_passive_init_2_rfci_no_iptis_user_prim_cb(): Initialization decoded fine!
+_decode_passive_init_2_rfci_no_iptis_transport_prim_cb()
+Transport: DL len=4: e4 00 24 00
+OK.
diff --git a/tests/lapd/lapd_test.c b/tests/lapd/lapd_test.c
index 8a4e9319..f2fccf87 100644
--- a/tests/lapd/lapd_test.c
+++ b/tests/lapd/lapd_test.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
@@ -67,12 +63,23 @@ static const uint8_t cm[] = {
0x29, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80,
};
-static const uint8_t ua[] = {
+static const uint8_t ua_cm[] = {
0x01, 0x73, 0x41, 0x05, 0x24, 0x31, 0x03, 0x50,
0x18, 0x93, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00,
0x00, 0x00, 0x80, 0x2b, 0x2b, 0x2b, 0x2b
};
+static const uint8_t pr[] = {
+ 0x06, 0x27, 0x07, 0x03, 0x50, 0x58, 0x92, 0x05,
+ 0xf4, 0x44, 0x59, 0xba, 0x63,
+};
+
+static const uint8_t ua_pr[] = {
+ 0x01, 0x73, 0x35, 0x06, 0x27, 0x07, 0x03, 0x50,
+ 0x58, 0x92, 0x05, 0xf4, 0x44, 0x59, 0xba, 0x63,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+};
+
static const uint8_t mm[] = {
0x00, 0x0c, 0x00, 0x03, 0x01, 0x01, 0x20, 0x02,
0x00, 0x0b, 0x00, 0x03, 0x05, 0x04, 0x0d
@@ -177,6 +184,7 @@ static int send(struct msgb *in_msg, struct lapdm_channel *chan)
/* LAPDm requires those... */
pp.u.data.chan_nr = 0;
pp.u.data.link_id = 0;
+ pp.u.data.fn = 0;
/* feed into the LAPDm code of libosmogsm */
rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch);
OSMO_ASSERT(rc == 0 || rc == -EBUSY);
@@ -199,6 +207,7 @@ static int send_buf(const uint8_t *buf, size_t len, struct lapdm_channel *chan)
/* LAPDm requires those... */
pp.u.data.chan_nr = 0;
pp.u.data.link_id = 0;
+ pp.u.data.fn = 0;
/* feed into the LAPDm code of libosmogsm */
rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch);
OSMO_ASSERT(rc == 0 || rc == -EBUSY);
@@ -306,8 +315,8 @@ static int dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp,
/* Take message from queue */
rc = lapdm_phsap_dequeue_prim(le, pp);
- fprintf(stderr, "dequeue: got rc %d: %s\n", rc,
- rc <= 0 ? strerror(-rc) : "-");
+ printf("lapdm_phsap_dequeue_prim(): got rc %d: %s\n",
+ rc, rc <= 0 ? strerror(-rc) : "-");
if (rc < 0)
return rc;
@@ -317,15 +326,14 @@ static int dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp,
l3_len = msgb_l3len(pp->oph.msg);
l2_header_len -= l3_len;
} else
- fprintf(stderr, "MSGB: L3 is undefined\n");
+ printf("MSGB: L3 is undefined\n");
if (l2_header_len < 0 || l2_header_len > pp->oph.msg->data_len) {
- fprintf(stderr,
- "MSGB inconsistent: data = %p, l2 = %p, l3 = %p, tail = %p\n",
- pp->oph.msg->data,
- pp->oph.msg->l2h,
- pp->oph.msg->l3h,
- pp->oph.msg->tail);
+ printf("MSGB inconsistent: data = %p, l2 = %p, l3 = %p, tail = %p\n",
+ pp->oph.msg->data,
+ pp->oph.msg->l2h,
+ pp->oph.msg->l3h,
+ pp->oph.msg->tail);
l2_header_len = -1;
}
@@ -370,7 +378,7 @@ static int ms_to_bts_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *_ctx
return 0;
}
-static void test_lapdm_polling()
+static void test_lapdm_polling(void)
{
printf("I do some very simple LAPDm test.\n");
@@ -464,7 +472,7 @@ static void test_lapdm_polling()
lapdm_channel_exit(&ms_to_bts_channel);
}
-static void test_lapdm_contention_resolution()
+static void test_lapdm_contention_resolution(void)
{
printf("I test contention resultion by having two mobiles collide and "
"first mobile repeating SABM.\n");
@@ -491,14 +499,14 @@ static void test_lapdm_contention_resolution()
send_sabm(&bts_to_ms_channel, 0, cm, sizeof(cm));
rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
CHECK_RC(rc);
- OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua_cm, ARRAY_SIZE(ua_cm)) == 0);
msgb_free(pp.oph.msg);
/* Send SABM MS 2, we must get nothing, due to collision */
cm2 = malloc(sizeof(cm));
memcpy(cm2, cm, sizeof(cm));
cm2[0] += 1;
- send_sabm(&bts_to_ms_channel, 0, cm2, sizeof(cm2));
+ send_sabm(&bts_to_ms_channel, 0, cm2, sizeof(cm));
free(cm2);
rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
OSMO_ASSERT(rc == -ENODEV);
@@ -507,7 +515,7 @@ static void test_lapdm_contention_resolution()
send_sabm(&bts_to_ms_channel, 0, cm, sizeof(cm));
rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
CHECK_RC(rc);
- OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua_cm, ARRAY_SIZE(ua_cm)) == 0);
msgb_free(pp.oph.msg);
/* clean up */
@@ -517,7 +525,7 @@ static void test_lapdm_contention_resolution()
lapdm_channel_exit(&bts_to_ms_channel);
}
-static void test_lapdm_early_release()
+static void test_lapdm_early_release(void)
{
printf("I test RF channel release of an unestablished channel.\n");
@@ -568,10 +576,11 @@ static void lapdm_establish(const uint8_t *est_req, size_t est_req_size)
lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state);
lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state);
- /* Send the release request */
+ /* Send the establish request */
msg = create_est_req(est_req, est_req_size);
rc = lapdm_rslms_recvmsg(msg, &bts_to_ms_channel);
- fprintf(stderr, "recvmsg: got rc %d: %s\n", rc, rc <= 0 ? strerror(-rc) : "???");
+ printf("lapdm_rslms_recvmsg(): got rc %d: %s\n",
+ rc, rc <= 0 ? strerror(-rc) : "???");
OSMO_ASSERT(rc == 0);
/* Take message from queue */
@@ -596,7 +605,7 @@ static void lapdm_establish(const uint8_t *est_req, size_t est_req_size)
msgb_free(msg);
}
-static void test_lapdm_establishment()
+static void test_lapdm_establishment(void)
{
printf("I test RF channel establishment.\n");
printf("Testing SAPI3/SDCCH\n");
@@ -670,7 +679,7 @@ static void dump_queue(struct llist_head *head)
printf("\n");
}
-static void test_lapdm_desync()
+static void test_lapdm_desync(void)
{
printf("I test if desync problems exist in LAPDm\n");
@@ -698,7 +707,7 @@ static void test_lapdm_desync()
rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH");
CHECK_RC(rc);
- OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0);
+ OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua_cm, ARRAY_SIZE(ua_cm)) == 0);
msgb_free(pp.oph.msg);
printf("\nSending Classmark Change\n");
@@ -757,6 +766,63 @@ static void test_lapdm_desync()
lapdm_channel_exit(&bts_to_ms_channel);
}
+static void test_lapdm_sapi_prio(void)
+{
+ static const uint8_t dl_sabm[] = { 0x0f, 0x3f, 0x01 };
+ struct osmo_phsap_prim pp1, pp2;
+ struct lapdm_channel lc = { };
+ struct msgb *msg;
+ int rc;
+
+ printf("\n=== I test SAPI0/SAPI3 prioritization ===\n\n");
+
+ /* BTS to MS in polling mode */
+ lapdm_channel_init(&lc, LAPDM_MODE_BTS);
+ lapdm_channel_set_flags(&lc, LAPDM_ENT_F_POLLING_ONLY);
+ lapdm_channel_set_l1(&lc, NULL, NULL);
+ lapdm_channel_set_l3(&lc, bts_to_ms_dummy_tx_cb, NULL);
+
+ /* MS establishes a SAPI=0 link by sending func=SABM on SDCCH */
+ printf("MS is establishing a SAPI=0 link\n");
+ send_sabm(&lc, 0, pr, sizeof(pr));
+
+ /* BTS establishes a SAPI=3 link by sending func=SABM on SDCCH */
+ msg = create_est_req(est_req_sdcch_sapi3, sizeof(est_req_sdcch_sapi3));
+ printf("BTS is establishing a SAPI=3 link\n");
+ rc = lapdm_rslms_recvmsg(msg, &lc);
+ OSMO_ASSERT(rc == 0);
+
+ /* Make sure that we get func=UA on SDCCH from the BTS first */
+ rc = dequeue_prim(&lc.lapdm_dcch, &pp1, "DCCH");
+ CHECK_RC(rc);
+
+ /* Contention resolution is completed, we should get func=SABM now */
+ rc = dequeue_prim(&lc.lapdm_dcch, &pp2, "DCCH");
+ CHECK_RC(rc);
+
+ /* Check the actual message payload */
+ rc = memcmp(pp1.oph.msg->l2h, ua_pr, sizeof(ua_pr));
+ printf("Checking the func=UA message: %s\n", rc == 0 ? "OK" : "FAIL");
+ rc = memcmp(pp2.oph.msg->l2h, dl_sabm, sizeof(dl_sabm));
+ printf("Checking the func=SABM message: %s\n", rc == 0 ? "OK" : "FAIL");
+
+ msgb_free(pp1.oph.msg);
+ msgb_free(pp2.oph.msg);
+
+ /* Make sure that the queues are empty now */
+ printf("\nChecking whether the DCCH/SACCH queues are empty\n");
+ rc = dequeue_prim(&lc.lapdm_dcch, &pp1, "DCCH");
+ OSMO_ASSERT(rc < 0);
+ rc = dequeue_prim(&lc.lapdm_dcch, &pp2, "SACCH");
+ OSMO_ASSERT(rc < 0);
+
+ /* Clean up */
+ lapdm_channel_exit(&lc);
+
+ /* Check if exit is idempotent */
+ lapdm_channel_exit(&lc);
+}
+
int main(int argc, char **argv)
{
void *ctx = talloc_named_const(NULL, 0, "lapd_test");
@@ -773,6 +839,7 @@ int main(int argc, char **argv)
test_lapdm_contention_resolution();
test_lapdm_establishment();
test_lapdm_desync();
+ test_lapdm_sapi_prio();
printf("Success.\n");
diff --git a/tests/lapd/lapd_test.ok b/tests/lapd/lapd_test.ok
index 835ca2b3..065886c3 100644
--- a/tests/lapd/lapd_test.ok
+++ b/tests/lapd/lapd_test.ok
@@ -5,39 +5,58 @@ bts_to_ms_tx_cb: MS->BTS(us) message 25
BTS: Verifying CM request.
Confirming
+lapdm_phsap_dequeue_prim(): got rc 0: Success
Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b
ms_to_bts_tx_cb: BTS->MS(us) message 6
MS: Verifying incoming primitive.
Sending back to MS
+lapdm_phsap_dequeue_prim(): got rc 0: Success
Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
Message: [L2]> 03 00 0d [L3]> 05 04 0d 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
ms_to_bts_tx_cb: BTS->MS(us) message 12
MS: Verifying incoming MM message: 3
ms_to_bts_l1_cb: MS(us) -> BTS prim message
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
Sending back to BTS
ms_to_bts_l1_cb: MS(us) -> BTS prim message
bts_to_ms_tx_cb: MS->BTS(us) message 14
BTS: Verifying dummy message.
+lapdm_phsap_dequeue_prim(): got rc 0: Success
+MSGB: L3 is undefined
Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x00
Message: [L2]> 01 21 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
I test RF channel release of an unestablished channel.
I test contention resultion by having two mobiles collide and first mobile repeating SABM.
bts_to_ms_tx_cb: MS->BTS(us) message 25
BTS: Verifying CM request.
+lapdm_phsap_dequeue_prim(): got rc 0: Success
Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
+lapdm_phsap_dequeue_prim(): got rc 0: Success
Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b
I test RF channel establishment.
Testing SAPI3/SDCCH
+lapdm_rslms_recvmsg(): got rc 0: Success
+lapdm_phsap_dequeue_prim(): got rc 0: Success
Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x03
Message: [L2]> 0f 3f 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
Testing SAPI3/SACCH
+lapdm_rslms_recvmsg(): got rc 0: Success
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
+lapdm_phsap_dequeue_prim(): got rc 0: Success
Took message from ACCH queue: L2 header size 5, L3 size 18, SAP 0x1000000, 0/0, Link 0x43
Message: [L2]> 00 00 0f 3f 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
I test if desync problems exist in LAPDm
Establishing SAPI=0
@@ -46,6 +65,7 @@ bts_to_ms_dummy_tx_cb: MS->BTS(us) message 25
Dumping queue:
00 00 17 [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80
+lapdm_phsap_dequeue_prim(): got rc 0: Success
Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b
@@ -55,6 +75,8 @@ bts_to_ms_dummy_tx_cb: MS->BTS(us) message 27
Dumping queue:
00 00 17 [L2]> 01 21 01
+lapdm_phsap_dequeue_prim(): got rc 0: Success
+MSGB: L3 is undefined
Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x00
Message: [L2]> 01 21 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
@@ -70,6 +92,7 @@ bts_to_ms_dummy_tx_cb: MS->BTS(us) message 22
Dumping queue:
00 00 17 [L2]> 03 40 0d [L3]> 06 35 01
+lapdm_phsap_dequeue_prim(): got rc 0: Success
Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
Message: [L2]> 03 40 0d [L3]> 06 35 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
@@ -79,6 +102,8 @@ bts_to_ms_dummy_tx_cb: MS->BTS(us) message 11
Dumping queue:
00 00 17 [L2]> 01 61 01
+lapdm_phsap_dequeue_prim(): got rc 0: Success
+MSGB: L3 is undefined
Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x00
Message: [L2]> 01 61 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
@@ -87,6 +112,7 @@ bts_to_ms_dummy_tx_cb: MS->BTS(us) message 6
Dumping queue:
+lapdm_phsap_dequeue_prim(): got rc 0: Success
Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x03
Message: [L2]> 0d 73 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
@@ -94,6 +120,26 @@ Sending CP-DATA
Dumping queue:
+lapdm_phsap_dequeue_prim(): got rc 0: Success
+MSGB: L3 is undefined
Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x03
Message: [L2]> 0d 21 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+
+=== I test SAPI0/SAPI3 prioritization ===
+
+MS is establishing a SAPI=0 link
+bts_to_ms_dummy_tx_cb: MS->BTS(us) message 22
+BTS is establishing a SAPI=3 link
+lapdm_phsap_dequeue_prim(): got rc 0: Success
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00
+Message: [L2]> 01 73 35 [L3]> 06 27 07 03 50 58 92 05 f4 44 59 ba 63 2b 2b 2b 2b 2b 2b 2b
+lapdm_phsap_dequeue_prim(): got rc 0: Success
+Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x03
+Message: [L2]> 0f 3f 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
+Checking the func=UA message: OK
+Checking the func=SABM message: OK
+
+Checking whether the DCCH/SACCH queues are empty
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
+lapdm_phsap_dequeue_prim(): got rc -19: No such device
Success.
diff --git a/tests/logging/logging_gsmtap_test.c b/tests/logging/logging_gsmtap_test.c
new file mode 100644
index 00000000..5ede5a47
--- /dev/null
+++ b/tests/logging/logging_gsmtap_test.c
@@ -0,0 +1,64 @@
+/* simple test for gsmtap logging */
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+#include <stdlib.h>
+
+static const struct log_info_cat default_categories[] = {};
+
+const struct log_info log_info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+extern struct log_info *osmo_log_info;
+
+int main(int argc, char **argv)
+{
+ struct log_target *stderr_target;
+ struct log_target *gsmtap_target;
+
+ log_init(&log_info, NULL);
+ stderr_target = log_target_create_stderr();
+ log_add_target(stderr_target);
+ log_set_all_filter(stderr_target, 1);
+ log_set_print_filename2(stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category_hex(stderr_target, 0);
+ log_set_print_category(stderr_target, 1);
+ log_set_use_color(stderr_target, 0);
+ log_parse_category_mask(stderr_target, "DLGLOBAL,1");
+
+ gsmtap_target = log_target_create_gsmtap("127.0.0.2", 4729, "gsmtap", 1, 1);
+ log_add_target(gsmtap_target);
+ log_set_all_filter(gsmtap_target, 1);
+ log_parse_category_mask(gsmtap_target, "DLGLOBAL,1");
+
+ log_target_file_switch_to_stream(stderr_target);
+
+ log_set_category_filter(stderr_target, DLIO, 1, LOGL_DEBUG);
+
+ for (int i = 0; i < 200; i++)
+ DEBUGP(DLGLOBAL, "Repeating message (i = %d)\n", i);
+
+ for (int i = 0; i < 200; i++)
+ osmo_select_main(1);
+
+ return 0;
+}
diff --git a/tests/logging/logging_gsmtap_test.err b/tests/logging/logging_gsmtap_test.err
new file mode 100644
index 00000000..92016abb
--- /dev/null
+++ b/tests/logging/logging_gsmtap_test.err
@@ -0,0 +1,200 @@
+DLGLOBAL Repeating message (i = 0)
+DLGLOBAL Repeating message (i = 1)
+DLGLOBAL Repeating message (i = 2)
+DLGLOBAL Repeating message (i = 3)
+DLGLOBAL Repeating message (i = 4)
+DLGLOBAL Repeating message (i = 5)
+DLGLOBAL Repeating message (i = 6)
+DLGLOBAL Repeating message (i = 7)
+DLGLOBAL Repeating message (i = 8)
+DLGLOBAL Repeating message (i = 9)
+DLGLOBAL Repeating message (i = 10)
+DLGLOBAL Repeating message (i = 11)
+DLGLOBAL Repeating message (i = 12)
+DLGLOBAL Repeating message (i = 13)
+DLGLOBAL Repeating message (i = 14)
+DLGLOBAL Repeating message (i = 15)
+DLGLOBAL Repeating message (i = 16)
+DLGLOBAL Repeating message (i = 17)
+DLGLOBAL Repeating message (i = 18)
+DLGLOBAL Repeating message (i = 19)
+DLGLOBAL Repeating message (i = 20)
+DLGLOBAL Repeating message (i = 21)
+DLGLOBAL Repeating message (i = 22)
+DLGLOBAL Repeating message (i = 23)
+DLGLOBAL Repeating message (i = 24)
+DLGLOBAL Repeating message (i = 25)
+DLGLOBAL Repeating message (i = 26)
+DLGLOBAL Repeating message (i = 27)
+DLGLOBAL Repeating message (i = 28)
+DLGLOBAL Repeating message (i = 29)
+DLGLOBAL Repeating message (i = 30)
+DLGLOBAL Repeating message (i = 31)
+DLGLOBAL Repeating message (i = 32)
+DLGLOBAL Repeating message (i = 33)
+DLGLOBAL Repeating message (i = 34)
+DLGLOBAL Repeating message (i = 35)
+DLGLOBAL Repeating message (i = 36)
+DLGLOBAL Repeating message (i = 37)
+DLGLOBAL Repeating message (i = 38)
+DLGLOBAL Repeating message (i = 39)
+DLGLOBAL Repeating message (i = 40)
+DLGLOBAL Repeating message (i = 41)
+DLGLOBAL Repeating message (i = 42)
+DLGLOBAL Repeating message (i = 43)
+DLGLOBAL Repeating message (i = 44)
+DLGLOBAL Repeating message (i = 45)
+DLGLOBAL Repeating message (i = 46)
+DLGLOBAL Repeating message (i = 47)
+DLGLOBAL Repeating message (i = 48)
+DLGLOBAL Repeating message (i = 49)
+DLGLOBAL Repeating message (i = 50)
+DLGLOBAL Repeating message (i = 51)
+DLGLOBAL Repeating message (i = 52)
+DLGLOBAL Repeating message (i = 53)
+DLGLOBAL Repeating message (i = 54)
+DLGLOBAL Repeating message (i = 55)
+DLGLOBAL Repeating message (i = 56)
+DLGLOBAL Repeating message (i = 57)
+DLGLOBAL Repeating message (i = 58)
+DLGLOBAL Repeating message (i = 59)
+DLGLOBAL Repeating message (i = 60)
+DLGLOBAL Repeating message (i = 61)
+DLGLOBAL Repeating message (i = 62)
+DLGLOBAL Repeating message (i = 63)
+DLGLOBAL Repeating message (i = 64)
+DLGLOBAL Repeating message (i = 65)
+DLGLOBAL Repeating message (i = 66)
+DLGLOBAL Repeating message (i = 67)
+DLGLOBAL Repeating message (i = 68)
+DLGLOBAL Repeating message (i = 69)
+DLGLOBAL Repeating message (i = 70)
+DLGLOBAL Repeating message (i = 71)
+DLGLOBAL Repeating message (i = 72)
+DLGLOBAL Repeating message (i = 73)
+DLGLOBAL Repeating message (i = 74)
+DLGLOBAL Repeating message (i = 75)
+DLGLOBAL Repeating message (i = 76)
+DLGLOBAL Repeating message (i = 77)
+DLGLOBAL Repeating message (i = 78)
+DLGLOBAL Repeating message (i = 79)
+DLGLOBAL Repeating message (i = 80)
+DLGLOBAL Repeating message (i = 81)
+DLGLOBAL Repeating message (i = 82)
+DLGLOBAL Repeating message (i = 83)
+DLGLOBAL Repeating message (i = 84)
+DLGLOBAL Repeating message (i = 85)
+DLGLOBAL Repeating message (i = 86)
+DLGLOBAL Repeating message (i = 87)
+DLGLOBAL Repeating message (i = 88)
+DLGLOBAL Repeating message (i = 89)
+DLGLOBAL Repeating message (i = 90)
+DLGLOBAL Repeating message (i = 91)
+DLGLOBAL Repeating message (i = 92)
+DLGLOBAL Repeating message (i = 93)
+DLGLOBAL Repeating message (i = 94)
+DLGLOBAL Repeating message (i = 95)
+DLGLOBAL Repeating message (i = 96)
+DLGLOBAL Repeating message (i = 97)
+DLGLOBAL Repeating message (i = 98)
+DLGLOBAL Repeating message (i = 99)
+DLGLOBAL Repeating message (i = 100)
+DLGLOBAL Repeating message (i = 101)
+DLGLOBAL Repeating message (i = 102)
+DLGLOBAL Repeating message (i = 103)
+DLGLOBAL Repeating message (i = 104)
+DLGLOBAL Repeating message (i = 105)
+DLGLOBAL Repeating message (i = 106)
+DLGLOBAL Repeating message (i = 107)
+DLGLOBAL Repeating message (i = 108)
+DLGLOBAL Repeating message (i = 109)
+DLGLOBAL Repeating message (i = 110)
+DLGLOBAL Repeating message (i = 111)
+DLGLOBAL Repeating message (i = 112)
+DLGLOBAL Repeating message (i = 113)
+DLGLOBAL Repeating message (i = 114)
+DLGLOBAL Repeating message (i = 115)
+DLGLOBAL Repeating message (i = 116)
+DLGLOBAL Repeating message (i = 117)
+DLGLOBAL Repeating message (i = 118)
+DLGLOBAL Repeating message (i = 119)
+DLGLOBAL Repeating message (i = 120)
+DLGLOBAL Repeating message (i = 121)
+DLGLOBAL Repeating message (i = 122)
+DLGLOBAL Repeating message (i = 123)
+DLGLOBAL Repeating message (i = 124)
+DLGLOBAL Repeating message (i = 125)
+DLGLOBAL Repeating message (i = 126)
+DLGLOBAL Repeating message (i = 127)
+DLGLOBAL Repeating message (i = 128)
+DLGLOBAL Repeating message (i = 129)
+DLGLOBAL Repeating message (i = 130)
+DLGLOBAL Repeating message (i = 131)
+DLGLOBAL Repeating message (i = 132)
+DLGLOBAL Repeating message (i = 133)
+DLGLOBAL Repeating message (i = 134)
+DLGLOBAL Repeating message (i = 135)
+DLGLOBAL Repeating message (i = 136)
+DLGLOBAL Repeating message (i = 137)
+DLGLOBAL Repeating message (i = 138)
+DLGLOBAL Repeating message (i = 139)
+DLGLOBAL Repeating message (i = 140)
+DLGLOBAL Repeating message (i = 141)
+DLGLOBAL Repeating message (i = 142)
+DLGLOBAL Repeating message (i = 143)
+DLGLOBAL Repeating message (i = 144)
+DLGLOBAL Repeating message (i = 145)
+DLGLOBAL Repeating message (i = 146)
+DLGLOBAL Repeating message (i = 147)
+DLGLOBAL Repeating message (i = 148)
+DLGLOBAL Repeating message (i = 149)
+DLGLOBAL Repeating message (i = 150)
+DLGLOBAL Repeating message (i = 151)
+DLGLOBAL Repeating message (i = 152)
+DLGLOBAL Repeating message (i = 153)
+DLGLOBAL Repeating message (i = 154)
+DLGLOBAL Repeating message (i = 155)
+DLGLOBAL Repeating message (i = 156)
+DLGLOBAL Repeating message (i = 157)
+DLGLOBAL Repeating message (i = 158)
+DLGLOBAL Repeating message (i = 159)
+DLGLOBAL Repeating message (i = 160)
+DLGLOBAL Repeating message (i = 161)
+DLGLOBAL Repeating message (i = 162)
+DLGLOBAL Repeating message (i = 163)
+DLGLOBAL Repeating message (i = 164)
+DLGLOBAL Repeating message (i = 165)
+DLGLOBAL Repeating message (i = 166)
+DLGLOBAL Repeating message (i = 167)
+DLGLOBAL Repeating message (i = 168)
+DLGLOBAL Repeating message (i = 169)
+DLGLOBAL Repeating message (i = 170)
+DLGLOBAL Repeating message (i = 171)
+DLGLOBAL Repeating message (i = 172)
+DLGLOBAL Repeating message (i = 173)
+DLGLOBAL Repeating message (i = 174)
+DLGLOBAL Repeating message (i = 175)
+DLGLOBAL Repeating message (i = 176)
+DLGLOBAL Repeating message (i = 177)
+DLGLOBAL Repeating message (i = 178)
+DLGLOBAL Repeating message (i = 179)
+DLGLOBAL Repeating message (i = 180)
+DLGLOBAL Repeating message (i = 181)
+DLGLOBAL Repeating message (i = 182)
+DLGLOBAL Repeating message (i = 183)
+DLGLOBAL Repeating message (i = 184)
+DLGLOBAL Repeating message (i = 185)
+DLGLOBAL Repeating message (i = 186)
+DLGLOBAL Repeating message (i = 187)
+DLGLOBAL Repeating message (i = 188)
+DLGLOBAL Repeating message (i = 189)
+DLGLOBAL Repeating message (i = 190)
+DLGLOBAL Repeating message (i = 191)
+DLGLOBAL Repeating message (i = 192)
+DLGLOBAL Repeating message (i = 193)
+DLGLOBAL Repeating message (i = 194)
+DLGLOBAL Repeating message (i = 195)
+DLGLOBAL Repeating message (i = 196)
+DLGLOBAL Repeating message (i = 197)
+DLGLOBAL Repeating message (i = 198)
+DLGLOBAL Repeating message (i = 199)
diff --git a/tests/logging/logging_test.c b/tests/logging/logging_test.c
index 8fd71d0c..3548b1dd 100644
--- a/tests/logging/logging_test.c
+++ b/tests/logging/logging_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/logging.h>
@@ -37,19 +33,19 @@ static const struct log_info_cat default_categories[] = {
[DRLL] = {
.name = "DRLL",
.description = "A-bis Radio Link Layer (RLL)",
- .color = "\033[1;31m",
+ .color = OSMO_LOGCOLOR_RED,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DCC] = {
.name = "DCC",
.description = "Layer3 Call Control (CC)",
- .color = "\033[1;32m",
+ .color = OSMO_LOGCOLOR_GREEN,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMM] = {
.name = NULL,
.description = "Layer3 Mobility Management (MM)",
- .color = "\033[1;33m",
+ .color = OSMO_LOGCOLOR_YELLOW,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
@@ -77,10 +73,16 @@ int main(int argc, char **argv)
stderr_target = log_target_create_stderr();
log_add_target(stderr_target);
log_set_all_filter(stderr_target, 1);
- log_set_print_filename(stderr_target, 0);
+ log_set_print_filename2(stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category_hex(stderr_target, 0);
log_set_print_category(stderr_target, 1);
log_set_use_color(stderr_target, 0);
+ if (argc > 1 && !strcmp(argv[1], "wqueue"))
+ log_target_file_switch_to_wqueue(stderr_target);
+ else
+ log_target_file_switch_to_stream(stderr_target);
+
log_parse_category_mask(stderr_target, "DRLL:DCC");
log_parse_category_mask(stderr_target, "DRLL");
@@ -128,5 +130,13 @@ int main(int argc, char **argv)
log_set_category_filter(stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
DEBUGP(DLGLOBAL, "You should see this (DLGLOBAL on DEBUG)\n");
+ /* Test printing of the filename */
+ log_set_print_filename2(stderr_target, LOG_FILENAME_BASENAME);
+
+ log_set_print_filename_pos(stderr_target, LOG_FILENAME_POS_HEADER_END);
+ DEBUGP(DLGLOBAL, "A message with source info printed first\n");
+ log_set_print_filename_pos(stderr_target, LOG_FILENAME_POS_LINE_END);
+ DEBUGP(DLGLOBAL, "A message with source info printed last\n");
+
return 0;
}
diff --git a/tests/logging/logging_test.err b/tests/logging/logging_test.err
index 17b3cad0..01ab8782 100644
--- a/tests/logging/logging_test.err
+++ b/tests/logging/logging_test.err
@@ -7,3 +7,5 @@ DLGLOBAL You should see this on DLGLOBAL (c)
DLGLOBAL You should see this on DLGLOBAL (d)
DLGLOBAL You should see this on DLGLOBAL (e)
DLGLOBAL You should see this (DLGLOBAL on DEBUG)
+DLGLOBAL logging_test.c:137 A message with source info printed first
+DLGLOBAL A message with source info printed last (logging_test.c:139)
diff --git a/tests/logging/logging_vty_test.c b/tests/logging/logging_vty_test.c
index e7019f61..c1a28533 100644
--- a/tests/logging/logging_vty_test.c
+++ b/tests/logging/logging_vty_test.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#define _GNU_SOURCE
@@ -82,7 +78,7 @@ DEFUN(log_sweep, log_sweep_cmd,
return CMD_SUCCESS;
}
-static void vty_commands_init()
+static void vty_commands_init(void)
{
install_element_ve(&log_sweep_cmd);
}
@@ -125,7 +121,7 @@ const struct log_info log_info = {
.num_cat = ARRAY_SIZE(default_categories),
};
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -254,6 +250,7 @@ int main(int argc, char **argv)
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_target_file_switch_to_wqueue(osmo_stderr_target);
if (cmdline_config.config_file) {
rc = vty_read_config_file(cmdline_config.config_file, NULL);
@@ -264,7 +261,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
@@ -288,10 +285,10 @@ int main(int argc, char **argv)
osmo_select_main(0);
}
- log_fini();
+ talloc_report(tall_vty_ctx, stderr);
+ talloc_report_full(root_ctx, stderr);
- talloc_free(root_ctx);
- talloc_free(tall_vty_ctx);
+ log_fini();
return 0;
}
diff --git a/tests/logging/logging_vty_test.vty b/tests/logging/logging_vty_test.vty
index d77f8ce4..da09be76 100644
--- a/tests/logging/logging_vty_test.vty
+++ b/tests/logging/logging_vty_test.vty
@@ -29,7 +29,7 @@ logging_vty_test(config)# no log stderr
logging_vty_test(config)# exit
logging_vty_test# logging level force-all notice
-Logging was not enabled.
+% Logging was not enabled.
logging_vty_test# logging enable
logging_vty_test# logging filter all 1
@@ -48,12 +48,13 @@ logging_vty_test# list
logging color (0|1)
logging timestamp (0|1)
logging print extended-timestamp (0|1)
+ logging print thread-id (0|1)
logging print category (0|1)
logging print category-hex (0|1)
logging print level (0|1)
logging print file (0|1|basename) [last]
logging set-log-mask MASK
- logging level (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)
+ logging level (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal|liuup|lpfcp|lcsn1|lio) (debug|info|notice|error|fatal)
logging level set-all (debug|info|notice|error|fatal)
logging level force-all (debug|info|notice|error|fatal)
no logging level force-all
@@ -69,6 +70,10 @@ logging_vty_test# logging ?
set-log-mask Set the logmask of this logging target
level Set the log level for a specified category
+logging_vty_test# logging timestamp ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with current timestamp
+
logging_vty_test# logging level ?
... ! all
aa Antropomorphic Armadillos (AA)
@@ -108,6 +113,98 @@ logging_vty_test# logging level set-all ?
error Log error messages and higher levels
fatal Log only fatal messages
+logging_vty_test# logging print ?
+ extended-timestamp Configure log message timestamping
+ thread-id Configure log message logging Thread ID
+ category Configure log message
+ category-hex Configure log message
+ level Configure log message
+ file Configure log message
+
+logging_vty_test# logging print extended-timestamp ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with current timestamp with YYYYMMDDhhmmssnnn
+
+logging_vty_test# logging print thread-id ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with current Thread ID
+
+logging_vty_test# logging print category ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with category/subsystem name
+
+logging_vty_test# logging print category-hex ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with category/subsystem nr in hex ('<000b>')
+
+logging_vty_test# logging print level ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with the log level name
+
+logging_vty_test# logging print file ?
+ 0 Don't prefix each log message
+ 1 Prefix each log message with the source file and line
+ basename Prefix each log message with the source file's basename (strip leading paths) and line
+
+logging_vty_test# logging print file basename ?
+ [last] Log source file info at the end of a log line. If omitted, log source file info just before the log text.
+
+logging_vty_test# configure terminal
+logging_vty_test(config)# log stderr
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging timestamp 0
+... !timestamp
+
+logging_vty_test(config-log)# logging timestamp 1
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging timestamp 1
+... !timestamp
+
+logging_vty_test(config-log)# ### with 'extended-timestamp 1', 'logging timestamp' is not shown
+logging_vty_test(config-log)# logging print extended-timestamp 1
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging print extended-timestamp 1
+... !timestamp
+
+logging_vty_test(config-log)# ### 'logging timestamp 0' effect not shown while 'extended-timestamp' == 1
+logging_vty_test(config-log)# logging timestamp 0
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging print extended-timestamp 1
+... !timestamp
+
+logging_vty_test(config-log)# ### 'logging timestamp 1' remains set upon 'extended-timestamp 0'
+logging_vty_test(config-log)# logging timestamp 1
+logging_vty_test(config-log)# logging print extended-timestamp 0
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging timestamp 1
+... !timestamp
+
+logging_vty_test(config-log)# logging timestamp 0
+logging_vty_test(config-log)# show running-config
+...
+log stderr
+... !timestamp
+ logging timestamp 0
+... !timestamp
+
+logging_vty_test(config-log)# exit
+logging_vty_test(config)# no log stderr
+logging_vty_test(config)# exit
logging_vty_test# log-sweep
DAA DEBUG Log message for DAA on level LOGL_DEBUG
@@ -471,37 +568,45 @@ DEEE FATAL Log message for DEEE on level LOGL_FATAL
logging_vty_test# list
...
- logp (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal) .LOGMESSAGE
+ logp (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro|lns|lbssgp|lnsdata|lnssignal|liuup|lpfcp|lcsn1|lio) (debug|info|notice|error|fatal) .LOGMESSAGE
...
logging_vty_test# logp?
logp Print a message on all log outputs; useful for placing markers in test logs
logging_vty_test# logp ?
- aa Antropomorphic Armadillos (AA)
- bb Bidirectional Breadspread (BB)
- ccc Chaos Communication Congress (CCC)
- dddd Dehydrated Dribbling Duck Dunkers (DDDD)
- eee Exhaustive Entropy Extraction (EEE)
- lglobal Library-internal global log family
- llapd LAPD in libosmogsm
- linp A-bis Intput Subsystem
- lmux A-bis B-Subchannel TRAU Frame Multiplex
- lmi A-bis Input Driver for Signalling
- lmib A-bis Input Driver for B-Channels (voice)
- lsms Layer3 Short Message Service (SMS)
- lctrl Control Interface
- lgtp GPRS GTP library
- lstats Statistics messages and logging
- lgsup Generic Subscriber Update Protocol
- loap Osmocom Authentication Protocol
- lss7 libosmo-sigtran Signalling System 7
- lsccp libosmo-sigtran SCCP Implementation
- lsua libosmo-sigtran SCCP User Adaptation
- lm3ua libosmo-sigtran MTP3 User Adaptation
- lmgcp libosmo-mgcp Media Gateway Control Protocol
- ljibuf libosmo-netif Jitter Buffer
- lrspro Remote SIM protocol
+ aa Antropomorphic Armadillos (AA)
+ bb Bidirectional Breadspread (BB)
+ ccc Chaos Communication Congress (CCC)
+ dddd Dehydrated Dribbling Duck Dunkers (DDDD)
+ eee Exhaustive Entropy Extraction (EEE)
+ lglobal Library-internal global log family
+ llapd LAPD in libosmogsm
+ linp A-bis Intput Subsystem
+ lmux A-bis B-Subchannel TRAU Frame Multiplex
+ lmi A-bis Input Driver for Signalling
+ lmib A-bis Input Driver for B-Channels (voice)
+ lsms Layer3 Short Message Service (SMS)
+ lctrl Control Interface
+ lgtp GPRS GTP library
+ lstats Statistics messages and logging
+ lgsup Generic Subscriber Update Protocol
+ loap Osmocom Authentication Protocol
+ lss7 libosmo-sigtran Signalling System 7
+ lsccp libosmo-sigtran SCCP Implementation
+ lsua libosmo-sigtran SCCP User Adaptation
+ lm3ua libosmo-sigtran MTP3 User Adaptation
+ lmgcp libosmo-mgcp Media Gateway Control Protocol
+ ljibuf libosmo-netif Jitter Buffer
+ lrspro Remote SIM protocol
+ lns GPRS NS layer
+ lbssgp GPRS BSSGP layer
+ lnsdata GPRS NS layer data PDU
+ lnssignal GPRS NS layer signal PDU
+ liuup Iu UP layer
+ lpfcp libosmo-pfcp Packet Forwarding Control Protocol
+ lcsn1 libosmo-csn1 Concrete Syntax Notation 1 codec
+ lio libosmocore IO Subsystem
logging_vty_test# logp lglobal ?
debug Log debug messages and higher levels
@@ -521,3 +626,6 @@ DAA ERROR This is the log message
logging_vty_test# logp lglobal debug This log message is not echoed
logging_vty_test# logp lglobal notice This log message is echoed
DLGLOBAL NOTICE This log message is echoed
+
+logging_vty_test# logp lctrl notice This is a CTRL specific message
+DLCTRL NOTICE This is a CTRL specific message
diff --git a/tests/loggingrb/logging_test.err b/tests/loggingrb/logging_test.err
index b59d2e83..e070561f 100644
--- a/tests/loggingrb/logging_test.err
+++ b/tests/loggingrb/logging_test.err
@@ -1,3 +1,2 @@
-You should see this
-You should see this
- \ No newline at end of file
+You should see this
+You should see this
diff --git a/tests/loggingrb/loggingrb_test.c b/tests/loggingrb/loggingrb_test.c
index 3372c0c3..fd11896a 100644
--- a/tests/loggingrb/loggingrb_test.c
+++ b/tests/loggingrb/loggingrb_test.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/logging.h>
@@ -34,19 +30,19 @@ static const struct log_info_cat default_categories[] = {
[DRLL] = {
.name = "DRLL",
.description = "A-bis Radio Link Layer (RLL)",
- .color = "\033[1;31m",
+ .color = OSMO_LOGCOLOR_RED,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DCC] = {
.name = "DCC",
.description = "Layer3 Call Control (CC)",
- .color = "\033[1;32m",
+ .color = OSMO_LOGCOLOR_GREEN,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMM] = {
.name = NULL,
.description = "Layer3 Mobility Management (MM)",
- .color = "\033[1;33m",
+ .color = OSMO_LOGCOLOR_BLUE,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
@@ -64,7 +60,9 @@ int main(int argc, char **argv)
ringbuf_target = log_target_create_rb(0x1000);
log_add_target(ringbuf_target);
log_set_all_filter(ringbuf_target, 1);
- log_set_print_filename(ringbuf_target, 0);
+ log_set_print_filename2(ringbuf_target, LOG_FILENAME_NONE);
+ log_set_print_category(ringbuf_target, 0);
+ log_set_print_category_hex(ringbuf_target, 0);
log_parse_category_mask(ringbuf_target, "DRLL:DCC");
log_parse_category_mask(ringbuf_target, "DRLL");
diff --git a/tests/msgb/msgb_test.c b/tests/msgb/msgb_test.c
index ffaa1557..7966103b 100644
--- a/tests/msgb/msgb_test.c
+++ b/tests/msgb/msgb_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdlib.h>
@@ -65,7 +61,7 @@ static int osmo_panic_try(volatile int *exception, int setjmp_result)
return *exception == 0;
}
-static void test_msgb_api()
+static void test_msgb_api(void)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
unsigned char *cptr = NULL;
@@ -121,7 +117,7 @@ static void test_msgb_api()
msgb_free(msg);
}
-static void test_msgb_api_errors()
+static void test_msgb_api_errors(void)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
volatile int e = 0;
@@ -142,7 +138,7 @@ static void test_msgb_api_errors()
osmo_set_panic_handler(NULL);
}
-static void test_msgb_copy()
+static void test_msgb_copy(void)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
struct msgb *msg2;
@@ -165,19 +161,31 @@ static void test_msgb_copy()
OSMO_ASSERT(msgb_l1len(msg) == msgb_l1len(msg2));
OSMO_ASSERT(msgb_l2len(msg) == msgb_l2len(msg2));
OSMO_ASSERT(msgb_l3len(msg) == msgb_l3len(msg2));
- OSMO_ASSERT(msg->tail - msg->l4h == msg2->tail - msg2->l4h);
+ OSMO_ASSERT(msgb_l4len(msg) == msgb_l4len(msg2));
+
+ if (!msgb_eq_data_print(msg2, msg->data, msgb_length(msg)))
+ printf("copy test failed!\n");
- for (i = 0; i < msgb_length(msg2); i++)
- OSMO_ASSERT(msg2->data[i] == (uint8_t)i);
+ if (!msgb_eq_l1_data_print(msg2, msgb_l1(msg), msgb_l1len(msg)))
+ printf("copy test failed at L1!\n");
+ if (!msgb_eq_l2_data_print(msg2, msgb_l2(msg), msgb_l2len(msg)))
+ printf("copy test failed at L2!\n");
+ if (!msgb_eq_l3_data_print(msg2, msgb_l3(msg), msgb_l3len(msg)))
+ printf("copy test failed at L3!\n");
+ if (!msgb_eq_l4_data_print(msg2, msgb_l4(msg), msgb_l4len(msg)))
+ printf("copy test failed at L4!\n");
printf("Src: %s\n", msgb_hexdump(msg));
- printf("Dst: %s\n", msgb_hexdump(msg));
+ printf("Dst: %s\n", msgb_hexdump(msg2));
+
+ OSMO_ASSERT(msgb_test_invariant(msg));
+ OSMO_ASSERT(msgb_test_invariant(msg2));
msgb_free(msg);
msgb_free(msg2);
}
-static void test_msgb_resize_area()
+static void test_msgb_resize_area(void)
{
struct msgb *msg = msgb_alloc_headroom(4096, 128, "data");
int rc;
@@ -277,7 +285,7 @@ static void test_msgb_resize_area()
osmo_set_panic_handler(NULL);
}
-static void test_msgb_printf()
+static void test_msgb_printf(void)
{
struct msgb *msg;
struct msgb *msg_ref;
diff --git a/tests/msgfile/msgfile_test.c b/tests/msgfile/msgfile_test.c
index 2684b6a4..ec58816b 100644
--- a/tests/msgfile/msgfile_test.c
+++ b/tests/msgfile/msgfile_test.c
@@ -13,14 +13,11 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/msgfile.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
#include <stdio.h>
@@ -43,8 +40,8 @@ int main(int argc, char **argv)
{
struct osmo_config_list *entries;
- /* todo use msgfile_test.c.in and replace the path */
- entries = osmo_config_list_parse(NULL, "msgconfig.cfg");
+ OSMO_ASSERT(argc > 1);
+ entries = osmo_config_list_parse(NULL, argv[1]);
dump_entries(entries);
talloc_free(entries);
diff --git a/tests/oap/oap_client_test.c b/tests/oap/oap_client_test.c
index dadf2ee7..622912f9 100644
--- a/tests/oap/oap_client_test.c
+++ b/tests/oap/oap_client_test.c
@@ -260,7 +260,8 @@ int main(int argc, char **argv)
OSMO_ASSERT(osmo_stderr_target);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0);
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_parse_category_mask(osmo_stderr_target, "DLOAP,1");
diff --git a/tests/oap/oap_test.c b/tests/oap/oap_test.c
index 32676ca3..bebd6031 100644
--- a/tests/oap/oap_test.c
+++ b/tests/oap/oap_test.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
diff --git a/tests/osmo-auc-gen/osmo-auc-gen_test.ok b/tests/osmo-auc-gen/osmo-auc-gen_test.ok
index 2840783a..2da0b024 100644
--- a/tests/osmo-auc-gen/osmo-auc-gen_test.ok
+++ b/tests/osmo-auc-gen/osmo-auc-gen_test.ok
@@ -1,7 +1,7 @@
> osmo-auc-gen -3 -a milenage -r 6a61050765caa32c90371370e5d6dc2d -k 1dc4f974325cce611e54f516dc1fec56 -o 2a48162ff3edca4adf0b7b5e527d6c16 -s 0
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 6a61050765caa32c90371370e5d6dc2d
@@ -9,6 +9,8 @@ AUTN: 790c5d80c47b0000716ce00883bc39e1
IK: 6cf555588bb61ab2ff23cd333c05ed09
CK: f0b29f50a7d873f30336473bdc35d04f
RES: f511d3a7f06e6a30
+IMS nonce: amEFB2XKoyyQNxNw5dbcLXkMXYDEewAAcWzgCIO8OeE=
+IMS res: 9RHTp/BuajA=
SRES: 057fb997
Kc: 60524000cc5e5407
SQN: 0
@@ -16,7 +18,7 @@ IND: 0
> osmo-auc-gen -3 -a milenage -r 6a61050765caa32c90371370e5d6dc2d -k 1dc4f974325cce611e54f516dc1fec56 -o 2a48162ff3edca4adf0b7b5e527d6c16 -s 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 6a61050765caa32c90371370e5d6dc2d
@@ -24,6 +26,8 @@ AUTN: 790c5d80c47a000058508ab3864e26a0
IK: 6cf555588bb61ab2ff23cd333c05ed09
CK: f0b29f50a7d873f30336473bdc35d04f
RES: f511d3a7f06e6a30
+IMS nonce: amEFB2XKoyyQNxNw5dbcLXkMXYDEegAAWFCKs4ZOJqA=
+IMS res: 9RHTp/BuajA=
SRES: 057fb997
Kc: 60524000cc5e5407
SQN: 1
@@ -31,7 +35,7 @@ IND: 1
> osmo-auc-gen -3 -a milenage -r 6a61050765caa32c90371370e5d6dc2d -k 1dc4f974325cce611e54f516dc1fec56 -o 2a48162ff3edca4adf0b7b5e527d6c16 -s 23
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 6a61050765caa32c90371370e5d6dc2d
@@ -39,6 +43,8 @@ AUTN: 790c5d80c46c0000e74d796ec095dbee
IK: 6cf555588bb61ab2ff23cd333c05ed09
CK: f0b29f50a7d873f30336473bdc35d04f
RES: f511d3a7f06e6a30
+IMS nonce: amEFB2XKoyyQNxNw5dbcLXkMXYDEbAAA5015bsCV2+4=
+IMS res: 9RHTp/BuajA=
SRES: 057fb997
Kc: 60524000cc5e5407
SQN: 23
@@ -46,7 +52,7 @@ IND: 23
> osmo-auc-gen -3 -a milenage -r 1dc4f974325cce611e54f516dc1fec56 -k 2a48162ff3edca4adf0b7b5e527d6c16 -o 6a61050765caa32c90371370e5d6dc2d -s 42
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 1dc4f974325cce611e54f516dc1fec56
@@ -54,6 +60,8 @@ AUTN: 434a46a71aeb0000fedc563f27a0916c
IK: d7213dd74860ccb8c14e54c0c4abc91c
CK: c350653d72f7a5bac3a27422e5186019
RES: 912cdfaadd7b0154
+IMS nonce: HcT5dDJczmEeVPUW3B/sVkNKRqca6wAA/txWPyegkWw=
+IMS res: kSzfqt17AVQ=
SRES: 4c57defe
Kc: 169d78081b24c007
SQN: 42
@@ -61,7 +69,7 @@ IND: 10
> osmo-auc-gen -3 -a milenage -r 2a48162ff3edca4adf0b7b5e527d6c16 -k 6a61050765caa32c90371370e5d6dc2d -o 1dc4f974325cce611e54f516dc1fec56 -s 99
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 2a48162ff3edca4adf0b7b5e527d6c16
@@ -69,6 +77,8 @@ AUTN: bfbf3332c91e0000d6199cad31d15f26
IK: 191a93c4396113bff6939d4f98e169a6
CK: 9c38d9089265ed5ea164e190a65c200d
RES: fd40205be2c9c7b2
+IMS nonce: KkgWL/PtykrfC3teUn1sFr+/MzLJHgAA1hmcrTHRXyY=
+IMS res: /UAgW+LJx7I=
SRES: 1f89e7e9
Kc: d2d5361395b9b74a
SQN: 99
@@ -76,7 +86,7 @@ IND: 3
> osmo-auc-gen -3 -a milenage -r 6a61050765caa32c90371370e5d6dc2d -k 2a48162ff3edca4adf0b7b5e527d6c16 -o 1dc4f974325cce611e54f516dc1fec56 -s 281474976710655
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 6a61050765caa32c90371370e5d6dc2d
@@ -84,6 +94,8 @@ AUTN: afb993e4f4b8000069cdeebb4a4b5b58
IK: c348c2fe2f3e1fb37a7ae1638163bd98
CK: e740c156278705a14e1a99ba6d31334f
RES: 7c04e86a67967fcd
+IMS nonce: amEFB2XKoyyQNxNw5dbcLa+5k+T0uAAAac3uu0pLW1g=
+IMS res: fAToameWf80=
SRES: 1b9297a7
Kc: 10687b71e4eb94c5
SQN: 281474976710655
@@ -91,7 +103,7 @@ IND: 31
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -99,6 +111,8 @@ AUTN: 8704f5ba55d30000541dde77ea5b1d8c
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV0wAAVB3ed+pbHYw=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 32
@@ -107,7 +121,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 5
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -115,6 +129,8 @@ AUTN: 8704f5ba55d6000079267a4b347ad890
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV1gAAeSZ6SzR62JA=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 37
@@ -123,7 +139,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 23
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -131,6 +147,8 @@ AUTN: 8704f5ba55c40000129ddaa4f5016e25
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpVxAAAEp3apPUBbiU=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 55
@@ -139,7 +157,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 31
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -147,6 +165,8 @@ AUTN: 8704f5ba55cc00009d169f5ff89f6087
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpVzAAAnRafX/ifYIc=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 63
@@ -155,7 +175,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 0
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -163,6 +183,8 @@ AUTN: 8704f5ba55eb0000d7fc4f7f19cfc180
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV6wAA1/xPfxnPwYA=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 24
@@ -171,7 +193,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -179,6 +201,8 @@ AUTN: 8704f5ba55eb0000d7fc4f7f19cfc180
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV6wAA1/xPfxnPwYA=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 24
@@ -187,7 +211,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 1 --ind 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -195,6 +219,8 @@ AUTN: 8704f5ba55ea0000aab06de3fd6c01af
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpV6gAAqrBt4/1sAa8=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 25
@@ -203,7 +229,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 8
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -211,6 +237,8 @@ AUTN: 8704f5ba54f30000cbba2fbba3c5e242
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpU8wAAy7ovu6PF4kI=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 256
@@ -219,7 +247,7 @@ SQN.MS: 23
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 8 --ind 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
RAND: 39fa2f4e3d523d8619a73b4f65c3e14d
@@ -227,6 +255,8 @@ AUTN: 8704f5ba54f200008f8e14579da5ecbb
IK: 27497388b6cb044648f396aa155b95ef
CK: f64735036e5871319c679f4742a75ea1
RES: e229c19e791f2e41
+IMS nonce: OfovTj1SPYYZpztPZcPhTYcE9bpU8gAAj44UV52l7Ls=
+IMS res: 4inBnnkfLkE=
SRES: 9b36efdf
Kc: 059a4f668f6fbe39
SQN: 257
@@ -236,27 +266,27 @@ SQN.MS: 23
expecting error:
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind -1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
expecting error:
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 32
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
expecting error:
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind 42
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
expecting error:
> osmo-auc-gen -3 -a milenage -r 39fa2f4e3d523d8619a73b4f65c3e14d -k EB215756028D60E3275E613320AEC880 -o FB2A3D1B360F599ABAB99DB8669F8308 -A 979498b1f72d3e28c59fa2e72f9c --ind-len 0 --ind 1
-osmo-auc-gen (C) 2011-2012 by Harald Welte
+osmo-auc-gen (C) 2011-2023 by Harald Welte
This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY
diff --git a/tests/osmo_io/osmo_io_test.c b/tests/osmo_io/osmo_io_test.c
new file mode 100644
index 00000000..93beef4f
--- /dev/null
+++ b/tests/osmo_io/osmo_io_test.c
@@ -0,0 +1,178 @@
+/*
+ * (C) 2023 by sysmocom s.f.m.c
+ * Author: Daniel Willmann <daniel@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/osmo_io.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/utils.h>
+
+#include "config.h"
+
+#define TEST_START() printf("Running %s\n", __func__)
+
+static uint8_t TESTDATA[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
+};
+
+
+static void *ctx = NULL;
+
+static void read_cb(struct osmo_io_fd *iofd, int rc, struct msgb *msg)
+{
+ printf("%s: read() msg with len=%d\n", osmo_iofd_get_name(iofd), rc);
+ if (msg)
+ printf("%s\n", osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+
+ talloc_free(msg);
+}
+
+static void write_cb(struct osmo_io_fd *iofd, int rc, struct msgb *msg)
+{
+ uint8_t *buf;
+ printf("%s: write() returned rc=%d\n", osmo_iofd_get_name(iofd), rc);
+ if (rc == 0) {
+ msg = msgb_alloc(1024, "Test data");
+ buf = msgb_put(msg, sizeof(TESTDATA));
+ memcpy(buf, TESTDATA, sizeof(TESTDATA));
+
+ osmo_iofd_write_msgb(iofd, msg);
+ }
+}
+
+struct osmo_io_ops ioops_conn_read_write = {
+ .read_cb = read_cb,
+ .write_cb = write_cb,
+};
+
+static void test_connected(void)
+{
+ int fds[2] = {0, 0}, rc;
+ struct osmo_io_fd *iofd1, *iofd2;
+
+ TEST_START();
+
+ rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ OSMO_ASSERT(rc == 0);
+
+ iofd1 = osmo_iofd_setup(ctx, fds[0], "ep1", OSMO_IO_FD_MODE_READ_WRITE, &ioops_conn_read_write, NULL);
+ osmo_iofd_register(iofd1, fds[0]);
+ iofd2 = osmo_iofd_setup(ctx, fds[1], "ep2", OSMO_IO_FD_MODE_READ_WRITE, &ioops_conn_read_write, NULL);
+ osmo_iofd_register(iofd2, fds[1]);
+ // Explicitly check if ep1 is connected through write_cb
+ osmo_iofd_notify_connected(iofd1);
+
+ /* Allow enough cycles to handle the messages */
+ for (int i = 0; i < 128; i++)
+ osmo_select_main(1);
+
+ osmo_iofd_free(iofd1);
+ osmo_iofd_free(iofd2);
+
+ for (int i = 0; i < 128; i++)
+ osmo_select_main(1);
+}
+
+static void recvfrom_cb(struct osmo_io_fd *iofd, int rc, struct msgb *msg,
+ const struct osmo_sockaddr *saddr)
+{
+ printf("%s: recvfrom() msg with len=%d\n", osmo_iofd_get_name(iofd), rc);
+ if (msg)
+ printf("%s\n", osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+
+ talloc_free(msg);
+}
+
+static void sendto_cb(struct osmo_io_fd *iofd, int rc, struct msgb *msg,
+ const struct osmo_sockaddr *daddr)
+{
+ printf("%s: sendto() returned rc=%d\n", osmo_iofd_get_name(iofd), rc);
+}
+
+struct osmo_io_ops ioops_conn_recvfrom_sendto = {
+ .sendto_cb = sendto_cb,
+ .recvfrom_cb = recvfrom_cb,
+};
+
+static void test_unconnected(void)
+{
+ int fds[2] = {0, 0}, rc;
+ struct osmo_io_fd *iofd1, *iofd2;
+ struct msgb *msg;
+ uint8_t *buf;
+
+ TEST_START();
+
+ rc = socketpair(AF_UNIX, SOCK_DGRAM, 0, fds);
+ OSMO_ASSERT(rc == 0);
+
+ iofd1 = osmo_iofd_setup(ctx, fds[0], "ep1", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &ioops_conn_recvfrom_sendto, NULL);
+ osmo_iofd_register(iofd1, fds[0]);
+ iofd2 = osmo_iofd_setup(ctx, fds[1], "ep2", OSMO_IO_FD_MODE_RECVFROM_SENDTO, &ioops_conn_recvfrom_sendto, NULL);
+ osmo_iofd_register(iofd2, fds[1]);
+
+ msg = msgb_alloc(1024, "Test data");
+ buf = msgb_put(msg, sizeof(TESTDATA));
+ memcpy(buf, TESTDATA, sizeof(TESTDATA));
+
+ osmo_iofd_sendto_msgb(iofd1, msg, 0, NULL);
+
+ /* Allow enough cycles to handle the messages */
+ for (int i = 0; i < 128; i++)
+ osmo_select_main(1);
+
+ osmo_iofd_free(iofd1);
+ osmo_iofd_free(iofd2);
+
+ for (int i = 0; i < 128; i++)
+ osmo_select_main(1);
+}
+static const struct log_info_cat default_categories[] = {
+};
+
+static struct log_info info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+int main(int argc, char *argv[])
+{
+ ctx = talloc_named_const(NULL, 0, "osmo_io_test");
+ osmo_init_logging2(ctx, &info);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+
+ test_connected();
+ test_unconnected();
+
+ return EXIT_SUCCESS;
+}
diff --git a/debian/patches/series b/tests/osmo_io/osmo_io_test.err
index e69de29b..e69de29b 100644
--- a/debian/patches/series
+++ b/tests/osmo_io/osmo_io_test.err
diff --git a/tests/osmo_io/osmo_io_test.ok b/tests/osmo_io/osmo_io_test.ok
new file mode 100644
index 00000000..6527c191
--- /dev/null
+++ b/tests/osmo_io/osmo_io_test.ok
@@ -0,0 +1,9 @@
+Running test_connected
+ep1: write() returned rc=0
+ep1: write() returned rc=16
+ep2: read() msg with len=16
+01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10
+Running test_unconnected
+ep1: sendto() returned rc=16
+ep2: recvfrom() msg with len=16
+01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10
diff --git a/tests/rlp/rlp_test.c b/tests/rlp/rlp_test.c
new file mode 100644
index 00000000..be26c99d
--- /dev/null
+++ b/tests/rlp/rlp_test.c
@@ -0,0 +1,217 @@
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/rlp.h>
+
+struct rlp_testcase {
+ const char *name;
+ const char *encoded_hex;
+ struct osmo_rlp_frame_decoded decoded;
+};
+
+
+const struct rlp_testcase testcases[] = {
+ {
+ .name = "XID1",
+ .encoded_hex = "f95f1100213d313d414e6108510600000000000000000000000000c13c6b",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_XID,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0x6b3cc1,
+ .info = { 0x11, 0x00, 0x21, 0x3d, 0x31, 0x3d, 0x41, 0x4e, 0x61, 0x08,
+ 0x51, 0x06, },
+ .info_len = 25,
+ },
+ }, {
+ .name = "XID2",
+ .encoded_hex = "f95f1101213d313d41305106610774000008060000000000000000ba14a0",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_XID,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0xa014ba,
+ .info = { 0x11, 0x01, 0x21, 0x3d, 0x31, 0x3d, 0x41, 0x30, 0x51, 0x06,
+ 0x61, 0x07, 0x74, 0x00, 0x00, 0x08, 0x06, },
+ .info_len = 25,
+ },
+ }, {
+ .name = "SABM",
+ .encoded_hex = "f91f0000000000000000000000000000000000000000000000000063b2f3",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_SABM,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0xf3b263,
+ .info = {},
+ .info_len = 0,
+ },
+ }, {
+ .name = "UA",
+ .encoded_hex = "f8330000000000000000000000000000000000000000000000000029d801",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_UA,
+ .s_ftype = 0,
+ .c_r = 0,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0x01d829,
+ .info = {},
+ .info_len = 0,
+ },
+ }, {
+ .name = "IS1",
+ .encoded_hex = "01001f000000000000000000000000000000000000000000000000f174ad",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_IS,
+ .u_ftype = 0,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 0,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0xad74f1,
+ .info = { 0x1f, },
+ .info_len = 25,
+ },
+ }, {
+ .name = "IS2",
+ .encoded_hex = "010401661fffffffffffffffffffffffffffffffffffffffffffff388cd3",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_IS,
+ .u_ftype = 0,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 0,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 1,
+ .fcs = 0xd38c38,
+ .info = { 0x01, 0x66, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff },
+ .info_len = 25,
+ },
+ }, {
+ .name = "DISC",
+ .encoded_hex = "f923000000000000000000000000000000000000000000000000007986f2",
+ .decoded = {
+ .version = 0,
+ .ftype = OSMO_RLP_FT_U,
+ .u_ftype = OSMO_RLP_U_FT_DISC,
+ .s_ftype = 0,
+ .c_r = 1,
+ .p_f = 1,
+ .s_bits = 0,
+ .n_s = 0,
+ .n_r = 0,
+ .fcs = 0xf28679,
+ .info = { },
+ .info_len = 0,
+ },
+ }
+};
+
+static void rlp_frame_print_u(const struct osmo_rlp_frame_decoded *rf)
+{
+ OSMO_ASSERT(rf->ftype == OSMO_RLP_FT_U);
+ printf("C/R=%u P/F=%u U %s (FCS=0x%06x) %s\n", rf->c_r, rf->p_f,
+ get_value_string(osmo_rlp_ftype_u_vals, rf->u_ftype),
+ rf->fcs,
+ rf->u_ftype == OSMO_RLP_U_FT_XID ? osmo_hexdump_nospc(rf->info, rf->info_len) : "");
+}
+
+static void rlp_frame_print_s(const struct osmo_rlp_frame_decoded *rf)
+{
+ OSMO_ASSERT(rf->ftype == OSMO_RLP_FT_S);
+ printf("C/R=%u P/F=%u S N(R)=%u %s (FCS=0x%06x)\n", rf->c_r, rf->p_f,
+ rf->n_r, get_value_string(osmo_rlp_ftype_s_vals, rf->s_ftype),
+ rf->fcs);
+}
+
+static void rlp_frame_print_is(const struct osmo_rlp_frame_decoded *rf)
+{
+ OSMO_ASSERT(rf->ftype == OSMO_RLP_FT_IS);
+ printf("C/R=%u P/F=%u IS N(R)=%u N(S)=%u %s (FCS=0x%06x) %s\n", rf->c_r, rf->p_f,
+ rf->n_r, rf->n_s, get_value_string(osmo_rlp_ftype_s_vals, rf->s_ftype),
+ rf->fcs, osmo_hexdump_nospc(rf->info, rf->info_len));
+}
+
+static void rlp_frame_print(const struct osmo_rlp_frame_decoded *rf)
+{
+ switch (rf->ftype) {
+ case OSMO_RLP_FT_U:
+ rlp_frame_print_u(rf);
+ break;
+ case OSMO_RLP_FT_S:
+ rlp_frame_print_s(rf);
+ break;
+ case OSMO_RLP_FT_IS:
+ rlp_frame_print_is(rf);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void execute_rlp_test(const struct rlp_testcase *tc)
+{
+ struct osmo_rlp_frame_decoded decoded;
+ uint8_t inbuf[240/8];
+ int rc;
+
+ printf("=== STARTING TESTCASE '%s'\n", tc->name);
+
+ rc = osmo_hexparse(tc->encoded_hex, inbuf, sizeof(inbuf));
+ OSMO_ASSERT(rc == 240/8);
+
+ printf("Decoding %s:\n", tc->encoded_hex);
+ rc = osmo_rlp_decode(&decoded, 0, inbuf, rc);
+ OSMO_ASSERT(rc == 0);
+
+ printf("Comparing...\n");
+ rlp_frame_print(&decoded);
+ if (memcmp(&decoded, &tc->decoded, sizeof(decoded))) {
+ printf("DOESN'T MATCH EXPECTED DECODE:\n");
+ rlp_frame_print(&tc->decoded);
+ }
+
+ printf("Reencoding...\n");
+ uint8_t reencoded[240/8];
+ rc = osmo_rlp_encode(reencoded, sizeof(reencoded), &tc->decoded);
+ OSMO_ASSERT(rc == 240/8);
+ if (memcmp(inbuf, reencoded, sizeof(inbuf)))
+ printf("DOESN'T MATCH EXPECTED ENCODE FROM ABOVE\n");
+}
+
+int main(int argc, char **argv)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(testcases); i++) {
+ const struct rlp_testcase *tc = &testcases[i];
+ execute_rlp_test(tc);
+ }
+
+}
diff --git a/tests/rlp/rlp_test.ok b/tests/rlp/rlp_test.ok
new file mode 100644
index 00000000..6d073dbd
--- /dev/null
+++ b/tests/rlp/rlp_test.ok
@@ -0,0 +1,35 @@
+=== STARTING TESTCASE 'XID1'
+Decoding f95f1100213d313d414e6108510600000000000000000000000000c13c6b:
+Comparing...
+C/R=1 P/F=1 U XID (FCS=0x6b3cc1) 1100213d313d414e6108510600000000000000000000000000
+Reencoding...
+=== STARTING TESTCASE 'XID2'
+Decoding f95f1101213d313d41305106610774000008060000000000000000ba14a0:
+Comparing...
+C/R=1 P/F=1 U XID (FCS=0xa014ba) 1101213d313d41305106610774000008060000000000000000
+Reencoding...
+=== STARTING TESTCASE 'SABM'
+Decoding f91f0000000000000000000000000000000000000000000000000063b2f3:
+Comparing...
+C/R=1 P/F=1 U SABM (FCS=0xf3b263)
+Reencoding...
+=== STARTING TESTCASE 'UA'
+Decoding f8330000000000000000000000000000000000000000000000000029d801:
+Comparing...
+C/R=0 P/F=1 U UA (FCS=0x01d829)
+Reencoding...
+=== STARTING TESTCASE 'IS1'
+Decoding 01001f000000000000000000000000000000000000000000000000f174ad:
+Comparing...
+C/R=1 P/F=0 IS N(R)=0 N(S)=0 RR (FCS=0xad74f1) 1f000000000000000000000000000000000000000000000000
+Reencoding...
+=== STARTING TESTCASE 'IS2'
+Decoding 010401661fffffffffffffffffffffffffffffffffffffffffffff388cd3:
+Comparing...
+C/R=1 P/F=0 IS N(R)=1 N(S)=0 RR (FCS=0xd38c38) 01661fffffffffffffffffffffffffffffffffffffffffffff
+Reencoding...
+=== STARTING TESTCASE 'DISC'
+Decoding f923000000000000000000000000000000000000000000000000007986f2:
+Comparing...
+C/R=1 P/F=1 U DISC (FCS=0xf28679)
+Reencoding...
diff --git a/tests/sercomm/sercomm_test.c b/tests/sercomm/sercomm_test.c
index 058c9eb4..9bffc0d8 100644
--- a/tests/sercomm/sercomm_test.c
+++ b/tests/sercomm/sercomm_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/tests/sim/sim_test.c b/tests/sim/sim_test.c
index 425ce11d..2e2eec58 100644
--- a/tests/sim/sim_test.c
+++ b/tests/sim/sim_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/tests/sms/sms_test.c b/tests/sms/sms_test.c
index 06153964..912c0829 100644
--- a/tests/sms/sms_test.c
+++ b/tests/sms/sms_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -219,7 +215,7 @@ static const struct test_case test_decode[] =
},
};
-static void test_octet_return()
+static void test_octet_return(void)
{
char out[256];
int oct, septets;
@@ -268,6 +264,54 @@ static void test_gen_oa(void)
printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len));
}
+static void test_enc_large_msg(void)
+{
+ uint8_t enc_buf[2048 * 7 / 8];
+ char large_msg[2048 + 1];
+ int i, j, nsep, noct = 0;
+
+ printf("\nRunning %s\n", __func__);
+
+ /* Expected chunks (repeated) in the output buffer */
+ const uint8_t exp_chunk[] = { 0xc1, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x83 };
+
+ /* Length variants to be tested */
+ static const size_t nlen[] = { 2048, 1024, 555, 512, 260, 255, 250 };
+
+ memset(&large_msg[0], (int) 'A', sizeof(large_msg) - 1);
+
+ for (i = 0; i < ARRAY_SIZE(nlen); i++) {
+ /* Clear the output buffer first */
+ memset(&enc_buf[0], 0x00, sizeof(enc_buf));
+ /* Limit length of the input string */
+ large_msg[nlen[i]] = '\0';
+
+ /* How many octets we expect to be used? */
+ int noct_exp = nlen[i] * 7 / 8;
+ if (nlen[i] % 8 != 0)
+ noct_exp++;
+
+ /* Encode a sequence of 'A' repeated nlen[i] times */
+ nsep = gsm_7bit_encode_n(&enc_buf[0], sizeof(enc_buf), large_msg, &noct);
+ printf("gsm_7bit_encode_n(len=%zu) processed %d septets (expected %zu): %s\n",
+ nlen[i], nsep, nlen[i], nsep == nlen[i] ? "OK" : "FAIL");
+ printf("gsm_7bit_encode_n(len=%zu) used %d octets in the buffer (expected %d): %s\n",
+ nlen[i], noct, noct_exp, noct == noct_exp ? "OK" : "FAIL");
+
+ /* The encoding result is expected to consist of repeated chunks */
+ for (j = 0; j < noct_exp; j += sizeof(exp_chunk)) {
+ size_t len = OSMO_MIN(noct_exp - j, sizeof(exp_chunk));
+ if (nlen[i] % 8 != 0) /* skip incomplete octets */
+ len--;
+ if (memcmp(&enc_buf[j], exp_chunk, len) != 0) {
+ printf("\tUnexpected chunk at enc_buf[%d:%zu]: %s\n",
+ j, len, osmo_hexdump(&enc_buf[j], len));
+ break; /* No need to show them all */
+ }
+ }
+ }
+}
+
int main(int argc, char** argv)
{
printf("SMS testing\n");
@@ -336,7 +380,7 @@ int main(int argc, char** argv)
memcpy(tmp, septet_data, concatenated_part1_septet_length);
/* In our case: test_multiple_decode[0].ud_hdr_ind equals number of padding bits*/
- octet_length = gsm_septets2octets(coded, tmp, concatenated_part1_septet_length, test_multiple_encode[0].ud_hdr_ind);
+ octet_length = gsm_septet_pack(coded, tmp, concatenated_part1_septet_length, test_multiple_encode[0].ud_hdr_ind);
/* copy header */
memset(tmp, 0x42, sizeof(tmp));
@@ -354,7 +398,7 @@ int main(int argc, char** argv)
memcpy(tmp, septet_data + concatenated_part1_septet_length, concatenated_part2_septet_length);
/* In our case: test_multiple_decode[1].ud_hdr_ind equals number of padding bits*/
- octet_length = gsm_septets2octets(coded, tmp, concatenated_part2_septet_length, test_multiple_encode[1].ud_hdr_ind);
+ octet_length = gsm_septet_pack(coded, tmp, concatenated_part2_septet_length, test_multiple_encode[1].ud_hdr_ind);
/* copy header */
memset(tmp, 0x42, sizeof(tmp));
@@ -396,6 +440,7 @@ int main(int argc, char** argv)
test_octet_return();
test_gen_oa();
+ test_enc_large_msg();
printf("OK\n");
return 0;
diff --git a/tests/sms/sms_test.ok b/tests/sms/sms_test.ok
index a71567de..de1fce3a 100644
--- a/tests/sms/sms_test.ok
+++ b/tests/sms/sms_test.ok
@@ -18,4 +18,20 @@ Result: len(12) data(14 a1 21 43 65 87 09 21 43 65 87 19 )
Result: len(2) data(00 91 )
Result: len(9) data(0e d0 4f 78 d9 2d 9c 0e 01 )
Result: len(12) data(14 d0 4f 78 d9 2d 9c 0e c3 e2 31 19 )
+
+Running test_enc_large_msg
+gsm_7bit_encode_n(len=2048) processed 2048 septets (expected 2048): OK
+gsm_7bit_encode_n(len=2048) used 1792 octets in the buffer (expected 1792): OK
+gsm_7bit_encode_n(len=1024) processed 1024 septets (expected 1024): OK
+gsm_7bit_encode_n(len=1024) used 896 octets in the buffer (expected 896): OK
+gsm_7bit_encode_n(len=555) processed 555 septets (expected 555): OK
+gsm_7bit_encode_n(len=555) used 486 octets in the buffer (expected 486): OK
+gsm_7bit_encode_n(len=512) processed 512 septets (expected 512): OK
+gsm_7bit_encode_n(len=512) used 448 octets in the buffer (expected 448): OK
+gsm_7bit_encode_n(len=260) processed 260 septets (expected 260): OK
+gsm_7bit_encode_n(len=260) used 228 octets in the buffer (expected 228): OK
+gsm_7bit_encode_n(len=255) processed 255 septets (expected 255): OK
+gsm_7bit_encode_n(len=255) used 224 octets in the buffer (expected 224): OK
+gsm_7bit_encode_n(len=250) processed 250 septets (expected 250): OK
+gsm_7bit_encode_n(len=250) used 219 octets in the buffer (expected 219): OK
OK
diff --git a/tests/smscb/cbsp_test.c b/tests/smscb/cbsp_test.c
new file mode 100644
index 00000000..2dbdded7
--- /dev/null
+++ b/tests/smscb/cbsp_test.c
@@ -0,0 +1,108 @@
+/*
+ * (C) 2022 by sysmocom - s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gsm/protocol/gsm_48_049.h>
+#include <osmocom/gsm/cbsp.h>
+
+/*
+CBSP WRITE-REPLACE FAILURE
+ Message Type: WRITE-REPLACE FAILURE (3)
+ Message Length: 44
+ IE: Message Identifier: 0x0031
+ Information Element Identifier: Message Identifier (14)
+ Message Identifier: 0x0031
+ IE: New Serial Number: 0x4170
+ Information Element Identifier: New Serial Number (3)
+ New Serial Number: 0x4170
+ IE: Failure List: 2 items
+ Information Element Identifier: Failure List (9)
+ Information Element Length: 15
+ Failure List Item: MCC 901 International Mobile, shared code, MNC 70 Clementvale Baltic OÜ, LAC 0x0018, CI 0x0030: Cause Cell-identity-not-valid
+ Cell ID Discriminator: CGI (0)
+ Mobile Country Code (MCC): International Mobile, shared code (901)
+ Mobile Network Code (MNC): Clementvale Baltic OÜ (70)
+ Location Area Code (LAC): 0x0018
+ Cell Identifier (CI): 0x0030
+ Cause: Cell-identity-not-valid (0x03)
+ Failure List Item: LAC 02711, CI 0xc351: Cause LAI-or-LAC-not-valid
+ Cell ID Discriminator: LAC+CI (1)
+ Location Area Code (LAC): 0x2711
+ Cell Identifier (CI): 0xc351
+ Cause: LAI-or-LAC-not-valid (0x0f)
+ IE: Cell List (CGI): 2 items
+ Information Element Identifier: Cell List (4)
+ Information Element Length: 15
+ Cell ID Discriminator: CGI (0)
+ Cell List Item: MCC 901 International Mobile, shared code, MNC 70 Clementvale Baltic OÜ, LAC 0x0017, CI 0x002a
+ Mobile Country Code (MCC): International Mobile, shared code (901)
+ Mobile Network Code (MNC): Clementvale Baltic OÜ (70)
+ Location Area Code (LAC): 0x0017
+ Cell Identifier (CI): 0x002a
+ Cell List Item: MCC 901 International Mobile, shared code, MNC 70 Clementvale Baltic OÜ, LAC 0x0018, CI 0x002a
+ Mobile Country Code (MCC): International Mobile, shared code (901)
+ Mobile Network Code (MNC): Clementvale Baltic OÜ (70)
+ Location Area Code (LAC): 0x0018
+ Cell Identifier (CI): 0x002a
+ IE: Channel Indicator: basic channel
+ Information Element Identifier: Channel Indicator (18)
+ Channel Indicator: basic channel (0x00)
+*/
+static const char write_repl_fail_with_failure_list[] =
+ "0300002c0e003103417009000f0009f1070018003003012711c3510f04000f0009f1070017002a09f1070018002a1200";
+
+static struct msgb *msgb_from_hex(unsigned int size, const char *hex)
+{
+ struct msgb *msg = msgb_alloc(size, "test_cbsp");
+ OSMO_ASSERT(msg);
+ msg->l1h = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
+ msg->l2h = msg->l1h + sizeof(struct cbsp_header);
+ return msg;
+}
+
+static void test_decode(void)
+{
+ struct msgb *msg;
+ struct osmo_cbsp_decoded *cbsp_dec;
+
+ printf("=== %s start ===\n", __func__);
+
+ msg = msgb_from_hex(sizeof(write_repl_fail_with_failure_list),
+ write_repl_fail_with_failure_list);
+
+ cbsp_dec = osmo_cbsp_decode(NULL, msg);
+ OSMO_ASSERT(cbsp_dec);
+
+ talloc_free(cbsp_dec);
+ msgb_free(msg);
+
+ printf("=== %s end ===\n", __func__);
+}
+
+int main(int argc, char **argv)
+{
+ test_decode();
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/smscb/cbsp_test.ok b/tests/smscb/cbsp_test.ok
new file mode 100644
index 00000000..a837de57
--- /dev/null
+++ b/tests/smscb/cbsp_test.ok
@@ -0,0 +1,2 @@
+=== test_decode start ===
+=== test_decode end ===
diff --git a/tests/smscb/gsm0341_test.c b/tests/smscb/gsm0341_test.c
index c400f5c8..966a00fd 100644
--- a/tests/smscb/gsm0341_test.c
+++ b/tests/smscb/gsm0341_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <string.h>
@@ -29,7 +25,7 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
-struct gsm341_ms_message *gen_msg_from_text(uint16_t msg_id, const char *text)
+struct gsm341_ms_message *gen_msg_from_text(uint16_t msg_id, uint8_t msg_code, const char *text)
{
struct gsm341_ms_message *cbmsg;
int text_len = strlen(text);
@@ -38,11 +34,11 @@ struct gsm341_ms_message *gen_msg_from_text(uint16_t msg_id, const char *text)
uint8_t payload[text_len];
int payload_octets;
- srand(time(NULL));
+ //srand(time(NULL));
gsm_7bit_encode_n(payload, sizeof(payload), text, &payload_octets);
//cbmsg = gsm0341_build_msg(NULL, 0, rand(), 0, msg_id, 0x0f, 1, 1, payload, payload_octets);
- cbmsg = gsm0341_build_msg(NULL, 0, rand(), 0, msg_id, 0x00, 1, 1, payload, payload_octets);
+ cbmsg = gsm0341_build_msg(NULL, 0, msg_code, 0, msg_id, 0x00, 1, 1, payload, payload_octets);
printf("%s\n", osmo_hexdump_nospc((uint8_t *)cbmsg, sizeof(*cbmsg)+payload_octets));
@@ -54,6 +50,7 @@ int main(int argc, char **argv)
uint16_t msg_id = GSM341_MSGID_ETWS_CMAS_MONTHLY_TEST;
char *text = "Mahlzeit!";
char tbuf[GSM341_MAX_CHARS+1];
+ struct gsm341_ms_message *cbmsg;
if (argc > 1)
msg_id = atoi(argv[1]);
@@ -67,7 +64,8 @@ int main(int argc, char **argv)
sizeof(tbuf)-strlen(text));
tbuf[GSM341_MAX_CHARS] = 0;
- gen_msg_from_text(msg_id, tbuf);
+ cbmsg = gen_msg_from_text(msg_id, 1, tbuf);
+ talloc_free(cbmsg);
return EXIT_SUCCESS;
}
diff --git a/tests/smscb/gsm0341_test.ok b/tests/smscb/gsm0341_test.ok
new file mode 100644
index 00000000..600797c3
--- /dev/null
+++ b/tests/smscb/gsm0341_test.ok
@@ -0,0 +1 @@
+0010111c0011cd309aad2fa7e9a146a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d168341a8d46a3d100
diff --git a/tests/smscb/smscb_test.c b/tests/smscb/smscb_test.c
index 5925f69b..3b6b74d3 100644
--- a/tests/smscb/smscb_test.c
+++ b/tests/smscb/smscb_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/protocol/gsm_03_41.h>
diff --git a/tests/sockaddr_str/sockaddr_str_test.c b/tests/sockaddr_str/sockaddr_str_test.c
index bf7d7380..4e8500ba 100644
--- a/tests/sockaddr_str/sockaddr_str_test.c
+++ b/tests/sockaddr_str/sockaddr_str_test.c
@@ -18,10 +18,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -55,6 +51,7 @@ struct osmo_sockaddr_str oip_data[] = {
{ .af = AF_INET6, .ip = "::1:10.9.8.7", .port = 1 },
{ .af = AF_INET, .ip = "0.0.0.0", .port = 5 },
{ .af = AF_INET6, .ip = "::", .port = 5 },
+ { .af = AF_INET6, .ip = "0::", .port = 5 },
};
const char *af_name(int af)
@@ -71,21 +68,18 @@ const char *af_name(int af)
}
}
-static const struct value_string err_names[] = {
- { -EINVAL, "-EINVAL" },
- {}
-};
-
-static inline const char *err_name(int err)
-{ return get_value_string(err_names, err); }
-
static inline const char *rc_name(int rc)
{
- if (!rc)
+ switch (rc) {
+ case -EINVAL:
+ return "rc == -EINVAL";
+ case -EAFNOSUPPORT:
+ return "rc == -EAFNOSUPPORT";
+ case 0:
return "rc == 0";
- if (rc < 0)
- return "rc < 0";
- return "rc > 0";
+ default:
+ return rc < 0 ? "rc < 0" : "rc > 0";
+ }
}
void dump_oip(const struct osmo_sockaddr_str *oip)
@@ -93,7 +87,7 @@ void dump_oip(const struct osmo_sockaddr_str *oip)
printf("{ .af = %s, .ip = %s, .port = %u }\n", af_name(oip->af), osmo_quote_str(oip->ip, -1), oip->port);
}
-void sockaddr_str_test_conversions()
+void sockaddr_str_test_conversions(void)
{
int i;
char buf[1024];
@@ -148,7 +142,8 @@ void sockaddr_str_test_conversions()
uint32_t a = 0;
rc = osmo_sockaddr_str_to_32(x, &a);
- printf(" osmo_sockaddr_str_to_32() %s uint32_t=0x%x\n", rc_name(rc), a);
+ printf(" osmo_sockaddr_str_to_32() %s uint8_t[4]=[ %s]\n", rc_name(rc),
+ osmo_hexdump((void*)&a, sizeof(a)));
if (rc == 0) {
struct osmo_sockaddr_str back;
@@ -163,13 +158,14 @@ void sockaddr_str_test_conversions()
{
uint32_t a = 0;
- rc = osmo_sockaddr_str_to_32n(x, &a);
- printf(" osmo_sockaddr_str_to_32n() %s uint32_t=0x%x\n", rc_name(rc), a);
+ rc = osmo_sockaddr_str_to_32h(x, &a);
+ printf(" osmo_sockaddr_str_to_32h() %s uint8_t[4]=[ %s]\n", rc_name(rc),
+ osmo_hexdump((void*)&a, sizeof(a)));
if (rc == 0) {
struct osmo_sockaddr_str back;
- rc = osmo_sockaddr_str_from_32n(&back, a, x->port);
- printf(" -> osmo_sockaddr_str_from_32n() %s ", rc_name(rc));
+ rc = osmo_sockaddr_str_from_32h(&back, a, x->port);
+ printf(" -> osmo_sockaddr_str_from_32h() %s ", rc_name(rc));
dump_oip(&back);
if (memcmp(x, &back, sizeof(back)))
printf(" DIFFERS!\n");
@@ -236,9 +232,34 @@ void sockaddr_str_test_conversions()
}
+static void test_osmo_sockaddr_str_cmp(void)
+{
+ int i;
+ printf("\n\n%s\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(oip_data); i++) {
+ /* use a copy to not hit the pointer comparison in osmo_sockaddr_str_cmp(). */
+ struct osmo_sockaddr_str _a = oip_data[i];
+ struct osmo_sockaddr_str *a = &_a;
+ int j;
+ printf("[%2d]\n", i);
+
+ for (j = 0; j < ARRAY_SIZE(oip_data); j++) {
+ struct osmo_sockaddr_str *b = &oip_data[j];
+ int ip_rc = osmo_sockaddr_str_cmp(a, b);
+ printf(" osmo_sockaddr_str_cmp(): " OSMO_SOCKADDR_STR_FMT "%s %s " OSMO_SOCKADDR_STR_FMT "%s\n",
+ OSMO_SOCKADDR_STR_FMT_ARGS(a),
+ osmo_sockaddr_str_is_nonzero(a) ? "" : "(zero)",
+ ip_rc < 0? "<" : (ip_rc == 0 ? "==" : ">" ),
+ OSMO_SOCKADDR_STR_FMT_ARGS(b),
+ osmo_sockaddr_str_is_nonzero(b) ? "" : "(zero)");
+ }
+ }
+}
+
int main(int argc, char **argv)
{
sockaddr_str_test_conversions();
+ test_osmo_sockaddr_str_cmp();
return 0;
}
diff --git a/tests/sockaddr_str/sockaddr_str_test.ok b/tests/sockaddr_str/sockaddr_str_test.ok
index 5ebf7be6..a8c16614 100644
--- a/tests/sockaddr_str/sockaddr_str_test.ok
+++ b/tests/sockaddr_str/sockaddr_str_test.ok
@@ -6,14 +6,14 @@
osmo_sockaddr_str_is_nonzero() = true
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=01020304
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc == 0 uint32_t=0x4030201
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ 01 02 03 04 ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
- osmo_sockaddr_str_to_32n() rc == 0 uint32_t=0x1020304
- -> osmo_sockaddr_str_from_32n() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
+ osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ 04 03 02 01 ]
+ -> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=02000005010203040000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200000501020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
@@ -25,14 +25,14 @@
osmo_sockaddr_str_is_nonzero() = false
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=00000000
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc == 0 uint32_t=0x0
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ 00 00 00 00 ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
- osmo_sockaddr_str_to_32n() rc == 0 uint32_t=0x0
- -> osmo_sockaddr_str_from_32n() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
+ osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ 00 00 00 00 ]
+ -> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=02000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 0 }
@@ -44,14 +44,14 @@
osmo_sockaddr_str_is_nonzero() = true
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=ffffffff
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc == 0 uint32_t=0xffffffff
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ ff ff ff ff ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
- osmo_sockaddr_str_to_32n() rc == 0 uint32_t=0xffffffff
- -> osmo_sockaddr_str_from_32n() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
+ osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ ff ff ff ff ]
+ -> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=0200ffffffffffff0000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200ffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "255.255.255.255", .port = 65535 }
@@ -61,13 +61,13 @@
OSMO_SOCKADDR_STR_FMT: '0.0.0.256:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000001000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000001000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "0.0.0.256", .port = 1 }
@@ -75,26 +75,26 @@
OSMO_SOCKADDR_STR_FMT: 'not an ip address:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000001000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_from_str() rc < 0 { .af = AF_UNSPEC, .ip = "not an ip address", .port = 1 }
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000001000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_from_str() rc == -EINVAL { .af = AF_UNSPEC, .ip = "not an ip address", .port = 1 }
{ .af = AF_INET6, .ip = "1:2:3::4", .port = 5 }
- OSMO_SOCKADDR_STR_FMT: '1:2:3::4:5'
+ OSMO_SOCKADDR_STR_FMT: '[1:2:3::4]:5'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = true
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00010002000300000000000000000004
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "1:2:3::4", .port = 5 }
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000005000000000001000200030000000000000000000400000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "1:2:3::4", .port = 5 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00000500000000000100020003000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -103,15 +103,15 @@
{ .af = AF_INET6, .ip = "::", .port = 0 }
- OSMO_SOCKADDR_STR_FMT: ':::0'
+ OSMO_SOCKADDR_STR_FMT: '[::]:0'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00000000000000000000000000000000
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::", .port = 0 }
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::", .port = 0 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -120,15 +120,15 @@
{ .af = AF_INET6, .ip = "::1", .port = 0 }
- OSMO_SOCKADDR_STR_FMT: '::1:0'
+ OSMO_SOCKADDR_STR_FMT: '[::1]:0'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00000000000000000000000000000001
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::1", .port = 0 }
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000000000000000000000000000000000000000000000100000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::1", .port = 0 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -137,15 +137,15 @@
{ .af = AF_INET6, .ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", .port = 65535 }
- OSMO_SOCKADDR_STR_FMT: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:65535'
+ OSMO_SOCKADDR_STR_FMT: '[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = true
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=ffffffffffffffffffffffffffffffff
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", .port = 65535 }
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a00ffff00000000ffffffffffffffffffffffffffffffff00000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", .port = 65535 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00ffff00000000ffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
@@ -154,16 +154,16 @@
{ .af = AF_INET6, .ip = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", .port = 65535 }
- OSMO_SOCKADDR_STR_FMT: 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:65535'
+ OSMO_SOCKADDR_STR_FMT: '[FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = true
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=ffffffffffffffffffffffffffffffff
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", .port = 65535 }
DIFFERS!
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a00ffff00000000ffffffffffffffffffffffffffffffff00000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", .port = 65535 }
DIFFERS!
@@ -174,44 +174,44 @@
{ .af = AF_INET6, .ip = "::fffff", .port = 1 }
- OSMO_SOCKADDR_STR_FMT: '::fffff:1'
+ OSMO_SOCKADDR_STR_FMT: '[::fffff]:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=0a000001000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0a00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=0a000001000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0a00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET6, .ip = "::fffff", .port = 1 }
{ .af = AF_INET6, .ip = "not an ip address", .port = 1 }
- OSMO_SOCKADDR_STR_FMT: 'not an ip address:1'
+ OSMO_SOCKADDR_STR_FMT: '[not an ip address]:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=0a000001000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0a00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_from_str() rc < 0 { .af = AF_UNSPEC, .ip = "not an ip address", .port = 1 }
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=0a000001000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0a00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_from_str() rc == -EINVAL { .af = AF_UNSPEC, .ip = "not an ip address", .port = 1 }
{ .af = AF_INET6, .ip = "1.2.3.4", .port = 5 }
- OSMO_SOCKADDR_STR_FMT: '1.2.3.4:5'
+ OSMO_SOCKADDR_STR_FMT: '[1.2.3.4]:5'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
DIFFERS!
@@ -220,13 +220,13 @@
OSMO_SOCKADDR_STR_FMT: '1:2:3::4:5'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000005000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000005000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET6, .ip = "1:2:3::4", .port = 5 }
DIFFERS!
@@ -235,13 +235,13 @@
OSMO_SOCKADDR_STR_FMT: '1.2.3.4:5'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 5 }
DIFFERS!
@@ -250,27 +250,27 @@
OSMO_SOCKADDR_STR_FMT: ':5'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000005000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000005000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc < 0 { .af = AF_UNSPEC, .ip = "", .port = 5 }
{ .af = AF_INET6, .ip = "", .port = 5 }
- OSMO_SOCKADDR_STR_FMT: ':5'
+ OSMO_SOCKADDR_STR_FMT: '[]:5'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc < 0 { .af = AF_UNSPEC, .ip = "", .port = 5 }
@@ -280,14 +280,14 @@
osmo_sockaddr_str_is_nonzero() = false
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=01020304
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc == 0 uint32_t=0x4030201
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ 01 02 03 04 ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
- osmo_sockaddr_str_to_32n() rc == 0 uint32_t=0x1020304
- -> osmo_sockaddr_str_from_32n() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
+ osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ 04 03 02 01 ]
+ -> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=02000000010203040000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200000001020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "1.2.3.4", .port = 0 }
@@ -297,28 +297,28 @@
OSMO_SOCKADDR_STR_FMT: '1.2.3:4:5:0'
osmo_sockaddr_str_is_set() = false
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=02000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
- osmo_sockaddr_str_to_sockaddr() rc < 0 sockaddr_storage=0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_in_addr() rc == -EINVAL in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EINVAL uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=02000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr() rc == -EINVAL sockaddr_storage=0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET6, .ip = "1.2.3:4:5", .port = 0 }
DIFFERS!
{ .af = AF_INET6, .ip = "::1:10.9.8.7", .port = 1 }
- OSMO_SOCKADDR_STR_FMT: '::1:10.9.8.7:1'
+ OSMO_SOCKADDR_STR_FMT: '[::1:10.9.8.7]:1'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = true
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=0000000000000000000000010a090807
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::1:a09:807", .port = 1 }
DIFFERS!
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000001000000000000000000000000000000010a09080700000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::1:a09:807", .port = 1 }
DIFFERS!
@@ -334,31 +334,606 @@
osmo_sockaddr_str_is_nonzero() = false
osmo_sockaddr_str_to_in_addr() rc == 0 in_addr=00000000
-> osmo_sockaddr_str_from_in_addr() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
- osmo_sockaddr_str_to_in6_addr() rc < 0 in6_addr=00000000000000000000000000000000
- osmo_sockaddr_str_to_32() rc == 0 uint32_t=0x0
+ osmo_sockaddr_str_to_in6_addr() rc == -EINVAL in6_addr=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == 0 uint8_t[4]=[ 00 00 00 00 ]
-> osmo_sockaddr_str_from_32() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
- osmo_sockaddr_str_to_32n() rc == 0 uint32_t=0x0
- -> osmo_sockaddr_str_from_32n() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
+ osmo_sockaddr_str_to_32h() rc == 0 uint8_t[4]=[ 00 00 00 00 ]
+ -> osmo_sockaddr_str_from_32h() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
osmo_sockaddr_str_to_sockaddr_in() rc == 0 sockaddr_in=02000005000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr_in() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
- osmo_sockaddr_str_to_sockaddr_in6() rc < 0 sockaddr_in6=00000000000000000000000000000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == -EINVAL sockaddr_in6=00000000000000000000000000000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0200000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET, .ip = "0.0.0.0", .port = 5 }
{ .af = AF_INET6, .ip = "::", .port = 5 }
- OSMO_SOCKADDR_STR_FMT: ':::5'
+ OSMO_SOCKADDR_STR_FMT: '[::]:5'
osmo_sockaddr_str_is_set() = true
osmo_sockaddr_str_is_nonzero() = false
- osmo_sockaddr_str_to_in_addr() rc < 0 in_addr=00000000
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00000000000000000000000000000000
-> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
- osmo_sockaddr_str_to_32() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_32n() rc < 0 uint32_t=0x0
- osmo_sockaddr_str_to_sockaddr_in() rc < 0 sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
+
+
+{ .af = AF_INET6, .ip = "0::", .port = 5 }
+ OSMO_SOCKADDR_STR_FMT: '[0::]:5'
+ osmo_sockaddr_str_is_set() = true
+ osmo_sockaddr_str_is_nonzero() = false
+ osmo_sockaddr_str_to_in_addr() rc == -EAFNOSUPPORT in_addr=00000000
+ osmo_sockaddr_str_to_in6_addr() rc == 0 in6_addr=00000000000000000000000000000000
+ -> osmo_sockaddr_str_from_in6_addr() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
+ DIFFERS!
+ osmo_sockaddr_str_to_32() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_32h() rc == -EAFNOSUPPORT uint8_t[4]=[ 00 00 00 00 ]
+ osmo_sockaddr_str_to_sockaddr_in() rc == -EINVAL sockaddr_in=00000000000000000000000000000000
+ osmo_sockaddr_str_to_sockaddr_in6() rc == 0 sockaddr_in6=0a000005000000000000000000000000000000000000000000000000
+ -> osmo_sockaddr_str_from_sockaddr_in6() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
+ DIFFERS!
+ osmo_sockaddr_str_to_sockaddr() rc == 0 sockaddr_storage=0a00000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ -> osmo_sockaddr_str_from_sockaddr() rc == 0 { .af = AF_INET6, .ip = "::", .port = 5 }
+ DIFFERS!
+ osmo_sockaddr_str_from_str() rc == 0 { .af = AF_INET6, .ip = "0::", .port = 5 }
+
+
+test_osmo_sockaddr_str_cmp
+[ 0]
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 == 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 > :5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < []:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5 < [0::]:5(zero)
+[ 1]
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) == 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < []:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:0(zero) < [0::]:5(zero)
+[ 2]
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 == 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 > :5(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < []:5(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): 255.255.255.255:65535 < [0::]:5(zero)
+[ 3]
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) == 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < []:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.256:1(zero) < [0::]:5(zero)
+[ 4]
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) == not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < []:5(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): not an ip address:1(zero) < [0::]:5(zero)
+[ 5]
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 == [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > :5(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > []:5(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [1:2:3::4]:5 > [0::]:5(zero)
+[ 6]
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [::]:0(zero) == [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [::]:0(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [::]:0(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > []:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [::]:0(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:0(zero) < [0::]:5(zero)
+[ 7]
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) == [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > []:5(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [::1]:0(zero) > [0::]:5(zero)
+[ 8]
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 == [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 == [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > :5(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > []:5(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 > [0::]:5(zero)
+[ 9]
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 == [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 == [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > :5(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > []:5(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535 > [0::]:5(zero)
+[10]
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) == [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > []:5(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [::fffff]:1(zero) > [0::]:5(zero)
+[11]
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) == [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > []:5(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [not an ip address]:1(zero) > [0::]:5(zero)
+[12]
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) == [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > []:5(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [1.2.3.4]:5(zero) > [0::]:5(zero)
+[13]
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) == 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < []:5(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): 1:2:3::4:5(zero) < [0::]:5(zero)
+[14]
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) == 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < :5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < []:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:5(zero) < [0::]:5(zero)
+[15]
+ osmo_sockaddr_str_cmp(): :5(zero) < 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): :5(zero) < 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): :5(zero) < 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): :5(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): :5(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): :5(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) == :5(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < []:5(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): :5(zero) < 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): :5(zero) < [0::]:5(zero)
+[16]
+ osmo_sockaddr_str_cmp(): []:5(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): []:5(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): []:5(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): []:5(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): []:5(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): []:5(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) == []:5(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): []:5(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): []:5(zero) < [0::]:5(zero)
+[17]
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < []:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) == 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3.4:0(zero) < [0::]:5(zero)
+[18]
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < []:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) == 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): 1.2.3:4:5:0(zero) < [0::]:5(zero)
+[19]
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > :5(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > []:5(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 == [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [::1:10.9.8.7]:1 > [0::]:5(zero)
+[20]
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [::]:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < []:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) == 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [::]:5(zero)
+ osmo_sockaddr_str_cmp(): 0.0.0.0:5(zero) < [0::]:5(zero)
+[21]
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [::]:5(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [::]:5(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > []:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [::]:5(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) == [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [::]:5(zero) == [0::]:5(zero)
+[22]
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > 1.2.3.4:5
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > 0.0.0.0:0(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > 255.255.255.255:65535
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > 0.0.0.256:1(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > not an ip address:1(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) < [1:2:3::4]:5
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > [::]:0(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) < [::1]:0(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) < [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) < [FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) < [::fffff]:1(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) < [not an ip address]:1(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) < [1.2.3.4]:5(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > 1:2:3::4:5(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > 1.2.3.4:5(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > :5(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > []:5(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > 1.2.3.4:0(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > 1.2.3:4:5:0(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) < [::1:10.9.8.7]:1
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) > 0.0.0.0:5(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) == [::]:5(zero)
+ osmo_sockaddr_str_cmp(): [0::]:5(zero) == [0::]:5(zero)
diff --git a/tests/socket/socket_sctp_test.c b/tests/socket/socket_sctp_test.c
new file mode 100644
index 00000000..5948abc0
--- /dev/null
+++ b/tests/socket/socket_sctp_test.c
@@ -0,0 +1,234 @@
+/*
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include "config.h"
+
+void *ctx = NULL;
+
+#ifdef HAVE_LIBSCTP
+static uint16_t sock_get_local_port(int fd, bool is_v6) {
+ struct sockaddr_storage sa;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&sa;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa;
+ socklen_t len = sizeof(sa);
+ int local_port;
+
+ OSMO_ASSERT(getsockname(fd, (struct sockaddr*)&sa, &len) == 0);
+ if(!is_v6)
+ local_port = osmo_load16be(&sin->sin_port);
+ else
+ local_port = osmo_load16be(&sin6->sin6_port);
+ //printf("Checking osmo_sock_init2_multiaddr() port: %" PRIu16 "\n", listen_port_v4);
+ return local_port;
+}
+
+/* Test API osmo_sock_init2_multiaddr with 1 local/remote address */
+static int test_sockinit2_multiaddr(const char **addrv4_loc, const char **addrv6_loc,
+ const char **addrv4_rem, const char **addrv6_rem,
+ size_t addrv4_size, size_t addrv6_size)
+{
+ int fd, rc;
+ int listen_fd_v4, listen_fd_v6;
+ int listen_port_v4, listen_port_v6;
+
+ printf("Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port\n");
+
+ listen_fd_v4 = osmo_sock_init2_multiaddr(AF_INET, SOCK_STREAM, IPPROTO_SCTP,
+ addrv4_loc, addrv4_size, 0,
+ NULL, 0, 0, OSMO_SOCK_F_BIND);
+ OSMO_ASSERT(listen_fd_v4 >= 0);
+ /* expect it to be blocking */
+ rc = fcntl(listen_fd_v4, F_GETFL);
+ OSMO_ASSERT(!(rc & O_NONBLOCK));
+
+ listen_port_v4 = sock_get_local_port(listen_fd_v4, false);
+
+ printf("Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv6 port\n");
+
+ listen_fd_v6 = osmo_sock_init2_multiaddr(AF_INET6, SOCK_STREAM, IPPROTO_SCTP,
+ addrv6_loc, addrv6_size, 0,
+ NULL, 0, 0, OSMO_SOCK_F_BIND);
+ OSMO_ASSERT(listen_fd_v6 >= 0);
+ /* expect it to be blocking */
+ rc = fcntl(listen_fd_v6, F_GETFL);
+ OSMO_ASSERT(!(rc & O_NONBLOCK));
+
+ listen_port_v6 = sock_get_local_port(listen_fd_v6, true);
+
+ printf("Checking osmo_sock_init2_multiaddr() for OSMO_SOCK_F_NONBLOCK\n");
+ fd = osmo_sock_init2_multiaddr(AF_INET, SOCK_STREAM, IPPROTO_SCTP,
+ addrv4_loc, addrv4_size, 0,
+ NULL, 0, 0, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK);
+ OSMO_ASSERT(fd >= 0);
+ /* expect it to be blocking */
+ rc = fcntl(fd, F_GETFL);
+ OSMO_ASSERT(rc & O_NONBLOCK);
+ close(fd);
+
+ printf("Checking osmo_sock_init2_multiaddr() for invalid flags\n");
+ fd = osmo_sock_init2_multiaddr(AF_INET, SOCK_STREAM, IPPROTO_SCTP,
+ addrv4_loc, addrv4_size, 0,
+ NULL, 0, 0, 0);
+ OSMO_ASSERT(fd < 0);
+
+ printf("Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT\n");
+ fd = osmo_sock_init2_multiaddr(AF_INET, SOCK_STREAM, IPPROTO_SCTP,
+ addrv4_rem, addrv4_size, 0,
+ addrv4_rem, addrv4_size, listen_port_v4,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd >= 0);
+ close(fd);
+
+ printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv4 & IPv6\n");
+ fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP,
+ addrv4_rem, addrv4_size, 0,
+ addrv6_rem, addrv6_size, listen_port_v6,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd < 0);
+
+ printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv6 & IPv4\n");
+ fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP,
+ addrv6_rem, addrv6_size, 0,
+ addrv4_rem, addrv4_size, listen_port_v4,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd < 0);
+
+ printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4\n");
+ fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP,
+ addrv4_rem, addrv4_size, 0,
+ addrv4_rem, addrv4_size, listen_port_v4,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd >= 0);
+ close(fd);
+
+ printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6\n");
+ fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP,
+ addrv6_rem, addrv6_size, 0,
+ addrv6_rem, addrv6_size, listen_port_v6,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd >= 0);
+ close(fd);
+
+ close(listen_fd_v4);
+ close(listen_fd_v6);
+ printf("Done\n");
+ return 0;
+}
+
+/* Test API osmo_sock_init2_multiaddr with 1 local/remote address */
+static int test_sockinit2_multiaddr_simple(void)
+{
+ const char *addrv4_loc[] = { "0.0.0.0" };
+ const char *addrv6_loc[] = { "::" };
+ const char *addrv4_rem[] = { "127.0.0.1" };
+ const char *addrv6_rem[] = { "::1" };
+
+ return test_sockinit2_multiaddr(addrv4_loc, addrv6_loc,
+ addrv4_rem, addrv6_rem, 1, 1);
+}
+
+/* Test API osmo_sock_init2_multiaddr with several local/remote address */
+static int test_sockinit2_multiaddr_several(void)
+{
+ const char *addrv4_localhost[] = { "127.0.0.1", "127.0.0.2" };
+ const char *addrv6_localhost[] = { "::1" };
+
+ return test_sockinit2_multiaddr(addrv4_localhost, addrv6_localhost,
+ addrv4_localhost, addrv6_localhost, 2, 1);
+}
+
+/* Test API osmo_sock_init2_multiaddr with several local/remote address, using both ipv4+v6 */
+static int test_sockinit2_multiaddr_mixed(void)
+{
+ const char *addr_localhost[] = { "127.0.0.1", "127.0.0.2", "::1" };
+ size_t addr_size = ARRAY_SIZE(addr_localhost);
+
+ int listen_fd, listen_port, fd;
+
+ printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_INET IPv4+v6 fails\n");
+ listen_fd = osmo_sock_init2_multiaddr(AF_INET, SOCK_STREAM, IPPROTO_SCTP,
+ addr_localhost, addr_size, 0,
+ NULL, 0, 0, OSMO_SOCK_F_BIND);
+ OSMO_ASSERT(listen_fd < 0);
+
+ printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_INET6 IPv4+v6 fails\n");
+ listen_fd = osmo_sock_init2_multiaddr(AF_INET6, SOCK_STREAM, IPPROTO_SCTP,
+ addr_localhost, addr_size, 0,
+ NULL, 0, 0, OSMO_SOCK_F_BIND);
+ OSMO_ASSERT(listen_fd < 0);
+
+ printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_UNSPEC IPv4+v6 succeeds\n");
+ listen_fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP,
+ addr_localhost, addr_size, 0,
+ NULL, 0, 0, OSMO_SOCK_F_BIND);
+ OSMO_ASSERT(listen_fd >= 0);
+
+ listen_port = sock_get_local_port(listen_fd, true);
+
+ printf("Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4\n");
+ fd = osmo_sock_init2_multiaddr(AF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP,
+ addr_localhost, addr_size, 0,
+ addr_localhost, addr_size, listen_port,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd >= 0);
+ close(fd);
+
+ close(listen_fd);
+ return 0;
+}
+#endif /* ifdef HAVE_LIBSCTP */
+
+const struct log_info_cat default_categories[] = {
+};
+
+static struct log_info info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+int main(int argc, char *argv[])
+{
+ ctx = talloc_named_const(NULL, 0, "socket_test_sctp");
+ osmo_init_logging2(ctx, &info);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+#ifdef HAVE_LIBSCTP
+ test_sockinit2_multiaddr_simple();
+ test_sockinit2_multiaddr_several();
+ test_sockinit2_multiaddr_mixed();
+#endif /* ifdef HAVE_LIBSCTP */
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/socket/socket_sctp_test.err b/tests/socket/socket_sctp_test.err
new file mode 100644
index 00000000..7583a2a2
--- /dev/null
+++ b/tests/socket/socket_sctp_test.err
@@ -0,0 +1,8 @@
+invalid: you have to specify either BIND or CONNECT flags
+Invalid v4 vs v6 in local vs remote addresses: local: v4 remote: v6
+Invalid v4 vs v6 in local vs remote addresses: local: v6 remote: v4
+invalid: you have to specify either BIND or CONNECT flags
+Invalid v4 vs v6 in local vs remote addresses: local: v4 remote: v6
+Invalid v4 vs v6 in local vs remote addresses: local: v6 remote: v4
+getaddrinfo(::1, 0) failed: Address family for hostname not supported
+getaddrinfo(127.0.0.1, 0) failed: Address family for hostname not supported
diff --git a/tests/socket/socket_sctp_test.ok b/tests/socket/socket_sctp_test.ok
new file mode 100644
index 00000000..7608e889
--- /dev/null
+++ b/tests/socket/socket_sctp_test.ok
@@ -0,0 +1,24 @@
+Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port
+Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv6 port
+Checking osmo_sock_init2_multiaddr() for OSMO_SOCK_F_NONBLOCK
+Checking osmo_sock_init2_multiaddr() for invalid flags
+Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv4 & IPv6
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv6 & IPv4
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6
+Done
+Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv4 port
+Checking osmo_sock_init2_multiaddr() with bind to a random local SCTP IPv6 port
+Checking osmo_sock_init2_multiaddr() for OSMO_SOCK_F_NONBLOCK
+Checking osmo_sock_init2_multiaddr() for invalid flags
+Checking osmo_sock_init2_multiaddr() for combined BIND + CONNECT
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv4 & IPv6
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) must fail on mixed IPv6 & IPv4
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv6
+Done
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_INET IPv4+v6 fails
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_INET6 IPv4+v6 fails
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND on AF_UNSPEC IPv4+v6 succeeds
+Checking osmo_sock_init2_multiaddr(AF_UNSPEC) BIND + CONNECT on IPv4
diff --git a/tests/socket/socket_test.c b/tests/socket/socket_test.c
index 37e02819..34130b26 100644
--- a/tests/socket/socket_test.c
+++ b/tests/socket/socket_test.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -23,6 +19,8 @@
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
@@ -32,8 +30,9 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
-#include "../config.h"
+#include "config.h"
void *ctx = NULL;
@@ -120,11 +119,341 @@ static int test_sockinit2(void)
* FreeBSD 10 or 11 VM at home */
OSMO_ASSERT(!strncmp(name, "(r=127.0.0.1:53<->l=127.0.0.1", 29));
#endif
+
+ printf("Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv4 & IPv6\n");
+ fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "::1", 53,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd < 0);
+
+ printf("Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv6 & IPv4\n");
+ fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "127.0.0.1", 53,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd < 0);
+
+ printf("Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv4\n");
+ fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "127.0.0.1", 53,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd >= 0);
+
+ printf("Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv6\n");
+ fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "::1", 53,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd >= 0);
+
+ printf("Checking osmo_sock_init2(AF_UNSPEC) BIND on IPv4\n");
+ fd = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, NULL, 0,
+ OSMO_SOCK_F_BIND);
+ OSMO_ASSERT(fd >= 0);
+
talloc_free(name);
return 0;
}
+static int test_get_ip_and_port(void)
+{
+ int fd, rc;
+ char ip[INET6_ADDRSTRLEN] = { };
+ char port[6] = { };
+
+ printf("Checking test_get_ip_and_port() for combined BIND + CONNECT on IPv4\n");
+ fd = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, "127.0.0.1", 0, "127.0.0.1", 55,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+
+ OSMO_ASSERT(fd >= 0);
+
+ /* get the remote */
+ rc = osmo_sock_get_ip_and_port(fd, ip, sizeof(ip), port, sizeof(port), false);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(strncmp(ip, "127.0.0.1", INET6_ADDRSTRLEN) == 0);
+ OSMO_ASSERT(strncmp(port, "55", 6) == 0);
+
+ printf("Checking test_get_ip_and_port() for combined BIND + CONNECT on IPv6\n");
+ fd = osmo_sock_init2(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, "::1", 0, "::1", 55,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd >= 0);
+
+ /* get the remote */
+ rc = osmo_sock_get_ip_and_port(fd, ip, sizeof(ip), port, sizeof(port), false);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(strncmp(ip, "::1", INET6_ADDRSTRLEN) == 0);
+ OSMO_ASSERT(strncmp(port, "55", 6) == 0);
+
+ return 0;
+}
+
+static int test_sockinit_osa(void)
+{
+ int fd, rc;
+ char *name;
+
+ struct osmo_sockaddr localhost4 = {};
+ struct osmo_sockaddr localhost6 = {};
+ struct osmo_sockaddr localhost4_noport = {};
+ struct osmo_sockaddr localhost6_noport = {};
+ struct osmo_sockaddr any4 = {};
+ struct osmo_sockaddr any6 = {};
+ struct osmo_sockaddr invalid = {};
+
+ localhost4.u.sin = (struct sockaddr_in){
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = inet_addr("127.0.0.1"),
+ .sin_port = htons(42),
+ };
+
+ localhost6.u.sin6 = (struct sockaddr_in6){
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(42),
+ };
+ inet_pton(AF_INET6, "::1", &localhost6.u.sin6.sin6_addr);
+
+ localhost4_noport = localhost4;
+ localhost4_noport.u.sin.sin_port = htons(0);
+ localhost6_noport = localhost6;
+ localhost6_noport.u.sin6.sin6_port = htons(0);
+
+ any4.u.sin = (struct sockaddr_in){
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = inet_addr("0.0.0.0"),
+ .sin_port = htons(0),
+ };
+ any6.u.sin6 = (struct sockaddr_in6){
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(0),
+ };
+ inet_pton(AF_INET6, "::", &any6.u.sin6.sin6_addr);
+
+ invalid.u.sa.sa_family = AF_UNSPEC;
+
+ printf("Checking osmo_sock_init_osa() with bind to a random local UDP port\n");
+ fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP,
+ &any4, NULL, OSMO_SOCK_F_BIND);
+ OSMO_ASSERT(fd >= 0);
+ name = osmo_sock_get_name(ctx, fd);
+ /* expect it to be not connected. We cannot match on INADDR_ANY,
+ * as apparently that won't work on FreeBSD if there's only one
+ * address (e.g. 137.0.0.1) assigned to the entire system, like
+ * the Osmocom FreeBSD build slaves */
+ OSMO_ASSERT(!strncmp(name, "(r=NULL<->", 9));
+ talloc_free(name);
+ /* expect it to be blocking */
+ rc = fcntl(fd, F_GETFL);
+ OSMO_ASSERT(!(rc & O_NONBLOCK));
+ close(fd);
+
+ printf("Checking osmo_sock_init_osa() IPv4 for OSMO_SOCK_F_NONBLOCK\n");
+ fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP,
+ &any4, NULL, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK);
+ OSMO_ASSERT(fd >= 0);
+ /* expect it to be blocking */
+ rc = fcntl(fd, F_GETFL);
+ OSMO_ASSERT(rc & O_NONBLOCK);
+ close(fd);
+
+ printf("Checking osmo_sock_init_osa() IPv6 for OSMO_SOCK_F_NONBLOCK\n");
+ fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP,
+ &any6, NULL, OSMO_SOCK_F_BIND|OSMO_SOCK_F_NONBLOCK);
+ OSMO_ASSERT(fd >= 0);
+ /* expect it to be blocking */
+ rc = fcntl(fd, F_GETFL);
+ OSMO_ASSERT(rc & O_NONBLOCK);
+ close(fd);
+
+ printf("Checking osmo_sock_init_osa() for invalid flags\n");
+ fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &any4, NULL, 0);
+ OSMO_ASSERT(fd < 0);
+
+ printf("Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv4\n");
+ fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost4_noport, &localhost4,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd >= 0);
+ name = osmo_sock_get_name(ctx, fd);
+#ifndef __FreeBSD__
+ /* For some reason, on the jenkins.osmocom.org build slave with
+ * FreeBSD 10 inside a jail, it fails. Works fine on laforge's
+ * FreeBSD 10 or 11 VM at home */
+ OSMO_ASSERT(!strncmp(name, "(r=127.0.0.1:42<->l=127.0.0.1", 29));
+#endif
+ close(fd);
+
+ printf("Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv6\n");
+ fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost6_noport, &localhost6,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd >= 0);
+ name = osmo_sock_get_name(ctx, fd);
+#ifndef __FreeBSD__
+ /* For some reason, on the jenkins.osmocom.org build slave with
+ * FreeBSD 10 inside a jail, it fails. Works fine on laforge's
+ * FreeBSD 10 or 11 VM at home */
+ OSMO_ASSERT(!strncmp(name, "(r=::1:42<->l=::1", 17));
+#endif
+ close(fd);
+
+ printf("Checking osmo_sock_init_osa() must fail on mixed IPv4 & IPv6\n");
+ fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost4_noport, &localhost6,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd < 0);
+
+ printf("Checking osmo_sock_init_osa() must fail on mixed IPv6 & IPv4\n");
+ fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &localhost6_noport, &localhost4,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd < 0);
+
+ printf("Checking osmo_sock_init_osa() must fail on invalid osmo_sockaddr\n");
+ fd = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &invalid, &localhost4,
+ OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT);
+ OSMO_ASSERT(fd < 0);
+
+ talloc_free(name);
+
+ return 0;
+}
+
+static void test_osa_str(void)
+{
+ char buf[256];
+ const char *result;
+ struct osmo_sockaddr localhost4 = {};
+ struct osmo_sockaddr localhost6 = {};
+ struct osmo_sockaddr osa = {};
+
+ localhost4.u.sin = (struct sockaddr_in){
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = inet_addr("127.0.0.1"),
+ .sin_port = htons(42),
+ };
+
+ localhost6.u.sin6 = (struct sockaddr_in6){
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(42),
+ };
+ inet_pton(AF_INET6, "::1", &localhost6.u.sin6.sin6_addr);
+
+ /* test a too short str */
+ memset(&buf[0], 0, sizeof(buf));
+ result = osmo_sockaddr_to_str_buf(buf, 1, &localhost4);
+ printf("Checking osmo_sockaddr_to_str_buf to small IPv4\n");
+ OSMO_ASSERT(result == NULL);
+
+ memset(&buf[0], 0, sizeof(buf));
+ result = osmo_sockaddr_to_str_buf(buf, sizeof(buf), &localhost4);
+ printf("Checking osmo_sockaddr_to_str_buf IPv4\n");
+ OSMO_ASSERT(!strncmp("127.0.0.1:42", result, sizeof(buf)));
+
+ memset(&buf[0], 0, sizeof(buf));
+ result = osmo_sockaddr_to_str_buf(buf, 256, &localhost6);
+ printf("Checking osmo_sockaddr_to_str_buf IPv6\n");
+ OSMO_ASSERT(!strncmp("[::1]:42", result, sizeof(buf)));
+
+ memset(&buf[0], 0, sizeof(buf));
+ printf("Checking osmo_sockaddr_to_str_buf too short IPv6\n");
+ result = osmo_sockaddr_to_str_buf(buf, 8, &localhost6);
+ OSMO_ASSERT(result == NULL);
+ osmo_sockaddr_to_str_buf2(buf, 8, &localhost6);
+ OSMO_ASSERT(!strncmp("[::1]:4", buf, sizeof(buf)));
+
+ memset(&buf[0], 0, sizeof(buf));
+ result = osmo_sockaddr_to_str_buf(buf, 5, &localhost6);
+ printf("Checking osmo_sockaddr_to_str_buf too short IPv6\n");
+ OSMO_ASSERT(result == NULL);
+
+ localhost6.u.sin6.sin6_port = 0;
+ memset(&buf[0], 0, sizeof(buf));
+ result = osmo_sockaddr_to_str_buf(buf, 5, &localhost6);
+ printf("Checking osmo_sockaddr_to_str_buf only 5 bytes IPv6\n");
+ OSMO_ASSERT(result == NULL);
+
+ inet_pton(AF_INET6, "::", &localhost6.u.sin6.sin6_addr);
+ memset(&buf[0], 0, sizeof(buf));
+ result = osmo_sockaddr_to_str_buf(buf, 5, &localhost6);
+ printf("Checking osmo_sockaddr_to_str_buf only 5 bytes IPv6\n");
+ OSMO_ASSERT(!strncmp("[::]", result, sizeof(buf)));
+
+ inet_pton(AF_INET6, "2003:1234:5678:90ab:cdef:1234:4321:4321", &localhost6.u.sin6.sin6_addr);
+ memset(&buf[0], 0, sizeof(buf));
+ result = osmo_sockaddr_to_str_buf(buf, sizeof(buf), &localhost6);
+ printf("Checking osmo_sockaddr_to_str_buf long IPv6\n");
+ OSMO_ASSERT(!strncmp("[2003:1234:5678:90ab:cdef:1234:4321:4321]", result, sizeof(buf)));
+
+ localhost6.u.sin6.sin6_port = htons(23420);
+ memset(&buf[0], 0, sizeof(buf));
+ result = osmo_sockaddr_to_str_buf(buf, sizeof(buf), &localhost6);
+ printf("Checking osmo_sockaddr_to_str_buf long IPv6 port\n");
+ OSMO_ASSERT(!strncmp("[2003:1234:5678:90ab:cdef:1234:4321:4321]:23420", result, sizeof(buf)));
+
+ result = osmo_sockaddr_to_str(&localhost6);
+ printf("Checking osmo_sockaddr_to_str_buf long IPv6 port static buffer\n");
+ OSMO_ASSERT(!strncmp("[2003:1234:5678:90ab:cdef:1234:4321:4321]:23420", result, sizeof(buf)));
+
+ printf("Checking osmo_sockaddr_from_str_and_uint for 0.0.0.0\n");
+ OSMO_ASSERT(osmo_sockaddr_from_str_and_uint(&osa, "0.0.0.0", 1234) == 0);
+ OSMO_ASSERT(osmo_sockaddr_is_any(&osa));
+
+ printf("Checking osmo_sockaddr_from_str_and_uint for ::\n");
+ OSMO_ASSERT(osmo_sockaddr_from_str_and_uint(&osa, "::", 1234) == 0);
+ OSMO_ASSERT(osmo_sockaddr_is_any(&osa));
+
+ printf("Checking osmo_sockaddr_from_str_and_uint for 1.2.3.4\n");
+ OSMO_ASSERT(osmo_sockaddr_from_str_and_uint(&osa, "1.2.3.4", 1234) == 0);
+ OSMO_ASSERT(!osmo_sockaddr_is_any(&osa));
+}
+
+static void test_osa_netmask_prefixlen(void)
+{
+ struct osmo_sockaddr ipv4;
+ struct osmo_sockaddr ipv6;
+ int rc;
+
+ ipv4.u.sin = (struct sockaddr_in){
+ .sin_family = AF_INET,
+ };
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("0.0.0.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 0);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("255.0.0.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 8);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.0.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 16);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.255.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 24);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("255.255.255.255");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ OSMO_ASSERT(rc == 32);
+
+ ipv4.u.sin.sin_addr.s_addr = inet_addr("0.255.0.0");
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv4);
+ /* FIXME: This shows the implementation is not that robust checking validity of input netmask: */
+ OSMO_ASSERT(rc == 8);
+
+ ipv6.u.sin6 = (struct sockaddr_in6){
+ .sin6_family = AF_INET6,
+ };
+
+ inet_pton(AF_INET6, "fe::", &ipv6.u.sin6.sin6_addr);
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6);
+ OSMO_ASSERT(rc == 7);
+
+ inet_pton(AF_INET6, "ff::", &ipv6.u.sin6.sin6_addr);
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6);
+ OSMO_ASSERT(rc == 8);
+
+ inet_pton(AF_INET6, "ff:ff::", &ipv6.u.sin6.sin6_addr);
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6);
+ OSMO_ASSERT(rc == 16);
+
+ inet_pton(AF_INET6, "ff:ff::ff", &ipv6.u.sin6.sin6_addr);
+ rc = osmo_sockaddr_netmask_to_prefixlen(&ipv6);
+ /* FIXME: This shows the implementation is not that robust checking validity of input netmask: */
+ OSMO_ASSERT(rc == 24);
+}
const struct log_info_cat default_categories[] = {
};
@@ -139,10 +468,16 @@ int main(int argc, char *argv[])
ctx = talloc_named_const(NULL, 0, "socket_test");
osmo_init_logging2(ctx, &info);
log_set_use_color(osmo_stderr_target, 0);
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
test_sockinit();
test_sockinit2();
+ test_get_ip_and_port();
+ test_sockinit_osa();
+ test_osa_str();
+ test_osa_netmask_prefixlen();
return EXIT_SUCCESS;
}
diff --git a/tests/socket/socket_test.err b/tests/socket/socket_test.err
index ed6e1865..3c198ac3 100644
--- a/tests/socket/socket_test.err
+++ b/tests/socket/socket_test.err
@@ -1,2 +1,8 @@
invalid: both bind and connect flags set: 0.0.0.0:0
invalid: you have to specify either BIND or CONNECT flags
+Unable to find a common protocol (IPv4 or IPv6) for local host: 127.0.0.1 and remote host: ::1.
+Unable to find a common protocol (IPv4 or IPv6) for local host: ::1 and remote host: 127.0.0.1.
+invalid: you have to specify either BIND or CONNECT flags
+invalid: the family for local and remote endpoint must be same.
+invalid: the family for local and remote endpoint must be same.
+invalid: the family for local and remote endpoint must be same.
diff --git a/tests/socket/socket_test.ok b/tests/socket/socket_test.ok
index 4b24fbce..2b1c1006 100644
--- a/tests/socket/socket_test.ok
+++ b/tests/socket/socket_test.ok
@@ -5,3 +5,32 @@ Checking osmo_sock_init2() with bind to a random local UDP port
Checking osmo_sock_init2() for OSMO_SOCK_F_NONBLOCK
Checking osmo_sock_init2() for invalid flags
Checking osmo_sock_init2() for combined BIND + CONNECT
+Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv4 & IPv6
+Checking osmo_sock_init2(AF_UNSPEC) must fail on mixed IPv6 & IPv4
+Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv4
+Checking osmo_sock_init2(AF_UNSPEC) BIND + CONNECT on IPv6
+Checking osmo_sock_init2(AF_UNSPEC) BIND on IPv4
+Checking test_get_ip_and_port() for combined BIND + CONNECT on IPv4
+Checking test_get_ip_and_port() for combined BIND + CONNECT on IPv6
+Checking osmo_sock_init_osa() with bind to a random local UDP port
+Checking osmo_sock_init_osa() IPv4 for OSMO_SOCK_F_NONBLOCK
+Checking osmo_sock_init_osa() IPv6 for OSMO_SOCK_F_NONBLOCK
+Checking osmo_sock_init_osa() for invalid flags
+Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv4
+Checking osmo_sock_init_osa() for combined BIND + CONNECT on IPv6
+Checking osmo_sock_init_osa() must fail on mixed IPv4 & IPv6
+Checking osmo_sock_init_osa() must fail on mixed IPv6 & IPv4
+Checking osmo_sock_init_osa() must fail on invalid osmo_sockaddr
+Checking osmo_sockaddr_to_str_buf to small IPv4
+Checking osmo_sockaddr_to_str_buf IPv4
+Checking osmo_sockaddr_to_str_buf IPv6
+Checking osmo_sockaddr_to_str_buf too short IPv6
+Checking osmo_sockaddr_to_str_buf too short IPv6
+Checking osmo_sockaddr_to_str_buf only 5 bytes IPv6
+Checking osmo_sockaddr_to_str_buf only 5 bytes IPv6
+Checking osmo_sockaddr_to_str_buf long IPv6
+Checking osmo_sockaddr_to_str_buf long IPv6 port
+Checking osmo_sockaddr_to_str_buf long IPv6 port static buffer
+Checking osmo_sockaddr_from_str_and_uint for 0.0.0.0
+Checking osmo_sockaddr_from_str_and_uint for ::
+Checking osmo_sockaddr_from_str_and_uint for 1.2.3.4
diff --git a/tests/soft_uart/soft_uart_test.c b/tests/soft_uart/soft_uart_test.c
new file mode 100644
index 00000000..7280bdcd
--- /dev/null
+++ b/tests/soft_uart/soft_uart_test.c
@@ -0,0 +1,658 @@
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/soft_uart.h>
+
+#define SUART_TEST_BEGIN \
+ do { \
+ printf("\nExecuting %s\n", __func__); \
+ } while (0)
+
+static struct {
+ size_t data_len;
+ const uint8_t *data;
+} g_tx_cb_cfg;
+
+static void suart_rx_cb(void *priv, struct msgb *msg, unsigned int flags)
+{
+ fprintf(stdout, "%s(flags=%02x): %s\n",
+ __func__, flags, msgb_hexdump(msg));
+ msgb_free(msg);
+}
+
+static void suart_tx_cb(void *priv, struct msgb *msg)
+{
+ size_t n_bytes;
+
+ n_bytes = OSMO_MIN(g_tx_cb_cfg.data_len, msg->data_len);
+ if (g_tx_cb_cfg.data != NULL && n_bytes > 0)
+ memcpy(msgb_put(msg, n_bytes), g_tx_cb_cfg.data, n_bytes);
+
+ fprintf(stdout, "%s(len=%u/%u): %s\n",
+ __func__, msg->len, msg->data_len, msgb_hexdump(msg));
+}
+
+static void suart_status_change_cb(void *priv, unsigned int status)
+{
+ fprintf(stdout, "%s(status=0x%08x)\n", __func__, status);
+}
+
+static const struct osmo_soft_uart_cfg suart_test_default_cfg = {
+ .num_data_bits = 8,
+ .num_stop_bits = 1,
+ .parity_mode = OSMO_SUART_PARITY_NONE,
+ .rx_buf_size = 128,
+ .rx_cb = &suart_rx_cb,
+ .tx_cb = &suart_tx_cb,
+ .status_change_cb = &suart_status_change_cb,
+};
+
+static void test_rx_exec(struct osmo_soft_uart *suart,
+ const char *input)
+{
+ for (unsigned int i = 0; input[i] != '\0'; i++) {
+ ubit_t ubit;
+ int rc;
+
+ switch (input[i]) {
+ case '0':
+ case '1':
+ ubit = input[i] - '0';
+ rc = osmo_soft_uart_rx_ubits(suart, &ubit, 1);
+ OSMO_ASSERT(rc == 0); /* 0 on success */
+ break;
+ case 'F':
+ printf("%s() @ %u: flush the Rx buffer\n", __func__, i);
+ osmo_soft_uart_flush_rx(suart);
+ break;
+ case ' ': /* padding */
+ continue;
+ default:
+ printf("%s() @ %u: unknown opcode '%c'\n",
+ __func__, i, input[i]);
+ break;
+ }
+ }
+}
+
+static void test_rx(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_rx(suart, true);
+
+ printf("======== testing 8-N-1 (no data)\n");
+ test_rx_exec(suart, "F11111F11111F");
+
+ printf("======== testing 8-N-1 (fill up flush)\n");
+ cfg = suart_test_default_cfg;
+ cfg.rx_buf_size = 4;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "11111" /* no data */
+ "0 01111011 1"
+ "0 10110101 1"
+ "0 01111101 1"
+ "0 11110111 1" /* filled up, expect flush */
+ "0 00000000 1"
+ "0 01010101 1"
+ "0 10101010 1"
+ "0 11111111 1" /* filled up, expect flush */
+ "F" /* flush! (for sanity) */
+ );
+
+ printf("======== testing 8-N-1 (HELLO)\n");
+ cfg = suart_test_default_cfg;
+ cfg.num_stop_bits = 1;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "111111" /* no data */
+ "0 00010010 1F" /* 'H', flush! */
+ "0 10100010 1F" /* 'E', flush! */
+ "1111111111111" /* no data */
+ "0 00110010 1F" /* 'L', flush! */
+ "0 00110010 1F" /* 'L', flush! */
+ "1111111111111" /* no data */
+ "0 11110010 1F" /* 'O', flush! */
+ );
+
+ printf("======== testing 8-N-1 (framing errors)\n");
+ test_rx_exec(suart, "11111" /* no data */
+ "0 00000000 0" /* stop bit != 1, expect flush */
+ "0 01010101 0" /* stop bit != 1, expect flush */
+ "0 11111111 1" /* stop bit == 1, recovery */
+ "F" /* flush! */
+ );
+
+ printf("======== testing 8-N-2 (HELLO)\n");
+ cfg = suart_test_default_cfg;
+ cfg.num_stop_bits = 2;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "11111111" /* no data */
+ "0 00010010 1F1F" /* 'H', flush! */
+ "0 10100010 1F1F" /* 'E', flush! */
+ "111111111111111" /* no data */
+ "0 00110010 1F1F" /* 'L', flush! */
+ "0 00110010 1F1F" /* 'L', flush! */
+ "111111111111111" /* no data */
+ "0 11110010 1F1F" /* 'O', flush! */
+ );
+
+ printf("======== testing 8-N-2 (framing errors)\n");
+ test_rx_exec(suart, "11111" /* no data */
+ "0 00000000 00" /* stop bit != 1, expect flush */
+ "0 01010101 01" /* stop bit != 1, expect flush */
+ "0 10101010 10" /* stop bit != 1, expect flush */
+ "0 11111111 11" /* stop bit == 1, recovery */
+ "F" /* flush! (for sanity) */
+ );
+
+
+ printf("======== testing 8-E-1 (invalid parity)\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_EVEN;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "1111111" /* no data */
+ "0 00000000 1 1" /* odd parity, expect flush */
+ "0 10000000 0 1" /* odd parity, expect flush */
+ "0 11111111 1 1" /* odd parity, expect flush */
+ "F" /* flush! (for sanity) */
+ );
+ printf("======== testing 8-E-1 (valid parity)\n");
+ test_rx_exec(suart, "1111111" /* no data */
+ "0 00000000 0 1"
+ "0 11111111 0 1"
+ "0 01010101 0 1"
+ "0 10101010 0 1"
+ "F" /* flush! */
+ "0 00000001 1 1"
+ "0 00000111 1 1"
+ "0 00011111 1 1"
+ "0 01111111 1 1"
+ "F" /* flush! */
+ );
+
+ printf("======== testing 8-O-1 (invalid parity)\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_ODD;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart,
+ "0 00000000 0 1" /* even parity, expect flush */
+ "0 10000000 1 1" /* even parity, expect flush */
+ "0 11111111 0 1" /* even parity, expect flush */
+ "F" /* flush! (for sanity) */
+ );
+ printf("======== testing 8-O-1 (valid parity)\n");
+ test_rx_exec(suart, "1111111" /* no data */
+ "0 00000000 1 1"
+ "0 11111111 1 1"
+ "0 01010101 1 1"
+ "0 10101010 1 1"
+ "F" /* flush! */
+ "0 00000001 0 1"
+ "0 00000111 0 1"
+ "0 00011111 0 1"
+ "0 01111111 0 1"
+ "F" /* flush! */
+ );
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_rx_flush(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ printf("calling osmo_soft_uart_flush_rx() while Rx disabled\n");
+ osmo_soft_uart_flush_rx(suart);
+
+ printf("enabling the receiver\n");
+ osmo_soft_uart_set_rx(suart, true);
+
+ printf("calling osmo_soft_uart_flush_rx() while Rx enabled, but no data\n");
+ osmo_soft_uart_flush_rx(suart);
+
+ /* FIXME: this scenario demonstrates a problem that may occur when the user
+ * flushes the Rx buffer manually while the soft-UART state reflects flags
+ * of an incomplete symbol, for which we're waiting the stop bit. */
+ printf("testing corner case: manual flushing during a parity error (8-E-1)\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_EVEN;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_rx_exec(suart, "1111111" /* no data */
+ "0 01010101 0 1" /* even parity, correct */
+ "0 10101010 0 1" /* even parity, correct */
+ "0 11111111 1" /* odd parity, incorrect, but stop bit is pending */
+ "F" /* manual flush happens before receiving the stop bit */
+ "1" /* finally, the stop bit is received */
+ );
+ /* test_rx_exec() @ 47: flush the Rx buffer
+ * suart_rx_cb(flags=02): aa 55 <--- this is wrong, should be flags=00
+ * suart_rx_cb(flags=02): ff <--- this is expected due to odd parity */
+
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_tx_rx_exec_one(struct osmo_soft_uart *suart,
+ size_t n_bits_total, size_t n_bits_frame)
+{
+ ubit_t tx_buf[n_bits_total];
+ ubit_t *ptr = &tx_buf[0];
+ int rc;
+
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], n_bits_total);
+ OSMO_ASSERT(rc == n_bits_total);
+
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], n_bits_total);
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ printf("%s(n_bits_total=%zu):", __func__, n_bits_total);
+ while (n_bits_total > 0) {
+ size_t n_bits = OSMO_MIN(n_bits_frame, n_bits_total);
+ printf(" %s", osmo_ubit_dump(ptr, n_bits));
+ n_bits_total -= n_bits;
+ ptr += n_bits;
+ }
+ printf("\n");
+}
+
+static void test_tx_rx_exec(struct osmo_soft_uart *suart, size_t n_bits_frame)
+{
+ const uint8_t tx_data[][4] = {
+ { 0xde, 0xad, 0xbe, 0xef },
+ { 0x00, 0xaa, 0x55, 0xff },
+ { 0x01, 0x02, 0x04, 0x08 },
+ { 0x10, 0x20, 0x40, 0x80 },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(tx_data); i++) {
+ g_tx_cb_cfg.data_len = 4;
+ g_tx_cb_cfg.data = tx_data[i];
+ test_tx_rx_exec_one(suart, 4 * n_bits_frame, n_bits_frame);
+ }
+
+ g_tx_cb_cfg.data_len = 0;
+ g_tx_cb_cfg.data = NULL;
+ test_tx_rx_exec_one(suart, 4 * n_bits_frame, n_bits_frame);
+}
+
+static void test_tx_rx(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ /* expect -EAGAIN when the transmitter is not enabled */
+ rc = osmo_soft_uart_tx_ubits(suart, NULL, 42);
+ OSMO_ASSERT(rc == -EAGAIN);
+ /* expect -EAGAIN when the receiver is not enabled */
+ rc = osmo_soft_uart_rx_ubits(suart, NULL, 42);
+ OSMO_ASSERT(rc == -EAGAIN);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ printf("======== testing 8-N-1\n");
+ test_tx_rx_exec(suart, (1 + 8 + 1));
+
+ printf("======== testing 8-N-2\n");
+ cfg = suart_test_default_cfg;
+ cfg.num_stop_bits = 2;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 2));
+
+ printf("======== testing 8-E-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_EVEN;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 1 + 1));
+
+ printf("======== testing 8-O-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_ODD;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 1 + 1));
+
+ printf("======== testing 8-M-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_MARK;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 1 + 1));
+
+ printf("======== testing 8-S-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.parity_mode = OSMO_SUART_PARITY_SPACE;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 8 + 1 + 1));
+
+ printf("======== testing 6-N-1\n");
+ cfg = suart_test_default_cfg;
+ cfg.num_data_bits = 6;
+ osmo_soft_uart_configure(suart, &cfg);
+ test_tx_rx_exec(suart, (1 + 6 + 1));
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_tx_rx_pull_n(unsigned int n)
+{
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[32];
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ g_tx_cb_cfg.data = (void *)"\x55";
+ g_tx_cb_cfg.data_len = 1;
+
+ printf("======== pulling %lu bits (%u at a time)\n", sizeof(tx_buf), n);
+ for (unsigned int i = 0; i < sizeof(tx_buf); i += n) {
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[i], n);
+ OSMO_ASSERT(rc == n);
+ }
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("======== feeding %lu bits into the receiver\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_modem_status(void)
+{
+ struct osmo_soft_uart *suart;
+ unsigned int status;
+
+ SUART_TEST_BEGIN;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ printf("initial status=0x%08x\n", osmo_soft_uart_get_status(suart));
+
+ printf("de-asserting DCD, which was not asserted\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DCD, false);
+ OSMO_ASSERT(osmo_soft_uart_get_status(suart) == 0x00); /* no change */
+
+ printf("asserting both RI and DCD, expecting the callback to be called twice\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_RI, true);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DCD, true);
+ status = osmo_soft_uart_get_status(suart);
+ OSMO_ASSERT(status == (OSMO_SUART_STATUS_F_RI | OSMO_SUART_STATUS_F_DCD));
+
+ printf("de-asserting RI, expecting the callback to be called\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_RI, false);
+ status = osmo_soft_uart_get_status(suart);
+ OSMO_ASSERT(status == (OSMO_SUART_STATUS_F_DCD));
+
+ printf("resetting to 0x00, expecting the callback to be called\n");
+ osmo_soft_uart_set_status(suart, 0x00);
+ OSMO_ASSERT(osmo_soft_uart_get_status(suart) == 0x00);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_flow_control_dtr_dsr(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[40];
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ g_tx_cb_cfg.data = (void *)"\x42\x42\x42\x42";
+ g_tx_cb_cfg.data_len = 4;
+
+ cfg = suart_test_default_cfg;
+ cfg.flow_ctrl_mode = OSMO_SUART_FLOW_CTRL_DTR_DSR;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ /* expect the initial status to be 0 (all lines de-asserted) */
+ printf("initial status=0x%08x\n", osmo_soft_uart_get_status(suart));
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yield nothing\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to yield nothing\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ /* both DTR and DSR are asserted, expect both Rx and Tx to work */
+ printf("======== asserting both DTR and DSR\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DTR, true);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DSR, true);
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield %zu bits (requesting %zu bits)\n",
+ sizeof(tx_buf), sizeof(tx_buf));
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == sizeof(tx_buf));
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("expecting osmo_soft_uart_rx_ubits() to "
+ "consume %zu bits and yield %zu chars\n",
+ sizeof(tx_buf), sizeof(tx_buf) / 10);
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ /* make the transmitter consume one char, but pull only 2 bits */
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield 2 bits (requesting 2 bits)\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], 2);
+ OSMO_ASSERT(rc == 2);
+
+ /* CTS gets de-asserted, the transmitter is shutting down */
+ printf("======== de-asserting DSR\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_DSR, false);
+
+ /* expect only the remaining 8 bits to be pulled out */
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield 8 bits (requesting %zu bits)\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[2], sizeof(tx_buf) - 2);
+ OSMO_ASSERT(rc == 8);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to "
+ "consume %zu bits and yield a pending char\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_flow_control_rts_cts(void)
+{
+ struct osmo_soft_uart_cfg cfg;
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[40];
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ g_tx_cb_cfg.data = (void *)"\x42\x42\x42\x42";
+ g_tx_cb_cfg.data_len = 4;
+
+ cfg = suart_test_default_cfg;
+ cfg.flow_ctrl_mode = OSMO_SUART_FLOW_CTRL_RTS_CTS;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+ osmo_soft_uart_set_rx(suart, true);
+
+ /* expect the initial status to be 0 (all lines de-asserted) */
+ printf("initial status=0x%08x\n", osmo_soft_uart_get_status(suart));
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ printf("expecting osmo_soft_uart_tx_ubits() to yield nothing\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to yield nothing\n");
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ /* both RTS/RTR and CTS are asserted, expect both Rx and Tx to work */
+ printf("======== asserting both CTS and RTS/RTR\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_CTS, true);
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_RTS_RTR, true);
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield %zu bits (requesting %zu bits)\n",
+ sizeof(tx_buf), sizeof(tx_buf));
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == sizeof(tx_buf));
+ printf("%s\n", osmo_ubit_dump(&tx_buf[0], sizeof(tx_buf)));
+
+ printf("expecting osmo_soft_uart_rx_ubits() to "
+ "consume %zu bits and yield %zu chars\n",
+ sizeof(tx_buf), sizeof(tx_buf) / 10);
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ memset(&tx_buf[0], 1, sizeof(tx_buf)); /* pre-initialize */
+
+ /* make the transmitter consume one char, but pull only 2 bits */
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield 2 bits (requesting 2 bits)\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], 2);
+ OSMO_ASSERT(rc == 2);
+
+ /* CTS gets de-asserted, the transmitter is shutting down */
+ printf("======== de-asserting CTS\n");
+ osmo_soft_uart_set_status_line(suart, OSMO_SUART_STATUS_F_CTS, false);
+
+ /* expect only the remaining 8 bits to be pulled out */
+ printf("expecting osmo_soft_uart_tx_ubits() to "
+ "yield 8 bits (requesting %zu bits)\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[2], sizeof(tx_buf) - 2);
+ OSMO_ASSERT(rc == 8);
+
+ printf("expecting osmo_soft_uart_rx_ubits() to "
+ "consume %zu bits and yield a pending char\n", sizeof(tx_buf));
+ rc = osmo_soft_uart_rx_ubits(suart, &tx_buf[0], sizeof(tx_buf));
+ OSMO_ASSERT(rc == 0);
+ osmo_soft_uart_flush_rx(suart);
+
+ osmo_soft_uart_free(suart);
+}
+
+static void test_tx_pull(void)
+{
+ struct osmo_soft_uart *suart;
+ ubit_t tx_buf[25 * 2];
+ int rc;
+
+ SUART_TEST_BEGIN;
+
+ g_tx_cb_cfg.data = (void *)"\x42\x42\x42\x42\x42";
+ g_tx_cb_cfg.data_len = 5;
+
+ suart = osmo_soft_uart_alloc(NULL, __func__, &suart_test_default_cfg);
+ OSMO_ASSERT(suart != NULL);
+
+ osmo_soft_uart_set_tx(suart, true);
+
+ printf("pulling 25 bits (first time) out of the transmitter\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[0], sizeof(tx_buf) / 2);
+ OSMO_ASSERT(rc == 25);
+
+ printf("pulling 25 bits (second time) out of the transmitter\n");
+ rc = osmo_soft_uart_tx_ubits(suart, &tx_buf[25], sizeof(tx_buf) / 2);
+ OSMO_ASSERT(rc == 25);
+
+ /* FIXME: we pull total 25 + 25 == 50 bits out of the transmitter, which is enough
+ * to fit 5 characters (assuming 8-N-1). However, the current impelementation would
+ * pull only 2 + 2 == characters total, wasting 5 + 5 == 10 bits for padding. */
+
+ osmo_soft_uart_free(suart);
+}
+
+int main(int argc, char **argv)
+{
+ test_rx();
+ test_rx_flush();
+ test_tx_rx();
+
+ /* test pulling small number of bits at a time */
+ test_tx_rx_pull_n(1);
+ test_tx_rx_pull_n(2);
+ test_tx_rx_pull_n(4);
+ test_tx_rx_pull_n(8);
+
+ test_tx_pull();
+
+ /* test flow control */
+ test_modem_status();
+ test_flow_control_dtr_dsr();
+ test_flow_control_rts_cts();
+
+ return 0;
+}
diff --git a/tests/soft_uart/soft_uart_test.ok b/tests/soft_uart/soft_uart_test.ok
new file mode 100644
index 00000000..dcd7ceb4
--- /dev/null
+++ b/tests/soft_uart/soft_uart_test.ok
@@ -0,0 +1,278 @@
+
+Executing test_rx
+======== testing 8-N-1 (no data)
+test_rx_exec() @ 0: flush the Rx buffer
+test_rx_exec() @ 6: flush the Rx buffer
+test_rx_exec() @ 12: flush the Rx buffer
+======== testing 8-N-1 (fill up flush)
+suart_rx_cb(flags=00): de ad be ef
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_rx_exec() @ 101: flush the Rx buffer
+======== testing 8-N-1 (HELLO)
+test_rx_exec() @ 18: flush the Rx buffer
+suart_rx_cb(flags=00): 48
+test_rx_exec() @ 31: flush the Rx buffer
+suart_rx_cb(flags=00): 45
+test_rx_exec() @ 57: flush the Rx buffer
+suart_rx_cb(flags=00): 4c
+test_rx_exec() @ 70: flush the Rx buffer
+suart_rx_cb(flags=00): 4c
+test_rx_exec() @ 96: flush the Rx buffer
+suart_rx_cb(flags=00): 4f
+======== testing 8-N-1 (framing errors)
+suart_rx_cb(flags=01): 00
+suart_rx_cb(flags=01): aa
+test_rx_exec() @ 41: flush the Rx buffer
+suart_rx_cb(flags=00): ff
+======== testing 8-N-2 (HELLO)
+test_rx_exec() @ 20: flush the Rx buffer
+test_rx_exec() @ 22: flush the Rx buffer
+suart_rx_cb(flags=00): 48
+test_rx_exec() @ 35: flush the Rx buffer
+test_rx_exec() @ 37: flush the Rx buffer
+suart_rx_cb(flags=00): 45
+test_rx_exec() @ 65: flush the Rx buffer
+test_rx_exec() @ 67: flush the Rx buffer
+suart_rx_cb(flags=00): 4c
+test_rx_exec() @ 80: flush the Rx buffer
+test_rx_exec() @ 82: flush the Rx buffer
+suart_rx_cb(flags=00): 4c
+test_rx_exec() @ 110: flush the Rx buffer
+test_rx_exec() @ 112: flush the Rx buffer
+suart_rx_cb(flags=00): 4f
+======== testing 8-N-2 (framing errors)
+suart_rx_cb(flags=01): 00
+suart_rx_cb(flags=01): aa
+suart_rx_cb(flags=01): 55
+test_rx_exec() @ 57: flush the Rx buffer
+suart_rx_cb(flags=00): ff
+======== testing 8-E-1 (invalid parity)
+suart_rx_cb(flags=02): 00
+suart_rx_cb(flags=02): 01
+suart_rx_cb(flags=02): ff
+test_rx_exec() @ 49: flush the Rx buffer
+======== testing 8-E-1 (valid parity)
+test_rx_exec() @ 63: flush the Rx buffer
+suart_rx_cb(flags=00): 00 ff aa 55
+test_rx_exec() @ 120: flush the Rx buffer
+suart_rx_cb(flags=00): 80 e0 f8 fe
+======== testing 8-O-1 (invalid parity)
+suart_rx_cb(flags=02): 00
+suart_rx_cb(flags=02): 01
+suart_rx_cb(flags=02): ff
+test_rx_exec() @ 42: flush the Rx buffer
+======== testing 8-O-1 (valid parity)
+test_rx_exec() @ 63: flush the Rx buffer
+suart_rx_cb(flags=00): 00 ff aa 55
+test_rx_exec() @ 120: flush the Rx buffer
+suart_rx_cb(flags=00): 80 e0 f8 fe
+
+Executing test_rx_flush
+calling osmo_soft_uart_flush_rx() while Rx disabled
+enabling the receiver
+calling osmo_soft_uart_flush_rx() while Rx enabled, but no data
+testing corner case: manual flushing during a parity error (8-E-1)
+test_rx_exec() @ 47: flush the Rx buffer
+suart_rx_cb(flags=02): aa 55
+suart_rx_cb(flags=02): ff
+
+Executing test_tx_rx
+======== testing 8-N-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=40): 0011110111 0101101011 0011111011 0111101111
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=40): 0000000001 0010101011 0101010101 0111111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=40): 0100000001 0010000001 0001000001 0000100001
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=40): 0000010001 0000001001 0000000101 0000000011
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=40): 1111111111 1111111111 1111111111 1111111111
+======== testing 8-N-2
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101111 01011010111 00111110111 01111011111
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000011 00101010111 01010101011 01111111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000011 00100000011 00010000011 00001000011
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100011 00000010011 00000001011 00000000111
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 8-E-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101101 01011010111 00111110101 01111011111
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000001 00101010101 01010101001 01111111101
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000011 00100000011 00010000011 00001000011
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100011 00000010011 00000001011 00000000111
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 8-O-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101111 01011010101 00111110111 01111011101
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000011 00101010111 01010101011 01111111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000001 00100000001 00010000001 00001000001
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100001 00000010001 00000001001 00000000101
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 8-M-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101111 01011010111 00111110111 01111011111
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000011 00101010111 01010101011 01111111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000011 00100000011 00010000011 00001000011
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100011 00000010011 00000001011 00000000111
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 8-S-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): de ad be ef
+test_tx_rx_exec_one(n_bits_total=44): 00111101101 01011010101 00111110101 01111011101
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 aa 55 ff
+test_tx_rx_exec_one(n_bits_total=44): 00000000001 00101010101 01010101001 01111111101
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=44): 01000000001 00100000001 00010000001 00001000001
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 40 80
+test_tx_rx_exec_one(n_bits_total=44): 00000100001 00000010001 00000001001 00000000101
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=44): 11111111111 11111111111 11111111111 11111111111
+======== testing 6-N-1
+suart_tx_cb(len=4/4): de ad be ef
+suart_rx_cb(flags=00): 1e 2d 3e 2f
+test_tx_rx_exec_one(n_bits_total=32): 00111101 01011011 00111111 01111011
+suart_tx_cb(len=4/4): 00 aa 55 ff
+suart_rx_cb(flags=00): 00 2a 15 3f
+test_tx_rx_exec_one(n_bits_total=32): 00000001 00101011 01010101 01111111
+suart_tx_cb(len=4/4): 01 02 04 08
+suart_rx_cb(flags=00): 01 02 04 08
+test_tx_rx_exec_one(n_bits_total=32): 01000001 00100001 00010001 00001001
+suart_tx_cb(len=4/4): 10 20 40 80
+suart_rx_cb(flags=00): 10 20 00 00
+test_tx_rx_exec_one(n_bits_total=32): 00000101 00000011 00000001 00000001
+suart_tx_cb(len=0/4):
+test_tx_rx_exec_one(n_bits_total=32): 11111111 11111111 11111111 11111111
+
+Executing test_tx_rx_pull_n
+======== pulling 32 bits (1 at a time)
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+01010101010101010101010101010101
+======== feeding 32 bits into the receiver
+suart_rx_cb(flags=00): 55 55 55
+
+Executing test_tx_rx_pull_n
+======== pulling 32 bits (2 at a time)
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+01010101010101010101010101010101
+======== feeding 32 bits into the receiver
+suart_rx_cb(flags=00): 55 55 55
+
+Executing test_tx_rx_pull_n
+======== pulling 32 bits (4 at a time)
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+01010101011101010101011101010101
+======== feeding 32 bits into the receiver
+suart_rx_cb(flags=00): 55 55
+
+Executing test_tx_rx_pull_n
+======== pulling 32 bits (8 at a time)
+suart_tx_cb(len=1/1): 55
+suart_tx_cb(len=1/1): 55
+01010101011111110101010101111111
+======== feeding 32 bits into the receiver
+suart_rx_cb(flags=00): 55 55
+
+Executing test_tx_pull
+pulling 25 bits (first time) out of the transmitter
+suart_tx_cb(len=2/2): 42 42
+pulling 25 bits (second time) out of the transmitter
+suart_tx_cb(len=2/2): 42 42
+
+Executing test_modem_status
+initial status=0x00000000
+de-asserting DCD, which was not asserted
+asserting both RI and DCD, expecting the callback to be called twice
+suart_status_change_cb(status=0x00000008)
+suart_status_change_cb(status=0x0000000a)
+de-asserting RI, expecting the callback to be called
+suart_status_change_cb(status=0x00000002)
+resetting to 0x00, expecting the callback to be called
+suart_status_change_cb(status=0x00000000)
+
+Executing test_flow_control_dtr_dsr
+initial status=0x00000000
+expecting osmo_soft_uart_tx_ubits() to yield nothing
+expecting osmo_soft_uart_rx_ubits() to yield nothing
+======== asserting both DTR and DSR
+suart_status_change_cb(status=0x00000001)
+suart_status_change_cb(status=0x00000005)
+expecting osmo_soft_uart_tx_ubits() to yield 40 bits (requesting 40 bits)
+suart_tx_cb(len=4/4): 42 42 42 42
+0010000101001000010100100001010010000101
+expecting osmo_soft_uart_rx_ubits() to consume 40 bits and yield 4 chars
+suart_rx_cb(flags=00): 42 42 42 42
+expecting osmo_soft_uart_tx_ubits() to yield 2 bits (requesting 2 bits)
+suart_tx_cb(len=1/1): 42
+======== de-asserting DSR
+suart_status_change_cb(status=0x00000001)
+expecting osmo_soft_uart_tx_ubits() to yield 8 bits (requesting 40 bits)
+expecting osmo_soft_uart_rx_ubits() to consume 40 bits and yield a pending char
+suart_rx_cb(flags=00): 42
+
+Executing test_flow_control_rts_cts
+initial status=0x00000000
+expecting osmo_soft_uart_tx_ubits() to yield nothing
+expecting osmo_soft_uart_rx_ubits() to yield nothing
+======== asserting both CTS and RTS/RTR
+suart_status_change_cb(status=0x00000020)
+suart_status_change_cb(status=0x00000030)
+expecting osmo_soft_uart_tx_ubits() to yield 40 bits (requesting 40 bits)
+suart_tx_cb(len=4/4): 42 42 42 42
+0010000101001000010100100001010010000101
+expecting osmo_soft_uart_rx_ubits() to consume 40 bits and yield 4 chars
+suart_rx_cb(flags=00): 42 42 42 42
+expecting osmo_soft_uart_tx_ubits() to yield 2 bits (requesting 2 bits)
+suart_tx_cb(len=1/1): 42
+======== de-asserting CTS
+suart_status_change_cb(status=0x00000010)
+expecting osmo_soft_uart_tx_ubits() to yield 8 bits (requesting 40 bits)
+expecting osmo_soft_uart_rx_ubits() to consume 40 bits and yield a pending char
+suart_rx_cb(flags=00): 42
diff --git a/tests/stats/stats_test.c b/tests/stats/stats_test.c
index 71f710a9..eda4129b 100644
--- a/tests/stats/stats_test.c
+++ b/tests/stats/stats_test.c
@@ -1,6 +1,6 @@
/* tests for statistics */
/*
- * (C) 2015 sysmocom - s.m.f.c. GmbH
+ * (C) 2015 sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -16,18 +16,17 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
+#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stats.h>
+#include <stat_item_internal.h>
+
#include <stdio.h>
#include <inttypes.h>
@@ -87,11 +86,10 @@ static void stat_test(void)
struct osmo_stat_item_group *sgrp2;
const struct osmo_stat_item *sitem1, *sitem2;
- int rc;
int32_t value;
- int32_t rd_a = 0;
- int32_t rd_b = 0;
int i;
+ int64_t sum1;
+ int64_t sum2;
OSMO_ASSERT(statg != NULL);
@@ -109,144 +107,151 @@ static void stat_test(void)
sitem1 = osmo_stat_item_get_by_name(statg, "item.a");
OSMO_ASSERT(sitem1 != NULL);
- OSMO_ASSERT(sitem1 == statg->items[TEST_A_ITEM]);
+ OSMO_ASSERT(sitem1 == osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
sitem2 = osmo_stat_item_get_by_name(statg, "item.b");
OSMO_ASSERT(sitem2 != NULL);
OSMO_ASSERT(sitem2 != sitem1);
- OSMO_ASSERT(sitem2 == statg->items[TEST_B_ITEM]);
+ OSMO_ASSERT(sitem2 == osmo_stat_item_group_get_item(statg, TEST_B_ITEM));
- value = osmo_stat_item_get_last(statg->items[TEST_A_ITEM]);
+ /* No value set yet, expecting default value from osmo_stat_item_desc definition above. */
+ value = osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
OSMO_ASSERT(value == -1);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc == 0);
-
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 1);
-
- value = osmo_stat_item_get_last(statg->items[TEST_A_ITEM]);
- OSMO_ASSERT(value == 1);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
+ /* No value set yet, expecting new value in all fields */
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 1);
+ sum1 = 1;
+ value = osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
OSMO_ASSERT(value == 1);
+ OSMO_ASSERT(sitem1->value.n == 1);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 1);
+ OSMO_ASSERT(sitem1->value.max == 1);
+ OSMO_ASSERT(sitem1->value.sum == 1);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc == 0);
-
+ sum2 = 0;
for (i = 2; i <= 32; i++) {
- osmo_stat_item_set(statg->items[TEST_A_ITEM], i);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == i);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 1000 + i);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), i);
+ sum1 += i;
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), 1000 + i);
+ sum2 += 1000 + i;
}
+ OSMO_ASSERT(sitem1->value.n == 32);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 32);
+ OSMO_ASSERT(sitem1->value.max == 32);
+ OSMO_ASSERT(sitem1->value.sum == sum1);
+
+ OSMO_ASSERT(sitem2->value.n == 31);
+ OSMO_ASSERT(sitem2->value.min == 1002);
+ OSMO_ASSERT(sitem2->value.last == 1032);
+ OSMO_ASSERT(sitem2->value.max == 1032);
+ OSMO_ASSERT(sitem2->value.sum == sum2);
/* check if dec & inc is working */
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 42);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 42);
-
- osmo_stat_item_dec(statg->items[TEST_A_ITEM], 21);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 21);
-
- osmo_stat_item_inc(statg->items[TEST_A_ITEM], 21);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 42);
-
- /* Keep 2 in FIFO */
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 33);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + 33);
-
- for (i = 34; i <= 64; i++) {
- osmo_stat_item_set(statg->items[TEST_A_ITEM], i);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == i-1);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 1000 + i-1);
- }
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 64);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 1000 + 64);
-
- /* Overrun FIFOs */
- for (i = 65; i <= 96; i++) {
- osmo_stat_item_set(statg->items[TEST_A_ITEM], i);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i);
- }
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 93);
-
- for (i = 94; i <= 96; i++) {
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == i);
- }
-
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 1000 + 90);
-
- for (i = 91; i <= 96; i++) {
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 1000 + i);
- }
-
- /* Test Discard (single item) */
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 97);
- rc = osmo_stat_item_discard(statg->items[TEST_A_ITEM], &rd_a);
- OSMO_ASSERT(rc > 0);
-
- rc = osmo_stat_item_discard(statg->items[TEST_A_ITEM], &rd_a);
- OSMO_ASSERT(rc == 0);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc == 0);
-
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 98);
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc > 0);
- OSMO_ASSERT(value == 98);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc == 0);
-
- /* Test Discard (all items) */
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 99);
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 100);
- osmo_stat_item_set(statg->items[TEST_A_ITEM], 101);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 99);
- osmo_stat_item_set(statg->items[TEST_B_ITEM], 100);
-
- rc = osmo_stat_item_discard_all(&rd_a);
- rc = osmo_stat_item_discard_all(&rd_b);
-
- rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value);
- OSMO_ASSERT(rc == 0);
- rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value);
- OSMO_ASSERT(rc == 0);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 42);
+ sum1 += 42;
+ OSMO_ASSERT(sitem1->value.n == 33);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 42);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 42);
+ OSMO_ASSERT(sitem1->value.max == 42);
+ OSMO_ASSERT(sitem1->value.sum == sum1);
+
+ osmo_stat_item_dec(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 21);
+ sum1 += 42 - 21;
+ OSMO_ASSERT(sitem1->value.n == 34);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 21);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 21);
+ OSMO_ASSERT(sitem1->value.max == 42);
+ OSMO_ASSERT(sitem1->value.sum == sum1);
+
+ osmo_stat_item_inc(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 21);
+ sum1 += 42;
+ OSMO_ASSERT(sitem1->value.n == 35);
+ OSMO_ASSERT(sitem1->value.min == 1);
+ OSMO_ASSERT(sitem1->value.last == 42);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 42);
+ OSMO_ASSERT(sitem1->value.max == 42);
+ OSMO_ASSERT(sitem1->value.sum == sum1);
+
+ /* Test item flush, reporting period elapsing */
+ osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ OSMO_ASSERT(sitem1->value.n == 0);
+ OSMO_ASSERT(sitem1->value.min == 42);
+ OSMO_ASSERT(sitem1->value.last == 42);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 42);
+ OSMO_ASSERT(sitem1->value.max == 42);
+ OSMO_ASSERT(sitem1->value.sum == 0);
+
+ /* Still see the previous reporting period in reported.* */
+ OSMO_ASSERT(sitem1->reported.n == 35);
+ OSMO_ASSERT(sitem1->reported.min == 1);
+ OSMO_ASSERT(sitem1->reported.last == 42);
+ OSMO_ASSERT(sitem1->reported.max == 42);
+ OSMO_ASSERT(sitem1->reported.sum == sum1);
+
+ /* After a flush, the first item replaces the last, min and max */
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 97);
+ OSMO_ASSERT(sitem1->value.n == 1);
+ OSMO_ASSERT(sitem1->value.min == 97);
+ OSMO_ASSERT(sitem1->value.last == 97);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 97);
+ OSMO_ASSERT(sitem1->value.max == 97);
+ OSMO_ASSERT(sitem1->value.sum == 97);
+
+ /* ...and still see the previous reporting period in reported.* */
+ OSMO_ASSERT(sitem1->reported.n == 35);
+ OSMO_ASSERT(sitem1->reported.min == 1);
+ OSMO_ASSERT(sitem1->reported.last == 42);
+ OSMO_ASSERT(sitem1->reported.max == 42);
+ OSMO_ASSERT(sitem1->reported.sum == sum1);
+
+ /* If an entire reporting period elapses without a new value, the last seen value remains. */
+ osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ OSMO_ASSERT(sitem1->value.n == 0);
+ OSMO_ASSERT(sitem1->value.min == 97);
+ OSMO_ASSERT(sitem1->value.last == 97);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 97);
+ OSMO_ASSERT(sitem1->value.max == 97);
+ OSMO_ASSERT(sitem1->value.sum == 0);
+
+ /* now the previous reporting period got turned around */
+ OSMO_ASSERT(sitem1->reported.n == 0);
+ OSMO_ASSERT(sitem1->reported.min == 97);
+ OSMO_ASSERT(sitem1->reported.last == 97);
+ OSMO_ASSERT(sitem1->reported.max == 97);
+ OSMO_ASSERT(sitem1->reported.sum == 0);
+
+ /* Another empty reporting period, everything remained the same. */
+ osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ OSMO_ASSERT(sitem1->value.n == 0);
+ OSMO_ASSERT(sitem1->value.min == 97);
+ OSMO_ASSERT(sitem1->value.last == 97);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 97);
+ OSMO_ASSERT(sitem1->value.max == 97);
+ OSMO_ASSERT(sitem1->value.sum == 0);
+ OSMO_ASSERT(sitem1->reported.n == 0);
+ OSMO_ASSERT(sitem1->reported.min == 97);
+ OSMO_ASSERT(sitem1->reported.last == 97);
+ OSMO_ASSERT(sitem1->reported.max == 97);
+ OSMO_ASSERT(sitem1->reported.sum == 0);
+
+ /* Test Reset, place back to default value. The previously reported value remains the same. */
+ osmo_stat_item_reset(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));
+ OSMO_ASSERT(sitem1->value.n == 0);
+ OSMO_ASSERT(sitem1->value.min == -1);
+ OSMO_ASSERT(sitem1->value.last == -1);
+ OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == -1);
+ OSMO_ASSERT(sitem1->value.max == -1);
+ OSMO_ASSERT(sitem1->value.sum == 0);
+ OSMO_ASSERT(sitem1->reported.n == 0);
+ OSMO_ASSERT(sitem1->reported.min == 97);
+ OSMO_ASSERT(sitem1->reported.last == 97);
+ OSMO_ASSERT(sitem1->reported.max == 97);
+ OSMO_ASSERT(sitem1->reported.sum == 0);
osmo_stat_item_group_free(statg);
@@ -258,7 +263,8 @@ static void stat_test(void)
/* define a special stats reporter for testing */
-static int send_count;
+static int sent_counter_vals;
+static int sent_stat_item_vals;
enum {
OSMO_STATS_REPORTER_TEST = OSMO_STATS_REPORTER_LOG + 1,
@@ -271,13 +277,13 @@ static int stats_reporter_test_send_counter(struct osmo_stats_reporter *srep,
{
const char *group_name = ctrg ? ctrg->desc->group_name_prefix : "";
- printf(" %s: counter p=%s g=%s i=%u n=%s v=%lld d=%lld\n",
+ fprintf(stderr, " %s: counter p=%s g=%s i=%u n=%s v=%lld d=%lld\n",
srep->name,
srep->name_prefix ? srep->name_prefix : "",
group_name, ctrg ? ctrg->idx : 0,
desc->name, (long long)value, (long long)delta);
- send_count += 1;
+ sent_counter_vals++;
return 0;
}
@@ -285,25 +291,25 @@ static int stats_reporter_test_send_item(struct osmo_stats_reporter *srep,
const struct osmo_stat_item_group *statg,
const struct osmo_stat_item_desc *desc, int64_t value)
{
- printf(" %s: item p=%s g=%s i=%u n=%s v=%"PRId64" u=%s\n",
+ fprintf(stderr, " %s: item p=%s g=%s i=%u n=%s v=%"PRId64" u=%s\n",
srep->name,
srep->name_prefix ? srep->name_prefix : "",
statg->desc->group_name_prefix, statg->idx,
desc->name, value, desc->unit ? desc->unit : "");
- send_count += 1;
+ sent_stat_item_vals++;
return 0;
}
static int stats_reporter_test_open(struct osmo_stats_reporter *srep)
{
- printf(" %s: open\n", srep->name);
+ fprintf(stderr, " %s: open\n", srep->name);
return 0;
}
static int stats_reporter_test_close(struct osmo_stats_reporter *srep)
{
- printf(" %s: close\n", srep->name);
+ fprintf(stderr, " %s: close\n", srep->name);
return 0;
}
@@ -322,8 +328,19 @@ static struct osmo_stats_reporter *stats_reporter_create_test(const char *name)
return srep;
}
+static void _do_report(int expect_counter_vals, int expect_stat_item_vals, int line)
+{
+ sent_counter_vals = 0;
+ sent_stat_item_vals = 0;
+ osmo_stats_report();
+ fprintf(stderr, "reported: %d counter vals, %d stat item vals\n", sent_counter_vals, sent_stat_item_vals);
+ OSMO_ASSERT(sent_counter_vals == expect_counter_vals);
+ OSMO_ASSERT(sent_stat_item_vals == expect_stat_item_vals);
+}
+
+#define do_report(A, B) _do_report(A, B, __LINE__)
-static void test_reporting()
+static void test_reporting(void)
{
struct osmo_stats_reporter *srep1, *srep2, *srep;
struct osmo_stat_item_group *statg1, *statg2;
@@ -332,7 +349,7 @@ static void test_reporting()
int rc;
- printf("Start test: %s\n", __func__);
+ fprintf(stderr, "Start test: %s\n", __func__);
/* Allocate counters and items */
statg1 = osmo_stat_item_group_alloc(stats_ctx, &statg_desc, 1);
@@ -374,112 +391,100 @@ static void test_reporting()
rc = osmo_stats_reporter_set_max_class(srep2, OSMO_STATS_CLASS_SUBSCRIBER);
OSMO_ASSERT(rc >= 0);
- printf("report (initial):\n");
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 20);
+ fprintf(stderr, "report (initial):\n");
+ do_report(12, 8);
- printf("report (srep1 global):\n");
+ fprintf(stderr, "report (srep1 global):\n");
/* force single flush */
osmo_stats_reporter_set_max_class(srep1, OSMO_STATS_CLASS_GLOBAL);
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 10);
+ do_report(6, 4);
- printf("report (srep1 peer):\n");
+ fprintf(stderr, "report (srep1 peer):\n");
/* force single flush */
osmo_stats_reporter_set_max_class(srep1, OSMO_STATS_CLASS_PEER);
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 14);
+ do_report(6, 8);
- printf("report (srep1 subscriber):\n");
+ fprintf(stderr, "report (srep1 subscriber):\n");
/* force single flush */
osmo_stats_reporter_set_max_class(srep1, OSMO_STATS_CLASS_SUBSCRIBER);
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 20);
+ do_report(12, 8);
- printf("report (srep2 disabled):\n");
+ fprintf(stderr, "report (srep2 disabled):\n");
/* force single flush */
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
rc = osmo_stats_reporter_disable(srep2);
OSMO_ASSERT(rc >= 0);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 10);
+ do_report(6, 4);
- printf("report (srep2 enabled, no flush forced):\n");
+ fprintf(stderr, "report (srep2 enabled, no flush forced):\n");
rc = osmo_stats_reporter_enable(srep2);
OSMO_ASSERT(rc >= 0);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 10);
+ do_report(6, 4);
- printf("report (should be empty):\n");
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 0);
+ fprintf(stderr, "report (should be empty):\n");
+ do_report(0, 0);
- printf("report (group 1, counter 1 update):\n");
- rate_ctr_inc(&ctrg1->ctr[TEST_A_CTR]);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 2);
+ fprintf(stderr, "report (group 1, counter 1 update):\n");
+ rate_ctr_inc(rate_ctr_group_get_ctr(ctrg1, TEST_A_CTR));
+ do_report(2, 0);
- printf("report (group 1, item 1 update):\n");
- osmo_stat_item_set(statg1->items[TEST_A_ITEM], 10);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 2);
+ fprintf(stderr, "report (group 1, item 1 update):\n");
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 10);
+ do_report(0, 2);
+
+ fprintf(stderr, "report (group 1, item 1 update twice, with same value):\n");
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 10);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 10);
+ do_report(0, 0);
+
+ fprintf(stderr, "report (group 1, item 1 update twice, check max):\n");
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 20);
+ osmo_stat_item_set(osmo_stat_item_group_get_item(statg1, TEST_A_ITEM), 10);
+ do_report(0, 2);
+
+ fprintf(stderr, "report (group 1, item 1 no update, send last item (!= last max), OS#5215):\n");
+ do_report(0, 2);
+
+ fprintf(stderr, "report (group 1, item 1 no update, nothing to send):\n");
+ do_report(0, 0);
- printf("report (remove statg1, ctrg1):\n");
+ fprintf(stderr, "report (remove statg1, ctrg1):\n");
/* force single flush */
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
osmo_stat_item_group_free(statg1);
rate_ctr_group_free(ctrg1);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 12);
+ do_report(8, 4);
- printf("report (remove srep1):\n");
+ fprintf(stderr, "report (remove srep1):\n");
/* force single flush */
srep1->force_single_flush = 1;
srep2->force_single_flush = 1;
osmo_stats_reporter_free(srep1);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 6);
+ do_report(4, 2);
- printf("report (remove statg2):\n");
+ fprintf(stderr, "report (remove statg2):\n");
/* force single flush */
srep2->force_single_flush = 1;
osmo_stat_item_group_free(statg2);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 4);
+ do_report(4, 0);
- printf("report (remove srep2):\n");
+ fprintf(stderr, "report (remove srep2):\n");
/* force single flush */
srep2->force_single_flush = 1;
osmo_stats_reporter_free(srep2);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 0);
+ do_report(0, 0);
- printf("report (remove ctrg2, should be empty):\n");
+ fprintf(stderr, "report (remove ctrg2, should be empty):\n");
rate_ctr_group_free(ctrg2);
- send_count = 0;
- osmo_stats_report();
- OSMO_ASSERT(send_count == 0);
+ do_report(0, 0);
rate_ctr_group_free(ctrg3);
@@ -487,17 +492,24 @@ static void test_reporting()
OSMO_ASSERT(talloc_total_blocks(stats_ctx) == 1);
talloc_free(stats_ctx);
- printf("End test: %s\n", __func__);
+ fprintf(stderr, "End test: %s\n", __func__);
}
int main(int argc, char **argv)
{
- static const struct log_info log_info = {};
- log_init(&log_info, NULL);
+ void *ctx = talloc_named_const(NULL, 0, "main");
+ osmo_init_logging2(ctx, NULL);
+
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_level(osmo_stderr_target, 1);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_use_color(osmo_stderr_target, 0);
osmo_stat_item_init(NULL);
stat_test();
test_reporting();
+ talloc_free(ctx);
return 0;
}
diff --git a/tests/stats/stats_test.err b/tests/stats/stats_test.err
new file mode 100644
index 00000000..4acd35d0
--- /dev/null
+++ b/tests/stats/stats_test.err
@@ -0,0 +1,163 @@
+Start test: test_reporting
+DLGLOBAL ERROR counter group 'ctr-test:one' already exists for index 2, instead using index 3. This is a software bug that needs fixing.
+DLGLOBAL ERROR 'ctr-test.one_dot' is not a valid counter group identifier
+DLGLOBAL NOTICE counter group name mangled: 'ctr-test.one_dot' -> 'ctr-test:one_dot'
+DLGLOBAL NOTICE counter group name mangled: 'ctr.a' -> 'ctr:a'
+DLGLOBAL NOTICE counter group name mangled: 'ctr.b' -> 'ctr:b'
+ test1: open
+ test2: open
+report (initial):
+ test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test1: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 12 counter vals, 8 stat item vals
+report (srep1 global):
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 6 counter vals, 4 stat item vals
+report (srep1 peer):
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 6 counter vals, 8 stat item vals
+report (srep1 subscriber):
+ test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test1: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 12 counter vals, 8 stat item vals
+report (srep2 disabled):
+ test2: close
+ test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test1: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 6 counter vals, 4 stat item vals
+report (srep2 enabled, no flush forced):
+ test2: open
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
+reported: 6 counter vals, 4 stat item vals
+report (should be empty):
+reported: 0 counter vals, 0 stat item vals
+report (group 1, counter 1 update):
+ test1: counter p= g=ctr-test:one i=1 n=ctr:a v=1 d=1
+ test2: counter p= g=ctr-test:one i=1 n=ctr:a v=1 d=1
+reported: 2 counter vals, 0 stat item vals
+report (group 1, item 1 update):
+ test1: item p= g=test.one i=1 n=item.a v=10 u=ma
+ test2: item p= g=test.one i=1 n=item.a v=10 u=ma
+reported: 0 counter vals, 2 stat item vals
+report (group 1, item 1 update twice, with same value):
+reported: 0 counter vals, 0 stat item vals
+report (group 1, item 1 update twice, check max):
+ test1: item p= g=test.one i=1 n=item.a v=20 u=ma
+ test2: item p= g=test.one i=1 n=item.a v=20 u=ma
+reported: 0 counter vals, 2 stat item vals
+report (group 1, item 1 no update, send last item (!= last max), OS#5215):
+ test1: item p= g=test.one i=1 n=item.a v=10 u=ma
+ test2: item p= g=test.one i=1 n=item.a v=10 u=ma
+reported: 0 counter vals, 2 stat item vals
+report (group 1, item 1 no update, nothing to send):
+reported: 0 counter vals, 0 stat item vals
+report (remove statg1, ctrg1):
+ test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+reported: 8 counter vals, 4 stat item vals
+report (remove srep1):
+ test1: close
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+ test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
+ test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
+reported: 4 counter vals, 2 stat item vals
+report (remove statg2):
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
+ test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
+reported: 4 counter vals, 0 stat item vals
+report (remove srep2):
+ test2: close
+reported: 0 counter vals, 0 stat item vals
+report (remove ctrg2, should be empty):
+reported: 0 counter vals, 0 stat item vals
+End test: test_reporting
diff --git a/tests/stats/stats_test.ok b/tests/stats/stats_test.ok
index 8628adb7..e69de29b 100644
--- a/tests/stats/stats_test.ok
+++ b/tests/stats/stats_test.ok
@@ -1,132 +0,0 @@
-Start test: test_reporting
- test1: open
- test2: open
-report (initial):
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
- test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
-report (srep1 global):
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
-report (srep1 peer):
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
- test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
-report (srep1 subscriber):
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
- test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
-report (srep2 disabled):
- test2: close
- test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test1: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test1: item p= g=test.one i=1 n=item.b v=-1 u=kb
-report (srep2 enabled, no flush forced):
- test2: open
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=1 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test2: item p= g=test.one i=1 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=1 n=item.b v=-1 u=kb
-report (should be empty):
-report (group 1, counter 1 update):
- test2: counter p= g=ctr-test:one i=1 n=ctr:a v=1 d=1
- test1: counter p= g=ctr-test:one i=1 n=ctr:a v=1 d=1
-report (group 1, item 1 update):
- test2: item p= g=test.one i=1 n=item.a v=10 u=ma
- test1: item p= g=test.one i=1 n=item.a v=10 u=ma
-report (remove statg1, ctrg1):
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test1: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test1: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test1: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
- test1: item p= g=test.one i=2 n=item.b v=-1 u=kb
-report (remove srep1):
- test1: close
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
- test2: item p= g=test.one i=2 n=item.a v=-1 u=ma
- test2: item p= g=test.one i=2 n=item.b v=-1 u=kb
-report (remove statg2):
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one_dot i=3 n=ctr:b v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:a v=0 d=0
- test2: counter p= g=ctr-test:one i=2 n=ctr:b v=0 d=0
-report (remove srep2):
- test2: close
-report (remove ctrg2, should be empty):
-End test: test_reporting
diff --git a/tests/stats/stats_vty_test.c b/tests/stats/stats_vty_test.c
new file mode 100644
index 00000000..29cbf5ec
--- /dev/null
+++ b/tests/stats/stats_vty_test.c
@@ -0,0 +1,88 @@
+/*
+ * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <signal.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/vty.h>
+
+static void *root_ctx = NULL;
+static int quit = 0;
+
+static void signal_handler(int signal)
+{
+ fprintf(stdout, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGINT:
+ case SIGTERM:
+ quit++;
+ break;
+ }
+}
+
+static struct vty_app_info vty_info = {
+ .name = "stats_vty_test",
+};
+
+static const struct log_info_cat default_categories[] = { };
+
+const struct log_info log_info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
+
+int main(int argc, char **argv)
+{
+ int rc;
+
+ root_ctx = talloc_named_const(NULL, 0, "stats_vty_test");
+
+ osmo_init_logging2(root_ctx, &log_info);
+
+ vty_info.tall_ctx = root_ctx;
+ vty_init(&vty_info);
+
+ osmo_stats_vty_add_cmds();
+
+ rc = telnet_init_default(root_ctx, NULL, 42042);
+ if (rc < 0)
+ return 2;
+
+ signal(SIGINT, &signal_handler);
+ signal(SIGTERM, &signal_handler);
+ osmo_init_ignore_signals();
+
+ while (!quit)
+ osmo_select_main(0);
+
+ talloc_free(tall_vty_ctx);
+ talloc_free(root_ctx);
+
+ return 0;
+}
diff --git a/tests/stats/stats_vty_test.vty b/tests/stats/stats_vty_test.vty
new file mode 100644
index 00000000..8732d50b
--- /dev/null
+++ b/tests/stats/stats_vty_test.vty
@@ -0,0 +1,217 @@
+stats_vty_test> en
+stats_vty_test# configure terminal
+stats_vty_test(config)# list
+...
+ stats reporter statsd [NAME]
+ no stats reporter statsd [NAME]
+ stats reporter log [NAME]
+ no stats reporter log [NAME]
+ stats interval <0-65535>
+ stats-tcp interval <0-65535>
+...
+
+stats_vty_test(config)# ### No reporters shall be configured by default
+stats_vty_test(config)# show running-config
+... !stats reporter
+
+
+stats_vty_test(config)# ### Create a statsd reporter
+stats_vty_test(config)# stats reporter statsd
+stats_vty_test(config-stats)# list
+...
+ local-ip ADDR
+ no local-ip
+ remote-ip ADDR
+ remote-port <1-65535>
+ mtu <100-65535>
+ no mtu
+ prefix PREFIX
+ no prefix
+ level (global|peer|subscriber)
+ enable
+ disable
+ flush-period <0-65535>
+...
+
+stats_vty_test(config-stats)# show running-config
+...
+stats interval 5
+stats reporter statsd
+ level global
+ no prefix
+ disable
+...
+
+stats_vty_test(config-stats)# level subscriber
+stats_vty_test(config-stats)# prefix statsd-prefix
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ level subscriber
+ prefix statsd-prefix
+...
+
+stats_vty_test(config-stats)# remote-ip 192.168.1.200
+stats_vty_test(config-stats)# remote-port 6969
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+... !local-ip
+
+stats_vty_test(config-stats)# local-ip 192.168.1.100
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ local-ip 192.168.1.100
+...
+
+stats_vty_test(config-stats)# no local-ip
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+... !local-ip
+
+stats_vty_test(config-stats)# mtu 1337
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ mtu 1337
+...
+
+stats_vty_test(config-stats)# no mtu
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+... !mtu
+
+stats_vty_test(config-stats)# flush-period 43556
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ level subscriber
+ prefix statsd-prefix
+ flush-period 43556
+...
+
+stats_vty_test(config-stats)# flush-period 0
+stats_vty_test(config-stats)# show running-config
+...
+stats reporter statsd
+... !flush-period
+
+stats_vty_test(config-stats)# enable
+stats_vty_test(config-stats)# exit
+stats_vty_test(config)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ level subscriber
+ prefix statsd-prefix
+ enable
+...
+
+
+stats_vty_test(config)# ### Create a statsd reporter
+stats_vty_test(config)# stats reporter log
+stats_vty_test(config-stats)# level peer
+stats_vty_test(config-stats)# prefix log-prefix
+stats_vty_test(config-stats)# enable
+stats_vty_test(config-stats)# exit
+stats_vty_test(config)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ level subscriber
+ prefix statsd-prefix
+ enable
+stats reporter log
+ level peer
+ prefix log-prefix
+ enable
+...
+
+
+stats_vty_test(config)# ### Create an additional statsd reporter
+stats_vty_test(config)# stats reporter statsd statsd-foo
+stats_vty_test(config-stats)# level global
+stats_vty_test(config-stats)# prefix statsd-one-prefix
+stats_vty_test(config-stats)# remote-ip 192.168.2.200
+stats_vty_test(config-stats)# remote-port 9696
+stats_vty_test(config-stats)# flush-period 1
+stats_vty_test(config-stats)# exit
+
+stats_vty_test(config)# ### Create an additional log reporter
+stats_vty_test(config)# stats reporter log log-bar
+stats_vty_test(config-stats)# level global
+stats_vty_test(config-stats)# prefix log-bar-prefix
+stats_vty_test(config-stats)# flush-period 2
+stats_vty_test(config-stats)# exit
+
+stats_vty_test(config)# ### Create an additional log reporter
+stats_vty_test(config)# stats reporter log log-zoo
+stats_vty_test(config-stats)# level global
+stats_vty_test(config-stats)# prefix log-zoo-prefix
+stats_vty_test(config-stats)# flush-period 3
+stats_vty_test(config-stats)# exit
+
+stats_vty_test(config)# ### We should have 5 reporters now
+stats_vty_test(config)# show running-config
+...
+stats reporter statsd
+ remote-ip 192.168.1.200
+ remote-port 6969
+ level subscriber
+ prefix statsd-prefix
+ enable
+stats reporter log
+ level peer
+ prefix log-prefix
+ enable
+stats reporter statsd statsd-foo
+ remote-ip 192.168.2.200
+ remote-port 9696
+ level global
+ prefix statsd-one-prefix
+ flush-period 1
+ disable
+stats reporter log log-bar
+ level global
+ prefix log-bar-prefix
+ flush-period 2
+ disable
+stats reporter log log-zoo
+ level global
+ prefix log-zoo-prefix
+ flush-period 3
+ disable
+...
+
+
+stats_vty_test(config)# ### Test removing reporters
+stats_vty_test(config)# no stats reporter statsd statsd-foo
+stats_vty_test(config)# no stats reporter log log-bar
+stats_vty_test(config)# no stats reporter log log-zoo
+stats_vty_test(config)# show running-config
+... !(foo|bar|zoo)
+
+stats_vty_test(config)# no stats reporter statsd statsd-foo
+% There is no such statsd reporter with name 'statsd-foo'
+stats_vty_test(config)# no stats reporter log log-zoo
+% There is no such log reporter with name 'log-zoo'
+
+
+stats_vty_test(config)# stats interval 1337
+stats_vty_test(config)# show running-config
+...
+stats interval 1337
+...
diff --git a/tests/strrb/strrb_test.c b/tests/strrb/strrb_test.c
index 4282d1aa..cdeaa346 100644
--- a/tests/strrb/strrb_test.c
+++ b/tests/strrb/strrb_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/tests/tdef/tdef_test.c b/tests/tdef/tdef_test.c
index 276330a8..710a4aa0 100644
--- a/tests/tdef/tdef_test.c
+++ b/tests/tdef/tdef_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -38,9 +34,12 @@ static void *ctx = NULL;
static struct osmo_tdef tdefs[] = {
{ .T=1, .default_val=100, .desc="100s" },
{ .T=2, .default_val=100, .unit=OSMO_TDEF_MS, .desc="100ms" },
- { .T=3, .default_val=100, .unit=OSMO_TDEF_M, .desc="100m" },
+ { .T=3, .default_val=50, .unit=OSMO_TDEF_M, .desc="50m" },
{ .T=4, .default_val=100, .unit=OSMO_TDEF_CUSTOM, .desc="100 potatoes" },
+ { .T=-5, .default_val=100, .unit=OSMO_TDEF_MS, .desc="X-100ms" },
+ { .T=-6, .default_val=100, .unit=OSMO_TDEF_US, .desc="X-100us" },
+
{ .T=7, .default_val=50, .desc="Water Boiling Timeout", .min_val=20, .max_val=800 }, // default is .unit=OSMO_TDEF_S == 0
{ .T=8, .default_val=300, .desc="Tea brewing" },
{ .T=9, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" },
@@ -54,6 +53,7 @@ static struct osmo_tdef tdefs[] = {
{ .T=1006, .default_val=0, .unit=OSMO_TDEF_S, .desc="zero s" },
{ .T=1007, .default_val=0, .unit=OSMO_TDEF_M, .desc="zero m" },
{ .T=1008, .default_val=0, .unit=OSMO_TDEF_CUSTOM, .desc="zero" },
+ { .T=1009, .default_val=0, .unit=OSMO_TDEF_US, .desc="zero us" },
{ .T=0, .default_val=1, .unit=OSMO_TDEF_CUSTOM, .desc="zero" },
@@ -86,14 +86,15 @@ static struct osmo_tdef tdefs_range[] = {
printf("osmo_tdef_get(%d, %s)\t= %lu\n", T, osmo_tdef_unit_name(AS_UNIT), val); \
} while (0)
-void print_tdef_info(unsigned int T)
+void print_tdef_info(int T)
{
const struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, T);
if (!t) {
- printf("T%d=NULL", T);
+ printf(OSMO_T_FMT "=NULL", OSMO_T_FMT_ARGS(T));
return;
}
- printf("T%d=%lu%s", T, t->val, osmo_tdef_unit_name(t->unit));
+ printf(OSMO_T_FMT "=%lu%s",
+ OSMO_T_FMT_ARGS(T), t->val, osmo_tdef_unit_name(t->unit));
if (t->val != t->default_val)
printf("(def=%lu)", t->default_val);
printf("\n");
@@ -109,9 +110,9 @@ static void test_tdef_get(bool test_range)
osmo_tdefs_reset(tdefs); // make all values the default
for (i = 0; i < ARRAY_SIZE(tdefs)-1; i++) {
- unsigned int T = tdefs[i].T;
+ int T = tdefs[i].T;
print_tdef_info(T);
- for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_CUSTOM; as_unit++) {
+ for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_US; as_unit++) {
print_tdef_get_short(tdefs, T, as_unit);
}
}
@@ -120,15 +121,15 @@ static void test_tdef_get(bool test_range)
return;
for (i = 0; i < ARRAY_SIZE(tdefs_range)-1; i++) {
- unsigned int T = tdefs_range[i].T;
+ int T = tdefs_range[i].T;
print_tdef_info(T);
- for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_CUSTOM; as_unit++) {
+ for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_US; as_unit++) {
print_tdef_get_short(tdefs_range, T, as_unit);
}
}
}
-static void test_tdef_get_nonexisting()
+static void test_tdef_get_nonexisting(void)
{
printf("\n%s()\n", __func__);
@@ -136,15 +137,17 @@ static void test_tdef_get_nonexisting()
print_tdef_get(tdefs, 5, OSMO_TDEF_MS);
print_tdef_get(tdefs, 5, OSMO_TDEF_M);
print_tdef_get(tdefs, 5, OSMO_TDEF_CUSTOM);
+ print_tdef_get(tdefs, 5, OSMO_TDEF_US);
}
-static void test_tdef_set_and_get()
+static void test_tdef_set_and_get(void)
{
struct osmo_tdef *t;
printf("\n%s()\n", __func__);
printf("setting 7 = 42\n");
t = osmo_tdef_get_entry(tdefs, 7);
+ OSMO_ASSERT(t != NULL);
OSMO_ASSERT(osmo_tdef_val_in_range(t, 42));
t->val = 42;
print_tdef_info(7);
@@ -152,6 +155,7 @@ static void test_tdef_set_and_get()
print_tdef_get_short(tdefs, 7, OSMO_TDEF_S);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_M);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM);
+ print_tdef_get_short(tdefs, 7, OSMO_TDEF_US);
printf("setting 7 = 420\n");
OSMO_ASSERT(osmo_tdef_set(tdefs, 7, 420, OSMO_TDEF_S) == 0);
@@ -160,6 +164,7 @@ static void test_tdef_set_and_get()
print_tdef_get_short(tdefs, 7, OSMO_TDEF_S);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_M);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM);
+ print_tdef_get_short(tdefs, 7, OSMO_TDEF_US);
printf("setting 7 = 10 (ERANGE)\n");
OSMO_ASSERT(!osmo_tdef_val_in_range(t, 10));
@@ -169,6 +174,7 @@ static void test_tdef_set_and_get()
print_tdef_get_short(tdefs, 7, OSMO_TDEF_S);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_M);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM);
+ print_tdef_get_short(tdefs, 7, OSMO_TDEF_US);
printf("setting 7 = 900 (ERANGE)\n");
OSMO_ASSERT(!osmo_tdef_val_in_range(t, 900));
@@ -178,6 +184,7 @@ static void test_tdef_set_and_get()
print_tdef_get_short(tdefs, 7, OSMO_TDEF_S);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_M);
print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM);
+ print_tdef_get_short(tdefs, 7, OSMO_TDEF_US);
printf("setting 23 = 50 (EEXIST)\n");
OSMO_ASSERT(osmo_tdef_set(tdefs, 23, 50, OSMO_TDEF_S) == -EEXIST);
@@ -193,6 +200,8 @@ enum test_tdef_fsm_states {
S_B,
S_C,
S_D,
+ S_E,
+ S_F,
S_G,
S_H,
S_I,
@@ -202,6 +211,7 @@ enum test_tdef_fsm_states {
S_M,
S_N,
S_O,
+ /* ... gap ... */
S_X,
S_Y,
S_Z,
@@ -213,6 +223,9 @@ static const struct osmo_tdef_state_timeout test_tdef_state_timeouts[32] = {
[S_C] = { .T = 3 },
[S_D] = { .T = 4 },
+ [S_E] = { .T = -5 },
+ [S_F] = { .T = -6 },
+
[S_G] = { .T = 7 },
[S_H] = { .T = 8 },
[S_I] = { .T = 9 },
@@ -243,31 +256,15 @@ static const struct osmo_fsm_state test_tdef_fsm_states[] = {
#define DEF_STATE(NAME) \
[S_##NAME] = { \
.name = #NAME, \
- .out_state_mask = 0 \
- | S(S_A) \
- | S(S_B) \
- | S(S_C) \
- | S(S_D) \
- | S(S_G) \
- | S(S_H) \
- | S(S_I) \
- | S(S_J) \
- | S(S_K) \
- | S(S_L) \
- | S(S_M) \
- | S(S_N) \
- | S(S_O) \
- | S(S_X) \
- | S(S_Y) \
- | S(S_Z) \
- , \
+ .out_state_mask = 0xffffffff, \
}
DEF_STATE(A),
DEF_STATE(B),
DEF_STATE(C),
DEF_STATE(D),
-
+ DEF_STATE(E),
+ DEF_STATE(F),
DEF_STATE(G),
DEF_STATE(H),
DEF_STATE(I),
@@ -314,7 +311,7 @@ const struct timeval fake_time_start_time = { 123, 456 };
osmo_timers_update(); \
} while (0)
-void fake_time_start()
+void fake_time_start(void)
{
struct timespec *clock_override;
@@ -351,9 +348,9 @@ static void print_fsm_state(struct osmo_fsm_inst *fi)
osmo_fsm_state_name(&test_tdef_fsm, NEXT_STATE), rc); \
} else { \
struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, st->T); \
- printf(" --> %s (configured as T%d%s %lu %s) rc=%d;\t", \
+ printf(" --> %s (configured as " OSMO_T_FMT "%s %lu %s) rc=%d;\t", \
osmo_fsm_state_name(&test_tdef_fsm, NEXT_STATE), \
- st->T, st->keep_timer ? "(keep_timer)" : "", \
+ OSMO_T_FMT_ARGS(st->T), st->keep_timer ? " (keep_timer)" : "", \
t? t->val : 0, t? osmo_tdef_unit_name(t->unit) : "-", \
rc); \
} \
@@ -380,7 +377,8 @@ static void test_tdef_state_timeout(bool test_range)
test_tdef_fsm_state_chg(tdefs, S_B);
test_tdef_fsm_state_chg(tdefs, S_C);
test_tdef_fsm_state_chg(tdefs, S_D);
-
+ test_tdef_fsm_state_chg(tdefs, S_E);
+ test_tdef_fsm_state_chg(tdefs, S_F);
test_tdef_fsm_state_chg(tdefs, S_G);
test_tdef_fsm_state_chg(tdefs, S_H);
test_tdef_fsm_state_chg(tdefs, S_I);
@@ -471,11 +469,18 @@ int main(int argc, char **argv)
ctx = talloc_named_const(NULL, 0, "tdef_test.c");
osmo_init_logging2(ctx, NULL);
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_level(osmo_stderr_target, 1);
log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
log_set_use_color(osmo_stderr_target, 0);
- osmo_fsm_register(&test_tdef_fsm);
+ osmo_fsm_log_addr(false);
+ osmo_fsm_log_timeouts(true);
+
+ log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
+
+ OSMO_ASSERT(osmo_fsm_register(&test_tdef_fsm) == 0);
test_tdef_get(argc > 1);
test_tdef_get_nonexisting();
diff --git a/tests/tdef/tdef_test.err b/tests/tdef/tdef_test.err
new file mode 100644
index 00000000..8e5860d7
--- /dev/null
+++ b/tests/tdef/tdef_test.err
@@ -0,0 +1,25 @@
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: Allocated
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: State change to A (T1, 100s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: State change to B (T2, 100ms)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){B}: State change to C (T3, 3000s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){C}: State change to D (T4, 100s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){D}: State change to E (X5, 100ms)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){E}: State change to F (X6, 1ms)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){F}: State change to G (T7, 50s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){G}: State change to H (T8, 300s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){H}: State change to I (T9, 300s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){I}: State change to J (T10, 1200s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){J}: State change to K (keeping T10, 1076.954s remaining)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){K}: State change to A (T1, 100s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: State change to K (keeping T1, 76.954s remaining)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){K}: State change to A (T1, 100s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){A}: State change to L (keeping T1, 76.954s remaining)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){L}: State change to O (no timeout)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){O}: State change to L (T123, 1s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){L}: State change to O (no timeout)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){O}: State change to X (no timeout)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){X}: State change to Y (T666, 999s)
+DLGLOBAL DEBUG tdef_test(test_tdef_state_timeout){Y}: State change to Z (no timeout)
+DLGLOBAL ERROR tdef_test(test_tdef_state_timeout){Z}: transition to state B not permitted!
+DLGLOBAL ERROR tdef_test(test_tdef_state_timeout){Z}: transition to state C not permitted!
+DLGLOBAL ERROR tdef_test(test_tdef_state_timeout){Z}: transition to state D not permitted!
diff --git a/tests/tdef/tdef_test.ok b/tests/tdef/tdef_test.ok
index 3c4a0930..6ceaffcc 100644
--- a/tests/tdef/tdef_test.ok
+++ b/tests/tdef/tdef_test.ok
@@ -5,92 +5,128 @@ osmo_tdef_get(1, s) = 100
osmo_tdef_get(1, ms) = 100000
osmo_tdef_get(1, m) = 2
osmo_tdef_get(1, custom-unit) = 100
+osmo_tdef_get(1, us) = 100000000
T2=100ms
osmo_tdef_get(2, s) = 1
osmo_tdef_get(2, ms) = 100
osmo_tdef_get(2, m) = 1
osmo_tdef_get(2, custom-unit) = 100
-T3=100m
-osmo_tdef_get(3, s) = 6000
-osmo_tdef_get(3, ms) = 6000000
-osmo_tdef_get(3, m) = 100
-osmo_tdef_get(3, custom-unit) = 100
+osmo_tdef_get(2, us) = 100000
+T3=50m
+osmo_tdef_get(3, s) = 3000
+osmo_tdef_get(3, ms) = 3000000
+osmo_tdef_get(3, m) = 50
+osmo_tdef_get(3, custom-unit) = 50
+osmo_tdef_get(3, us) = 3000000000
T4=100custom-unit
osmo_tdef_get(4, s) = 100
osmo_tdef_get(4, ms) = 100
osmo_tdef_get(4, m) = 100
osmo_tdef_get(4, custom-unit) = 100
+osmo_tdef_get(4, us) = 100
+X5=100ms
+osmo_tdef_get(-5, s) = 1
+osmo_tdef_get(-5, ms) = 100
+osmo_tdef_get(-5, m) = 1
+osmo_tdef_get(-5, custom-unit) = 100
+osmo_tdef_get(-5, us) = 100000
+X6=100us
+osmo_tdef_get(-6, s) = 1
+osmo_tdef_get(-6, ms) = 1
+osmo_tdef_get(-6, m) = 1
+osmo_tdef_get(-6, custom-unit) = 100
+osmo_tdef_get(-6, us) = 100
T7=50s
osmo_tdef_get(7, s) = 50
osmo_tdef_get(7, ms) = 50000
osmo_tdef_get(7, m) = 1
osmo_tdef_get(7, custom-unit) = 50
+osmo_tdef_get(7, us) = 50000000
T8=300s
osmo_tdef_get(8, s) = 300
osmo_tdef_get(8, ms) = 300000
osmo_tdef_get(8, m) = 5
osmo_tdef_get(8, custom-unit) = 300
+osmo_tdef_get(8, us) = 300000000
T9=5m
osmo_tdef_get(9, s) = 300
osmo_tdef_get(9, ms) = 300000
osmo_tdef_get(9, m) = 5
osmo_tdef_get(9, custom-unit) = 5
+osmo_tdef_get(9, us) = 300000000
T10=20m
osmo_tdef_get(10, s) = 1200
osmo_tdef_get(10, ms) = 1200000
osmo_tdef_get(10, m) = 20
osmo_tdef_get(10, custom-unit) = 20
+osmo_tdef_get(10, us) = 1200000000
T1000=2000ms
osmo_tdef_get(1000, s) = 2
osmo_tdef_get(1000, ms) = 2000
osmo_tdef_get(1000, m) = 1
osmo_tdef_get(1000, custom-unit) = 2000
+osmo_tdef_get(1000, us) = 2000000
T1001=60000ms
osmo_tdef_get(1001, s) = 60
osmo_tdef_get(1001, ms) = 60000
osmo_tdef_get(1001, m) = 1
osmo_tdef_get(1001, custom-unit) = 60000
+osmo_tdef_get(1001, us) = 60000000
T1004=1ms
osmo_tdef_get(1004, s) = 1
osmo_tdef_get(1004, ms) = 1
osmo_tdef_get(1004, m) = 1
osmo_tdef_get(1004, custom-unit) = 1
+osmo_tdef_get(1004, us) = 1000
T1005=0ms
osmo_tdef_get(1005, s) = 0
osmo_tdef_get(1005, ms) = 0
osmo_tdef_get(1005, m) = 0
osmo_tdef_get(1005, custom-unit) = 0
+osmo_tdef_get(1005, us) = 0
T1006=0s
osmo_tdef_get(1006, s) = 0
osmo_tdef_get(1006, ms) = 0
osmo_tdef_get(1006, m) = 0
osmo_tdef_get(1006, custom-unit) = 0
+osmo_tdef_get(1006, us) = 0
T1007=0m
osmo_tdef_get(1007, s) = 0
osmo_tdef_get(1007, ms) = 0
osmo_tdef_get(1007, m) = 0
osmo_tdef_get(1007, custom-unit) = 0
+osmo_tdef_get(1007, us) = 0
T1008=0custom-unit
osmo_tdef_get(1008, s) = 0
osmo_tdef_get(1008, ms) = 0
osmo_tdef_get(1008, m) = 0
osmo_tdef_get(1008, custom-unit) = 0
+osmo_tdef_get(1008, us) = 0
+T1009=0us
+osmo_tdef_get(1009, s) = 0
+osmo_tdef_get(1009, ms) = 0
+osmo_tdef_get(1009, m) = 0
+osmo_tdef_get(1009, custom-unit) = 0
+osmo_tdef_get(1009, us) = 0
T0=1custom-unit
osmo_tdef_get(0, s) = 1
osmo_tdef_get(0, ms) = 1
osmo_tdef_get(0, m) = 1
osmo_tdef_get(0, custom-unit) = 1
+osmo_tdef_get(0, us) = 1
T123=1s
osmo_tdef_get(123, s) = 1
osmo_tdef_get(123, ms) = 1000
osmo_tdef_get(123, m) = 1
osmo_tdef_get(123, custom-unit) = 1
+osmo_tdef_get(123, us) = 1000000
test_tdef_get_nonexisting()
osmo_tdef_get(tdefs, 5, s, 999) = 999
osmo_tdef_get(tdefs, 5, ms, 999) = 999
osmo_tdef_get(tdefs, 5, m, 999) = 999
osmo_tdef_get(tdefs, 5, custom-unit, 999) = 999
+osmo_tdef_get(tdefs, 5, us, 999) = 999
test_tdef_set_and_get()
setting 7 = 42
@@ -99,24 +135,28 @@ osmo_tdef_get(7, ms) = 42000
osmo_tdef_get(7, s) = 42
osmo_tdef_get(7, m) = 1
osmo_tdef_get(7, custom-unit) = 42
+osmo_tdef_get(7, us) = 42000000
setting 7 = 420
T7=420s(def=50)
osmo_tdef_get(7, ms) = 420000
osmo_tdef_get(7, s) = 420
osmo_tdef_get(7, m) = 7
osmo_tdef_get(7, custom-unit) = 420
+osmo_tdef_get(7, us) = 420000000
setting 7 = 10 (ERANGE)
T7=420s(def=50)
osmo_tdef_get(7, ms) = 420000
osmo_tdef_get(7, s) = 420
osmo_tdef_get(7, m) = 7
osmo_tdef_get(7, custom-unit) = 420
+osmo_tdef_get(7, us) = 420000000
setting 7 = 900 (ERANGE)
T7=420s(def=50)
osmo_tdef_get(7, ms) = 420000
osmo_tdef_get(7, s) = 420
osmo_tdef_get(7, m) = 7
osmo_tdef_get(7, custom-unit) = 420
+osmo_tdef_get(7, us) = 420000000
setting 23 = 50 (EEXIST)
resetting
T7=50s
@@ -125,9 +165,11 @@ osmo_tdef_get(7, s) = 50
test_tdef_state_timeout()
state=A T=0, no timeout
--> A (configured as T1 100 s) rc=0; state=A T=1, 100.000000 s remaining
- --> B (configured as T2 100 ms) rc=0; state=B T=2, 1.000000 s remaining
- --> C (configured as T3 100 m) rc=0; state=C T=3, 6000.000000 s remaining
+ --> B (configured as T2 100 ms) rc=0; state=B T=2, 0.100000 s remaining
+ --> C (configured as T3 50 m) rc=0; state=C T=3, 3000.000000 s remaining
--> D (configured as T4 100 custom-unit) rc=0; state=D T=4, 100.000000 s remaining
+ --> E (configured as X5 100 ms) rc=0; state=E T=-5, 0.100000 s remaining
+ --> F (configured as X6 100 us) rc=0; state=F T=-6, 0.001000 s remaining
--> G (configured as T7 50 s) rc=0; state=G T=7, 50.000000 s remaining
--> H (configured as T8 300 s) rc=0; state=H T=8, 300.000000 s remaining
--> I (configured as T9 5 m) rc=0; state=I T=9, 300.000000 s remaining
@@ -135,17 +177,17 @@ state=A T=0, no timeout
- test keep_timer:
Time passes: 123.045678 s
state=J T=10, 1076.954322 s remaining
- --> K (configured as T0(keep_timer) 1 custom-unit) rc=0; state=K T=10, 1076.954322 s remaining
+ --> K (configured as T0 (keep_timer) 1 custom-unit) rc=0; state=K T=10, 1076.954322 s remaining
--> A (configured as T1 100 s) rc=0; state=A T=1, 100.000000 s remaining
Time passes: 23.045678 s
state=A T=1, 76.954322 s remaining
- --> K (configured as T0(keep_timer) 1 custom-unit) rc=0; state=K T=1, 76.954322 s remaining
+ --> K (configured as T0 (keep_timer) 1 custom-unit) rc=0; state=K T=1, 76.954322 s remaining
--> A (configured as T1 100 s) rc=0; state=A T=1, 100.000000 s remaining
Time passes: 23.045678 s
state=A T=1, 76.954322 s remaining
- --> L (configured as T123(keep_timer) 1 s) rc=0; state=L T=1, 76.954322 s remaining
+ --> L (configured as T123 (keep_timer) 1 s) rc=0; state=L T=1, 76.954322 s remaining
--> O (no timer configured for this state) rc=0; state=O T=0, no timeout
- --> L (configured as T123(keep_timer) 1 s) rc=0; state=L T=123, 1.000000 s remaining
+ --> L (configured as T123 (keep_timer) 1 s) rc=0; state=L T=123, 1.000000 s remaining
- test T=0:
--> O (no timer configured for this state) rc=0; state=O T=0, no timeout
- test no timer:
@@ -155,5 +197,5 @@ state=A T=1, 76.954322 s remaining
- test disallowed transition:
--> Z (no timer configured for this state) rc=0; state=Z T=0, no timeout
--> B (configured as T2 100 ms) rc=-1; state=Z T=0, no timeout
- --> C (configured as T3 100 m) rc=-1; state=Z T=0, no timeout
+ --> C (configured as T3 50 m) rc=-1; state=Z T=0, no timeout
--> D (configured as T4 100 custom-unit) rc=-1; state=Z T=0, no timeout
diff --git a/tests/tdef/tdef_vty_test_config_root.c b/tests/tdef/tdef_vty_config_root_test.c
index 92113e87..8c46d958 100644
--- a/tests/tdef/tdef_vty_test_config_root.c
+++ b/tests/tdef/tdef_vty_config_root_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
@@ -45,7 +41,7 @@
/* ------------------- HERE IS THE INTERESTING TDEF RELEVANT PART ------------------- */
/* This example keeps several separate timer groups and offers 'timer' VTY commands at the root of the config node. See
- * the tdef_vty_test_config_root.vty transcript test.
+ * the tdef_vty_config_root_test.vty transcript test.
*/
static struct osmo_tdef tdefs_test[] = {
@@ -102,7 +98,7 @@ enum tdef_vty_test_nodes {
/* This example puts 'timer' configuration commands directly at the root of the CONFIG_NODE.
* This TIMER_NODE is merely needed as a hook for the vty_write() command, but becomes an empty node in the VTY docs.
* It is possible to cheat around needing this if you choose to config_write_timer() in another root nodes' write cb.
- * Another example using a 'network' subnode is \ref tdef_vty_test_config_subnode.c */
+ * Another example using a 'network' subnode is \ref tdef_vty_config_subnode_test.c */
static struct cmd_node timer_node = {
TIMER_NODE,
"%s(config-timer)# ",
@@ -115,7 +111,7 @@ static int config_write_timer(struct vty *vty)
return CMD_SUCCESS;
}
-static void timer_init_vty()
+static void timer_init_vty(void)
{
/* Again, this is merely to get a vty write hook, see above. */
install_node(&timer_node, config_write_timer);
@@ -127,7 +123,7 @@ static void timer_init_vty()
void *root_ctx = NULL;
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -264,7 +260,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
diff --git a/tests/tdef/tdef_vty_test_config_root.vty b/tests/tdef/tdef_vty_config_root_test.vty
index 6a53b805..6a53b805 100644
--- a/tests/tdef/tdef_vty_test_config_root.vty
+++ b/tests/tdef/tdef_vty_config_root_test.vty
diff --git a/tests/tdef/tdef_vty_test_config_subnode.c b/tests/tdef/tdef_vty_config_subnode_test.c
index ce851f50..e3e165da 100644
--- a/tests/tdef/tdef_vty_test_config_subnode.c
+++ b/tests/tdef/tdef_vty_config_subnode_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
@@ -46,7 +42,7 @@
/* This example keeps a single global timer group and offers a custom 'timer' VTY command in a 'network' subnode below
* the CONFIG_NODE.
- * the tdef_vty_test_config_subnode.vty transcript test.
+ * the tdef_vty_config_subnode_test.vty transcript test.
*/
static struct osmo_tdef global_tdefs[] = {
@@ -106,7 +102,7 @@ static int config_write_gsmnet(struct vty *vty)
return CMD_SUCCESS;
}
-static void gsmnet_init_vty()
+static void gsmnet_init_vty(void)
{
install_node(&gsmnet_node, config_write_gsmnet);
install_element(CONFIG_NODE, &cfg_net_cmd);
@@ -120,7 +116,7 @@ static void gsmnet_init_vty()
void *root_ctx = NULL;
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -257,7 +253,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
diff --git a/tests/tdef/tdef_vty_test_config_subnode.vty b/tests/tdef/tdef_vty_config_subnode_test.vty
index 2605f71d..2605f71d 100644
--- a/tests/tdef/tdef_vty_test_config_subnode.vty
+++ b/tests/tdef/tdef_vty_config_subnode_test.vty
diff --git a/tests/tdef/tdef_vty_test_dynamic.c b/tests/tdef/tdef_vty_dynamic_test.c
index 20dae535..b646c54e 100644
--- a/tests/tdef/tdef_vty_test_dynamic.c
+++ b/tests/tdef/tdef_vty_dynamic_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
@@ -183,7 +179,7 @@ static int config_write_member(struct vty *vty)
return CMD_SUCCESS;
}
-static void member_init_vty()
+static void member_init_vty(void)
{
install_node(&member_node, config_write_member);
install_element(CONFIG_NODE, &cfg_member_cmd);
@@ -194,7 +190,7 @@ static void member_init_vty()
/* ------------------- THE REST is just boilerplate osmo main() ------------------- */
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -331,7 +327,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
diff --git a/tests/tdef/tdef_vty_test_dynamic.vty b/tests/tdef/tdef_vty_dynamic_test.vty
index 6aae746b..6aae746b 100644
--- a/tests/tdef/tdef_vty_test_dynamic.vty
+++ b/tests/tdef/tdef_vty_dynamic_test.vty
diff --git a/tests/testsuite.at b/tests/testsuite.at
index c231b964..e721b937 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -36,7 +36,7 @@ AT_CLEANUP
AT_SETUP([bitvec])
AT_KEYWORDS([bitvec])
cat $abs_srcdir/bitvec/bitvec_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/bitvec/bitvec_test], [0], [expout], [ignore])
+AT_CHECK([$abs_top_builddir/tests/bitvec/bitvec_test], [0], [expout])
AT_CLEANUP
AT_SETUP([bitcomp])
@@ -84,9 +84,8 @@ AT_CLEANUP
if ENABLE_MSGFILE
AT_SETUP([msgfile])
AT_KEYWORDS([msgfile])
-cp $abs_srcdir/msgfile/msgconfig.cfg .
cat $abs_srcdir/msgfile/msgfile_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/msgfile/msgfile_test], [0], [expout])
+AT_CHECK([$abs_top_builddir/tests/msgfile/msgfile_test $abs_srcdir/msgfile/msgconfig.cfg], [0], [expout])
AT_CLEANUP
endif
@@ -102,6 +101,18 @@ cat $abs_srcdir/smscb/smscb_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/smscb/smscb_test], [0], [expout])
AT_CLEANUP
+AT_SETUP([smscb_gsm0341])
+AT_KEYWORDS([smscb_gsm0341])
+cat $abs_srcdir/smscb/gsm0341_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/smscb/gsm0341_test], [0], [expout])
+AT_CLEANUP
+
+AT_SETUP([smscb_cbsp])
+AT_KEYWORDS([smscb_cbsp])
+cat $abs_srcdir/smscb/cbsp_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/smscb/cbsp_test], [0], [expout])
+AT_CLEANUP
+
AT_SETUP([ussd])
AT_KEYWORDS([ussd])
cat $abs_srcdir/ussd/ussd_test.ok > expout
@@ -114,6 +125,12 @@ cat $abs_srcdir/auth/milenage_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/auth/milenage_test], [0], [expout], [ignore])
AT_CLEANUP
+AT_SETUP([auth_xor2g])
+AT_KEYWORDS([auth_xor2g])
+cat $abs_srcdir/auth/xor2g_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/auth/xor2g_test], [0], [expout], [ignore])
+AT_CLEANUP
+
AT_SETUP([comp128])
AT_KEYWORDS([comp128])
cat $abs_srcdir/comp128/comp128_test.ok > expout
@@ -132,6 +149,12 @@ cat $abs_srcdir/gsm0502/gsm0502_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gsm0502/gsm0502_test], [0], [expout], [ignore])
AT_CLEANUP
+AT_SETUP([dtx])
+AT_KEYWORDS([dtx])
+cat $abs_srcdir/dtx/dtx_gsm0503_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/dtx/dtx_gsm0503_test], [0], [expout], [ignore])
+AT_CLEANUP
+
AT_SETUP([gsm0808])
AT_KEYWORDS([gsm0808])
cat $abs_srcdir/gsm0808/gsm0808_test.ok > expout
@@ -147,7 +170,14 @@ AT_CLEANUP
AT_SETUP([gsm0408])
AT_KEYWORDS([gsm0408])
cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [0], [expout], [ignore])
+cat $abs_srcdir/gsm0408/gsm0408_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([gsm48_rest_octets])
+AT_KEYWORDS([gsm48_rest_octets])
+cat $abs_srcdir/gsm48/rest_octets_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gsm48/rest_octets_test], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([gprs])
@@ -156,11 +186,24 @@ cat $abs_srcdir/gprs/gprs_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gprs/gprs_test], [0], [expout], [ignore])
AT_CLEANUP
-AT_SETUP([logging])
-AT_KEYWORDS([logging])
+AT_SETUP([logging_stream])
+AT_KEYWORDS([logging_stream])
+cat $abs_srcdir/logging/logging_test.ok > expout
+cat $abs_srcdir/logging/logging_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/logging/logging_test stream], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([logging_wqueue])
+AT_KEYWORDS([logging_wqueue])
cat $abs_srcdir/logging/logging_test.ok > expout
cat $abs_srcdir/logging/logging_test.err > experr
-AT_CHECK([$abs_top_builddir/tests/logging/logging_test], [0], [expout], [experr])
+AT_CHECK([$abs_top_builddir/tests/logging/logging_test wqueue], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([logging_gsmtap])
+AT_KEYWORDS([logging_gsmtap])
+cat $abs_srcdir/logging/logging_gsmtap_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/logging/logging_gsmtap_test 3>&1 1>&2 2>&3 |grep -v "enqueueing message failed" 3>&1 1>&2 2>&3 ], [], [ignore], [experr])
AT_CLEANUP
AT_SETUP([codec])
@@ -198,8 +241,8 @@ AT_CLEANUP
AT_SETUP([vty])
AT_KEYWORDS([vty])
cat $abs_srcdir/vty/vty_test.ok > expout
-cp $abs_srcdir/vty/*.cfg .
-AT_CHECK([$abs_top_builddir/tests/vty/vty_test], [0], [expout], [ignore])
+cat $abs_srcdir/vty/vty_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/vty/vty_test $abs_srcdir/vty], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([gprs-bssgp])
@@ -208,10 +251,24 @@ cat $abs_srcdir/gb/gprs_bssgp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gb/gprs_bssgp_test], [0], [expout], [ignore])
AT_CLEANUP
+AT_SETUP([gprs-bssgp-rim])
+AT_KEYWORDS([gprs-bssgp-rim])
+cat $abs_srcdir/gb/gprs_bssgp_rim_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gb/gprs_bssgp_rim_test], [0], [expout], [ignore])
+AT_CLEANUP
+
AT_SETUP([gprs-ns])
AT_KEYWORDS([gprs-ns])
cat $abs_srcdir/gb/gprs_ns_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/gb/gprs_ns_test], [0], [expout], [ignore])
+cat $abs_srcdir/gb/gprs_ns_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/gb/gprs_ns_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([gprs-ns2])
+AT_KEYWORDS([gprs-ns2])
+cat $abs_srcdir/gb/gprs_ns2_test.ok > expout
+cat $abs_srcdir/gb/gprs_ns2_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/gb/gprs_ns2_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([utils])
@@ -224,7 +281,8 @@ AT_CLEANUP
AT_SETUP([stats])
AT_KEYWORDS([stats])
cat $abs_srcdir/stats/stats_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/stats/stats_test], [0], [expout], [ignore])
+cat $abs_srcdir/stats/stats_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/stats/stats_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([write_queue])
@@ -303,10 +361,17 @@ AT_SETUP([socket])
AT_KEYWORDS([socket])
cat $abs_srcdir/socket/socket_test.ok > expout
cat $abs_srcdir/socket/socket_test.err > experr
-touch experr
AT_CHECK([$abs_top_builddir/tests/socket/socket_test], [0], [expout], [experr])
AT_CLEANUP
+AT_SETUP([socket_sctp])
+AT_KEYWORDS([socket_sctp])
+AT_SKIP_IF([! test -e $abs_top_builddir/tests/socket/socket_sctp_test])
+cat $abs_srcdir/socket/socket_sctp_test.ok > expout
+cat $abs_srcdir/socket/socket_sctp_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/socket/socket_sctp_test], [0], [expout], [experr])
+AT_CLEANUP
+
AT_SETUP([osmo-auc-gen])
AT_KEYWORDS([osmo-auc-gen])
cat $abs_srcdir/osmo-auc-gen/osmo-auc-gen_test.ok > expout
@@ -338,10 +403,17 @@ cat $abs_srcdir/gsm23003/gsm23003_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/gsm23003/gsm23003_test], [0], [expout], [ignore])
AT_CLEANUP
+AT_SETUP([gsm23236])
+AT_KEYWORDS([gsm23236])
+cat $abs_srcdir/gsm23236/gsm23236_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gsm23236/gsm23236_test], [0], [expout], [ignore])
+AT_CLEANUP
+
AT_SETUP([tdef])
AT_KEYWORDS([tdef])
cat $abs_srcdir/tdef/tdef_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/tdef/tdef_test], [0], [expout], [ignore])
+cat $abs_srcdir/tdef/tdef_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/tdef/tdef_test], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([sockaddr_str])
@@ -362,3 +434,116 @@ AT_KEYWORDS([context])
cat $abs_srcdir/context/context_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/context/context_test], [0], [expout], [ignore])
AT_CLEANUP
+
+AT_SETUP([exec])
+AT_KEYWORDS([exec])
+cat $abs_srcdir/exec/exec_test.ok > expout
+cat $abs_srcdir/exec/exec_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/exec/exec_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([i460_mux])
+AT_KEYWORDS([i460_mux])
+cat $abs_srcdir/i460_mux/i460_mux_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/i460_mux/i460_mux_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([bitgen])
+AT_KEYWORDS([bitgen])
+cat $abs_srcdir/bitgen/bitgen_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/bitgen/bitgen_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([gad])
+AT_KEYWORDS([gad])
+cat $abs_srcdir/gad/gad_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gad/gad_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([bsslap])
+AT_KEYWORDS([bsslap])
+cat $abs_srcdir/bsslap/bsslap_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/bsslap/bsslap_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([bssmap_le])
+AT_KEYWORDS([bssmap_le])
+cat $abs_srcdir/bssmap_le/bssmap_le_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/bssmap_le/bssmap_le_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([it_q])
+AT_KEYWORDS([it_q])
+cat $abs_srcdir/it_q/it_q_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/it_q/it_q_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([base64])
+AT_KEYWORDS([base64])
+cat $abs_srcdir/base64/base64_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/base64/base64_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([time_cc])
+AT_KEYWORDS([time_cc])
+cat $abs_srcdir/time_cc/time_cc_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/time_cc/time_cc_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([iuup])
+AT_KEYWORDS([iuup])
+cat $abs_srcdir/iuup/iuup_test.ok > expout
+cat $abs_srcdir/iuup/iuup_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/iuup/iuup_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([v110_frame_test])
+AT_KEYWORDS([v110_frame_test])
+cat $abs_srcdir/v110/frame_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/v110/frame_test], [], [expout],[])
+AT_CLEANUP
+
+AT_SETUP([v110_ra1_test])
+AT_KEYWORDS([v110_ra1_test])
+cat $abs_srcdir/v110/ra1_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/v110/ra1_test], [], [expout],[])
+AT_CLEANUP
+
+AT_SETUP([v110_ta_test])
+AT_KEYWORDS([v110_ta_test])
+cat $abs_srcdir/v110/ta_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/v110/ta_test], [], [], [experr])
+AT_CLEANUP
+
+AT_SETUP([gsm44021_frame_csd_test])
+AT_KEYWORDS([gsm44021_frame_csd_test])
+cat $abs_srcdir/gsm44021/frame_csd_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/gsm44021/frame_csd_test], [], [expout],[])
+AT_CLEANUP
+
+AT_SETUP([osmo_io])
+AT_KEYWORDS([osmo_io])
+cat $abs_srcdir/osmo_io/osmo_io_test.ok > expout
+cat $abs_srcdir/osmo_io/osmo_io_test.err > experr
+AT_CHECK([$abs_top_builddir/tests/osmo_io/osmo_io_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([osmo_io (uring)])
+AT_KEYWORDS([osmo_io (uring)])
+AT_SKIP_IF([ test "$ENABLE_URING" != "yes" || test "$ENABLE_URING_TESTS" != "yes" ])
+cat $abs_srcdir/osmo_io/osmo_io_test.ok > expout
+cat $abs_srcdir/osmo_io/osmo_io_test.err > experr
+AT_CHECK([LIBOSMO_IO_BACKEND=IO_URING $abs_top_builddir/tests/osmo_io/osmo_io_test], [0], [expout], [experr])
+AT_CLEANUP
+
+AT_SETUP([soft_uart])
+AT_KEYWORDS([soft_uart])
+cat $abs_srcdir/soft_uart/soft_uart_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/soft_uart/soft_uart_test], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([rlp])
+AT_KEYWORDS([rlp])
+cat $abs_srcdir/rlp/rlp_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/rlp/rlp_test], [0], [expout], [ignore])
+AT_CLEANUP
diff --git a/tests/time_cc/time_cc_test.c b/tests/time_cc/time_cc_test.c
new file mode 100644
index 00000000..e4a5aaf3
--- /dev/null
+++ b/tests/time_cc/time_cc_test.c
@@ -0,0 +1,768 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <inttypes.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/time_cc.h>
+
+enum my_ctrs {
+ CTR_CEIL,
+ CTR_ROUND,
+ CTR_FLOOR,
+};
+
+const struct rate_ctr_desc my_ctr_desc[] = {
+ [CTR_CEIL] = {"ceil", "testing round_threshold_usec = 1"},
+ [CTR_ROUND] = {"round", "testing round_threshold_usec = 0 = gran_usec/2"},
+ [CTR_FLOOR] = {"floor", "testing round_threshold_usec = gran_usec"},
+};
+
+const struct rate_ctr_group_desc my_ctrg_desc = {
+ "time_cc_test",
+ "Counters for osmo_time_cc test",
+ 0,
+ ARRAY_SIZE(my_ctr_desc),
+ my_ctr_desc,
+};
+
+struct rate_ctr_group *my_ctrg;
+
+
+enum my_obj_timers {
+ T_GRAN = -23,
+ T_ROUND_THRESH = -24,
+ T_FORGET_SUM = -25,
+};
+
+struct osmo_tdef g_my_obj_tdefs[] = {
+ { .T = T_GRAN, .default_val = 0, .unit = OSMO_TDEF_MS, .desc = "flag_cc granularity, or zero for 1 second" },
+ { .T = T_ROUND_THRESH, .default_val = 0, .unit = OSMO_TDEF_MS,
+ .desc = "flag_cc rounding threshold, or zero for half a granularity" },
+ { .T = T_FORGET_SUM, .default_val = 0, .unit = OSMO_TDEF_MS,
+ .desc = "flag_cc inactivity forget period, or zero to not forget any timings" },
+ {}
+};
+
+
+struct my_obj {
+ struct osmo_time_cc flag_cc_ceil;
+ struct osmo_time_cc flag_cc_round;
+ struct osmo_time_cc flag_cc_floor;
+};
+
+void my_obj_init(struct my_obj *my_obj)
+{
+ osmo_time_cc_init(&my_obj->flag_cc_ceil);
+ my_obj->flag_cc_ceil.cfg = (struct osmo_time_cc_cfg){
+ .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, CTR_CEIL),
+ .round_threshold_usec = 1,
+ .T_gran = T_GRAN,
+ .T_forget_sum = T_FORGET_SUM,
+ .T_defs = g_my_obj_tdefs,
+ };
+
+ osmo_time_cc_init(&my_obj->flag_cc_round);
+ my_obj->flag_cc_round.cfg = (struct osmo_time_cc_cfg){
+ .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, CTR_ROUND),
+ .T_gran = T_GRAN,
+ .T_round_threshold = T_ROUND_THRESH,
+ .T_forget_sum = T_FORGET_SUM,
+ .T_defs = g_my_obj_tdefs,
+ };
+
+ osmo_time_cc_init(&my_obj->flag_cc_floor);
+ my_obj->flag_cc_floor.cfg = (struct osmo_time_cc_cfg){
+ .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, CTR_FLOOR),
+ .round_threshold_usec = UINT64_MAX, /* always >= gran_usec */
+ .T_gran = T_GRAN,
+ .T_forget_sum = T_FORGET_SUM,
+ .T_defs = g_my_obj_tdefs,
+ };
+}
+
+void my_obj_event(struct my_obj *my_obj, bool flag)
+{
+ osmo_time_cc_set_flag(&my_obj->flag_cc_ceil, flag);
+ osmo_time_cc_set_flag(&my_obj->flag_cc_round, flag);
+ osmo_time_cc_set_flag(&my_obj->flag_cc_floor, flag);
+}
+
+void my_obj_destruct(struct my_obj *my_obj)
+{
+ osmo_time_cc_cleanup(&my_obj->flag_cc_ceil);
+ osmo_time_cc_cleanup(&my_obj->flag_cc_round);
+ osmo_time_cc_cleanup(&my_obj->flag_cc_floor);
+}
+
+static const struct log_info_cat log_categories[] = {
+};
+
+static const struct log_info log_info = {
+ .cat = log_categories,
+ .num_cat = ARRAY_SIZE(log_categories),
+};
+
+int main(int argc, char **argv)
+{
+ void *ctx = talloc_named_const(NULL, 0, "time_cc_test");
+ struct timespec *now;
+ struct my_obj my_obj = {0};
+
+ osmo_init_logging2(ctx, &log_info);
+
+ /* enable override for CLOCK_MONOTONIC */
+ osmo_clock_override_enable(CLOCK_MONOTONIC, true);
+ now = osmo_clock_override_gettimespec(CLOCK_MONOTONIC);
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+
+ /* enable override for osmo_gettimeofday(), for osmo_timer_schedule() */
+ osmo_gettimeofday_override = true;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_ctrg = rate_ctr_group_alloc(ctx, &my_ctrg_desc, 0);
+
+#define CHECK_RATE_CTRS(exp_ceil, exp_round, exp_floor) do { \
+ printf("%d CHECK_RATE_CTRS(" #exp_ceil ", " #exp_round ", " #exp_floor ")", \
+ my_obj.flag_cc_round.flag_state); \
+ while (osmo_select_main_ctx(1) > 0); \
+ if (exp_ceil != my_obj.flag_cc_ceil.cfg.rate_ctr->current \
+ || exp_round != my_obj.flag_cc_round.cfg.rate_ctr->current \
+ || exp_floor != my_obj.flag_cc_floor.cfg.rate_ctr->current) \
+ printf("\n ERROR on line %d: ctr_ceil=%"PRIu64" ctr_round=%"PRIu64" ctr_floor=%"PRIu64"\n", \
+ __LINE__, \
+ my_obj.flag_cc_ceil.cfg.rate_ctr->current, \
+ my_obj.flag_cc_round.cfg.rate_ctr->current, \
+ my_obj.flag_cc_floor.cfg.rate_ctr->current); \
+ else \
+ printf(" ok\n"); \
+ } while (0)
+
+#define ADD_MILLISECS_NO_SELECT(ms) do { \
+ osmo_clock_override_add(CLOCK_MONOTONIC, ms / 1000, (uint64_t)(ms % 1000) * 1000000); \
+ osmo_gettimeofday_override_add(ms / 1000, (uint64_t)(ms % 1000) * 1000); \
+ printf("%d ADD_MILLISECS(" #ms ") --> %ld.%03ld", my_obj.flag_cc_round.flag_state, \
+ now->tv_sec, now->tv_nsec/1000000); \
+ printf("\n"); \
+ } while (0)
+
+#define ADD_MILLISECS(ms) do { \
+ ADD_MILLISECS_NO_SELECT(ms); \
+ while (osmo_select_main_ctx(1) > 0); \
+ } while (0)
+
+#define FLAG(VAL) do { \
+ printf(" flag: %s -> %s\n", my_obj.flag_cc_round.flag_state ? "TRUE" : "FALSE", VAL ? "TRUE" : "FALSE"); \
+ my_obj_event(&my_obj, VAL); \
+ } while (0)
+
+ /*
+ * sum ^
+ * | ________
+ * | /
+ * | /
+ * | /
+ * 3*gran --+--------------------------------------+
+ * | /:
+ * | / :
+ * | - - - - - - - - - - - - - - - - - / :
+ * | /. :
+ * | / . :
+ * 2*gran --+--------------------------------+ . :
+ * | /: . :
+ * | / : . :
+ * | - - - - - - - - - -_________/ : . :
+ * | / . : . :
+ * | / . : . :
+ * 1*gran --+-----------------+ . : . :
+ * | /: . : . :
+ * | / : . : . :
+ * | - - - - - - -/ : . : . :
+ * | /. : . : . :
+ * | ....-------' . : . : . :
+ * 0 +----------------------------------------------------------> elapsed time
+ * . : . : . :
+ * _ _ _______ ____________
+ * flag: __| |_| |____| . : |_______|. : . : |__________
+ * f t f t f t . : f t. : . : f
+ * round_threshold_usec : . : . : . :
+ * = 1 usec: 0 1 . :2 . :3 . :4 = "ceil()"
+ * = 0 == gran_usec/2: 0 1 : 2 : 3 : = "round()"
+ * = gran_usec: 0 1 2 3 = "floor()"
+ */
+
+ printf("\n----------- cumulating time, without forget_sum\n\n");
+
+ my_obj_init(&my_obj);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 0.001s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(99);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(100);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(100);
+ /* flag has been true for 0.2s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(300);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(299);
+ /* flag has been true for 0.499s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 0.5s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS(499);
+ /* flag has been true for 0.999s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 1.0s */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS(1);
+ /* flag has been true for 1.001s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(299);
+ /* flag has been true for 1.3s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(false);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ ADD_MILLISECS(400);
+
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(true);
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(199);
+ /* flag has been true for 1.499s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(2);
+ /* flag has been true for 1.501s */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(498);
+ /* flag has been true for 1.999s */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(2);
+ /* flag has been true for 2.001s */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS(500);
+ /* flag has been true for 2.501s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(498);
+ /* flag has been true for 2.999s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(3);
+ /* flag has been true for 3.003s */
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS(200);
+ /* flag has been true for 3.203s */
+ CHECK_RATE_CTRS(4, 3, 3);
+ FLAG(false);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ ADD_MILLISECS(4321);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS(5678);
+ CHECK_RATE_CTRS(9, 9, 8);
+ FLAG(false);
+ CHECK_RATE_CTRS(9, 9, 8);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+ printf("\n----------- test forget_sum_usec\n\n");
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, 10, OSMO_TDEF_S);
+
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_obj_init(&my_obj);
+
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS(100);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(1000);
+ /* 1 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(8999);
+ /* 9.999 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(1);
+ /* 10 s of being false, forget_sum_usec has occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(1);
+ /* Since previous sums were forgotton, ceil() triggers again */
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* If the sum had not been forgotten, adding 400 ms to the initial 100 ms would have triggered round(). Verify
+ * that this does not occur, since now full 500 ms are required */
+ ADD_MILLISECS(399);
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* Adding another 100 ms will trigger round() */
+ ADD_MILLISECS(99);
+ CHECK_RATE_CTRS(2, 0, 0);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* If the sum had not been forgotten, adding 900 ms to the initial 100 ms would have triggered floor(). Verify
+ * that this does not occur, since now full 1000 ms are required. We already added 500 ms above. */
+ ADD_MILLISECS(400);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* Adding another 100 ms will trigger floor() */
+ ADD_MILLISECS(99);
+ CHECK_RATE_CTRS(2, 1, 0);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ /* Test that durations of false below forget_sum_usec never trigger a forget */
+ ADD_MILLISECS(300);
+ CHECK_RATE_CTRS(3, 1, 1);
+ /* internal counter is now at 0.3s above the last reported rate counter */
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ /* internal counter is now at 0.4s above the last reported rate counter */
+ CHECK_RATE_CTRS(3, 1, 1);
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(3, 2, 1);
+ ADD_MILLISECS(500);
+ CHECK_RATE_CTRS(3, 2, 2);
+
+ /* Test that repeated osmo_time_cc_set_flag(false) does not cancel a forget_sum_usec */
+ ADD_MILLISECS(300);
+ /* internal counter is now at 0.3s above the last reported rate counter */
+ CHECK_RATE_CTRS(4, 2, 2);
+ FLAG(false);
+ ADD_MILLISECS(5000);
+ /* Repeat 'false', must not affect forget_sum_usec */
+ FLAG(false);
+ ADD_MILLISECS(5000);
+ CHECK_RATE_CTRS(4, 2, 2);
+ /* 10 s have passed, forget_sum_usec has occurred.
+ * Hence ceil() will trigger again right away: */
+ FLAG(true);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(5, 2, 2);
+ /* Adding 200 ms to the initial 300 ms would have triggered round(), but no more after forget_sum_usec */
+ ADD_MILLISECS(199);
+ CHECK_RATE_CTRS(5, 2, 2);
+ /* Adding another 300 ms will trigger round() */
+ ADD_MILLISECS(299);
+ CHECK_RATE_CTRS(5, 2, 2);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(5, 3, 2);
+ /* Adding 700 ms to the initial 300 ms would have triggered ceil(), but no more after forget_sum_usec */
+ ADD_MILLISECS(200);
+ CHECK_RATE_CTRS(5, 3, 2);
+ /* Adding another 300 ms will trigger ceil() */
+ ADD_MILLISECS(299);
+ CHECK_RATE_CTRS(5, 3, 2);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(5, 3, 3);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+
+ /* Verify correctness when select() lags and runs timer callbacks too late */
+ printf("\n----------- cumulating time, without forget_sum, when timer cb are invoked late\n\n");
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, 0, OSMO_TDEF_S);
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_obj_init(&my_obj);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(100);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS_NO_SELECT(100);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(100);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS_NO_SELECT(100);
+ /* flag has been true for 0.2s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(300);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS_NO_SELECT(799);
+ /* flag has been true for 0.999s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 1.0s */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS_NO_SELECT(300);
+ /* flag has been true for 1.3s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(false);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ ADD_MILLISECS_NO_SELECT(400);
+
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(true);
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS_NO_SELECT(699);
+ /* flag has been true for 1.999s */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 2.0s */
+ CHECK_RATE_CTRS(2, 2, 2);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 2.001s */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS_NO_SELECT(499);
+ /* flag has been true for 2.5s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS_NO_SELECT(499);
+ /* flag has been true for 2.999s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 3.0s */
+ CHECK_RATE_CTRS(3, 3, 3);
+ ADD_MILLISECS_NO_SELECT(200);
+ /* flag has been true for 3.2s */
+ CHECK_RATE_CTRS(4, 3, 3);
+ FLAG(false);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ ADD_MILLISECS_NO_SELECT(4321);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS_NO_SELECT(5678);
+ CHECK_RATE_CTRS(9, 9, 8);
+ FLAG(false);
+ CHECK_RATE_CTRS(9, 9, 8);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+
+ printf("\n----------- test forget_sum, when timer cb are invoked late\n\n");
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, 10, OSMO_TDEF_S);
+
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_obj_init(&my_obj);
+
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS_NO_SELECT(100);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(1000);
+ /* 1 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(8999);
+ /* 9.999 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(1);
+ /* 10 s of being false, forget_sum_usec has occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* Since previous sums were forgotton, ceil() triggers again */
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* If the sum had not been forgotten, adding 400 ms to the initial 100 ms would have triggered round(). Verify
+ * that this does not occur, since now full 500 ms are required */
+ ADD_MILLISECS_NO_SELECT(399);
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* Adding another 100 ms will trigger round() */
+ ADD_MILLISECS_NO_SELECT(99);
+ CHECK_RATE_CTRS(2, 0, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* If the sum had not been forgotten, adding 900 ms to the initial 100 ms would have triggered floor(). Verify
+ * that this does not occur, since now full 1000 ms are required. We already added 500 ms above. */
+ ADD_MILLISECS_NO_SELECT(400);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* Adding another 100 ms will trigger floor() */
+ ADD_MILLISECS_NO_SELECT(99);
+ CHECK_RATE_CTRS(2, 1, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ /* Test that durations of false below forget_sum_usec never trigger a forget */
+ ADD_MILLISECS_NO_SELECT(300);
+ CHECK_RATE_CTRS(3, 1, 1);
+ /* internal counter is now at 0.3s above the last reported rate counter */
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ /* internal counter is now at 0.4s above the last reported rate counter */
+ CHECK_RATE_CTRS(3, 1, 1);
+ ADD_MILLISECS_NO_SELECT(100);
+ CHECK_RATE_CTRS(3, 2, 1);
+ ADD_MILLISECS_NO_SELECT(500);
+ CHECK_RATE_CTRS(3, 2, 2);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+
+#define SET_TDEFS(gran, round_thresh, forget_sum) do { \
+ osmo_tdef_set(g_my_obj_tdefs, T_GRAN, gran, OSMO_TDEF_MS); \
+ osmo_tdef_set(g_my_obj_tdefs, T_ROUND_THRESH, round_thresh, OSMO_TDEF_MS); \
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, forget_sum, OSMO_TDEF_S); \
+ printf("T_defs: T_gran=%luusec T_round_threshold=%luusec T_forget_sum=%luusec\n", \
+ osmo_tdef_get(g_my_obj_tdefs, T_GRAN, OSMO_TDEF_US, -1), \
+ osmo_tdef_get(g_my_obj_tdefs, T_ROUND_THRESH, OSMO_TDEF_US, -1), \
+ osmo_tdef_get(g_my_obj_tdefs, T_FORGET_SUM, OSMO_TDEF_US, -1)); \
+ } while (0)
+
+ printf("\n----------- test T_defs\n\n");
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ SET_TDEFS(100, 10, 0);
+
+ my_obj_init(&my_obj);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS(9);
+ /* flag has been true for 0.009s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 0.010s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS(90);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 1, 1);
+
+ SET_TDEFS(200, 190, 1);
+ /* gran is changed to 200ms, but still continues until the next scheduled event until the change is picked up.
+ * For ceil(), it is 1 ms ahead.
+ * For round(), it is 10 ms ahead.
+ * For floor(), it is at the next full (previous) gran 100 ms ahead.
+ * When T_defs change, all internal sums are reset to zero without reporting.
+ */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS(1);
+ /* 1ms elapsed: ceil() picks up the T_gran change, starts anew. */
+ /* elapsed: ceil 0 ms */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 1 ms */
+ /* ceil() increments because flag has been true for more than 1 us after reset */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(8);
+ /* 10 ms elapsed: round() picks up the T_gran change, starts anew */
+ /* elapsed: ceil 9 ms, round 0 ms */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(90);
+ /* 100 ms elapsed: floor() picks up the T_gran change, starts anew */
+ /* elapsed: ceil 99 ms, round 90 ms, floor 0 ms */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(99);
+ /* elapsed: ceil 198 ms, round 189 ms, floor 99 ms */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 199 ms, round 190 ms, floor 100 ms */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 200 ms, round 191 ms, floor 101 ms */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 201 ms, round 192 ms, floor 102 ms */
+ CHECK_RATE_CTRS(3, 2, 1);
+ ADD_MILLISECS(98);
+ /* elapsed: ceil 299 ms, round 290 ms, floor 200 ms */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS(99);
+ /* elapsed: ceil 398 ms, round 389 ms, floor 299 ms */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 399 ms, round 390 ms, floor 300 ms */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 400 ms, round 391 ms, floor 301 ms */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 401 ms, round 392 ms, floor 302 ms */
+ CHECK_RATE_CTRS(4, 3, 2);
+ ADD_MILLISECS(98);
+ /* elapsed: ceil 499 ms, round 490 ms, floor 400 ms */
+ CHECK_RATE_CTRS(4, 3, 3);
+
+
+ SET_TDEFS(100, 0, 0);
+ /* T_defs change, but they only get picked up upon the next event:
+ * For ceil(), it is 102 ms ahead.
+ * For round(), it is 100 ms ahead (thresh is still 190, currently at 90).
+ * For floor(), it is 200 ms ahead.
+ * When T_defs change, all internal sums are reset to zero without reporting.
+ */
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(4, 3, 3);
+ /* round() picks up the new T_defs. Internal sum resets, nothing else happens yet.
+ * round() schedules the next event 50 ms ahead. */
+ ADD_MILLISECS(2);
+ CHECK_RATE_CTRS(4, 3, 3);
+ /* ceil() picks up the change, its next event is 1 ms ahead. */
+ ADD_MILLISECS(1);
+ /* ceil: 0.001
+ * round: 0.003
+ * floor: still 97 ms until it picks up the change */
+ CHECK_RATE_CTRS(5, 3, 3);
+ ADD_MILLISECS(46);
+ CHECK_RATE_CTRS(5, 3, 3);
+ ADD_MILLISECS(1);
+ /* round() has first counter trigger after T_defs change. */
+ CHECK_RATE_CTRS(5, 4, 3);
+ /* ceil: 0.048
+ * round: 0.050
+ * floor: still 50 ms until it picks up the change */
+ ADD_MILLISECS(50);
+ /* floor() picks up the change. nothing happens yet. */
+ /* ceil: 0.098
+ * round: 0.100
+ * floor: 0.0 */
+ ADD_MILLISECS(2);
+ /* ceil: 0.100
+ * round: 0.102
+ * floor: 0.002 */
+ CHECK_RATE_CTRS(5, 4, 3);
+ ADD_MILLISECS(1);
+ /* ceil: 0.101
+ * round: 0.103
+ * floor: 0.003 */
+ CHECK_RATE_CTRS(6, 4, 3);
+ ADD_MILLISECS(46);
+ /* ceil: 0.147
+ * round: 0.149
+ * floor: 0.049 */
+ CHECK_RATE_CTRS(6, 4, 3);
+ ADD_MILLISECS(1);
+ /* ceil: 0.148
+ * round: 0.150
+ * floor: 0.050 */
+ CHECK_RATE_CTRS(6, 5, 3);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+ return 0;
+}
diff --git a/tests/time_cc/time_cc_test.ok b/tests/time_cc/time_cc_test.ok
new file mode 100644
index 00000000..ccf84d95
--- /dev/null
+++ b/tests/time_cc/time_cc_test.ok
@@ -0,0 +1,328 @@
+
+----------- cumulating time, without forget_sum
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.100
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23000.101
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(99) --> 23000.200
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.300
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.400
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(300) --> 23000.700
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(299) --> 23000.999
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23001.000
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(499) --> 23001.499
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23001.500
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23001.501
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(299) --> 23001.800
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+0 ADD_MILLISECS(400) --> 23002.200
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(199) --> 23002.399
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(2) --> 23002.401
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(498) --> 23002.899
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(2) --> 23002.901
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(500) --> 23003.401
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(498) --> 23003.899
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(3) --> 23003.902
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(200) --> 23004.102
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+0 ADD_MILLISECS(4321) --> 23008.423
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(5678) --> 23014.101
+1 CHECK_RATE_CTRS(9, 9, 8) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(9, 9, 8) ok
+
+----------- test forget_sum_usec
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.100
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1000) --> 23001.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(8999) --> 23010.099
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1) --> 23010.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.101
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(399) --> 23010.500
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(99) --> 23010.599
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.600
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(400) --> 23011.000
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(99) --> 23011.099
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23011.100
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(300) --> 23011.400
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23021.399
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23021.424
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23031.423
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23031.448
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23041.447
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23041.472
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23051.471
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23051.496
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+1 ADD_MILLISECS(100) --> 23051.596
+1 CHECK_RATE_CTRS(3, 2, 1) ok
+1 ADD_MILLISECS(500) --> 23052.096
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(300) --> 23052.396
+1 CHECK_RATE_CTRS(4, 2, 2) ok
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(5000) --> 23057.396
+ flag: FALSE -> FALSE
+0 ADD_MILLISECS(5000) --> 23062.396
+0 CHECK_RATE_CTRS(4, 2, 2) ok
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(1) --> 23062.397
+1 CHECK_RATE_CTRS(5, 2, 2) ok
+1 ADD_MILLISECS(199) --> 23062.596
+1 CHECK_RATE_CTRS(5, 2, 2) ok
+1 ADD_MILLISECS(299) --> 23062.895
+1 CHECK_RATE_CTRS(5, 2, 2) ok
+1 ADD_MILLISECS(1) --> 23062.896
+1 CHECK_RATE_CTRS(5, 3, 2) ok
+1 ADD_MILLISECS(200) --> 23063.096
+1 CHECK_RATE_CTRS(5, 3, 2) ok
+1 ADD_MILLISECS(299) --> 23063.395
+1 CHECK_RATE_CTRS(5, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23063.396
+1 CHECK_RATE_CTRS(5, 3, 3) ok
+
+----------- cumulating time, without forget_sum, when timer cb are invoked late
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.100
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.200
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.300
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.400
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(300) --> 23000.700
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(799) --> 23001.499
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23001.500
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(300) --> 23001.800
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+0 ADD_MILLISECS(400) --> 23002.200
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(699) --> 23002.899
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(1) --> 23002.900
+1 CHECK_RATE_CTRS(2, 2, 2) ok
+1 ADD_MILLISECS(1) --> 23002.901
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(499) --> 23003.400
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(499) --> 23003.899
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23003.900
+1 CHECK_RATE_CTRS(3, 3, 3) ok
+1 ADD_MILLISECS(200) --> 23004.100
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+0 ADD_MILLISECS(4321) --> 23008.421
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(5678) --> 23014.099
+1 CHECK_RATE_CTRS(9, 9, 8) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(9, 9, 8) ok
+
+----------- test forget_sum, when timer cb are invoked late
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.100
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1000) --> 23001.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(8999) --> 23010.099
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1) --> 23010.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.101
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(399) --> 23010.500
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(99) --> 23010.599
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.600
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(400) --> 23011.000
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(99) --> 23011.099
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23011.100
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(300) --> 23011.400
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23021.399
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23021.424
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23031.423
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23031.448
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23041.447
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23041.472
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23051.471
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23051.496
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+1 ADD_MILLISECS(100) --> 23051.596
+1 CHECK_RATE_CTRS(3, 2, 1) ok
+1 ADD_MILLISECS(500) --> 23052.096
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+
+----------- test T_defs
+
+T_defs: T_gran=100000usec T_round_threshold=10000usec T_forget_sum=0usec
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.100
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(9) --> 23000.109
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23000.110
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(90) --> 23000.200
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+T_defs: T_gran=200000usec T_round_threshold=190000usec T_forget_sum=1000000usec
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23000.201
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23000.202
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(8) --> 23000.210
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(90) --> 23000.300
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(99) --> 23000.399
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23000.400
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(1) --> 23000.401
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(1) --> 23000.402
+1 CHECK_RATE_CTRS(3, 2, 1) ok
+1 ADD_MILLISECS(98) --> 23000.500
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(99) --> 23000.599
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(1) --> 23000.600
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23000.601
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23000.602
+1 CHECK_RATE_CTRS(4, 3, 2) ok
+1 ADD_MILLISECS(98) --> 23000.700
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+T_defs: T_gran=100000usec T_round_threshold=0usec T_forget_sum=0usec
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(100) --> 23000.800
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(2) --> 23000.802
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(1) --> 23000.803
+1 CHECK_RATE_CTRS(5, 3, 3) ok
+1 ADD_MILLISECS(46) --> 23000.849
+1 CHECK_RATE_CTRS(5, 3, 3) ok
+1 ADD_MILLISECS(1) --> 23000.850
+1 CHECK_RATE_CTRS(5, 4, 3) ok
+1 ADD_MILLISECS(50) --> 23000.900
+1 ADD_MILLISECS(2) --> 23000.902
+1 CHECK_RATE_CTRS(5, 4, 3) ok
+1 ADD_MILLISECS(1) --> 23000.903
+1 CHECK_RATE_CTRS(6, 4, 3) ok
+1 ADD_MILLISECS(46) --> 23000.949
+1 CHECK_RATE_CTRS(6, 4, 3) ok
+1 ADD_MILLISECS(1) --> 23000.950
+1 CHECK_RATE_CTRS(6, 5, 3) ok
diff --git a/tests/timer/clk_override_test.c b/tests/timer/clk_override_test.c
index 308e8212..e67a6ed6 100644
--- a/tests/timer/clk_override_test.c
+++ b/tests/timer/clk_override_test.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
diff --git a/tests/timer/timer_test.c b/tests/timer/timer_test.c
index d2b0204d..9c51ad94 100644
--- a/tests/timer/timer_test.c
+++ b/tests/timer/timer_test.c
@@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -33,7 +29,7 @@
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
-#include "../config.h"
+#include "config.h"
static void main_timer_fired(void *data);
static void secondary_timer_fired(void *data);
diff --git a/tests/tlv/tlv_test.c b/tests/tlv/tlv_test.c
index 925d7628..8e8bd603 100644
--- a/tests/tlv/tlv_test.c
+++ b/tests/tlv/tlv_test.c
@@ -188,7 +188,7 @@ static void check_lv_shift_data_len(size_t data_len,
}
}
-static void test_tlv_shift_functions()
+static void test_tlv_shift_functions(void)
{
uint8_t test_data[1024];
uint8_t buf[1024];
@@ -250,7 +250,7 @@ static void test_tlv_shift_functions()
/* Most GSM related protocols clearly indicate that in case of duplicate
* IEs, only the first occurrence shall be used, while any further occurrences
* shall be ignored. See e.g. 3GPP TS 24.008 Section 8.6.3 */
-static void test_tlv_repeated_ie()
+static void test_tlv_repeated_ie(void)
{
uint8_t test_data[768];
int i, rc;
@@ -288,7 +288,7 @@ static void test_tlv_repeated_ie()
OSMO_ASSERT(dec3[2].lv[tag].val == &test_data[2 + 3 + 3]);
}
-static void test_tlv_encoder()
+static void test_tlv_encoder(void)
{
const uint8_t enc_ies[] = {
0x17, 0x14, 0x06, 0x2b, 0x12, 0x2b, 0x0b, 0x40, 0x2b, 0xb7, 0x05, 0xd0, 0x63, 0x82, 0x95, 0x03, 0x05, 0x40,
@@ -332,6 +332,152 @@ static void test_tlv_encoder()
msgb_free(msg);
}
+static void test_tlv_parser_bounds(void)
+{
+ struct tlv_definition tdef;
+ struct tlv_parsed dec;
+ uint8_t buf[32];
+
+ memset(&tdef, 0, sizeof(tdef));
+
+ printf("Testing TLV_TYPE_T decoder for out-of-bounds\n");
+ tdef.def[0x23].type = TLV_TYPE_T;
+ buf[0] = 0x23;
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 1, 0, 0) == 1);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 0, 0, 0) == 0);
+
+ printf("Testing TLV_TYPE_TV decoder for out-of-bounds\n");
+ tdef.def[0x23].type = TLV_TYPE_TV;
+ buf[0] = 0x23;
+ buf[1] = 0x42;
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 2, 0, 0) == 1);
+ OSMO_ASSERT(*TLVP_VAL(&dec, 0x23) == buf[1]);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 1, 0, 0) == OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+
+ printf("Testing TLV_TYPE_FIXED decoder for out-of-bounds\n");
+ tdef.def[0x23].type = TLV_TYPE_FIXED;
+ tdef.def[0x23].fixed_len = 2;
+ buf[0] = 0x23;
+ buf[1] = 0x42;
+ buf[2] = 0x55;
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 3, 0, 0) == 1);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 2, 0, 0) == OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+
+ printf("Testing TLV_TYPE_TLV decoder for out-of-bounds\n");
+ tdef.def[0x23].type = TLV_TYPE_TLV;
+ buf[0] = 0x23;
+ buf[1] = 0x02;
+ buf[2] = 0x55;
+ buf[3] = 0xAA;
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 4, 0, 0) == 1);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == &buf[2]);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 3, 0, 0) == OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 2, 0, 0) == OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 1, 0, 0) == OSMO_TLVP_ERR_OFS_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+
+ printf("Testing TLV_TYPE_vTvLV_GAN decoder for out-of-bounds\n");
+ tdef.def[0x23].type = TLV_TYPE_vTvLV_GAN;
+ buf[0] = 0x23;
+ buf[1] = 0x80;
+ buf[2] = 0x01;
+ buf[3] = 0xAA;
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 4, 0, 0) == 1);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == &buf[3]);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 3, 0, 0) == OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 2, 0, 0) == OSMO_TLVP_ERR_OFS_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 1, 0, 0) == OSMO_TLVP_ERR_OFS_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+
+ printf("Testing TLV_TYPE_TvLV decoder for out-of-bounds\n");
+ tdef.def[0x23].type = TLV_TYPE_TvLV;
+ buf[0] = 0x23;
+ buf[1] = 0x81;
+ buf[2] = 0xAA;
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 3, 0, 0) == 1);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == &buf[2]);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 2, 0, 0) == OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 1, 0, 0) == OSMO_TLVP_ERR_OFS_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+
+ printf("Testing TLV_TYPE_TL16V decoder for out-of-bounds\n");
+ tdef.def[0x23].type = TLV_TYPE_TL16V;
+ buf[0] = 0x23;
+ buf[1] = 0x00;
+ buf[2] = 0x01;
+ buf[3] = 0xAA;
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 4, 0, 0) == 1);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == &buf[3]);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 3, 0, 0) == OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 2, 0, 0) == OSMO_TLVP_ERR_OFS_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+ OSMO_ASSERT(tlv_parse(&dec, &tdef, buf, 1, 0, 0) == OSMO_TLVP_ERR_OFS_BEYOND_BUFFER);
+ OSMO_ASSERT(TLVP_VAL(&dec, 0x23) == NULL);
+}
+
+static void test_tlv_lens(void)
+{
+ uint16_t buf_len;
+ uint8_t buf[512];
+ uint8_t val[512] = { 0 };
+ uint16_t x;
+
+
+ for (x = 0; x < 16; x++) {
+ buf_len = lv_put(buf, x, val) - buf;
+ OSMO_ASSERT(buf_len == LV_GROSS_LEN(x));
+ buf_len = tlv_put(buf, 0x23, x, val) - buf;
+ OSMO_ASSERT(buf_len == TLV_GROSS_LEN(x));
+ buf_len = tlv16_put(buf, 0x23, x, (uint16_t *) val) - buf;
+ OSMO_ASSERT(buf_len == TLV16_GROSS_LEN(x));
+ buf_len = tl16v_put(buf, 0x23, x, val) - buf;
+ OSMO_ASSERT(buf_len == TL16V_GROSS_LEN(x));
+ buf_len = t16lv_put(buf, 0x2342, x, val) - buf;
+ OSMO_ASSERT(buf_len == T16LV_GROSS_LEN(x));
+ buf_len = tvlv_put(buf, 0x23, x, val) - buf;
+ OSMO_ASSERT(buf_len == TVLV_GROSS_LEN(x));
+ }
+
+ for (x = 250; x < 300; x++) {
+ buf_len = tl16v_put(buf, 0x23, x, val) - buf;
+ OSMO_ASSERT(buf_len == TL16V_GROSS_LEN(x));
+ buf_len = tvlv_put(buf, 0x23, x, val) - buf;
+ OSMO_ASSERT(buf_len == TVLV_GROSS_LEN(x));
+ }
+}
+
+static void test_tlv_type_single_tv(void)
+{
+ #define SAMPLE_SINGLE_TV_IE 0x08
+ const struct tlv_definition att_tlvdef = {
+ .def = {
+ [SAMPLE_SINGLE_TV_IE] = { TLV_TYPE_SINGLE_TV, 0 },
+ },
+ };
+ struct tlv_parsed tp;
+ int rc;
+ uint8_t exp_val = 0x03;
+ uint8_t buf[] = { (SAMPLE_SINGLE_TV_IE << 4) | (exp_val & 0x0f) };
+ const uint8_t *val;
+
+ rc = tlv_parse(&tp, &att_tlvdef, buf, sizeof(buf), 0, 0);
+ OSMO_ASSERT(rc == 1);
+ OSMO_ASSERT(TLVP_PRESENT(&tp, SAMPLE_SINGLE_TV_IE));
+ val = TLVP_VAL(&tp, SAMPLE_SINGLE_TV_IE);
+ OSMO_ASSERT(val);
+ OSMO_ASSERT(val == &buf[0]);
+ OSMO_ASSERT(*val == buf[0]);
+ OSMO_ASSERT((*val & 0x0f) == exp_val);
+}
+
int main(int argc, char **argv)
{
//osmo_init_logging2(ctx, &info);
@@ -339,6 +485,9 @@ int main(int argc, char **argv)
test_tlv_shift_functions();
test_tlv_repeated_ie();
test_tlv_encoder();
+ test_tlv_parser_bounds();
+ test_tlv_lens();
+ test_tlv_type_single_tv();
printf("Done.\n");
return EXIT_SUCCESS;
diff --git a/tests/tlv/tlv_test.ok b/tests/tlv/tlv_test.ok
index f3f0fd41..e24b889b 100644
--- a/tests/tlv/tlv_test.ok
+++ b/tests/tlv/tlv_test.ok
@@ -1,4 +1,11 @@
Test shift functions
Testing TLV encoder by decoding + re-encoding binary
Testing TLV encoder with IE ordering
+Testing TLV_TYPE_T decoder for out-of-bounds
+Testing TLV_TYPE_TV decoder for out-of-bounds
+Testing TLV_TYPE_FIXED decoder for out-of-bounds
+Testing TLV_TYPE_TLV decoder for out-of-bounds
+Testing TLV_TYPE_vTvLV_GAN decoder for out-of-bounds
+Testing TLV_TYPE_TvLV decoder for out-of-bounds
+Testing TLV_TYPE_TL16V decoder for out-of-bounds
Done.
diff --git a/tests/use_count/use_count_test.c b/tests/use_count/use_count_test.c
index 0b081c9c..b784aeb4 100644
--- a/tests/use_count/use_count_test.c
+++ b/tests/use_count/use_count_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
@@ -196,7 +192,7 @@ static struct foo *foo_alloc(const char *name, size_t static_entries)
return foo;
}
-void print_foos()
+void print_foos(void)
{
int count = 0;
struct foo *foo;
@@ -208,7 +204,7 @@ void print_foos()
fprintf(stderr, "%d foos\n\n", count);
}
-static void test_use_count_fsm()
+static void test_use_count_fsm(void)
{
struct foo *a, *b, *c;
log("\n%s()\n", __func__);
@@ -331,7 +327,7 @@ int main(int argc, char **argv)
log_set_print_level(osmo_stderr_target, 1);
log_set_use_color(osmo_stderr_target, 0);
- osmo_fsm_register(&foo_fsm);
+ OSMO_ASSERT(osmo_fsm_register(&foo_fsm) == 0);
test_use_count_fsm();
diff --git a/tests/use_count/use_count_test.err b/tests/use_count/use_count_test.err
index 97e74a51..29ebfb73 100644
--- a/tests/use_count/use_count_test.err
+++ b/tests/use_count/use_count_test.err
@@ -11,9 +11,9 @@ c: 0 (-)
3 foos
A few gets and puts, logging source file information
-DFOO NOTICE foo(a){IN_USE}: a +1 barring: now used by 1 (barring) (use_count_test.c:223)
-DFOO NOTICE foo(b){IN_USE}: b +1 barring: now used by 1 (barring) (use_count_test.c:225)
-DFOO NOTICE foo(b){IN_USE}: b +1 fighting: now used by 2 (barring,fighting) (use_count_test.c:226)
+DFOO NOTICE foo(a){IN_USE}: a +1 barring: now used by 1 (barring) (use_count_test.c:219)
+DFOO NOTICE foo(b){IN_USE}: b +1 barring: now used by 1 (barring) (use_count_test.c:221)
+DFOO NOTICE foo(b){IN_USE}: b +1 fighting: now used by 2 (barring,fighting) (use_count_test.c:222)
all use counts:
a: 1 (barring)
@@ -22,7 +22,7 @@ c: 0 (-)
3 foos
Attempt to get more than one on limited 'barring' user:
-DFOO ERROR foo(b){IN_USE}: Attempt to get more than one barring (use_count_test.c:231)
+DFOO ERROR foo(b){IN_USE}: Attempt to get more than one barring (use_count_test.c:227)
osmo_use_count_get_put(b, barring, 1) returned error: -34 Numerical result out of range
all use counts:
@@ -32,7 +32,7 @@ c: 0 (-)
3 foos
Put away one user of b
-DFOO NOTICE foo(b){IN_USE}: b -1 barring: now used by 1 (fighting) (use_count_test.c:235)
+DFOO NOTICE foo(b){IN_USE}: b -1 barring: now used by 1 (fighting) (use_count_test.c:231)
all use counts:
a: 1 (barring)
diff --git a/tests/ussd/ussd_test.c b/tests/ussd/ussd_test.c
index 80250711..2b8321d3 100644
--- a/tests/ussd/ussd_test.c
+++ b/tests/ussd/ussd_test.c
@@ -13,10 +13,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
diff --git a/tests/utils/utils_test.c b/tests/utils/utils_test.c
index 70d017fe..9ab12a15 100644
--- a/tests/utils/utils_test.c
+++ b/tests/utils/utils_test.c
@@ -14,10 +14,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/gsm/ipa.h>
@@ -34,6 +30,8 @@
#include <arpa/inet.h>
#include <errno.h>
#include <limits.h>
+#include <inttypes.h>
+#include <string.h>
static void hexdump_test(void)
{
@@ -345,7 +343,7 @@ static struct {
{ "DeafBeddedBabeAcceededFadedDecaff", 32, 32, false, false },
};
-bool test_is_hexstr()
+bool test_is_hexstr(void)
{
int i;
bool pass = true;
@@ -487,6 +485,7 @@ static void bcd2str_test(void)
{
int i;
uint8_t bcd[64];
+ uint8_t bcd2[64];
int rc;
printf("\nTesting bcd to string conversion\n");
@@ -511,6 +510,12 @@ static void bcd2str_test(void)
printf(" ERROR: expected rc=%d\n", t->expect_rc);
if (strcmp(str, t->expect_str))
printf(" ERROR: expected result %s\n", osmo_quote_str(t->expect_str, -1));
+
+ memset(bcd2, 0xff, sizeof(bcd2));
+ rc = osmo_str2bcd(bcd2, sizeof(bcd2), str, t->start_nibble, -1, t->allow_hex);
+ printf("osmo_str2bcd(start_nibble=%d) -> rc=%d\n", t->start_nibble, rc);
+ if (rc > 0)
+ printf(" = %s\n", osmo_hexdump(bcd2, rc));
}
printf("- zero output buffer\n");
@@ -531,7 +536,7 @@ static void str_escape_test(void)
const char *printable = "printable";
const char *res;
- printf("\nTesting string escaping\n");
+ printf("\nTesting string escaping: osmo_escape_str()\n");
printf("- all chars from 0 to 255 in batches of 16:\n");
in_buf[16] = '\0';
for (j = 0; j < 16; j++) {
@@ -571,7 +576,7 @@ static void str_quote_test(void)
const char *printable = "printable";
const char *res;
- printf("\nTesting string quoting\n");
+ printf("\nTesting string quoting: osmo_quote_str()\n");
printf("- all chars from 0 to 255 in batches of 16:\n");
in_buf[16] = '\0';
for (j = 0; j < 16; j++) {
@@ -616,6 +621,132 @@ static void str_quote_test(void)
printf("'%s'\n", osmo_quote_str_buf(NULL, -1, out_buf, 10));
}
+static void str_escape3_test(void)
+{
+ int i;
+ int j;
+ uint8_t in_buf[32];
+ char out_buf[11];
+ const char *printable = "printable";
+ const char *res;
+ void *ctx = talloc_named_const(NULL, 0, __func__);
+
+ printf("\nTesting string escaping: osmo_escape_cstr_buf()\n");
+ printf("- all chars from 0 to 255 in batches of 16:\n");
+ in_buf[16] = '\0';
+ for (j = 0; j < 16; j++) {
+ for (i = 0; i < 16; i++)
+ in_buf[i] = (j << 4) | i;
+ printf("\"%s\"\n", osmo_escape_cstr_c(ctx, (const char*)in_buf, 16));
+ }
+
+ printf("- nul terminated:\n");
+ printf("\"%s\"\n", osmo_escape_cstr_c(ctx, "termi\nated", -1));
+
+ printf("- passthru:\n");
+ res = osmo_escape_cstr_c(ctx, printable, -1);
+ if (strcmp(res, printable))
+ printf("NOT passed through! \"%s\"\n", res);
+ else
+ printf("passed through unchanged \"%s\"\n", res);
+
+ printf("- zero length:\n");
+ printf("\"%s\"\n", osmo_escape_cstr_c(ctx, "omitted", 0));
+
+ printf("- truncation when too long:\n");
+ memset(in_buf, 'x', sizeof(in_buf));
+ in_buf[0] = '\a';
+ in_buf[7] = 'E';
+ memset(out_buf, 0x7f, sizeof(out_buf));
+ osmo_escape_cstr_buf(out_buf, 10, (const char *)in_buf, sizeof(in_buf));
+ printf("\"%s\"\n", out_buf);
+ OSMO_ASSERT(out_buf[10] == 0x7f);
+
+ printf("- Test escaping an escaped string:\n");
+ res = "\x02\x03\n";
+ for (i = 0; i <= 3; i++) {
+ res = osmo_escape_cstr_c(ctx, res, -1);
+ printf("%d: '%s'\n", i, res);
+ }
+
+ talloc_free(ctx);
+}
+
+static void str_quote3_test(void)
+{
+ int i;
+ int j;
+ uint8_t in_buf[32];
+ char out_buf[11];
+ const char *printable = "printable";
+ const char *res;
+ void *ctx = talloc_named_const(NULL, 0, __func__);
+
+ printf("\nTesting string quoting: osmo_quote_cstr_buf()\n");
+ printf("- all chars from 0 to 255 in batches of 16:\n");
+ in_buf[16] = '\0';
+ for (j = 0; j < 16; j++) {
+ for (i = 0; i < 16; i++)
+ in_buf[i] = (j << 4) | i;
+ printf("%s\n", osmo_quote_cstr_c(ctx, (const char*)in_buf, 16));
+ }
+
+ printf("- nul terminated:\n");
+ printf("'%s'\n", osmo_quote_cstr_c(ctx, "termi\nated", -1));
+
+ printf("- never passthru:\n");
+ res = osmo_quote_cstr_c(ctx, printable, -1);
+ if (strcmp(res, printable))
+ printf("NOT passed through. '%s'\n", res);
+ else
+ printf("passed through unchanged '%s'\n", res);
+
+ printf("- zero length:\n");
+ printf("'%s'\n", osmo_quote_cstr_c(ctx, "omitted", 0));
+
+ printf("- truncation when too long:\n");
+ memset(in_buf, 'x', sizeof(in_buf));
+ in_buf[0] = '\a';
+ in_buf[6] = 'E';
+ memset(out_buf, 0x7f, sizeof(out_buf));
+ osmo_quote_cstr_buf(out_buf, 10, (const char *)in_buf, sizeof(in_buf));
+ printf("'%s'\n", out_buf);
+ OSMO_ASSERT(out_buf[10] == 0x7f);
+
+ printf("- always truncation, even when no escaping needed:\n");
+ memset(in_buf, 'x', sizeof(in_buf));
+ in_buf[7] = 'E'; /* dst has 10, less 1 quote and nul, leaves 8, i.e. in[7] is last */
+ in_buf[20] = '\0';
+ memset(out_buf, 0x7f, sizeof(out_buf));
+ osmo_quote_cstr_buf(out_buf, 10, (const char *)in_buf, -1);
+ printf("'%s'\n", out_buf);
+ OSMO_ASSERT(out_buf[0] == '"');
+ OSMO_ASSERT(out_buf[10] == 0x7f);
+
+ printf("- try to feed too little buf for quoting:\n");
+ osmo_quote_cstr_buf(out_buf, 2, "", -1);
+ printf("'%s'\n", out_buf);
+
+ printf("- Test quoting a quoted+escaped string:\n");
+ res = "\x02\x03\n";
+ for (i = 0; i <= 3; i++) {
+ res = osmo_quote_cstr_c(ctx, res, -1);
+ printf("%d: %s\n", i, res);
+ }
+
+ printf("- Test C-string equivalence:\n");
+#define TEST_STR "\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
+#define EMPTY_STR ""
+ printf("strcmp(OSMO_STRINGIFY_VAL(TEST_STR), osmo_quote_cstr_c(ctx, TEST_STR, 256)) == %d\n",
+ strcmp(OSMO_STRINGIFY_VAL(TEST_STR), osmo_quote_cstr_c(ctx, TEST_STR, 256)));
+ printf("strcmp(OSMO_STRINGIFY_VAL(EMPTY_STR), osmo_quote_cstr_c(ctx, EMPTY_STR, -1)) == %d\n",
+ strcmp(OSMO_STRINGIFY_VAL(EMPTY_STR), osmo_quote_cstr_c(ctx, EMPTY_STR, -1)));
+ printf("strcmp(\"NULL\", osmo_quote_cstr_c(ctx, NULL, -1)) == %d\n",
+ strcmp("NULL", osmo_quote_cstr_c(ctx, NULL, -1)));
+
+ talloc_free(ctx);
+}
+
static void isqrt_test(void)
{
int i;
@@ -636,12 +767,72 @@ static void isqrt_test(void)
}
}
+static void mod_test_mod(int x, int y, int expected_result)
+{
+ int result;
+ result = x % y;
+ printf(" %d mod %d = %d = %d\n", x, y, result, expected_result);
+ OSMO_ASSERT(result == expected_result);
+}
+
+static void mod_test_mod_flr(int x, int y, int expected_result)
+{
+ int result;
+ result = OSMO_MOD_FLR(x, y);
+ printf(" %d mod_flr %d = %d = %d\n", x, y, result, expected_result);
+ OSMO_ASSERT(result == expected_result);
+}
+
+static void mod_test_mod_euc(int x, int y, int expected_result)
+{
+ int result;
+ result = OSMO_MOD_EUC(x, y);
+ printf(" %d mod_euc %d = %d = %d\n", x, y, result, expected_result);
+ OSMO_ASSERT(result == expected_result);
+}
+
+static void mod_test(void)
+{
+ /* See also: Daan Leijen, Division and Modulus for Computer
+ * Scientists, section 1.3 */
+
+ printf("\nTesting built in truncated modulo for comparison:\n");
+ mod_test_mod(8, 3, 2);
+ mod_test_mod(8, -3, 2);
+ mod_test_mod(-8, 3, -2);
+ mod_test_mod(-8, -3, -2);
+ mod_test_mod(1, 2, 1);
+ mod_test_mod(1, -2, 1);
+ mod_test_mod(-1, 2, -1);
+ mod_test_mod(-1, -2, -1);
+
+ printf("\nTesting OSMO_MOD_FLR():\n");
+ mod_test_mod_flr(8, 3, 2);
+ mod_test_mod_flr(8, -3, -1);
+ mod_test_mod_flr(-8, 3, 1);
+ mod_test_mod_flr(-8, -3, -2);
+ mod_test_mod_flr(1, 2, 1);
+ mod_test_mod_flr(1, -2, -1);
+ mod_test_mod_flr(-1, 2, 1);
+ mod_test_mod_flr(-1, -2, -1);
+
+ printf("\nTesting OSMO_MOD_EUC():\n");
+ mod_test_mod_euc(8, 3, 2);
+ mod_test_mod_euc(8, -3, 2);
+ mod_test_mod_euc(-8, 3, 1);
+ mod_test_mod_euc(-8, -3, 1);
+ mod_test_mod_euc(1, 2, 1);
+ mod_test_mod_euc(1, -2, 1);
+ mod_test_mod_euc(-1, 2, 1);
+ mod_test_mod_euc(-1, -2, 1);
+}
struct osmo_sockaddr_to_str_and_uint_test_case {
uint16_t port;
bool omit_port;
const char *addr;
unsigned int addr_len;
+ int address_family; /* AF_INET / AF_INET6 */
bool omit_addr;
unsigned int expect_rc;
const char *expect_returned_addr;
@@ -652,24 +843,28 @@ struct osmo_sockaddr_to_str_and_uint_test_case osmo_sockaddr_to_str_and_uint_tes
.port = 0,
.addr = "0.0.0.0",
.addr_len = 20,
+ .address_family = AF_INET,
.expect_rc = 7,
},
{
.port = 65535,
.addr = "255.255.255.255",
.addr_len = 20,
+ .address_family = AF_INET,
.expect_rc = 15,
},
{
.port = 1234,
.addr = "234.23.42.123",
.addr_len = 20,
+ .address_family = AF_INET,
.expect_rc = 13,
},
{
.port = 1234,
.addr = "234.23.42.123",
.addr_len = 10,
+ .address_family = AF_INET,
.expect_rc = 13,
.expect_returned_addr = "234.23.42",
},
@@ -678,11 +873,13 @@ struct osmo_sockaddr_to_str_and_uint_test_case osmo_sockaddr_to_str_and_uint_tes
.omit_port = true,
.addr = "234.23.42.123",
.addr_len = 20,
+ .address_family = AF_INET,
.expect_rc = 13,
},
{
.port = 1234,
.addr = "234.23.42.123",
+ .address_family = AF_INET,
.omit_addr = true,
.expect_rc = 0,
.expect_returned_addr = "",
@@ -691,17 +888,83 @@ struct osmo_sockaddr_to_str_and_uint_test_case osmo_sockaddr_to_str_and_uint_tes
.port = 1234,
.addr = "234.23.42.123",
.addr_len = 0,
+ .address_family = AF_INET,
.expect_rc = 13,
.expect_returned_addr = "",
},
{
.port = 1234,
.addr = "234.23.42.123",
+ .address_family = AF_INET,
+ .omit_port = true,
+ .omit_addr = true,
+ .expect_rc = 0,
+ .expect_returned_addr = "",
+ },
+ {
+ .port = 1234,
+ .addr = "::",
+ .addr_len = 20,
+ .address_family = AF_INET6,
+ .expect_rc = 2,
+ },
+ {
+ .port = 1234,
+ .addr = "::1",
+ .addr_len = 20,
+ .address_family = AF_INET6,
+ .expect_rc = 3,
+ },
+ {
+ .port = 1234,
+ .addr = "::1",
+ .addr_len = 20,
+ .address_family = AF_INET6,
.omit_port = true,
+ .omit_addr = false,
+ .expect_rc = 3,
+ },
+ {
+ .port = 1234,
+ .addr = "::1",
+ .addr_len = 20,
+ .address_family = AF_INET6,
+ .omit_port = false,
.omit_addr = true,
.expect_rc = 0,
.expect_returned_addr = "",
},
+ {
+ .port = 1234,
+ .addr = "fd02:db8:1::1",
+ .addr_len = 20,
+ .address_family = AF_INET6,
+ .expect_rc = 13,
+ },
+ {
+ .port = 1234,
+ .addr = "2001:db8:1::ab9:C0A8:102",
+ .addr_len = 40,
+ .address_family = AF_INET6,
+ .expect_rc = 24,
+ .expect_returned_addr = "2001:db8:1::ab9:c0a8:102",
+ },
+ {
+ .port = 1234,
+ .addr = "2001:0db8:0001:0000:0000:0ab9:C0A8:0102",
+ .addr_len = 32,
+ .address_family = AF_INET6,
+ .expect_rc = 24,
+ .expect_returned_addr = "2001:db8:1::ab9:c0a8:102",
+ },
+ {
+ .port = 1234,
+ .addr = "::ffff:192.168.20.34",
+ .addr_len = 32,
+ .address_family = AF_INET6,
+ .expect_rc = 20,
+ .expect_returned_addr = "::ffff:192.168.20.34",
+ }
};
static void osmo_sockaddr_to_str_and_uint_test(void)
@@ -713,22 +976,35 @@ static void osmo_sockaddr_to_str_and_uint_test(void)
struct osmo_sockaddr_to_str_and_uint_test_case *t =
&osmo_sockaddr_to_str_and_uint_test_data[i];
- struct sockaddr_in sin = {
- .sin_family = AF_INET,
- .sin_port = htons(t->port),
- };
- inet_aton(t->addr, &sin.sin_addr);
+ struct sockaddr_storage sa;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ sa.ss_family = t->address_family;
+ switch (t->address_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)&sa;
+ OSMO_ASSERT(inet_pton(t->address_family, t->addr, &sin->sin_addr) == 1);
+ sin->sin_port = htons(t->port);
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)&sa;
+ OSMO_ASSERT(inet_pton(t->address_family, t->addr, &sin6->sin6_addr) == 1);
+ sin6->sin6_port = htons(t->port);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
- char addr[20] = {};
+ char addr[INET6_ADDRSTRLEN] = {};
uint16_t port = 0;
unsigned int rc;
rc = osmo_sockaddr_to_str_and_uint(
t->omit_addr? NULL : addr, t->addr_len,
t->omit_port? NULL : &port,
- (const struct sockaddr*)&sin);
+ (const struct sockaddr *)&sa);
- printf("[%d] %s:%u%s%s addr_len=%u --> %s:%u rc=%u\n",
+ printf("[%d] [%s]:%u%s%s addr_len=%u --> [%s]:%u rc=%u\n",
i,
t->addr ? : "-",
t->port,
@@ -807,7 +1083,7 @@ struct osmo_str_tolowupper_test_data osmo_str_tolowupper_tests[] = {
};
-static void osmo_str_tolowupper_test()
+static void osmo_str_tolowupper_test(void)
{
int i;
char buf[128];
@@ -981,7 +1257,7 @@ int strbuf_cascade(char *buf, size_t buflen)
return sb.chars_needed;
}
-void strbuf_test()
+void strbuf_test(void)
{
char buf[256];
int rc;
@@ -1004,9 +1280,6 @@ void strbuf_test()
snprintf(buf, sizeof(buf), "0x2b 0x2b 0x2b...");
printf("4: (need %d chars, had size=0) %s\n", rc, buf);
- rc = strbuf_example2(NULL, 99);
- printf("5: (need %d chars, had NULL buffer)\n", rc);
-
printf("\ncascade:\n");
rc = strbuf_cascade(buf, sizeof(buf));
printf("(need %d chars)\n%s\n", rc, buf);
@@ -1014,7 +1287,7 @@ void strbuf_test()
printf("(need %d chars, had size=63) %s\n", rc, buf);
}
-void strbuf_test_nolen()
+void strbuf_test_nolen(void)
{
char buf[20];
struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
@@ -1031,6 +1304,67 @@ void strbuf_test_nolen()
printf("%zu: %s (need=%zu)\n", sb.len, buf, sb.chars_needed);
}
+void strbuf_test_tail_for_buflen(size_t buflen)
+{
+ char buf[buflen];
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ printf("\n%s(%zu)\n", __func__, buflen);
+
+#define SHOW(N) \
+ printf(#N ": %s sb.chars_needed=%zu sb.pos=&sb.buf[%d]\n", \
+ osmo_quote_str(buf, -1), sb.chars_needed, (int)(sb.pos - sb.buf))
+
+ /* shorten in steps using OSMO_STRBUF_DROP_TAIL(), removing and re-adding a trailing newline. */
+ OSMO_STRBUF_PRINTF(sb, "banananana\n");
+ SHOW(1);
+ OSMO_STRBUF_DROP_TAIL(sb, 3);
+ SHOW(2);
+ OSMO_STRBUF_PRINTF(sb, "\n");
+ SHOW(3);
+ OSMO_STRBUF_DROP_TAIL(sb, 3);
+ SHOW(4);
+ OSMO_STRBUF_PRINTF(sb, "\n");
+ SHOW(5);
+
+ /* drop trailing newline */
+ OSMO_STRBUF_DROP_TAIL(sb, 1);
+ SHOW(6);
+
+ /* test writing something to the end and letting OSMO_STRBUF_ADDED_TAIL() know later */
+ int n = OSMO_MIN(6, OSMO_STRBUF_REMAIN(sb));
+ if (n)
+ memcpy(sb.pos, "bread\n", n);
+ OSMO_STRBUF_ADDED_TAIL(sb, 6);
+ SHOW(7);
+}
+
+void strbuf_test_tail(void)
+{
+ strbuf_test_tail_for_buflen(64);
+ strbuf_test_tail_for_buflen(32);
+ strbuf_test_tail_for_buflen(16);
+ strbuf_test_tail_for_buflen(8);
+ strbuf_test_tail_for_buflen(4);
+ strbuf_test_tail_for_buflen(1);
+}
+
+void strbuf_test_remain_char_count(void)
+{
+ char buf[20];
+ struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
+
+ printf("\n%s\n", __func__);
+
+ printf("remaining space: %zu\n", OSMO_STRBUF_REMAIN(sb));
+ printf("current char count: %zu\n", OSMO_STRBUF_CHAR_COUNT(sb));
+
+ printf("populating the buffer\n");
+ OSMO_STRBUF_PRINTF(sb, "osmocom");
+
+ printf("remaining space: %zu\n", OSMO_STRBUF_REMAIN(sb));
+ printf("current char count: %zu\n", OSMO_STRBUF_CHAR_COUNT(sb));
+}
+
static void startswith_test_str(const char *str, const char *startswith_str, bool expect_rc)
{
bool rc = osmo_str_startswith(str, startswith_str);
@@ -1040,7 +1374,7 @@ static void startswith_test_str(const char *str, const char *startswith_str, boo
printf(" ERROR: EXPECTED %s\n", expect_rc ? "true" : "false");
}
-static void startswith_test()
+static void startswith_test(void)
{
printf("\n%s()\n", __func__);
startswith_test_str(NULL, NULL, true);
@@ -1058,6 +1392,803 @@ static void startswith_test()
startswith_test_str("abc", "xyz", false);
}
+static int foo_name_buf(char *buf, size_t buflen, const char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+ return snprintf(buf, buflen, "%s", arg);
+}
+
+static char *foo_name_c(void *ctx, const char *arg)
+{
+ OSMO_NAME_C_IMPL(ctx, 10, "ERROR", foo_name_buf, arg)
+}
+
+static char *foo_name_c_null(void *ctx, const char *arg)
+{
+ OSMO_NAME_C_IMPL(ctx, 10, NULL, foo_name_buf, arg)
+}
+
+static char *foo_name_c_zero(void *ctx, const char *arg)
+{
+ OSMO_NAME_C_IMPL(ctx, 0, "ERROR", foo_name_buf, arg)
+}
+
+static char *foo_name_c_zero_null(void *ctx, const char *arg)
+{
+ OSMO_NAME_C_IMPL(ctx, 0, NULL, foo_name_buf, arg)
+}
+
+static void name_c_impl_test(void)
+{
+ char *test_strs[] = {
+ "test",
+ "longer than 10 chars",
+ NULL,
+ };
+ struct {
+ const char *label;
+ char *(*func)(void *, const char*);
+ } funcs[] = {
+ {
+ "OSMO_NAME_C_IMPL(10, \"ERROR\")",
+ foo_name_c,
+ },
+ {
+ "OSMO_NAME_C_IMPL(10, NULL)",
+ foo_name_c_null,
+ },
+ {
+ "OSMO_NAME_C_IMPL(0, \"ERROR\")",
+ foo_name_c_zero,
+ },
+ {
+ "OSMO_NAME_C_IMPL(0, NULL)",
+ foo_name_c_zero_null,
+ },
+ };
+
+ int i;
+ void *ctx = talloc_named_const(NULL, 0, __func__);
+ int allocs = talloc_total_blocks(ctx);
+
+ printf("\n%s\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(test_strs); i++) {
+ char *test_str = test_strs[i];
+ int j;
+ printf("%2d: %s\n", i, osmo_quote_str(test_str, -1));
+
+ for (j = 0; j < ARRAY_SIZE(funcs); j++) {
+ char *str = funcs[j].func(ctx, test_str);
+ printf(" %30s -> %s", funcs[j].label, osmo_quote_str(str, -1));
+ printf(" allocated %d", (int)talloc_total_blocks(ctx) - allocs);
+ if (str) {
+ printf(" %zu bytes, name '%s'", talloc_total_size(str), talloc_get_name(str));
+ talloc_free(str);
+ }
+ printf("\n");
+ }
+ }
+ talloc_free(ctx);
+}
+
+static void osmo_print_n_test(void)
+{
+ struct token_test {
+ const char *src;
+ size_t token_len;
+ size_t buf_size;
+ const char *expect_token;
+ int expect_rc;
+ };
+ struct token_test tests[] = {
+ { "foo=bar", 3, 100, "foo", 3 },
+ { "foo", 10, 100, "foo", 3 },
+ { "foo", 3, 100, "foo", 3 },
+ { NULL, 10, 100, "", 0 },
+ { "", 10, 100, "", 0 },
+ { "foo=bar", 0, 100, "", 0 },
+
+ { "foo=bar", 3, 2, "f", 3 },
+ { "foo", 10, 2, "f", 3 },
+ { "foo", 3, 2, "f", 3 },
+ { NULL, 10, 2, "", 0 },
+ { "", 10, 2, "", 0 },
+ { "foo=bar", 0, 2, "", 0 },
+
+ { "foo=bar", 3, 1, "", 3 },
+ { "foo", 10, 1, "", 3 },
+ { "foo", 3, 1, "", 3 },
+ { NULL, 10, 1, "", 0 },
+ { "", 10, 1, "", 0 },
+ { "foo=bar", 0, 1, "", 0 },
+
+ { "foo=bar", 3, 0, "unchanged", 3 },
+ { "foo", 10, 0, "unchanged", 3 },
+ { "foo", 3, 0, "unchanged", 3 },
+ { NULL, 10, 0, "unchanged", 0 },
+ { "", 10, 0, "unchanged", 0 },
+ { "foo=bar", 0, 0, "unchanged", 0 },
+ };
+ struct token_test *t;
+ printf("\n%s()\n", __func__);
+ for (t = tests; t - tests < ARRAY_SIZE(tests); t++) {
+ char buf[100] = "unchanged";
+ int rc = osmo_print_n(buf, t->buf_size, t->src, t->token_len);
+ printf("%s token_len=%zu buf_size=%zu", osmo_quote_str(t->src, -1), t->token_len, t->buf_size);
+ printf(" -> token=%s rc=%d", osmo_quote_str(buf, -1), rc);
+ if (strcmp(buf, t->expect_token))
+ printf(" ERROR: expected token %s", osmo_quote_str(t->expect_token, -1));
+ if (rc != t->expect_rc)
+ printf(" ERROR: expected rc %d", t->expect_rc);
+ printf("\n");
+ }
+}
+
+static void osmo_strnchr_test(void)
+{
+ struct test {
+ const char *haystack;
+ size_t haystack_len;
+ const char *needle;
+ int expect_offset;
+ };
+ struct test tests[] = {
+ { "foo=bar", 8, "=", 3 },
+ { "foo=bar", 4, "=", 3 },
+ { "foo=bar", 3, "=", -1 },
+ { "foo=bar", 0, "=", -1 },
+ { "foo\0=bar", 9, "=", -1 },
+ { "foo\0=bar", 9, "\0", 3 },
+ };
+ struct test *t;
+ printf("\n%s()\n", __func__);
+ for (t = tests; t - tests < ARRAY_SIZE(tests); t++) {
+ const char *r = osmo_strnchr(t->haystack, t->haystack_len, t->needle[0]);
+ int offset = -1;
+ if (r)
+ offset = r - t->haystack;
+ printf("osmo_strnchr(%s, %zu, ",
+ osmo_quote_str(t->haystack, -1), t->haystack_len);
+ printf("'%s') -> %d",
+ osmo_escape_str(t->needle, 1), offset);
+ if (offset != t->expect_offset)
+ printf(" ERROR expected %d", t->expect_offset);
+ printf("\n");
+ }
+}
+
+struct float_str_to_int_test {
+ unsigned int precision;
+ const char *str;
+ int64_t expect_val;
+ int expect_err;
+};
+struct float_str_to_int_test float_str_to_int_tests[] = {
+ { 0, "0", 0 },
+ { 0, "1", 1 },
+ { 0, "12.345", 12 },
+ { 0, "+12.345", 12 },
+ { 0, "-12.345", -12 },
+ { 0, "0.345", 0 },
+ { 0, ".345", 0 },
+ { 0, "-0.345", 0 },
+ { 0, "-.345", 0 },
+ { 0, "12.", 12 },
+ { 0, "-180", -180 },
+ { 0, "180", 180 },
+ { 0, "360", 360 },
+ { 0, "123.4567890123", 123 },
+ { 0, "123.4567890123456789012345", 123 },
+ { 0, "9223372036854775807", 9223372036854775807LL },
+ { 0, "-9223372036854775807", -9223372036854775807LL },
+ { 0, "-9223372036854775808", .expect_err = -ERANGE },
+ { 0, "9223372036854775808", .expect_err = -ERANGE },
+ { 0, "-9223372036854775809", .expect_err = -ERANGE },
+ { 0, "100000000000000000000", .expect_err = -ERANGE },
+ { 0, "-100000000000000000000", .expect_err = -ERANGE },
+ { 0, "999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 0, "-999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 0, "1.2.3", .expect_err = -EINVAL },
+ { 0, "foo", .expect_err = -EINVAL },
+ { 0, "1.foo", .expect_err = -EINVAL },
+ { 0, "1.foo", .expect_err = -EINVAL },
+ { 0, "12.-345", .expect_err = -EINVAL },
+ { 0, "-12.-345", .expect_err = -EINVAL },
+ { 0, "12.+345", .expect_err = -EINVAL },
+ { 0, "+12.+345", .expect_err = -EINVAL },
+ { 0, "", .expect_err = -EINVAL },
+ { 0, NULL, .expect_err = -EINVAL },
+
+ { 1, "0", 0 },
+ { 1, "1", 10 },
+ { 1, "12.345", 123 },
+ { 1, "+12.345", 123 },
+ { 1, "-12.345", -123 },
+ { 1, "0.345", 3 },
+ { 1, ".345", 3 },
+ { 1, "-0.345", -3 },
+ { 1, "-.345", -3 },
+ { 1, "12.", 120 },
+ { 1, "-180", -1800 },
+ { 1, "180", 1800 },
+ { 1, "360", 3600 },
+ { 1, "123.4567890123", 1234 },
+ { 1, "123.4567890123456789012345", 1234 },
+ { 1, "922337203685477580.7", 9223372036854775807LL },
+ { 1, "-922337203685477580.7", -9223372036854775807LL },
+ { 1, "-922337203685477580.8", .expect_err = -ERANGE },
+ { 1, "922337203685477580.8", .expect_err = -ERANGE },
+ { 1, "-922337203685477580.9", .expect_err = -ERANGE },
+ { 1, "100000000000000000000", .expect_err = -ERANGE },
+ { 1, "-100000000000000000000", .expect_err = -ERANGE },
+ { 1, "999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 1, "-999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 1, "1.2.3", .expect_err = -EINVAL },
+ { 1, "foo", .expect_err = -EINVAL },
+ { 1, "1.foo", .expect_err = -EINVAL },
+ { 1, "1.foo", .expect_err = -EINVAL },
+ { 1, "12.-345", .expect_err = -EINVAL },
+ { 1, "-12.-345", .expect_err = -EINVAL },
+ { 1, "12.+345", .expect_err = -EINVAL },
+ { 1, "+12.+345", .expect_err = -EINVAL },
+ { 1, "", .expect_err = -EINVAL },
+ { 1, NULL, .expect_err = -EINVAL },
+
+ { 6, "0", 0 },
+ { 6, "1", 1000000 },
+ { 6, "12.345", 12345000 },
+ { 6, "+12.345", 12345000 },
+ { 6, "-12.345", -12345000 },
+ { 6, "0.345", 345000 },
+ { 6, ".345", 345000 },
+ { 6, "-0.345", -345000 },
+ { 6, "-.345", -345000 },
+ { 6, "12.", 12000000 },
+ { 6, "-180", -180000000 },
+ { 6, "180", 180000000 },
+ { 6, "360", 360000000 },
+ { 6, "123.4567890123", 123456789 },
+ { 6, "123.4567890123456789012345", 123456789 },
+ { 6, "9223372036854.775807", 9223372036854775807LL },
+ { 6, "-9223372036854.775807", -9223372036854775807LL },
+ { 6, "-9223372036854.775808", .expect_err = -ERANGE },
+ { 6, "9223372036854.775808", .expect_err = -ERANGE },
+ { 6, "-9223372036854.775809", .expect_err = -ERANGE },
+ { 6, "100000000000000000000", .expect_err = -ERANGE },
+ { 6, "-100000000000000000000", .expect_err = -ERANGE },
+ { 6, "999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 6, "-999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 6, "1.2.3", .expect_err = -EINVAL },
+ { 6, "foo", .expect_err = -EINVAL },
+ { 6, "1.foo", .expect_err = -EINVAL },
+ { 6, "1.foo", .expect_err = -EINVAL },
+ { 6, "12.-345", .expect_err = -EINVAL },
+ { 6, "-12.-345", .expect_err = -EINVAL },
+ { 6, "12.+345", .expect_err = -EINVAL },
+ { 6, "+12.+345", .expect_err = -EINVAL },
+ { 6, "", .expect_err = -EINVAL },
+ { 6, NULL, .expect_err = -EINVAL },
+
+ { 18, "0", 0 },
+ { 18, "1", 1000000000000000000LL },
+ { 18, "1.2345", 1234500000000000000LL },
+ { 18, "+1.2345", 1234500000000000000LL },
+ { 18, "-1.2345", -1234500000000000000LL },
+ { 18, "0.345", 345000000000000000LL },
+ { 18, ".345", 345000000000000000LL },
+ { 18, "-0.345", -345000000000000000LL },
+ { 18, "-.345", -345000000000000000LL },
+ { 18, "2.", 2000000000000000000LL },
+ { 18, "-8", -8000000000000000000LL },
+ { 18, "1.234567890123", 1234567890123000000LL },
+ { 18, "1.234567890123456789012345", 1234567890123456789LL },
+ { 18, "123.4567890123", .expect_err = -ERANGE },
+ { 18, "9.223372036854775807", 9223372036854775807LL },
+ { 18, "-9.223372036854775807", -9223372036854775807LL },
+ { 18, "-9.223372036854775808", .expect_err = -ERANGE },
+ { 18, "9.223372036854775808", .expect_err = -ERANGE },
+ { 18, "-9.223372036854775809", .expect_err = -ERANGE },
+ { 18, "100000000000000000000", .expect_err = -ERANGE },
+ { 18, "-100000000000000000000", .expect_err = -ERANGE },
+ { 18, "999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 18, "-999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 18, "1.2.3", .expect_err = -EINVAL },
+ { 18, "foo", .expect_err = -EINVAL },
+ { 18, "1.foo", .expect_err = -EINVAL },
+ { 18, "1.foo", .expect_err = -EINVAL },
+ { 18, "12.-345", .expect_err = -EINVAL },
+ { 18, "-12.-345", .expect_err = -EINVAL },
+ { 18, "12.+345", .expect_err = -EINVAL },
+ { 18, "+12.+345", .expect_err = -EINVAL },
+ { 18, "", .expect_err = -EINVAL },
+ { 18, NULL, .expect_err = -EINVAL },
+
+ { 19, "0", 0 },
+ { 19, ".1", 1000000000000000000LL },
+ { 19, ".12345", 1234500000000000000LL },
+ { 19, "+.12345", 1234500000000000000LL },
+ { 19, "-.12345", -1234500000000000000LL },
+ { 19, "0.0345", 345000000000000000LL },
+ { 19, ".0345", 345000000000000000LL },
+ { 19, "-0.0345", -345000000000000000LL },
+ { 19, "-.0345", -345000000000000000LL },
+ { 19, ".2", 2000000000000000000LL },
+ { 19, "-.8", -8000000000000000000LL },
+ { 19, ".1234567890123", 1234567890123000000LL },
+ { 19, ".1234567890123456789012345", 1234567890123456789LL },
+ { 19, "123.4567890123", .expect_err = -ERANGE },
+ { 19, ".9223372036854775807", 9223372036854775807LL },
+ { 19, "-.9223372036854775807", -9223372036854775807LL },
+ { 19, "-.9223372036854775808", .expect_err = -ERANGE },
+ { 19, ".9223372036854775808", .expect_err = -ERANGE },
+ { 19, "-.9223372036854775809", .expect_err = -ERANGE },
+ { 19, "100000000000000000000", .expect_err = -ERANGE },
+ { 19, "-100000000000000000000", .expect_err = -ERANGE },
+ { 19, "999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 19, "-999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 19, "1.2.3", .expect_err = -EINVAL },
+ { 19, "foo", .expect_err = -EINVAL },
+ { 19, "1.foo", .expect_err = -EINVAL },
+ { 19, "1.foo", .expect_err = -EINVAL },
+ { 19, "12.-345", .expect_err = -EINVAL },
+ { 19, "-12.-345", .expect_err = -EINVAL },
+ { 19, "12.+345", .expect_err = -EINVAL },
+ { 19, "+12.+345", .expect_err = -EINVAL },
+ { 19, "", .expect_err = -EINVAL },
+ { 19, NULL, .expect_err = -EINVAL },
+
+ { 20, "0", 0 },
+ { 20, ".01", 1000000000000000000LL },
+ { 20, ".012345", 1234500000000000000LL },
+ { 20, "+.012345", 1234500000000000000LL },
+ { 20, "-.012345", -1234500000000000000LL },
+ { 20, "0.00345", 345000000000000000LL },
+ { 20, ".00345", 345000000000000000LL },
+ { 20, "-0.00345", -345000000000000000LL },
+ { 20, "-.00345", -345000000000000000LL },
+ { 20, ".02", 2000000000000000000LL },
+ { 20, "-.08", -8000000000000000000LL },
+ { 20, ".01234567890123", 1234567890123000000LL },
+ { 20, ".01234567890123456789012345", 1234567890123456789LL },
+ { 20, "12.34567890123", .expect_err = -ERANGE },
+ { 20, ".09223372036854775807", 9223372036854775807LL },
+ { 20, "-.09223372036854775807", -9223372036854775807LL },
+ { 20, "-.09223372036854775808", .expect_err = -ERANGE },
+ { 20, ".09223372036854775808", .expect_err = -ERANGE },
+ { 20, "-.09223372036854775809", .expect_err = -ERANGE },
+ { 20, ".1", .expect_err = -ERANGE },
+ { 20, "-.1", .expect_err = -ERANGE },
+ { 20, "999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 20, "-999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 20, "1.2.3", .expect_err = -EINVAL },
+ { 20, "foo", .expect_err = -EINVAL },
+ { 20, "1.foo", .expect_err = -EINVAL },
+ { 20, "1.foo", .expect_err = -EINVAL },
+ { 20, "12.-345", .expect_err = -EINVAL },
+ { 20, "-12.-345", .expect_err = -EINVAL },
+ { 20, "12.+345", .expect_err = -EINVAL },
+ { 20, "+12.+345", .expect_err = -EINVAL },
+ { 20, "", .expect_err = -EINVAL },
+ { 20, NULL, .expect_err = -EINVAL },
+
+ { 25, "0", 0 },
+ { 25, ".0000001", 1000000000000000000LL },
+ { 25, ".00000012345", 1234500000000000000LL },
+ { 25, "+.00000012345", 1234500000000000000LL },
+ { 25, "-.00000012345", -1234500000000000000LL },
+ { 25, "0.0000000345", 345000000000000000LL },
+ { 25, ".0000000345", 345000000000000000LL },
+ { 25, "-0.0000000345", -345000000000000000LL },
+ { 25, "-.0000000345", -345000000000000000LL },
+ { 25, ".0000002", 2000000000000000000LL },
+ { 25, "-.0000008", -8000000000000000000LL },
+ { 25, ".0000001234567890123", 1234567890123000000LL },
+ { 25, ".0000001234567890123456789012345", 1234567890123456789LL },
+ { 25, ".0001234567890123", .expect_err = -ERANGE },
+ { 25, ".0000009223372036854775807", 9223372036854775807LL },
+ { 25, "-.0000009223372036854775807", -9223372036854775807LL },
+ { 25, "-.0000009223372036854775808", .expect_err = -ERANGE },
+ { 25, ".0000009223372036854775808", .expect_err = -ERANGE },
+ { 25, "-.0000009223372036854775809", .expect_err = -ERANGE },
+ { 25, ".000001", .expect_err = -ERANGE },
+ { 25, "-.000001", .expect_err = -ERANGE },
+ { 25, "999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 25, "-999999999999999999999999999.99", .expect_err = -ERANGE },
+ { 25, "1.2.3", .expect_err = -EINVAL },
+ { 25, "foo", .expect_err = -EINVAL },
+ { 25, "1.foo", .expect_err = -EINVAL },
+ { 25, "1.foo", .expect_err = -EINVAL },
+ { 25, "12.-345", .expect_err = -EINVAL },
+ { 25, "-12.-345", .expect_err = -EINVAL },
+ { 25, "12.+345", .expect_err = -EINVAL },
+ { 25, "+12.+345", .expect_err = -EINVAL },
+ { 25, "", .expect_err = -EINVAL },
+ { 25, NULL, .expect_err = -EINVAL },
+};
+const char *errno_str(int rc)
+{
+ switch (rc) {
+ case -EINVAL:
+ return "=-EINVAL";
+ case -ERANGE:
+ return "=-ERANGE";
+ case -E2BIG:
+ return "=-E2BIG";
+ case -EOVERFLOW:
+ return "=-EOVERFLOW";
+ default:
+ return "";
+ }
+}
+void test_float_str_to_int(void)
+{
+ const struct float_str_to_int_test *t;
+ printf("--- %s\n", __func__);
+ for (t = float_str_to_int_tests; (t - float_str_to_int_tests) < ARRAY_SIZE(float_str_to_int_tests); t++) {
+ int rc;
+ int64_t val;
+ rc = osmo_float_str_to_int(&val, t->str, t->precision);
+ printf("osmo_float_str_to_int(%s, %u) -> rc=%d%s val=%" PRId64 "\n",
+ osmo_quote_str(t->str, -1), t->precision, rc, errno_str(rc), val);
+
+ if (rc != t->expect_err)
+ printf(" ERROR: expected rc=%d%s\n", t->expect_err, errno_str(t->expect_err));
+ if (val != t->expect_val)
+ printf(" ERROR: expected val=%" PRId64 "\n", t->expect_val);
+ if (rc != t->expect_err||val != t->expect_val)
+ exit(0);
+ }
+}
+
+struct int_to_float_str_test {
+ unsigned int precision;
+ int64_t val;
+ const char *expect_str;
+};
+struct int_to_float_str_test int_to_float_str_tests[] = {
+ { 0, 0, "0" },
+ { 0, 1, "1" },
+ { 0, 1000000, "1000000" },
+ { 0, -1000000, "-1000000" },
+ { 0, 1000001, "1000001" },
+ { 0, -1000001, "-1000001" },
+ { 0, 1000100, "1000100" },
+ { 0, -1010000, "-1010000" },
+ { 0, 1100000, "1100000" },
+ { 0, 10000000, "10000000" },
+ { 0, -10000000, "-10000000" },
+ { 0, 100000000, "100000000" },
+ { 0, -100000000, "-100000000" },
+ { 0, 9223372036854775807, "9223372036854775807" },
+ { 0, -9223372036854775807, "-9223372036854775807" },
+ { 0, INT64_MIN, "-ERR" },
+
+ { 1, 0, "0" },
+ { 1, 1, "0.1" },
+ { 1, 1000000, "100000" },
+ { 1, -1000000, "-100000" },
+ { 1, 1000001, "100000.1" },
+ { 1, -1000001, "-100000.1" },
+ { 1, 1000100, "100010" },
+ { 1, -1010000, "-101000" },
+ { 1, 1100000, "110000" },
+ { 1, 10000000, "1000000" },
+ { 1, -10000000, "-1000000" },
+ { 1, 100000000, "10000000" },
+ { 1, -100000000, "-10000000" },
+ { 1, 9223372036854775807, "922337203685477580.7" },
+ { 1, -9223372036854775807, "-922337203685477580.7" },
+ { 1, INT64_MIN, "-ERR" },
+
+ { 3, 0, "0" },
+ { 3, 1, "0.001" },
+ { 3, 1000000, "1000" },
+ { 3, -1000000, "-1000" },
+ { 3, 1000001, "1000.001" },
+ { 3, -1000001, "-1000.001" },
+ { 3, 1000100, "1000.1" },
+ { 3, -1010000, "-1010" },
+ { 3, 1100000, "1100" },
+ { 3, 10000000, "10000" },
+ { 3, -10000000, "-10000" },
+ { 3, 100000000, "100000" },
+ { 3, -100000000, "-100000" },
+ { 3, 9223372036854775807, "9223372036854775.807" },
+ { 3, -9223372036854775807, "-9223372036854775.807" },
+ { 3, INT64_MIN, "-ERR" },
+
+ { 6, 0, "0" },
+ { 6, 1, "0.000001" },
+ { 6, 1000000, "1" },
+ { 6, -1000000, "-1" },
+ { 6, 1000001, "1.000001" },
+ { 6, -1000001, "-1.000001" },
+ { 6, 1000100, "1.0001" },
+ { 6, -1010000, "-1.01" },
+ { 6, 1100000, "1.1" },
+ { 6, 10000000, "10" },
+ { 6, -10000000, "-10" },
+ { 6, 100000000, "100" },
+ { 6, -100000000, "-100" },
+ { 6, 9223372036854775807, "9223372036854.775807" },
+ { 6, -9223372036854775807, "-9223372036854.775807" },
+ { 6, INT64_MIN, "-ERR" },
+
+ { 17, 0, "0" },
+ { 17, 1, "0.00000000000000001" },
+ { 17, 1000000, "0.00000000001" },
+ { 17, -1000000, "-0.00000000001" },
+ { 17, 1000001, "0.00000000001000001" },
+ { 17, -1000001, "-0.00000000001000001" },
+ { 17, 1000100, "0.000000000010001" },
+ { 17, -1010000, "-0.0000000000101" },
+ { 17, 1100000, "0.000000000011" },
+ { 17, 10000000, "0.0000000001" },
+ { 17, -10000000, "-0.0000000001" },
+ { 17, 100000000, "0.000000001" },
+ { 17, -100000000, "-0.000000001" },
+ { 17, 9223372036854775807, "92.23372036854775807" },
+ { 17, -9223372036854775807, "-92.23372036854775807" },
+ { 17, INT64_MIN, "-ERR" },
+
+ { 18, 0, "0" },
+ { 18, 1, "0.000000000000000001" },
+ { 18, 1000000, "0.000000000001" },
+ { 18, -1000000, "-0.000000000001" },
+ { 18, 1000001, "0.000000000001000001" },
+ { 18, -1000001, "-0.000000000001000001" },
+ { 18, 1000100, "0.0000000000010001" },
+ { 18, -1010000, "-0.00000000000101" },
+ { 18, 1100000, "0.0000000000011" },
+ { 18, 10000000, "0.00000000001" },
+ { 18, -10000000, "-0.00000000001" },
+ { 18, 100000000, "0.0000000001" },
+ { 18, -100000000, "-0.0000000001" },
+ { 18, 9223372036854775807, "9.223372036854775807" },
+ { 18, -9223372036854775807, "-9.223372036854775807" },
+ { 18, INT64_MIN, "-ERR" },
+
+ { 19, 0, "0" },
+ { 19, 1, "0.0000000000000000001" },
+ { 19, 1000000, "0.0000000000001" },
+ { 19, -1000000, "-0.0000000000001" },
+ { 19, 1000001, "0.0000000000001000001" },
+ { 19, -1000001, "-0.0000000000001000001" },
+ { 19, 1000100, "0.00000000000010001" },
+ { 19, -1010000, "-0.000000000000101" },
+ { 19, 1100000, "0.00000000000011" },
+ { 19, 10000000, "0.000000000001" },
+ { 19, -10000000, "-0.000000000001" },
+ { 19, 100000000, "0.00000000001" },
+ { 19, -100000000, "-0.00000000001" },
+ { 19, 9223372036854775807, "0.9223372036854775807" },
+ { 19, -9223372036854775807, "-0.9223372036854775807" },
+ { 19, INT64_MIN, "-ERR" },
+
+ { 23, 0, "0" },
+ { 23, 1, "0.00000000000000000000001" },
+ { 23, 1000000, "0.00000000000000001" },
+ { 23, -1000000, "-0.00000000000000001" },
+ { 23, 1000001, "0.00000000000000001000001" },
+ { 23, -1000001, "-0.00000000000000001000001" },
+ { 23, 1000100, "0.000000000000000010001" },
+ { 23, -1010000, "-0.0000000000000000101" },
+ { 23, 1100000, "0.000000000000000011" },
+ { 23, 10000000, "0.0000000000000001" },
+ { 23, -10000000, "-0.0000000000000001" },
+ { 23, 100000000, "0.000000000000001" },
+ { 23, -100000000, "-0.000000000000001" },
+ { 23, 9223372036854775807, "0.00009223372036854775807" },
+ { 23, -9223372036854775807, "-0.00009223372036854775807" },
+ { 23, INT64_MIN, "-ERR" },
+};
+void test_int_to_float_str(void)
+{
+ const struct int_to_float_str_test *t;
+ printf("--- %s\n", __func__);
+ for (t = int_to_float_str_tests;
+ (t - int_to_float_str_tests) < ARRAY_SIZE(int_to_float_str_tests);
+ t++) {
+ char buf[128];
+ int rc;
+ rc = osmo_int_to_float_str_buf(buf, sizeof(buf), t->val, t->precision);
+ printf("osmo_int_to_float_str_buf(%" PRId64 ", %u) -> rc=%d str=%s\n", t->val, t->precision, rc,
+ osmo_quote_str(buf, -1));
+
+ if (rc != strlen(buf))
+ printf(" ERROR: expected rc=%zu\n", strlen(buf));
+ if (strcmp(buf, t->expect_str))
+ printf(" ERROR: expected str=%s\n", osmo_quote_str(t->expect_str, -1));
+ if (rc != strlen(buf) || strcmp(buf, t->expect_str))
+ exit(0);
+ }
+}
+
+struct str_to_int_test {
+ const char *str;
+ int base;
+ int min_val;
+ int max_val;
+ int expect_rc;
+ int expect_val;
+};
+/* Avoid using INT_MAX and INT_MIN because that would produce different test output on different architectures */
+struct str_to_int_test str_to_int_tests[] = {
+ { NULL, 10, -1000, 1000, -EINVAL, 0 },
+ { "", 10, -1000, 1000, -EINVAL, 0 },
+ { " ", 10, -1000, 1000, -EINVAL, 0 },
+ { "-", 10, -1000, 1000, -EINVAL, 0 },
+ { "--", 10, -1000, 1000, -EINVAL, 0 },
+ { "+", 10, -1000, 1000, -EINVAL, 0 },
+ { "++", 10, -1000, 1000, -EINVAL, 0 },
+
+ { "0", 10, -1000, 1000, 0, 0 },
+ { "1", 10, -1000, 1000, 0, 1 },
+ { "+1", 10, -1000, 1000, 0, 1 },
+ { "-1", 10, -1000, 1000, 0, -1 },
+ { "1000", 10, -1000, 1000, 0, 1000 },
+ { "+1000", 10, -1000, 1000, 0, 1000 },
+ { "-1000", 10, -1000, 1000, 0, -1000 },
+ { "1001", 10, -1000, 1000, -ERANGE, 1001 },
+ { "+1001", 10, -1000, 1000, -ERANGE, 1001 },
+ { "-1001", 10, -1000, 1000, -ERANGE, -1001 },
+
+ { "0", 16, -1000, 1000, 0, 0 },
+ { "1", 16, -1000, 1000, 0, 1 },
+ { "0x1", 16, -1000, 1000, 0, 1 },
+ { "+1", 16, -1000, 1000, 0, 1 },
+ { "-1", 16, -1000, 1000, 0, -1 },
+ { "+0x1", 16, -1000, 1000, 0, 1 },
+ { "-0x1", 16, -1000, 1000, 0, -1 },
+ { "3e8", 16, -1000, 1000, 0, 1000 },
+ { "3E8", 16, -1000, 1000, 0, 1000 },
+ { "0x3e8", 16, -1000, 1000, 0, 1000 },
+ { "0x3E8", 16, -1000, 1000, 0, 1000 },
+ { "+3e8", 16, -1000, 1000, 0, 1000 },
+ { "+3E8", 16, -1000, 1000, 0, 1000 },
+ { "+0x3e8", 16, -1000, 1000, 0, 1000 },
+ { "+0x3E8", 16, -1000, 1000, 0, 1000 },
+ { "-3e8", 16, -1000, 1000, 0, -1000 },
+ { "-3E8", 16, -1000, 1000, 0, -1000 },
+ { "-0x3e8", 16, -1000, 1000, 0, -1000 },
+ { "-0x3E8", 16, -1000, 1000, 0, -1000 },
+ { "3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "0x3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "0x3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+0x3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+0x3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "-3e9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-3E9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-0x3e9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-0x3E9", 16, -1000, 1000, -ERANGE, -1001 },
+
+ { "garble", 10, -1000, 1000, -EINVAL, 0 },
+ { "-garble", 10, -1000, 1000, -EINVAL, 0 },
+ { "0x123", 10, -1000, 1000, -E2BIG, 0 },
+ { "123potatoes", 10, -1000, 1000, -E2BIG, 123 },
+ { "123 potatoes", 10, -1000, 1000, -E2BIG, 123 },
+ { "123 ", 10, -1000, 1000, -E2BIG, 123 },
+ { "123.4", 10, -1000, 1000, -E2BIG, 123 },
+};
+void test_str_to_int(void)
+{
+ const struct str_to_int_test *t;
+ printf("--- %s\n", __func__);
+ for (t = str_to_int_tests; (t - str_to_int_tests) < ARRAY_SIZE(str_to_int_tests); t++) {
+ int rc;
+ int val;
+ rc = osmo_str_to_int(&val, t->str, t->base, t->min_val, t->max_val);
+ printf("osmo_str_to_int(%s, %d, %d, %d) -> rc=%d%s val=%d\n",
+ osmo_quote_str(t->str, -1), t->base, t->min_val, t->max_val, rc, errno_str(rc), val);
+
+ if (rc != t->expect_rc)
+ printf(" ERROR: expected rc=%d%s\n", t->expect_rc, errno_str(t->expect_rc));
+ if (val != t->expect_val)
+ printf(" ERROR: expected val=%d\n", t->expect_val);
+ }
+}
+
+struct str_to_int64_test {
+ const char *str;
+ int base;
+ int64_t min_val;
+ int64_t max_val;
+ int expect_rc;
+ int64_t expect_val;
+};
+struct str_to_int64_test str_to_int64_tests[] = {
+ { NULL, 10, -1000, 1000, -EINVAL, 0 },
+ { "", 10, -1000, 1000, -EINVAL, 0 },
+ { " ", 10, -1000, 1000, -EINVAL, 0 },
+ { "-", 10, -1000, 1000, -EINVAL, 0 },
+ { "--", 10, -1000, 1000, -EINVAL, 0 },
+ { "+", 10, -1000, 1000, -EINVAL, 0 },
+ { "++", 10, -1000, 1000, -EINVAL, 0 },
+
+ { "0", 10, -1000, 1000, 0, 0 },
+ { "1", 10, -1000, 1000, 0, 1 },
+ { "+1", 10, -1000, 1000, 0, 1 },
+ { "-1", 10, -1000, 1000, 0, -1 },
+ { "1000", 10, -1000, 1000, 0, 1000 },
+ { "+1000", 10, -1000, 1000, 0, 1000 },
+ { "-1000", 10, -1000, 1000, 0, -1000 },
+ { "1001", 10, -1000, 1000, -ERANGE, 1001 },
+ { "+1001", 10, -1000, 1000, -ERANGE, 1001 },
+ { "-1001", 10, -1000, 1000, -ERANGE, -1001 },
+
+ { "0", 16, -1000, 1000, 0, 0 },
+ { "1", 16, -1000, 1000, 0, 1 },
+ { "0x1", 16, -1000, 1000, 0, 1 },
+ { "+1", 16, -1000, 1000, 0, 1 },
+ { "-1", 16, -1000, 1000, 0, -1 },
+ { "+0x1", 16, -1000, 1000, 0, 1 },
+ { "-0x1", 16, -1000, 1000, 0, -1 },
+ { "3e8", 16, -1000, 1000, 0, 1000 },
+ { "3E8", 16, -1000, 1000, 0, 1000 },
+ { "0x3e8", 16, -1000, 1000, 0, 1000 },
+ { "0x3E8", 16, -1000, 1000, 0, 1000 },
+ { "+3e8", 16, -1000, 1000, 0, 1000 },
+ { "+3E8", 16, -1000, 1000, 0, 1000 },
+ { "+0x3e8", 16, -1000, 1000, 0, 1000 },
+ { "+0x3E8", 16, -1000, 1000, 0, 1000 },
+ { "-3e8", 16, -1000, 1000, 0, -1000 },
+ { "-3E8", 16, -1000, 1000, 0, -1000 },
+ { "-0x3e8", 16, -1000, 1000, 0, -1000 },
+ { "-0x3E8", 16, -1000, 1000, 0, -1000 },
+ { "3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "0x3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "0x3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+0x3e9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "+0x3E9", 16, -1000, 1000, -ERANGE, 1001 },
+ { "-3e9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-3E9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-0x3e9", 16, -1000, 1000, -ERANGE, -1001 },
+ { "-0x3E9", 16, -1000, 1000, -ERANGE, -1001 },
+
+ { "garble", 10, -1000, 1000, -EINVAL, 0 },
+ { "-garble", 10, -1000, 1000, -EINVAL, 0 },
+ { "0x123", 10, -1000, 1000, -E2BIG, 0 },
+ { "123potatoes", 10, -1000, 1000, -E2BIG, 123 },
+ { "123 potatoes", 10, -1000, 1000, -E2BIG, 123 },
+ { "123 ", 10, -1000, 1000, -E2BIG, 123 },
+ { "123.4", 10, -1000, 1000, -E2BIG, 123 },
+
+ { "-9223372036854775808", 10, INT64_MIN, INT64_MAX, 0, INT64_MIN },
+ { "9223372036854775807", 10, INT64_MIN, INT64_MAX, 0, INT64_MAX },
+
+ { "-9223372036854775809", 10, INT64_MIN, INT64_MAX, -EOVERFLOW, INT64_MIN },
+ { "9223372036854775808", 10, INT64_MIN, INT64_MAX, -EOVERFLOW, INT64_MAX },
+
+ { "-9223372036854775808", 10, -1000, 1000, -ERANGE, INT64_MIN },
+ { "9223372036854775807", 10, -1000, 1000, -ERANGE, INT64_MAX },
+ { "-9223372036854775809", 10, -1000, 1000, -EOVERFLOW, INT64_MIN },
+ { "9223372036854775808", 10, -1000, 1000, -EOVERFLOW, INT64_MAX },
+};
+void test_str_to_int64(void)
+{
+ const struct str_to_int64_test *t;
+ printf("--- %s\n", __func__);
+ for (t = str_to_int64_tests; (t - str_to_int64_tests) < ARRAY_SIZE(str_to_int64_tests); t++) {
+ int rc;
+ int64_t val;
+ rc = osmo_str_to_int64(&val, t->str, t->base, t->min_val, t->max_val);
+ printf("osmo_str_to_int64(%s, %d, %"PRId64", %"PRId64") -> rc=%d%s val=%"PRId64"\n",
+ osmo_quote_str(t->str, -1), t->base, t->min_val, t->max_val, rc, errno_str(rc), val);
+
+ if (rc != t->expect_rc)
+ printf(" ERROR: expected rc=%d%s\n", t->expect_rc, errno_str(t->expect_rc));
+ if (val != t->expect_val)
+ printf(" ERROR: expected val=%"PRId64"\n", t->expect_val);
+ }
+}
+
int main(int argc, char **argv)
{
static const struct log_info log_info = {};
@@ -1072,11 +2203,23 @@ int main(int argc, char **argv)
bcd2str_test();
str_escape_test();
str_quote_test();
+ str_escape3_test();
+ str_quote3_test();
isqrt_test();
+ mod_test();
osmo_sockaddr_to_str_and_uint_test();
osmo_str_tolowupper_test();
strbuf_test();
strbuf_test_nolen();
+ strbuf_test_tail();
+ strbuf_test_remain_char_count();
startswith_test();
+ name_c_impl_test();
+ osmo_print_n_test();
+ osmo_strnchr_test();
+ test_float_str_to_int();
+ test_int_to_float_str();
+ test_str_to_int();
+ test_str_to_int64();
return 0;
}
diff --git a/tests/utils/utils_test.ok b/tests/utils/utils_test.ok
index c150a8d0..8a66ba81 100644
--- a/tests/utils/utils_test.ok
+++ b/tests/utils/utils_test.ok
@@ -181,32 +181,46 @@ Testing bcd to string conversion
- BCD-input='1a 32 54 76 98 f0' nibbles=[1..11[ str_size=64
rc=10
-> "1234567890"
+osmo_str2bcd(start_nibble=1) -> rc=6
+ = 1f 32 54 76 98 f0
- BCD-input='1a 32 a4 cb 9d f0' nibbles=[1..11[ str_size=64
rc=-22
-> "1234ABCD90"
+osmo_str2bcd(start_nibble=1) -> rc=-22
- BCD-input='1a 32 a4 cb 9d f0' nibbles=[1..11[ str_size=64
rc=10
-> "1234ABCD90"
+osmo_str2bcd(start_nibble=1) -> rc=6
+ = 1f 32 a4 cb 9d f0
- BCD-input='1a 32 54 76 98 f0' nibbles=[1..12[ str_size=64
rc=-22
-> "1234567890F"
+osmo_str2bcd(start_nibble=1) -> rc=-22
- BCD-input='1a 32 54 76 98 f0' nibbles=[1..12[ str_size=64
rc=11
-> "1234567890F"
+osmo_str2bcd(start_nibble=1) -> rc=6
+ = 1f 32 54 76 98 f0
- BCD-input='1a 32 54 76 98 f0' nibbles=[0..12[ str_size=64
rc=12
-> "A1234567890F"
+osmo_str2bcd(start_nibble=0) -> rc=6
+ = 1a 32 54 76 98 f0
- BCD-input='1a 32 54 76 98 f0' nibbles=[1..12[ str_size=5
rc=11
-> "1234"
+osmo_str2bcd(start_nibble=1) -> rc=3
+ = 1f 32 f4
- BCD-input='' nibbles=[1..1[ str_size=64
rc=0
-> ""
+osmo_str2bcd(start_nibble=1) -> rc=1
+ = ff
- zero output buffer
bcd2str(NULL, ...) -> -12
bcd2str(dst, 0, ...) -> -12
-Testing string escaping
+Testing string escaping: osmo_escape_str()
- all chars from 0 to 255 in batches of 16:
"\0\1\2\3\4\5\6\a\b\t\n\v\f\r\14\15"
"\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31"
@@ -233,7 +247,7 @@ passed through unchanged "printable"
- truncation when too long:
"\axxxxxxE"
-Testing string quoting
+Testing string quoting: osmo_quote_str()
- all chars from 0 to 255 in batches of 16:
'"\0\1\2\3\4\5\6\a\b\t\n\v\f\r\14\15"'
'"\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31"'
@@ -266,17 +280,127 @@ NOT passed through. '"printable"'
- NULL string becomes a "NULL" literal:
'NULL'
+Testing string escaping: osmo_escape_cstr_buf()
+- all chars from 0 to 255 in batches of 16:
+"\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f"
+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+" !\"#$%&'()*+,-./"
+"0123456789:;<=>?"
+"@ABCDEFGHIJKLMNO"
+"PQRSTUVWXYZ[\\]^_"
+"`abcdefghijklmno"
+"pqrstuvwxyz{|}~\x7f"
+"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
+"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
+"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
+"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
+"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
+"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
+- nul terminated:
+"termi\nated"
+- passthru:
+passed through unchanged "printable"
+- zero length:
+""
+- truncation when too long:
+"\x07xxxxx"
+- Test escaping an escaped string:
+0: '\x02\x03\n'
+1: '\\x02\\x03\\n'
+2: '\\\\x02\\\\x03\\\\n'
+3: '\\\\\\\\x02\\\\\\\\x03\\\\\\\\n'
+
+Testing string quoting: osmo_quote_cstr_buf()
+- all chars from 0 to 255 in batches of 16:
+"\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f"
+"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+" !\"#$%&'()*+,-./"
+"0123456789:;<=>?"
+"@ABCDEFGHIJKLMNO"
+"PQRSTUVWXYZ[\\]^_"
+"`abcdefghijklmno"
+"pqrstuvwxyz{|}~\x7f"
+"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
+"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
+"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
+"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
+"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
+"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
+- nul terminated:
+'"termi\nated"'
+- never passthru:
+NOT passed through. '"printable"'
+- zero length:
+'""'
+- truncation when too long:
+'"\x07xxxx'
+- always truncation, even when no escaping needed:
+'"xxxxxxxE'
+- try to feed too little buf for quoting:
+'"'
+- Test quoting a quoted+escaped string:
+0: "\x02\x03\n"
+1: "\"\\x02\\x03\\n\""
+2: "\"\\\"\\\\x02\\\\x03\\\\n\\\"\""
+3: "\"\\\"\\\\\\\"\\\\\\\\x02\\\\\\\\x03\\\\\\\\n\\\\\\\"\\\"\""
+- Test C-string equivalence:
+strcmp(OSMO_STRINGIFY_VAL(TEST_STR), osmo_quote_cstr_c(ctx, TEST_STR, 256)) == 0
+strcmp(OSMO_STRINGIFY_VAL(EMPTY_STR), osmo_quote_cstr_c(ctx, EMPTY_STR, -1)) == 0
+strcmp("NULL", osmo_quote_cstr_c(ctx, NULL, -1)) == 0
+
Testing integer square-root
+Testing built in truncated modulo for comparison:
+ 8 mod 3 = 2 = 2
+ 8 mod -3 = 2 = 2
+ -8 mod 3 = -2 = -2
+ -8 mod -3 = -2 = -2
+ 1 mod 2 = 1 = 1
+ 1 mod -2 = 1 = 1
+ -1 mod 2 = -1 = -1
+ -1 mod -2 = -1 = -1
+
+Testing OSMO_MOD_FLR():
+ 8 mod_flr 3 = 2 = 2
+ 8 mod_flr -3 = -1 = -1
+ -8 mod_flr 3 = 1 = 1
+ -8 mod_flr -3 = -2 = -2
+ 1 mod_flr 2 = 1 = 1
+ 1 mod_flr -2 = -1 = -1
+ -1 mod_flr 2 = 1 = 1
+ -1 mod_flr -2 = -1 = -1
+
+Testing OSMO_MOD_EUC():
+ 8 mod_euc 3 = 2 = 2
+ 8 mod_euc -3 = 2 = 2
+ -8 mod_euc 3 = 1 = 1
+ -8 mod_euc -3 = 1 = 1
+ 1 mod_euc 2 = 1 = 1
+ 1 mod_euc -2 = 1 = 1
+ -1 mod_euc 2 = 1 = 1
+ -1 mod_euc -2 = 1 = 1
+
osmo_sockaddr_to_str_and_uint_test
-[0] 0.0.0.0:0 addr_len=20 --> 0.0.0.0:0 rc=7
-[1] 255.255.255.255:65535 addr_len=20 --> 255.255.255.255:65535 rc=15
-[2] 234.23.42.123:1234 addr_len=20 --> 234.23.42.123:1234 rc=13
-[3] 234.23.42.123:1234 addr_len=10 --> 234.23.42:1234 rc=13
-[4] 234.23.42.123:1234 (omit port) addr_len=20 --> 234.23.42.123:0 rc=13
-[5] 234.23.42.123:1234 (omit addr) addr_len=0 --> :1234 rc=0
-[6] 234.23.42.123:1234 addr_len=0 --> :1234 rc=13
-[7] 234.23.42.123:1234 (omit addr) (omit port) addr_len=0 --> :0 rc=0
+[0] [0.0.0.0]:0 addr_len=20 --> [0.0.0.0]:0 rc=7
+[1] [255.255.255.255]:65535 addr_len=20 --> [255.255.255.255]:65535 rc=15
+[2] [234.23.42.123]:1234 addr_len=20 --> [234.23.42.123]:1234 rc=13
+[3] [234.23.42.123]:1234 addr_len=10 --> [234.23.42]:1234 rc=13
+[4] [234.23.42.123]:1234 (omit port) addr_len=20 --> [234.23.42.123]:0 rc=13
+[5] [234.23.42.123]:1234 (omit addr) addr_len=0 --> []:1234 rc=0
+[6] [234.23.42.123]:1234 addr_len=0 --> []:1234 rc=13
+[7] [234.23.42.123]:1234 (omit addr) (omit port) addr_len=0 --> []:0 rc=0
+[8] [::]:1234 addr_len=20 --> [::]:1234 rc=2
+[9] [::1]:1234 addr_len=20 --> [::1]:1234 rc=3
+[10] [::1]:1234 (omit port) addr_len=20 --> [::1]:0 rc=3
+[11] [::1]:1234 (omit addr) addr_len=20 --> []:1234 rc=0
+[12] [fd02:db8:1::1]:1234 addr_len=20 --> [fd02:db8:1::1]:1234 rc=13
+[13] [2001:db8:1::ab9:C0A8:102]:1234 addr_len=40 --> [2001:db8:1::ab9:c0a8:102]:1234 rc=24
+[14] [2001:0db8:0001:0000:0000:0ab9:C0A8:0102]:1234 addr_len=32 --> [2001:db8:1::ab9:c0a8:102]:1234 rc=24
+[15] [::ffff:192.168.20.34]:1234 addr_len=32 --> [::ffff:192.168.20.34]:1234 rc=20
osmo_str_tolowupper_test
osmo_str_tolower("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()")
@@ -334,7 +458,6 @@ OSMO_STRBUF_PRINTF():
2: (need 42 chars, had size=42) T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off
3: (need 42 chars, had size=42+1) T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off!
4: (need 42 chars, had size=0) 0x2b 0x2b 0x2b...
-5: (need 42 chars, had NULL buffer)
cascade:
(need 134 chars)
@@ -346,6 +469,67 @@ strbuf_test_nolen
more: 0001011100101010000 (need=19)
10: 000101110 (need=9)
+strbuf_test_tail_for_buflen(64)
+1: "banananana\n" sb.chars_needed=11 sb.pos=&sb.buf[11]
+2: "bananana" sb.chars_needed=8 sb.pos=&sb.buf[8]
+3: "bananana\n" sb.chars_needed=9 sb.pos=&sb.buf[9]
+4: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+5: "banana\n" sb.chars_needed=7 sb.pos=&sb.buf[7]
+6: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+7: "bananabread\n" sb.chars_needed=12 sb.pos=&sb.buf[12]
+
+strbuf_test_tail_for_buflen(32)
+1: "banananana\n" sb.chars_needed=11 sb.pos=&sb.buf[11]
+2: "bananana" sb.chars_needed=8 sb.pos=&sb.buf[8]
+3: "bananana\n" sb.chars_needed=9 sb.pos=&sb.buf[9]
+4: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+5: "banana\n" sb.chars_needed=7 sb.pos=&sb.buf[7]
+6: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+7: "bananabread\n" sb.chars_needed=12 sb.pos=&sb.buf[12]
+
+strbuf_test_tail_for_buflen(16)
+1: "banananana\n" sb.chars_needed=11 sb.pos=&sb.buf[11]
+2: "bananana" sb.chars_needed=8 sb.pos=&sb.buf[8]
+3: "bananana\n" sb.chars_needed=9 sb.pos=&sb.buf[9]
+4: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+5: "banana\n" sb.chars_needed=7 sb.pos=&sb.buf[7]
+6: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+7: "bananabread\n" sb.chars_needed=12 sb.pos=&sb.buf[12]
+
+strbuf_test_tail_for_buflen(8)
+1: "bananan" sb.chars_needed=11 sb.pos=&sb.buf[8]
+2: "bananan" sb.chars_needed=8 sb.pos=&sb.buf[8]
+3: "bananan" sb.chars_needed=9 sb.pos=&sb.buf[8]
+4: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+5: "banana\n" sb.chars_needed=7 sb.pos=&sb.buf[7]
+6: "banana" sb.chars_needed=6 sb.pos=&sb.buf[6]
+7: "bananab" sb.chars_needed=12 sb.pos=&sb.buf[7]
+
+strbuf_test_tail_for_buflen(4)
+1: "ban" sb.chars_needed=11 sb.pos=&sb.buf[4]
+2: "ban" sb.chars_needed=8 sb.pos=&sb.buf[4]
+3: "ban" sb.chars_needed=9 sb.pos=&sb.buf[4]
+4: "ban" sb.chars_needed=6 sb.pos=&sb.buf[4]
+5: "ban" sb.chars_needed=7 sb.pos=&sb.buf[4]
+6: "ban" sb.chars_needed=6 sb.pos=&sb.buf[4]
+7: "ban" sb.chars_needed=12 sb.pos=&sb.buf[4]
+
+strbuf_test_tail_for_buflen(1)
+1: "" sb.chars_needed=11 sb.pos=&sb.buf[1]
+2: "" sb.chars_needed=8 sb.pos=&sb.buf[1]
+3: "" sb.chars_needed=9 sb.pos=&sb.buf[1]
+4: "" sb.chars_needed=6 sb.pos=&sb.buf[1]
+5: "" sb.chars_needed=7 sb.pos=&sb.buf[1]
+6: "" sb.chars_needed=6 sb.pos=&sb.buf[1]
+7: "" sb.chars_needed=12 sb.pos=&sb.buf[1]
+
+strbuf_test_remain_char_count
+remaining space: 20
+current char count: 0
+populating the buffer
+remaining space: 13
+current char count: 7
+
startswith_test()
osmo_str_startswith(NULL, NULL) == true
osmo_str_startswith("", NULL) == true
@@ -360,3 +544,538 @@ osmo_str_startswith("abc", "ab") == true
osmo_str_startswith("abc", "abc") == true
osmo_str_startswith("abc", "abcd") == false
osmo_str_startswith("abc", "xyz") == false
+
+name_c_impl_test
+ 0: "test"
+ OSMO_NAME_C_IMPL(10, "ERROR") -> "test" allocated 1 10 bytes, name 'foo_name_c'
+ OSMO_NAME_C_IMPL(10, NULL) -> "test" allocated 1 10 bytes, name 'foo_name_c_null'
+ OSMO_NAME_C_IMPL(0, "ERROR") -> "test" allocated 1 5 bytes, name 'foo_name_c_zero'
+ OSMO_NAME_C_IMPL(0, NULL) -> "test" allocated 1 5 bytes, name 'foo_name_c_zero_null'
+ 1: "longer than 10 chars"
+ OSMO_NAME_C_IMPL(10, "ERROR") -> "longer than 10 chars" allocated 1 21 bytes, name 'foo_name_c'
+ OSMO_NAME_C_IMPL(10, NULL) -> "longer than 10 chars" allocated 1 21 bytes, name 'foo_name_c_null'
+ OSMO_NAME_C_IMPL(0, "ERROR") -> "longer than 10 chars" allocated 1 21 bytes, name 'foo_name_c_zero'
+ OSMO_NAME_C_IMPL(0, NULL) -> "longer than 10 chars" allocated 1 21 bytes, name 'foo_name_c_zero_null'
+ 2: NULL
+ OSMO_NAME_C_IMPL(10, "ERROR") -> "ERROR" allocated 1 6 bytes, name 'foo_name_c'
+ OSMO_NAME_C_IMPL(10, NULL) -> NULL allocated 0
+ OSMO_NAME_C_IMPL(0, "ERROR") -> "ERROR" allocated 1 6 bytes, name 'foo_name_c_zero'
+ OSMO_NAME_C_IMPL(0, NULL) -> NULL allocated 0
+
+osmo_print_n_test()
+"foo=bar" token_len=3 buf_size=100 -> token="foo" rc=3
+"foo" token_len=10 buf_size=100 -> token="foo" rc=3
+"foo" token_len=3 buf_size=100 -> token="foo" rc=3
+NULL token_len=10 buf_size=100 -> token="" rc=0
+"" token_len=10 buf_size=100 -> token="" rc=0
+"foo=bar" token_len=0 buf_size=100 -> token="" rc=0
+"foo=bar" token_len=3 buf_size=2 -> token="f" rc=3
+"foo" token_len=10 buf_size=2 -> token="f" rc=3
+"foo" token_len=3 buf_size=2 -> token="f" rc=3
+NULL token_len=10 buf_size=2 -> token="" rc=0
+"" token_len=10 buf_size=2 -> token="" rc=0
+"foo=bar" token_len=0 buf_size=2 -> token="" rc=0
+"foo=bar" token_len=3 buf_size=1 -> token="" rc=3
+"foo" token_len=10 buf_size=1 -> token="" rc=3
+"foo" token_len=3 buf_size=1 -> token="" rc=3
+NULL token_len=10 buf_size=1 -> token="" rc=0
+"" token_len=10 buf_size=1 -> token="" rc=0
+"foo=bar" token_len=0 buf_size=1 -> token="" rc=0
+"foo=bar" token_len=3 buf_size=0 -> token="unchanged" rc=3
+"foo" token_len=10 buf_size=0 -> token="unchanged" rc=3
+"foo" token_len=3 buf_size=0 -> token="unchanged" rc=3
+NULL token_len=10 buf_size=0 -> token="unchanged" rc=0
+"" token_len=10 buf_size=0 -> token="unchanged" rc=0
+"foo=bar" token_len=0 buf_size=0 -> token="unchanged" rc=0
+
+osmo_strnchr_test()
+osmo_strnchr("foo=bar", 8, '=') -> 3
+osmo_strnchr("foo=bar", 4, '=') -> 3
+osmo_strnchr("foo=bar", 3, '=') -> -1
+osmo_strnchr("foo=bar", 0, '=') -> -1
+osmo_strnchr("foo", 9, '=') -> -1
+osmo_strnchr("foo", 9, '\0') -> 3
+--- test_float_str_to_int
+osmo_float_str_to_int("0", 0) -> rc=0 val=0
+osmo_float_str_to_int("1", 0) -> rc=0 val=1
+osmo_float_str_to_int("12.345", 0) -> rc=0 val=12
+osmo_float_str_to_int("+12.345", 0) -> rc=0 val=12
+osmo_float_str_to_int("-12.345", 0) -> rc=0 val=-12
+osmo_float_str_to_int("0.345", 0) -> rc=0 val=0
+osmo_float_str_to_int(".345", 0) -> rc=0 val=0
+osmo_float_str_to_int("-0.345", 0) -> rc=0 val=0
+osmo_float_str_to_int("-.345", 0) -> rc=0 val=0
+osmo_float_str_to_int("12.", 0) -> rc=0 val=12
+osmo_float_str_to_int("-180", 0) -> rc=0 val=-180
+osmo_float_str_to_int("180", 0) -> rc=0 val=180
+osmo_float_str_to_int("360", 0) -> rc=0 val=360
+osmo_float_str_to_int("123.4567890123", 0) -> rc=0 val=123
+osmo_float_str_to_int("123.4567890123456789012345", 0) -> rc=0 val=123
+osmo_float_str_to_int("9223372036854775807", 0) -> rc=0 val=9223372036854775807
+osmo_float_str_to_int("-9223372036854775807", 0) -> rc=0 val=-9223372036854775807
+osmo_float_str_to_int("-9223372036854775808", 0) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("9223372036854775808", 0) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-9223372036854775809", 0) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("100000000000000000000", 0) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-100000000000000000000", 0) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("999999999999999999999999999.99", 0) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-999999999999999999999999999.99", 0) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("1.2.3", 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("foo", 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.-345", 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("-12.-345", 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.+345", 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("+12.+345", 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("", 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int(NULL, 0) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("0", 1) -> rc=0 val=0
+osmo_float_str_to_int("1", 1) -> rc=0 val=10
+osmo_float_str_to_int("12.345", 1) -> rc=0 val=123
+osmo_float_str_to_int("+12.345", 1) -> rc=0 val=123
+osmo_float_str_to_int("-12.345", 1) -> rc=0 val=-123
+osmo_float_str_to_int("0.345", 1) -> rc=0 val=3
+osmo_float_str_to_int(".345", 1) -> rc=0 val=3
+osmo_float_str_to_int("-0.345", 1) -> rc=0 val=-3
+osmo_float_str_to_int("-.345", 1) -> rc=0 val=-3
+osmo_float_str_to_int("12.", 1) -> rc=0 val=120
+osmo_float_str_to_int("-180", 1) -> rc=0 val=-1800
+osmo_float_str_to_int("180", 1) -> rc=0 val=1800
+osmo_float_str_to_int("360", 1) -> rc=0 val=3600
+osmo_float_str_to_int("123.4567890123", 1) -> rc=0 val=1234
+osmo_float_str_to_int("123.4567890123456789012345", 1) -> rc=0 val=1234
+osmo_float_str_to_int("922337203685477580.7", 1) -> rc=0 val=9223372036854775807
+osmo_float_str_to_int("-922337203685477580.7", 1) -> rc=0 val=-9223372036854775807
+osmo_float_str_to_int("-922337203685477580.8", 1) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("922337203685477580.8", 1) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-922337203685477580.9", 1) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("100000000000000000000", 1) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-100000000000000000000", 1) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("999999999999999999999999999.99", 1) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-999999999999999999999999999.99", 1) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("1.2.3", 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("foo", 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.-345", 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("-12.-345", 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.+345", 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("+12.+345", 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("", 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int(NULL, 1) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("0", 6) -> rc=0 val=0
+osmo_float_str_to_int("1", 6) -> rc=0 val=1000000
+osmo_float_str_to_int("12.345", 6) -> rc=0 val=12345000
+osmo_float_str_to_int("+12.345", 6) -> rc=0 val=12345000
+osmo_float_str_to_int("-12.345", 6) -> rc=0 val=-12345000
+osmo_float_str_to_int("0.345", 6) -> rc=0 val=345000
+osmo_float_str_to_int(".345", 6) -> rc=0 val=345000
+osmo_float_str_to_int("-0.345", 6) -> rc=0 val=-345000
+osmo_float_str_to_int("-.345", 6) -> rc=0 val=-345000
+osmo_float_str_to_int("12.", 6) -> rc=0 val=12000000
+osmo_float_str_to_int("-180", 6) -> rc=0 val=-180000000
+osmo_float_str_to_int("180", 6) -> rc=0 val=180000000
+osmo_float_str_to_int("360", 6) -> rc=0 val=360000000
+osmo_float_str_to_int("123.4567890123", 6) -> rc=0 val=123456789
+osmo_float_str_to_int("123.4567890123456789012345", 6) -> rc=0 val=123456789
+osmo_float_str_to_int("9223372036854.775807", 6) -> rc=0 val=9223372036854775807
+osmo_float_str_to_int("-9223372036854.775807", 6) -> rc=0 val=-9223372036854775807
+osmo_float_str_to_int("-9223372036854.775808", 6) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("9223372036854.775808", 6) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-9223372036854.775809", 6) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("100000000000000000000", 6) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-100000000000000000000", 6) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("999999999999999999999999999.99", 6) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-999999999999999999999999999.99", 6) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("1.2.3", 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("foo", 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.-345", 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("-12.-345", 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.+345", 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("+12.+345", 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("", 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int(NULL, 6) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("0", 18) -> rc=0 val=0
+osmo_float_str_to_int("1", 18) -> rc=0 val=1000000000000000000
+osmo_float_str_to_int("1.2345", 18) -> rc=0 val=1234500000000000000
+osmo_float_str_to_int("+1.2345", 18) -> rc=0 val=1234500000000000000
+osmo_float_str_to_int("-1.2345", 18) -> rc=0 val=-1234500000000000000
+osmo_float_str_to_int("0.345", 18) -> rc=0 val=345000000000000000
+osmo_float_str_to_int(".345", 18) -> rc=0 val=345000000000000000
+osmo_float_str_to_int("-0.345", 18) -> rc=0 val=-345000000000000000
+osmo_float_str_to_int("-.345", 18) -> rc=0 val=-345000000000000000
+osmo_float_str_to_int("2.", 18) -> rc=0 val=2000000000000000000
+osmo_float_str_to_int("-8", 18) -> rc=0 val=-8000000000000000000
+osmo_float_str_to_int("1.234567890123", 18) -> rc=0 val=1234567890123000000
+osmo_float_str_to_int("1.234567890123456789012345", 18) -> rc=0 val=1234567890123456789
+osmo_float_str_to_int("123.4567890123", 18) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("9.223372036854775807", 18) -> rc=0 val=9223372036854775807
+osmo_float_str_to_int("-9.223372036854775807", 18) -> rc=0 val=-9223372036854775807
+osmo_float_str_to_int("-9.223372036854775808", 18) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("9.223372036854775808", 18) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-9.223372036854775809", 18) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("100000000000000000000", 18) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-100000000000000000000", 18) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("999999999999999999999999999.99", 18) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-999999999999999999999999999.99", 18) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("1.2.3", 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("foo", 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.-345", 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("-12.-345", 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.+345", 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("+12.+345", 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("", 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int(NULL, 18) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("0", 19) -> rc=0 val=0
+osmo_float_str_to_int(".1", 19) -> rc=0 val=1000000000000000000
+osmo_float_str_to_int(".12345", 19) -> rc=0 val=1234500000000000000
+osmo_float_str_to_int("+.12345", 19) -> rc=0 val=1234500000000000000
+osmo_float_str_to_int("-.12345", 19) -> rc=0 val=-1234500000000000000
+osmo_float_str_to_int("0.0345", 19) -> rc=0 val=345000000000000000
+osmo_float_str_to_int(".0345", 19) -> rc=0 val=345000000000000000
+osmo_float_str_to_int("-0.0345", 19) -> rc=0 val=-345000000000000000
+osmo_float_str_to_int("-.0345", 19) -> rc=0 val=-345000000000000000
+osmo_float_str_to_int(".2", 19) -> rc=0 val=2000000000000000000
+osmo_float_str_to_int("-.8", 19) -> rc=0 val=-8000000000000000000
+osmo_float_str_to_int(".1234567890123", 19) -> rc=0 val=1234567890123000000
+osmo_float_str_to_int(".1234567890123456789012345", 19) -> rc=0 val=1234567890123456789
+osmo_float_str_to_int("123.4567890123", 19) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int(".9223372036854775807", 19) -> rc=0 val=9223372036854775807
+osmo_float_str_to_int("-.9223372036854775807", 19) -> rc=0 val=-9223372036854775807
+osmo_float_str_to_int("-.9223372036854775808", 19) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int(".9223372036854775808", 19) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-.9223372036854775809", 19) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("100000000000000000000", 19) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-100000000000000000000", 19) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("999999999999999999999999999.99", 19) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-999999999999999999999999999.99", 19) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("1.2.3", 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("foo", 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.-345", 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("-12.-345", 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.+345", 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("+12.+345", 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("", 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int(NULL, 19) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("0", 20) -> rc=0 val=0
+osmo_float_str_to_int(".01", 20) -> rc=0 val=1000000000000000000
+osmo_float_str_to_int(".012345", 20) -> rc=0 val=1234500000000000000
+osmo_float_str_to_int("+.012345", 20) -> rc=0 val=1234500000000000000
+osmo_float_str_to_int("-.012345", 20) -> rc=0 val=-1234500000000000000
+osmo_float_str_to_int("0.00345", 20) -> rc=0 val=345000000000000000
+osmo_float_str_to_int(".00345", 20) -> rc=0 val=345000000000000000
+osmo_float_str_to_int("-0.00345", 20) -> rc=0 val=-345000000000000000
+osmo_float_str_to_int("-.00345", 20) -> rc=0 val=-345000000000000000
+osmo_float_str_to_int(".02", 20) -> rc=0 val=2000000000000000000
+osmo_float_str_to_int("-.08", 20) -> rc=0 val=-8000000000000000000
+osmo_float_str_to_int(".01234567890123", 20) -> rc=0 val=1234567890123000000
+osmo_float_str_to_int(".01234567890123456789012345", 20) -> rc=0 val=1234567890123456789
+osmo_float_str_to_int("12.34567890123", 20) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int(".09223372036854775807", 20) -> rc=0 val=9223372036854775807
+osmo_float_str_to_int("-.09223372036854775807", 20) -> rc=0 val=-9223372036854775807
+osmo_float_str_to_int("-.09223372036854775808", 20) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int(".09223372036854775808", 20) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-.09223372036854775809", 20) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int(".1", 20) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-.1", 20) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("999999999999999999999999999.99", 20) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-999999999999999999999999999.99", 20) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("1.2.3", 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("foo", 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.-345", 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("-12.-345", 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.+345", 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("+12.+345", 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("", 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int(NULL, 20) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("0", 25) -> rc=0 val=0
+osmo_float_str_to_int(".0000001", 25) -> rc=0 val=1000000000000000000
+osmo_float_str_to_int(".00000012345", 25) -> rc=0 val=1234500000000000000
+osmo_float_str_to_int("+.00000012345", 25) -> rc=0 val=1234500000000000000
+osmo_float_str_to_int("-.00000012345", 25) -> rc=0 val=-1234500000000000000
+osmo_float_str_to_int("0.0000000345", 25) -> rc=0 val=345000000000000000
+osmo_float_str_to_int(".0000000345", 25) -> rc=0 val=345000000000000000
+osmo_float_str_to_int("-0.0000000345", 25) -> rc=0 val=-345000000000000000
+osmo_float_str_to_int("-.0000000345", 25) -> rc=0 val=-345000000000000000
+osmo_float_str_to_int(".0000002", 25) -> rc=0 val=2000000000000000000
+osmo_float_str_to_int("-.0000008", 25) -> rc=0 val=-8000000000000000000
+osmo_float_str_to_int(".0000001234567890123", 25) -> rc=0 val=1234567890123000000
+osmo_float_str_to_int(".0000001234567890123456789012345", 25) -> rc=0 val=1234567890123456789
+osmo_float_str_to_int(".0001234567890123", 25) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int(".0000009223372036854775807", 25) -> rc=0 val=9223372036854775807
+osmo_float_str_to_int("-.0000009223372036854775807", 25) -> rc=0 val=-9223372036854775807
+osmo_float_str_to_int("-.0000009223372036854775808", 25) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int(".0000009223372036854775808", 25) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-.0000009223372036854775809", 25) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int(".000001", 25) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-.000001", 25) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("999999999999999999999999999.99", 25) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("-999999999999999999999999999.99", 25) -> rc=-34=-ERANGE val=0
+osmo_float_str_to_int("1.2.3", 25) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("foo", 25) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 25) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("1.foo", 25) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.-345", 25) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("-12.-345", 25) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("12.+345", 25) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("+12.+345", 25) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int("", 25) -> rc=-22=-EINVAL val=0
+osmo_float_str_to_int(NULL, 25) -> rc=-22=-EINVAL val=0
+--- test_int_to_float_str
+osmo_int_to_float_str_buf(0, 0) -> rc=1 str="0"
+osmo_int_to_float_str_buf(1, 0) -> rc=1 str="1"
+osmo_int_to_float_str_buf(1000000, 0) -> rc=7 str="1000000"
+osmo_int_to_float_str_buf(-1000000, 0) -> rc=8 str="-1000000"
+osmo_int_to_float_str_buf(1000001, 0) -> rc=7 str="1000001"
+osmo_int_to_float_str_buf(-1000001, 0) -> rc=8 str="-1000001"
+osmo_int_to_float_str_buf(1000100, 0) -> rc=7 str="1000100"
+osmo_int_to_float_str_buf(-1010000, 0) -> rc=8 str="-1010000"
+osmo_int_to_float_str_buf(1100000, 0) -> rc=7 str="1100000"
+osmo_int_to_float_str_buf(10000000, 0) -> rc=8 str="10000000"
+osmo_int_to_float_str_buf(-10000000, 0) -> rc=9 str="-10000000"
+osmo_int_to_float_str_buf(100000000, 0) -> rc=9 str="100000000"
+osmo_int_to_float_str_buf(-100000000, 0) -> rc=10 str="-100000000"
+osmo_int_to_float_str_buf(9223372036854775807, 0) -> rc=19 str="9223372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775807, 0) -> rc=20 str="-9223372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775808, 0) -> rc=4 str="-ERR"
+osmo_int_to_float_str_buf(0, 1) -> rc=1 str="0"
+osmo_int_to_float_str_buf(1, 1) -> rc=3 str="0.1"
+osmo_int_to_float_str_buf(1000000, 1) -> rc=6 str="100000"
+osmo_int_to_float_str_buf(-1000000, 1) -> rc=7 str="-100000"
+osmo_int_to_float_str_buf(1000001, 1) -> rc=8 str="100000.1"
+osmo_int_to_float_str_buf(-1000001, 1) -> rc=9 str="-100000.1"
+osmo_int_to_float_str_buf(1000100, 1) -> rc=6 str="100010"
+osmo_int_to_float_str_buf(-1010000, 1) -> rc=7 str="-101000"
+osmo_int_to_float_str_buf(1100000, 1) -> rc=6 str="110000"
+osmo_int_to_float_str_buf(10000000, 1) -> rc=7 str="1000000"
+osmo_int_to_float_str_buf(-10000000, 1) -> rc=8 str="-1000000"
+osmo_int_to_float_str_buf(100000000, 1) -> rc=8 str="10000000"
+osmo_int_to_float_str_buf(-100000000, 1) -> rc=9 str="-10000000"
+osmo_int_to_float_str_buf(9223372036854775807, 1) -> rc=20 str="922337203685477580.7"
+osmo_int_to_float_str_buf(-9223372036854775807, 1) -> rc=21 str="-922337203685477580.7"
+osmo_int_to_float_str_buf(-9223372036854775808, 1) -> rc=4 str="-ERR"
+osmo_int_to_float_str_buf(0, 3) -> rc=1 str="0"
+osmo_int_to_float_str_buf(1, 3) -> rc=5 str="0.001"
+osmo_int_to_float_str_buf(1000000, 3) -> rc=4 str="1000"
+osmo_int_to_float_str_buf(-1000000, 3) -> rc=5 str="-1000"
+osmo_int_to_float_str_buf(1000001, 3) -> rc=8 str="1000.001"
+osmo_int_to_float_str_buf(-1000001, 3) -> rc=9 str="-1000.001"
+osmo_int_to_float_str_buf(1000100, 3) -> rc=6 str="1000.1"
+osmo_int_to_float_str_buf(-1010000, 3) -> rc=5 str="-1010"
+osmo_int_to_float_str_buf(1100000, 3) -> rc=4 str="1100"
+osmo_int_to_float_str_buf(10000000, 3) -> rc=5 str="10000"
+osmo_int_to_float_str_buf(-10000000, 3) -> rc=6 str="-10000"
+osmo_int_to_float_str_buf(100000000, 3) -> rc=6 str="100000"
+osmo_int_to_float_str_buf(-100000000, 3) -> rc=7 str="-100000"
+osmo_int_to_float_str_buf(9223372036854775807, 3) -> rc=20 str="9223372036854775.807"
+osmo_int_to_float_str_buf(-9223372036854775807, 3) -> rc=21 str="-9223372036854775.807"
+osmo_int_to_float_str_buf(-9223372036854775808, 3) -> rc=4 str="-ERR"
+osmo_int_to_float_str_buf(0, 6) -> rc=1 str="0"
+osmo_int_to_float_str_buf(1, 6) -> rc=8 str="0.000001"
+osmo_int_to_float_str_buf(1000000, 6) -> rc=1 str="1"
+osmo_int_to_float_str_buf(-1000000, 6) -> rc=2 str="-1"
+osmo_int_to_float_str_buf(1000001, 6) -> rc=8 str="1.000001"
+osmo_int_to_float_str_buf(-1000001, 6) -> rc=9 str="-1.000001"
+osmo_int_to_float_str_buf(1000100, 6) -> rc=6 str="1.0001"
+osmo_int_to_float_str_buf(-1010000, 6) -> rc=5 str="-1.01"
+osmo_int_to_float_str_buf(1100000, 6) -> rc=3 str="1.1"
+osmo_int_to_float_str_buf(10000000, 6) -> rc=2 str="10"
+osmo_int_to_float_str_buf(-10000000, 6) -> rc=3 str="-10"
+osmo_int_to_float_str_buf(100000000, 6) -> rc=3 str="100"
+osmo_int_to_float_str_buf(-100000000, 6) -> rc=4 str="-100"
+osmo_int_to_float_str_buf(9223372036854775807, 6) -> rc=20 str="9223372036854.775807"
+osmo_int_to_float_str_buf(-9223372036854775807, 6) -> rc=21 str="-9223372036854.775807"
+osmo_int_to_float_str_buf(-9223372036854775808, 6) -> rc=4 str="-ERR"
+osmo_int_to_float_str_buf(0, 17) -> rc=1 str="0"
+osmo_int_to_float_str_buf(1, 17) -> rc=19 str="0.00000000000000001"
+osmo_int_to_float_str_buf(1000000, 17) -> rc=13 str="0.00000000001"
+osmo_int_to_float_str_buf(-1000000, 17) -> rc=14 str="-0.00000000001"
+osmo_int_to_float_str_buf(1000001, 17) -> rc=19 str="0.00000000001000001"
+osmo_int_to_float_str_buf(-1000001, 17) -> rc=20 str="-0.00000000001000001"
+osmo_int_to_float_str_buf(1000100, 17) -> rc=17 str="0.000000000010001"
+osmo_int_to_float_str_buf(-1010000, 17) -> rc=16 str="-0.0000000000101"
+osmo_int_to_float_str_buf(1100000, 17) -> rc=14 str="0.000000000011"
+osmo_int_to_float_str_buf(10000000, 17) -> rc=12 str="0.0000000001"
+osmo_int_to_float_str_buf(-10000000, 17) -> rc=13 str="-0.0000000001"
+osmo_int_to_float_str_buf(100000000, 17) -> rc=11 str="0.000000001"
+osmo_int_to_float_str_buf(-100000000, 17) -> rc=12 str="-0.000000001"
+osmo_int_to_float_str_buf(9223372036854775807, 17) -> rc=20 str="92.23372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775807, 17) -> rc=21 str="-92.23372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775808, 17) -> rc=4 str="-ERR"
+osmo_int_to_float_str_buf(0, 18) -> rc=1 str="0"
+osmo_int_to_float_str_buf(1, 18) -> rc=20 str="0.000000000000000001"
+osmo_int_to_float_str_buf(1000000, 18) -> rc=14 str="0.000000000001"
+osmo_int_to_float_str_buf(-1000000, 18) -> rc=15 str="-0.000000000001"
+osmo_int_to_float_str_buf(1000001, 18) -> rc=20 str="0.000000000001000001"
+osmo_int_to_float_str_buf(-1000001, 18) -> rc=21 str="-0.000000000001000001"
+osmo_int_to_float_str_buf(1000100, 18) -> rc=18 str="0.0000000000010001"
+osmo_int_to_float_str_buf(-1010000, 18) -> rc=17 str="-0.00000000000101"
+osmo_int_to_float_str_buf(1100000, 18) -> rc=15 str="0.0000000000011"
+osmo_int_to_float_str_buf(10000000, 18) -> rc=13 str="0.00000000001"
+osmo_int_to_float_str_buf(-10000000, 18) -> rc=14 str="-0.00000000001"
+osmo_int_to_float_str_buf(100000000, 18) -> rc=12 str="0.0000000001"
+osmo_int_to_float_str_buf(-100000000, 18) -> rc=13 str="-0.0000000001"
+osmo_int_to_float_str_buf(9223372036854775807, 18) -> rc=20 str="9.223372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775807, 18) -> rc=21 str="-9.223372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775808, 18) -> rc=4 str="-ERR"
+osmo_int_to_float_str_buf(0, 19) -> rc=1 str="0"
+osmo_int_to_float_str_buf(1, 19) -> rc=21 str="0.0000000000000000001"
+osmo_int_to_float_str_buf(1000000, 19) -> rc=15 str="0.0000000000001"
+osmo_int_to_float_str_buf(-1000000, 19) -> rc=16 str="-0.0000000000001"
+osmo_int_to_float_str_buf(1000001, 19) -> rc=21 str="0.0000000000001000001"
+osmo_int_to_float_str_buf(-1000001, 19) -> rc=22 str="-0.0000000000001000001"
+osmo_int_to_float_str_buf(1000100, 19) -> rc=19 str="0.00000000000010001"
+osmo_int_to_float_str_buf(-1010000, 19) -> rc=18 str="-0.000000000000101"
+osmo_int_to_float_str_buf(1100000, 19) -> rc=16 str="0.00000000000011"
+osmo_int_to_float_str_buf(10000000, 19) -> rc=14 str="0.000000000001"
+osmo_int_to_float_str_buf(-10000000, 19) -> rc=15 str="-0.000000000001"
+osmo_int_to_float_str_buf(100000000, 19) -> rc=13 str="0.00000000001"
+osmo_int_to_float_str_buf(-100000000, 19) -> rc=14 str="-0.00000000001"
+osmo_int_to_float_str_buf(9223372036854775807, 19) -> rc=21 str="0.9223372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775807, 19) -> rc=22 str="-0.9223372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775808, 19) -> rc=4 str="-ERR"
+osmo_int_to_float_str_buf(0, 23) -> rc=1 str="0"
+osmo_int_to_float_str_buf(1, 23) -> rc=25 str="0.00000000000000000000001"
+osmo_int_to_float_str_buf(1000000, 23) -> rc=19 str="0.00000000000000001"
+osmo_int_to_float_str_buf(-1000000, 23) -> rc=20 str="-0.00000000000000001"
+osmo_int_to_float_str_buf(1000001, 23) -> rc=25 str="0.00000000000000001000001"
+osmo_int_to_float_str_buf(-1000001, 23) -> rc=26 str="-0.00000000000000001000001"
+osmo_int_to_float_str_buf(1000100, 23) -> rc=23 str="0.000000000000000010001"
+osmo_int_to_float_str_buf(-1010000, 23) -> rc=22 str="-0.0000000000000000101"
+osmo_int_to_float_str_buf(1100000, 23) -> rc=20 str="0.000000000000000011"
+osmo_int_to_float_str_buf(10000000, 23) -> rc=18 str="0.0000000000000001"
+osmo_int_to_float_str_buf(-10000000, 23) -> rc=19 str="-0.0000000000000001"
+osmo_int_to_float_str_buf(100000000, 23) -> rc=17 str="0.000000000000001"
+osmo_int_to_float_str_buf(-100000000, 23) -> rc=18 str="-0.000000000000001"
+osmo_int_to_float_str_buf(9223372036854775807, 23) -> rc=25 str="0.00009223372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775807, 23) -> rc=26 str="-0.00009223372036854775807"
+osmo_int_to_float_str_buf(-9223372036854775808, 23) -> rc=4 str="-ERR"
+--- test_str_to_int
+osmo_str_to_int(NULL, 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int(" ", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("-", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("--", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("+", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("++", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("0", 10, -1000, 1000) -> rc=0 val=0
+osmo_str_to_int("1", 10, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("+1", 10, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("-1", 10, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int("1000", 10, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+1000", 10, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("-1000", 10, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("-1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("0", 16, -1000, 1000) -> rc=0 val=0
+osmo_str_to_int("1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("0x1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("+1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("-1", 16, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int("+0x1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int("-0x1", 16, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int("3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("0x3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("0x3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+0x3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("+0x3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int("-3e8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("-3E8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("-0x3e8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("-0x3E8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int("3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("+0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int("-3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("-3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("-0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("-0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int("garble", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("-garble", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int("0x123", 10, -1000, 1000) -> rc=-7=-E2BIG val=0
+osmo_str_to_int("123potatoes", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int("123 potatoes", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int("123 ", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int("123.4", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+--- test_str_to_int64
+osmo_str_to_int64(NULL, 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64(" ", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("-", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("--", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("+", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("++", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("0", 10, -1000, 1000) -> rc=0 val=0
+osmo_str_to_int64("1", 10, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("+1", 10, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("-1", 10, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int64("1000", 10, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+1000", 10, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("-1000", 10, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("-1001", 10, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("0", 16, -1000, 1000) -> rc=0 val=0
+osmo_str_to_int64("1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("0x1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("+1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("-1", 16, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int64("+0x1", 16, -1000, 1000) -> rc=0 val=1
+osmo_str_to_int64("-0x1", 16, -1000, 1000) -> rc=0 val=-1
+osmo_str_to_int64("3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("0x3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("0x3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+0x3e8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("+0x3E8", 16, -1000, 1000) -> rc=0 val=1000
+osmo_str_to_int64("-3e8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("-3E8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("-0x3e8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("-0x3E8", 16, -1000, 1000) -> rc=0 val=-1000
+osmo_str_to_int64("3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("+0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=1001
+osmo_str_to_int64("-3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("-3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("-0x3e9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("-0x3E9", 16, -1000, 1000) -> rc=-34=-ERANGE val=-1001
+osmo_str_to_int64("garble", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("-garble", 10, -1000, 1000) -> rc=-22=-EINVAL val=0
+osmo_str_to_int64("0x123", 10, -1000, 1000) -> rc=-7=-E2BIG val=0
+osmo_str_to_int64("123potatoes", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int64("123 potatoes", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int64("123 ", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int64("123.4", 10, -1000, 1000) -> rc=-7=-E2BIG val=123
+osmo_str_to_int64("-9223372036854775808", 10, -9223372036854775808, 9223372036854775807) -> rc=0 val=-9223372036854775808
+osmo_str_to_int64("9223372036854775807", 10, -9223372036854775808, 9223372036854775807) -> rc=0 val=9223372036854775807
+osmo_str_to_int64("-9223372036854775809", 10, -9223372036854775808, 9223372036854775807) -> rc=-75=-EOVERFLOW val=-9223372036854775808
+osmo_str_to_int64("9223372036854775808", 10, -9223372036854775808, 9223372036854775807) -> rc=-75=-EOVERFLOW val=9223372036854775807
+osmo_str_to_int64("-9223372036854775808", 10, -1000, 1000) -> rc=-34=-ERANGE val=-9223372036854775808
+osmo_str_to_int64("9223372036854775807", 10, -1000, 1000) -> rc=-34=-ERANGE val=9223372036854775807
+osmo_str_to_int64("-9223372036854775809", 10, -1000, 1000) -> rc=-75=-EOVERFLOW val=-9223372036854775808
+osmo_str_to_int64("9223372036854775808", 10, -1000, 1000) -> rc=-75=-EOVERFLOW val=9223372036854775807
diff --git a/tests/v110/frame_test.c b/tests/v110/frame_test.c
new file mode 100644
index 00000000..ebc617c0
--- /dev/null
+++ b/tests/v110/frame_test.c
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+
+
+static void test_frame_enc(void)
+{
+ struct osmo_v110_decoded_frame fr;
+ ubit_t bits[80];
+ unsigned int i;
+
+ memset(&fr, 0, sizeof(fr));
+
+ /* we abuse the fact that ubit_t is 8bit so we can actually
+ * store integer values to clearly identify which bit ends up where */
+
+ /* D1..D48: 101..148 */
+ for (i = 0; i < ARRAY_SIZE(fr.d_bits); i++)
+ fr.d_bits[i] = 101 + i;
+ /* E1..E7: 201..207 */
+ for (i = 0; i < ARRAY_SIZE(fr.e_bits); i++)
+ fr.e_bits[i] = 201 + i;
+ /* S1..S9: 211..219 */
+ for (i = 0; i < ARRAY_SIZE(fr.s_bits); i++)
+ fr.s_bits[i] = 211 + i;
+ /* X1..X2: 221..222 */
+ for (i = 0; i < ARRAY_SIZE(fr.x_bits); i++)
+ fr.x_bits[i] = 221 + i;
+
+ /* run encoder and dump to stdout */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_v110_encode_frame(bits, sizeof(bits), &fr);
+ osmo_v110_ubit_dump(stdout, bits, sizeof(bits));
+
+ /* run decoder on what we just encoded */
+ memset(&fr, 0, sizeof(fr));
+ osmo_v110_decode_frame(&fr, bits, sizeof(bits));
+
+ /* re-encode and dump again 'expout' will match it. */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_v110_encode_frame(bits, sizeof(bits), &fr);
+ osmo_v110_ubit_dump(stdout, bits, sizeof(bits));
+}
+
+
+int main(int argc, char **argv)
+{
+ test_frame_enc();
+}
+
diff --git a/tests/v110/frame_test.ok b/tests/v110/frame_test.ok
new file mode 100644
index 00000000..ecefaa87
--- /dev/null
+++ b/tests/v110/frame_test.ok
@@ -0,0 +1,20 @@
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 211
+1 107 108 109 110 111 112 221
+1 113 114 115 116 117 118 213
+1 119 120 121 122 123 124 214
+1 201 202 203 204 205 206 207
+1 125 126 127 128 129 130 216
+1 131 132 133 134 135 136 222
+1 137 138 139 140 141 142 218
+1 143 144 145 146 147 148 219
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 211
+1 107 108 109 110 111 112 221
+1 113 114 115 116 117 118 213
+1 119 120 121 122 123 124 214
+1 201 202 203 204 205 206 207
+1 125 126 127 128 129 130 216
+1 131 132 133 134 135 136 222
+1 137 138 139 140 141 142 218
+1 143 144 145 146 147 148 219
diff --git a/tests/v110/ra1_test.c b/tests/v110/ra1_test.c
new file mode 100644
index 00000000..48db553d
--- /dev/null
+++ b/tests/v110/ra1_test.c
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/isdn/v110.h>
+
+
+static void test_ra1(enum osmo_v100_sync_ra1_rate rate)
+{
+ int user_rate = osmo_v110_sync_ra1_get_user_data_rate(rate);
+ int user_data_chunk_bits;
+ struct osmo_v110_decoded_frame fr;
+ ubit_t user_bits[48];
+ ubit_t bits[80];
+ unsigned int i;
+ int rc;
+
+ printf("\n======= User data rate %u\n", user_rate);
+
+ user_data_chunk_bits = osmo_v110_sync_ra1_get_user_data_chunk_bitlen(rate);
+ OSMO_ASSERT(user_data_chunk_bits >= 0);
+
+ /* we abuse the fact that ubit_t is 8bit so we can actually
+ * store integer values to clearly identify which bit ends up where */
+ memset(user_bits, 0xFE, sizeof(user_bits));
+ for (i = 0; i < user_data_chunk_bits; i++)
+ user_bits[i] = 101 + i;
+
+ printf("user_bits: ");
+ for (i = 0; i < user_data_chunk_bits; i++)
+ printf("%03d ", user_bits[i]);
+ printf("\n");
+
+ /* generate the decoded v.110 frame */
+ memset(&fr, 0, sizeof(fr));
+ rc = osmo_v110_sync_ra1_user_to_ir(rate, &fr, user_bits, user_data_chunk_bits);
+ OSMO_ASSERT(rc == 0);
+
+ /* run encoder and dump to stdout */
+ memset(bits, 0xff, sizeof(bits));
+ osmo_v110_encode_frame(bits, sizeof(bits), &fr);
+ printf("dumping %u encoded bits in V.110 frame:\n", user_data_chunk_bits);
+ osmo_v110_ubit_dump(stdout, bits, sizeof(bits));
+
+ /* run decoder on what we just encoded */
+ memset(&fr, 0, sizeof(fr));
+ osmo_v110_decode_frame(&fr, bits, sizeof(bits));
+ printf("dumping re-decoded V.110 frame:\n");
+ printf("E-bits: %s\n", osmo_hexdump(fr.e_bits, sizeof(fr.e_bits)));
+ printf("S-bits: %s\n", osmo_hexdump(fr.s_bits, sizeof(fr.s_bits)));
+
+ /* re-encode and dump again 'expout' will match it. */
+ memset(user_bits, 0xff, sizeof(user_bits));
+ rc = osmo_v110_sync_ra1_ir_to_user(rate, user_bits, sizeof(user_bits), &fr);
+ if (rc != user_data_chunk_bits) {
+ fprintf(stderr, "ERROR: adapt_ir_to_user() returned %d, expected %u\n", rc,
+ user_data_chunk_bits);
+ exit(23);
+ }
+ fprintf(stdout, "re-decoded user bits: ");
+ for (i = 0; i < user_data_chunk_bits; i++)
+ printf("%03d ", user_bits[i]);
+ printf("\n");
+}
+
+
+int main(int argc, char **argv)
+{
+ for (int i = 0; i < _NUM_OSMO_V110_SYNC_RA1; i++)
+ test_ra1(i);
+}
+
diff --git a/tests/v110/ra1_test.ok b/tests/v110/ra1_test.ok
new file mode 100644
index 00000000..8a61fc3f
--- /dev/null
+++ b/tests/v110/ra1_test.ok
@@ -0,0 +1,216 @@
+
+======= User data rate 600
+user_bits: 101 102 103 104 105 106
+dumping 6 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 101 101 101 101 101 0
+1 101 101 102 102 102 102 0
+1 102 102 102 102 103 103 0
+1 103 103 103 103 103 103 0
+1 1 0 0 0 0 0 0
+1 104 104 104 104 104 104 0
+1 104 104 105 105 105 105 0
+1 105 105 105 105 106 106 0
+1 106 106 106 106 106 106 0
+dumping re-decoded V.110 frame:
+E-bits: 01 00 00 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106
+
+======= User data rate 1200
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112
+dumping 12 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 101 101 101 102 102 0
+1 102 102 103 103 103 103 0
+1 104 104 104 104 105 105 0
+1 105 105 106 106 106 106 0
+1 0 1 0 0 0 0 0
+1 107 107 107 107 108 108 0
+1 108 108 109 109 109 109 0
+1 110 110 110 110 111 111 0
+1 111 111 112 112 112 112 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 00 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112
+
+======= User data rate 2400
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
+dumping 24 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 101 102 102 103 103 0
+1 104 104 105 105 106 106 0
+1 107 107 108 108 109 109 0
+1 110 110 111 111 112 112 0
+1 1 1 0 0 0 0 0
+1 113 113 114 114 115 115 0
+1 116 116 117 117 118 118 0
+1 119 119 120 120 121 121 0
+1 122 122 123 123 124 124 0
+dumping re-decoded V.110 frame:
+E-bits: 01 01 00 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
+
+======= User data rate 4800
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+dumping 48 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 111 112 0
+1 113 114 115 116 117 118 0
+1 119 120 121 122 123 124 0
+1 0 1 1 0 0 0 0
+1 125 126 127 128 129 130 0
+1 131 132 133 134 135 136 0
+1 137 138 139 140 141 142 0
+1 143 144 145 146 147 148 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+
+======= User data rate 7200
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+dumping 36 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 116 117 118 0
+1 1 0 1 0 0 0 0
+1 119 120 121 122 123 124 0
+1 125 126 127 128 1 1 0
+1 129 130 1 1 131 132 0
+1 1 1 133 134 135 136 0
+dumping re-decoded V.110 frame:
+E-bits: 01 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+
+======= User data rate 9600
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+dumping 48 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 111 112 0
+1 113 114 115 116 117 118 0
+1 119 120 121 122 123 124 0
+1 0 1 1 0 0 0 0
+1 125 126 127 128 129 130 0
+1 131 132 133 134 135 136 0
+1 137 138 139 140 141 142 0
+1 143 144 145 146 147 148 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+
+======= User data rate 12000
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
+dumping 30 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 1 1 1 0
+1 0 0 1 0 0 0 0
+1 116 117 118 119 120 121 0
+1 122 123 124 125 1 1 0
+1 126 127 1 1 128 129 0
+1 1 1 130 1 1 1 0
+dumping re-decoded V.110 frame:
+E-bits: 00 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
+
+======= User data rate 14400
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+dumping 36 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 116 117 118 0
+1 1 0 1 0 0 0 0
+1 119 120 121 122 123 124 0
+1 125 126 127 128 1 1 0
+1 129 130 1 1 131 132 0
+1 1 1 133 134 135 136 0
+dumping re-decoded V.110 frame:
+E-bits: 01 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+
+======= User data rate 19200
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+dumping 48 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 111 112 0
+1 113 114 115 116 117 118 0
+1 119 120 121 122 123 124 0
+1 0 1 1 0 0 0 0
+1 125 126 127 128 129 130 0
+1 131 132 133 134 135 136 0
+1 137 138 139 140 141 142 0
+1 143 144 145 146 147 148 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+
+======= User data rate 24000
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
+dumping 30 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 1 1 1 0
+1 0 0 1 0 0 0 0
+1 116 117 118 119 120 121 0
+1 122 123 124 125 1 1 0
+1 126 127 1 1 128 129 0
+1 1 1 130 1 1 1 0
+dumping re-decoded V.110 frame:
+E-bits: 00 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
+
+======= User data rate 28800
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+dumping 36 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 1 1 0
+1 111 112 1 1 113 114 0
+1 1 1 115 116 117 118 0
+1 1 0 1 0 0 0 0
+1 119 120 121 122 123 124 0
+1 125 126 127 128 1 1 0
+1 129 130 1 1 131 132 0
+1 1 1 133 134 135 136 0
+dumping re-decoded V.110 frame:
+E-bits: 01 00 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
+
+======= User data rate 38400
+user_bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
+dumping 48 encoded bits in V.110 frame:
+0 0 0 0 0 0 0 0
+1 101 102 103 104 105 106 0
+1 107 108 109 110 111 112 0
+1 113 114 115 116 117 118 0
+1 119 120 121 122 123 124 0
+1 0 1 1 0 0 0 0
+1 125 126 127 128 129 130 0
+1 131 132 133 134 135 136 0
+1 137 138 139 140 141 142 0
+1 143 144 145 146 147 148 0
+dumping re-decoded V.110 frame:
+E-bits: 00 01 01 00 00 00 00
+S-bits: 00 00 00 00 00 00 00 00 00
+re-decoded user bits: 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
diff --git a/tests/v110/ta_test.c b/tests/v110/ta_test.c
new file mode 100644
index 00000000..833830ef
--- /dev/null
+++ b/tests/v110/ta_test.c
@@ -0,0 +1,459 @@
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+
+#include <osmocom/isdn/v110.h>
+#include <osmocom/isdn/v110_ta.h>
+
+static void *test_ctx = NULL;
+
+/* inverse logic: ON = binary 0; OFF = binary 1 */
+#define V110_SX_BIT_ON 0
+#define V110_SX_BIT_OFF 1
+
+/*********************************************************************************
+ * V.110 TA configuration and callbacks
+ *********************************************************************************/
+
+static void v110_ta_test_rx_cb(void *priv, const ubit_t *buf, size_t buf_size)
+{
+ fprintf(stderr, "%s(buf_size=%zu): %s\n",
+ __func__, buf_size, osmo_ubit_dump(buf, buf_size));
+}
+
+static void v110_ta_test_tx_cb(void *priv, ubit_t *buf, size_t buf_size)
+{
+ for (size_t i = 0; i < buf_size; i++)
+ buf[i] = (i & 1);
+ fprintf(stderr, "%s(buf_size=%zu): %s\n",
+ __func__, buf_size, osmo_ubit_dump(buf, buf_size));
+}
+
+static void v110_ta_test_status_update_cb(void *priv, unsigned int status)
+{
+ fprintf(stderr, "%s(status=0x%08x)\n", __func__, status);
+}
+
+static const struct osmo_v110_ta_cfg v110_ta_test_cfg = {
+ .rate = OSMO_V110_SYNC_RA1_9600,
+ .rx_cb = &v110_ta_test_rx_cb,
+ .tx_cb = &v110_ta_test_tx_cb,
+ .status_update_cb = &v110_ta_test_status_update_cb,
+};
+
+/*********************************************************************************
+ * various helper functions
+ *********************************************************************************/
+
+static void v110_ta_test_init_df(struct osmo_v110_decoded_frame *df)
+{
+ /* quickly set all the bits to binary '1' */
+ memset(df, 1, sizeof(*df));
+ /* D-bits: 0101... pattern */
+ for (unsigned int i = 0; i < MAX_D_BITS; i += 2)
+ df->d_bits[i] = 0;
+ /* E-bits: E1/E2/E3 indicate 9600 bps */
+ df->e_bits[0] = 0;
+}
+
+static void v110_ta_test_dump_df(const struct osmo_v110_decoded_frame *df)
+{
+ fprintf(stderr, " D-bits: %s\n", osmo_ubit_dump(&df->d_bits[0], MAX_D_BITS));
+ fprintf(stderr, " E-bits: %s\n", osmo_ubit_dump(&df->e_bits[0], MAX_E_BITS));
+ fprintf(stderr, " S-bits: %s\n", osmo_ubit_dump(&df->s_bits[0], MAX_S_BITS));
+ fprintf(stderr, " X-bits: %s\n", osmo_ubit_dump(&df->x_bits[0], MAX_X_BITS));
+}
+
+static void v110_ta_test_dump_circuit(const struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit,
+ bool exp_state)
+{
+ bool state = osmo_v110_ta_get_circuit(ta, circuit);
+
+ fprintf(stderr, "circuit %s (%s) is %s (expected to be %s)\n",
+ osmo_v110_ta_circuit_name(circuit),
+ osmo_v110_ta_circuit_desc(circuit),
+ state ? "ON" : "OFF",
+ exp_state ? "ON" : "OFF");
+}
+
+static void v110_ta_test_set_circuit(struct osmo_v110_ta *ta,
+ enum osmo_v110_ta_circuit circuit,
+ bool active)
+{
+ int rc;
+
+ fprintf(stderr, "setting circuit %s (%s) %s\n",
+ osmo_v110_ta_circuit_name(circuit),
+ osmo_v110_ta_circuit_desc(circuit),
+ active ? "ON" : "OFF");
+
+ rc = osmo_v110_ta_set_circuit(ta, circuit, active);
+ fprintf(stderr, "osmo_v110_ta_set_circuit() returns %d\n", rc);
+}
+
+/*********************************************************************************
+ * the actual tests
+ *********************************************************************************/
+
+static void test_idle_ready(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ fprintf(stderr, "Initial status: 0x%08x\n", osmo_v110_ta_get_status(ta));
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): all bits set to binary '1'\n");
+ memset(&df, 1, sizeof(df));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): expecting all bits set to binary '1'\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, false);
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
+
+ osmo_v110_ta_free(ta);
+}
+
+static void test_conn_ta_line(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
+
+ /* we expect the TA FSM to be in V110_TA_ST_CON_TA_TO_LINE */
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-/X-bits are expected to be 1 (OFF)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ /* TODO: test implicit sync by sending V110_TA_EV_RX_FRAME_IND */
+
+ fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates sync event\n");
+ osmo_v110_ta_sync_ind(ta);
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are OFF, expect no state change\n");
+ v110_ta_test_init_df(&df);
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change\n");
+ memset(&df.s_bits[0], V110_SX_BIT_ON, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ osmo_v110_ta_free(ta);
+}
+
+static void _test_data_transfer_enter(struct osmo_v110_ta *ta)
+{
+ struct osmo_v110_decoded_frame df;
+ int rc;
+
+ OSMO_ASSERT(osmo_v110_ta_get_circuit(ta, OSMO_V110_TA_C_108) == false);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, true);
+
+ /* we expect the TA FSM to be in V110_TA_ST_CON_TA_TO_LINE */
+
+ fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates sync event\n");
+ osmo_v110_ta_sync_ind(ta);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change\n");
+ v110_ta_test_init_df(&df);
+ memset(&df.s_bits[0], V110_SX_BIT_ON, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+}
+
+static void test_data_transfer(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ _test_data_transfer_enter(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, true);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, true);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, true);
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): E1..E3-bits are expected to be 011 (9600)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): we also expect the .tx_cb() to be called\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): feed that frame that we pulled out back into the TA\n");
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ osmo_v110_ta_free(ta);
+}
+
+static void test_data_transfer_disc_local(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ _test_data_transfer_enter(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ fprintf(stderr, "local TE initiates disconnection\n");
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, false);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DISCONNECTING */
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-bits are expected to be 1 (OFF)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-bits are all expected to be 0\n");
+ rc = osmo_v110_ta_frame_out(ta, &df); /* TODO: what E-bits do we expect? */
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, true);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, true);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-/X-bits are ON, expect no state change\n");
+ v110_ta_test_init_df(&df);
+ memset(&df.s_bits[0], V110_SX_BIT_ON, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-bits are OFF, expect state change\n");
+ v110_ta_test_init_df(&df);
+ memset(&df.s_bits[0], V110_SX_BIT_OFF, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
+
+ osmo_v110_ta_free(ta);
+}
+
+static void test_data_transfer_disc_remote(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ _test_data_transfer_enter(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ fprintf(stderr, "remote TE initiates disconnection\n");
+ fprintf(stderr, "osmo_v110_ta_frame_in(): S-bits are OFF, X-bits are ON\n");
+ fprintf(stderr, "osmo_v110_ta_frame_in(): D-bits are all set to 0\n");
+ v110_ta_test_init_df(&df);
+ memset(&df.s_bits[0], V110_SX_BIT_OFF, sizeof(df.s_bits));
+ memset(&df.x_bits[0], V110_SX_BIT_ON, sizeof(df.x_bits));
+ memset(&df.d_bits[0], 0, sizeof(df.d_bits));
+ v110_ta_test_dump_df(&df);
+ rc = osmo_v110_ta_frame_in(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_in() returns %d\n", rc);
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
+
+ fprintf(stderr, "local TE confirms disconnection\n");
+ v110_ta_test_set_circuit(ta, OSMO_V110_TA_C_108, false);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DISCONNECTING */
+
+ osmo_v110_ta_desync_ind(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_106, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_107, false);
+ v110_ta_test_dump_circuit(ta, OSMO_V110_TA_C_109, false);
+
+ osmo_v110_ta_free(ta);
+}
+
+static void test_syncing(void)
+{
+ struct osmo_v110_decoded_frame df = { 0 };
+ struct osmo_v110_ta *ta;
+ int rc;
+
+ fprintf(stderr, "\n==== Running %s()\n", __func__);
+
+ ta = osmo_v110_ta_alloc(test_ctx, __func__, &v110_ta_test_cfg);
+ OSMO_ASSERT(ta != NULL);
+
+ /* we expect the TA FSM to be in V110_TA_ST_IDLE_READY */
+
+ _test_data_transfer_enter(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates out-of-sync event\n");
+ osmo_v110_ta_desync_ind(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_RESYNCING */
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): X-bits are expected to be 1 (OFF)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ fprintf(stderr, "osmo_v110_ta_sync_ind(): the lower layer indicates sync event\n");
+ osmo_v110_ta_sync_ind(ta);
+
+ /* we expect the TA FSM to be in V110_TA_ST_DATA_TRANSFER */
+
+ fprintf(stderr, "osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)\n");
+ fprintf(stderr, "osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()\n");
+ rc = osmo_v110_ta_frame_out(ta, &df);
+ fprintf(stderr, "osmo_v110_ta_frame_out() returns %d\n", rc);
+ if (rc == 0)
+ v110_ta_test_dump_df(&df);
+
+ osmo_v110_ta_free(ta);
+}
+
+int main(int argc, char **argv)
+{
+ test_ctx = talloc_named_const(NULL, 0, __FILE__);
+
+ osmo_init_logging2(test_ctx, NULL);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_print_level(osmo_stderr_target, 1);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_use_color(osmo_stderr_target, 0);
+
+ osmo_fsm_log_addr(false);
+ osmo_fsm_log_timeouts(true);
+
+ log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG);
+
+ test_idle_ready();
+ test_conn_ta_line();
+ /* TODO: test_conn_ta_line_timeout() */
+ test_data_transfer();
+ test_data_transfer_disc_local();
+ test_data_transfer_disc_remote();
+ /* TODO: test_disc_timeout() */
+ test_syncing();
+ /* TODO: test_syncing_timeout() */
+
+ log_fini();
+ OSMO_ASSERT(talloc_total_blocks(test_ctx) == 1);
+ talloc_free(test_ctx);
+
+ return 0;
+}
diff --git a/tests/v110/ta_test.err b/tests/v110/ta_test.err
new file mode 100644
index 00000000..e8f80e62
--- /dev/null
+++ b/tests/v110/ta_test.err
@@ -0,0 +1,270 @@
+
+==== Running test_idle_ready()
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: State change to IDLE_READY (no timeout)
+Initial status: 0x00000000
+circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
+circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
+circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
+osmo_v110_ta_frame_in(): all bits set to binary '1'
+ D-bits: 111111111111111111111111111111111111111111111111
+ E-bits: 1111111
+ S-bits: 111111111
+ X-bits: 11
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event RX_FRAME_IND
+v110_ta_test_rx_cb(buf_size=48): 111111111111111111111111111111111111111111111111
+osmo_v110_ta_frame_in() returns 0
+osmo_v110_ta_frame_out(): expecting all bits set to binary '1'
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event TX_FRAME_RTS
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 111111111111111111111111111111111111111111111111
+ E-bits: 1111111
+ S-bits: 111111111
+ X-bits: 11
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+setting circuit 108/DTR (Data Terminal Ready) OFF
+DLGLOBAL DEBUG V110-TA(test_idle_ready){CONNECT_TA_TO_LINE}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_idle_ready){CONNECT_TA_TO_LINE}: State change to IDLE_READY (no timeout)
+osmo_v110_ta_set_circuit() returns 0
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_idle_ready){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+DLGLOBAL DEBUG V110-TA(test_idle_ready){CONNECT_TA_TO_LINE}: Deallocated
+
+==== Running test_conn_ta_line()
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_frame_out(): S-/X-bits are expected to be 1 (OFF)
+osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event TX_FRAME_RTS
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 111111111111111111111111111111111111111111111111
+ E-bits: 1111111
+ S-bits: 111111111
+ X-bits: 11
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): D-/E-bits are all expected to be 1
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event TX_FRAME_RTS
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 111111111111111111111111111111111111111111111111
+ E-bits: 1111111
+ S-bits: 000000000
+ X-bits: 00
+osmo_v110_ta_frame_in(): S-/X-bits are OFF, expect no state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 111111111
+ X-bits: 11
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_rx_cb(buf_size=48): 111111111111111111111111111111111111111111111111
+osmo_v110_ta_frame_in() returns 0
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+DLGLOBAL DEBUG V110-TA(test_conn_ta_line){DATA_TRANSFER}: Deallocated
+
+==== Running test_data_transfer()
+DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_data_transfer){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_data_transfer){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+circuit 106/CTS (Clear to Send) is ON (expected to be ON)
+circuit 107/DSR (Data Set Ready) is ON (expected to be ON)
+circuit 109/DCD (Data Carrier Detect) is ON (expected to be ON)
+osmo_v110_ta_frame_out(): S-/X-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): E1..E3-bits are expected to be 011 (9600)
+osmo_v110_ta_frame_out(): we also expect the .tx_cb() to be called
+DLGLOBAL DEBUG V110-TA(test_data_transfer){DATA_TRANSFER}: Received Event TX_FRAME_RTS
+v110_ta_test_tx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+osmo_v110_ta_frame_in(): feed that frame that we pulled out back into the TA
+DLGLOBAL DEBUG V110-TA(test_data_transfer){DATA_TRANSFER}: Received Event RX_FRAME_IND
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+DLGLOBAL DEBUG V110-TA(test_data_transfer){DATA_TRANSFER}: Deallocated
+
+==== Running test_data_transfer_disc_local()
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+local TE initiates disconnection
+setting circuit 108/DTR (Data Terminal Ready) OFF
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DATA_TRANSFER}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DATA_TRANSFER}: State change to DISCONNECTING (T2, 5s)
+v110_ta_test_status_update_cb(status=0x00000014)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_frame_out(): S-bits are expected to be 1 (OFF)
+osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): D-bits are all expected to be 0
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: Received Event TX_FRAME_RTS
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 000000000000000000000000000000000000000000000000
+ E-bits: 1111111
+ S-bits: 111111111
+ X-bits: 00
+circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
+circuit 107/DSR (Data Set Ready) is ON (expected to be ON)
+circuit 109/DCD (Data Carrier Detect) is ON (expected to be ON)
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect no state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: Received Event RX_FRAME_IND
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+osmo_v110_ta_frame_in(): S-bits are OFF, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 111111111
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: Received Event RX_FRAME_IND
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){DISCONNECTING}: State change to IDLE_READY (no timeout)
+v110_ta_test_status_update_cb(status=0x00000000)
+v110_ta_test_rx_cb(buf_size=48): 111111111111111111111111111111111111111111111111
+osmo_v110_ta_frame_in() returns 0
+circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
+circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
+circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_local){IDLE_READY}: Deallocated
+
+==== Running test_data_transfer_disc_remote()
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+remote TE initiates disconnection
+osmo_v110_ta_frame_in(): S-bits are OFF, X-bits are ON
+osmo_v110_ta_frame_in(): D-bits are all set to 0
+ D-bits: 000000000000000000000000000000000000000000000000
+ E-bits: 0111111
+ S-bits: 111111111
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DATA_TRANSFER}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000000a)
+osmo_v110_ta_frame_in() returns 0
+circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
+circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
+local TE confirms disconnection
+setting circuit 108/DTR (Data Terminal Ready) OFF
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DATA_TRANSFER}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DATA_TRANSFER}: State change to DISCONNECTING (T2, 5s)
+v110_ta_test_status_update_cb(status=0x00000000)
+osmo_v110_ta_set_circuit() returns 0
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DISCONNECTING}: Received Event DESYNC_IND
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){DISCONNECTING}: State change to IDLE_READY (no timeout)
+circuit 106/CTS (Clear to Send) is OFF (expected to be OFF)
+circuit 107/DSR (Data Set Ready) is OFF (expected to be OFF)
+circuit 109/DCD (Data Carrier Detect) is OFF (expected to be OFF)
+DLGLOBAL DEBUG V110-TA(test_data_transfer_disc_remote){IDLE_READY}: Deallocated
+
+==== Running test_syncing()
+DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: Allocated
+DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: State change to IDLE_READY (no timeout)
+setting circuit 108/DTR (Data Terminal Ready) ON
+DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: Received Event V24_STATUS_CHG
+DLGLOBAL DEBUG V110-TA(test_syncing){IDLE_READY}: State change to CONNECT_TA_TO_LINE (T1, 10s)
+osmo_v110_ta_set_circuit() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_syncing){CONNECT_TA_TO_LINE}: Received Event SYNC_IND
+osmo_v110_ta_frame_in(): S-/X-bits are ON, expect state change
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_syncing){CONNECT_TA_TO_LINE}: Received Event RX_FRAME_IND
+v110_ta_test_status_update_cb(status=0x0000001e)
+DLGLOBAL DEBUG V110-TA(test_syncing){CONNECT_TA_TO_LINE}: State change to DATA_TRANSFER (no timeout)
+v110_ta_test_rx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_in() returns 0
+osmo_v110_ta_sync_ind(): the lower layer indicates out-of-sync event
+DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: Received Event DESYNC_IND
+DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: State change to RESYNCING (X1, 3s)
+osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): X-bits are expected to be 1 (OFF)
+osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()
+DLGLOBAL DEBUG V110-TA(test_syncing){RESYNCING}: Received Event TX_FRAME_RTS
+v110_ta_test_tx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 11
+osmo_v110_ta_sync_ind(): the lower layer indicates sync event
+DLGLOBAL DEBUG V110-TA(test_syncing){RESYNCING}: Received Event SYNC_IND
+DLGLOBAL DEBUG V110-TA(test_syncing){RESYNCING}: State change to DATA_TRANSFER (no timeout)
+osmo_v110_ta_frame_out(): S-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): X-bits are expected to be 0 (ON)
+osmo_v110_ta_frame_out(): D-bits are to be set by .tx_cb()
+DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: Received Event TX_FRAME_RTS
+v110_ta_test_tx_cb(buf_size=48): 010101010101010101010101010101010101010101010101
+osmo_v110_ta_frame_out() returns 0
+ D-bits: 010101010101010101010101010101010101010101010101
+ E-bits: 0111111
+ S-bits: 000000000
+ X-bits: 00
+DLGLOBAL DEBUG V110-TA(test_syncing){DATA_TRANSFER}: Deallocated
diff --git a/tests/vty/ok_deprecated_logging.cfg b/tests/vty/ok_deprecated_logging.cfg
new file mode 100644
index 00000000..2699719e
--- /dev/null
+++ b/tests/vty/ok_deprecated_logging.cfg
@@ -0,0 +1,3 @@
+log stderr
+ logging filter all 1
+ logging level depr debug
diff --git a/tests/vty/vty_test.c b/tests/vty/vty_test.c
index 0d68a6c0..a3f8489f 100644
--- a/tests/vty/vty_test.c
+++ b/tests/vty/vty_test.c
@@ -13,15 +13,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
+#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -29,6 +26,7 @@
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging_internal.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/utils.h>
@@ -41,6 +39,7 @@
#include <osmocom/vty/stats.h>
static enum event last_vty_connection_event = -1;
+static const char *cfg_path = NULL;
void *ctx = NULL;
static void test_cmd_string_from_valstr(void)
@@ -294,9 +293,12 @@ static void test_stats_vty(void)
void test_exit_by_indent(const char *fname, int expect_rc)
{
+ char fpath[PATH_MAX];
int rc;
+
printf("reading file %s, expecting rc=%d\n", fname, expect_rc);
- rc = vty_read_config_file(fname, NULL);
+ snprintf(&fpath[0], sizeof(fpath), "%s/%s", cfg_path, fname);
+ rc = vty_read_config_file(fpath, NULL);
printf("got rc=%d\n", rc);
OSMO_ASSERT(rc == expect_rc);
}
@@ -437,11 +439,54 @@ DEFUN(cfg_ret_warning, cfg_ret_warning_cmd,
return CMD_WARNING;
}
-void test_vty_add_cmds()
+DEFUN(cfg_numeric_range, cfg_numeric_range_cmd,
+#if ULONG_MAX == 18446744073709551615UL
+ "numeric-range <0-18446744073709551615>",
+#else
+ "numeric-range <0-4294967295>",
+#endif
+ "testing numeric range\n"
+ "the numeric range\n")
+{
+ printf("Called: 'return-success'\n");
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_range_base10, cfg_range_base10_cmd,
+ "range-base10 <0-999999>",
+ "testing decimal range\n"
+ "the decimal range\n")
+{
+ printf("Called: 'return-success'\n");
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_range_base16, cfg_range_base16_cmd,
+ "range-base16 <0x0-0x8888>",
+ "testing hexadecimal range\n"
+ "the hexadecimal range\n")
+{
+ printf("Called: 'return-success'\n");
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_range_baseboth, cfg_range_baseboth_cmd,
+ "range-baseboth (<0-999999>|<0x0-0x8888>)",
+ "testing both ranges\n"
+ "the decimal range\n"
+ "the hexadecimal range\n")
+{
+ printf("Called: 'return-success'\n");
+ return CMD_SUCCESS;
+}
+
+void test_vty_add_cmds(void)
{
install_element(CONFIG_NODE, &cfg_ret_warning_cmd);
install_element(CONFIG_NODE, &cfg_ret_success_cmd);
+ logging_vty_add_deprecated_subsys(tall_log_ctx, "depr");
+
install_element(CONFIG_NODE, &cfg_level1_cmd);
install_node(&level1_node, NULL);
install_element(LEVEL1_NODE, &cfg_level1_child_cmd);
@@ -458,9 +503,15 @@ void test_vty_add_cmds()
install_element_ve(&cfg_ambiguous_nr_2_cmd);
install_element_ve(&cfg_ambiguous_str_1_cmd);
install_element_ve(&cfg_ambiguous_str_2_cmd);
+
+ install_element_ve(&cfg_numeric_range_cmd);
+
+ install_element_ve(&cfg_range_base10_cmd);
+ install_element_ve(&cfg_range_base16_cmd);
+ install_element_ve(&cfg_range_baseboth_cmd);
}
-void test_is_cmd_ambiguous()
+void test_is_cmd_ambiguous(void)
{
struct vty *vty;
struct vty_test test;
@@ -479,27 +530,88 @@ void test_is_cmd_ambiguous()
destroy_test_vty(&test, vty);
}
-static int go_parent_cb(struct vty *vty)
+void test_numeric_range(void)
{
- /*
- * - For the interactive VTY tests above, it is expected to bounce back to
- * the CONFIG_NODE. Hence do so in go_parent_cb().
- * - In the config file parsing tests, setting vty->node in go_parent_cb() has no
- * effect, because we will subsequently pop a parent node from the parent stack
- * and override to go to the node that was recorded as the actual parent.
- */
- vty->node = CONFIG_NODE;
- vty->index = NULL;
- return 0;
+ struct vty *vty;
+ struct vty_test test;
+
+ printf("Going to test test_numeric_range()\n");
+ vty = create_test_vty(&test);
+
+ OSMO_ASSERT(do_vty_command(vty, "numeric-range 0") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "numeric-range 40000") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "numeric-range -400000") == CMD_ERR_NO_MATCH);
+
+ destroy_test_vty(&test, vty);
}
+void test_ranges(void)
+{
+ struct vty *vty;
+ struct vty_test test;
+
+ printf("Going to test test_ranges()\n");
+ vty = create_test_vty(&test);
+
+ printf("test range-base10\n");
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 0") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 40000") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 -400000") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 0x0") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 0x343") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base10 -0x343") == CMD_ERR_NO_MATCH);
+
+ printf("test range-base16\n");
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 0") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 40000") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 -400000") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 0x0") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 0x343") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-base16 -0x343") == CMD_ERR_NO_MATCH);
+
+ printf("test range-baseboth\n");
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth 0") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth 40000") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth -400000") == CMD_ERR_NO_MATCH);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth 0x0") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth 0x343") == CMD_SUCCESS);
+ OSMO_ASSERT(do_vty_command(vty, "range-baseboth -0x343") == CMD_ERR_NO_MATCH);
+
+ destroy_test_vty(&test, vty);
+}
+/* Application specific attributes */
+enum vty_test_attr {
+ VTY_TEST_ATTR_FOO = 0,
+ VTY_TEST_ATTR_BAR,
+ VTY_TEST_ATTR_ZOO,
+ VTY_TEST_ATTR_FOO_DUP,
+ VTY_TEST_ATTR_ZOO_DUP,
+ VTY_TEST_ATTR_UPPER,
+ VTY_TEST_ATTR_RAFC_DOT,
+ VTY_TEST_ATTR_RAFC_EXCL,
+ VTY_TEST_ATTR_RAFC_AT,
+};
+
int main(int argc, char **argv)
{
struct vty_app_info vty_info = {
.name = "VtyTest",
.version = 0,
- .go_parent_cb = go_parent_cb,
- .is_config_node = NULL,
+ .usr_attr_letters = {
+ [VTY_TEST_ATTR_FOO] = 'f',
+ [VTY_TEST_ATTR_BAR] = 'b',
+ [VTY_TEST_ATTR_ZOO] = 'z',
+
+ /* Duplicate detection check */
+ [VTY_TEST_ATTR_FOO_DUP] = 'f',
+ [VTY_TEST_ATTR_ZOO_DUP] = 'z',
+ /* Reserved for libraries */
+ [VTY_TEST_ATTR_UPPER] = 'X',
+ /* Reserved for global attribues */
+ [VTY_TEST_ATTR_RAFC_DOT] = '.',
+ [VTY_TEST_ATTR_RAFC_EXCL] = '!',
+ [VTY_TEST_ATTR_RAFC_AT] = '@',
+ },
};
const struct log_info_cat default_categories[] = {};
@@ -510,6 +622,12 @@ int main(int argc, char **argv)
};
void *stats_ctx;
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s CFG_PATH\n", argv[0]);
+ return 1;
+ }
+ cfg_path = argv[1];
+
ctx = talloc_named_const(NULL, 0, "stats test context");
stats_ctx = talloc_named_const(ctx, 1, "stats test context");
@@ -544,9 +662,13 @@ int main(int argc, char **argv)
test_exit_by_indent("ok_indented_root.cfg", 0);
test_exit_by_indent("ok_empty_parent.cfg", 0);
test_exit_by_indent("fail_cmd_ret_warning.cfg", -EINVAL);
+ test_exit_by_indent("ok_deprecated_logging.cfg", 0);
test_is_cmd_ambiguous();
+ test_numeric_range();
+ test_ranges();
+
/* Leak check */
OSMO_ASSERT(talloc_total_blocks(stats_ctx) == 1);
diff --git a/tests/vty/vty_test.err b/tests/vty/vty_test.err
new file mode 100644
index 00000000..b021425d
--- /dev/null
+++ b/tests/vty/vty_test.err
@@ -0,0 +1,77 @@
+Found duplicate flag letter 'f' in application specific attributes (index 0 vs 3)! Please fix.
+Found duplicate flag letter 'z' in application specific attributes (index 2 vs 4)! Please fix.
+Attribute flag letter 'X' is reserved for libraries! Please fix.
+Attribute flag character '.' is reserved for globals! Please fix.
+Attribute flag character '!' is reserved for globals! Please fix.
+Attribute flag character '@' is reserved for globals! Please fix.
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 1
+Got VTY event: 3
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 1
+Got VTY event: 2
+Got VTY event: 3
+There is no such command.
+Error occurred during reading the below line:
+ level1 b
+
+Inconsistent indentation -- leading whitespace must match adjacent lines, and
+indentation must reflect child node levels. A mix of tabs and spaces is
+allowed, but their sequence must not change within a child block.
+Error occurred during reading the below line:
+ level1 b
+
+Inconsistent indentation -- leading whitespace must match adjacent lines, and
+indentation must reflect child node levels. A mix of tabs and spaces is
+allowed, but their sequence must not change within a child block.
+Error occurred during reading the below line:
+ child1 b
+
+Error occurred during reading the below line:
+return-warning
+
+% Ignoring deprecated 'logging level depr (debug|info|notice|error|fatal)'
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 1
+Got VTY event: 3
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 1
+Got VTY event: 3
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 2
+Got VTY event: 1
+Got VTY event: 3
diff --git a/tests/vty/vty_test.ok b/tests/vty/vty_test.ok
index 0b5ac9c0..e97fbfc4 100644
--- a/tests/vty/vty_test.ok
+++ b/tests/vty/vty_test.ok
@@ -290,6 +290,8 @@ reading file fail_cmd_ret_warning.cfg, expecting rc=-22
Called: 'return-success'
Called: 'return-warning'
got rc=-22
+reading file ok_deprecated_logging.cfg, expecting rc=0
+got rc=0
Going to test is_cmd_ambiguous()
Going to execute 'ambiguous_nr'
Called: 'ambiguous_nr [<0-23>]' (argc=0)
@@ -309,4 +311,61 @@ Returned: 0, Current node: 1 '%s> '
Going to execute 'ambiguous_str arg keyword'
Called: 'ambiguous_str ARG keyword'
Returned: 0, Current node: 1 '%s> '
+Going to test test_numeric_range()
+Going to execute 'numeric-range 0'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'numeric-range 40000'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'numeric-range -400000'
+Returned: 2, Current node: 1 '%s> '
+Going to test test_ranges()
+test range-base10
+Going to execute 'range-base10 0'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-base10 40000'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-base10 -400000'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base10 0x0'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base10 0x343'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base10 -0x343'
+Returned: 2, Current node: 1 '%s> '
+test range-base16
+Going to execute 'range-base16 0'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base16 40000'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base16 -400000'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-base16 0x0'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-base16 0x343'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-base16 -0x343'
+Returned: 2, Current node: 1 '%s> '
+test range-baseboth
+Going to execute 'range-baseboth 0'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-baseboth 40000'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-baseboth -400000'
+Returned: 2, Current node: 1 '%s> '
+Going to execute 'range-baseboth 0x0'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-baseboth 0x343'
+Called: 'return-success'
+Returned: 0, Current node: 1 '%s> '
+Going to execute 'range-baseboth -0x343'
+Returned: 2, Current node: 1 '%s> '
All tests passed
diff --git a/tests/vty/vty_transcript_test.c b/tests/vty/vty_transcript_test.c
index 6651097e..5602c505 100644
--- a/tests/vty/vty_transcript_test.c
+++ b/tests/vty/vty_transcript_test.c
@@ -17,10 +17,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE
@@ -41,7 +37,7 @@
void *root_ctx = NULL;
-static void print_help()
+static void print_help(void)
{
printf( "options:\n"
" -h --help this text\n"
@@ -142,9 +138,34 @@ static void signal_handler(int signal)
}
}
+/* Application specific VTY attributes */
+enum {
+ TEST_ATTR_UNBELIEVABLE,
+ TEST_ATTR_MAGNIFICENT,
+ TEST_ATTR_WONDERFUL,
+ TEST_ATTR_UNUSED,
+};
+
static struct vty_app_info vty_info = {
.name = "vty_transcript_test",
.version = PACKAGE_VERSION,
+ .usr_attr_desc = {
+ /* Some random description strings, who cares... */
+ [TEST_ATTR_UNBELIEVABLE] = \
+ "Unbelievable: not able to be believed; unlikely to be true",
+ [TEST_ATTR_MAGNIFICENT] = \
+ "Magnificent: impressively beautiful, elaborate, or extravagant",
+ [TEST_ATTR_WONDERFUL] = \
+ "Wonderful: inspiring delight, pleasure, or admiration",
+ [TEST_ATTR_UNUSED] = \
+ "Intentionally unused attribute, ignore me",
+ },
+ .usr_attr_letters = {
+ [TEST_ATTR_UNBELIEVABLE] = 'u',
+ [TEST_ATTR_MAGNIFICENT] = 'm',
+ [TEST_ATTR_WONDERFUL] = 'w',
+ [TEST_ATTR_UNUSED] = 'n',
+ },
};
static const struct log_info_cat default_categories[] = {};
@@ -186,12 +207,205 @@ DEFUN(multi2, multi2_cmd,
return CMD_SUCCESS;
}
-static void init_vty_cmds()
+#define X(f) (1 << f)
+
+enum {
+ ATTR_TEST_NODE = _LAST_OSMOVTY_NODE + 1,
+ NEST_A_NODE,
+ NEST_B_NODE,
+ NEST_C_NODE,
+};
+
+static struct cmd_node attr_test_node = {
+ ATTR_TEST_NODE,
+ "%s(config-attr-test)# ",
+ 1
+};
+
+DEFUN(cfg_attr_test, cfg_attr_test_cmd,
+ "attribute-test",
+ "Enter attribute test node\n")
+{
+ vty->index = NULL;
+ vty->node = ATTR_TEST_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(cfg_attr_deprecated,
+ cfg_attr_deprecated_cmd,
+ "foo-deprecated",
+ "This command is deprecated\n")
+{
+ return CMD_WARNING;
+}
+
+DEFUN_HIDDEN(cfg_attr_hidden,
+ cfg_attr_hidden_cmd,
+ "foo-hidden [expert-mode]",
+ "This command is hidden\n"
+ "But can be seen in the expert mode\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_attr_immediate, cfg_attr_immediate_cmd,
+ "foo-immediate",
+ "Applies immediately\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_attr_node_exit, cfg_attr_node_exit_cmd,
+ "foo-node-exit",
+ "Applies on node exit\n",
+ CMD_ATTR_NODE_EXIT)
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_app_attr_unbelievable,
+ cfg_app_attr_unbelievable_cmd,
+ X(TEST_ATTR_UNBELIEVABLE),
+ "app-unbelievable",
+ "Unbelievable help message\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_app_attr_magnificent,
+ cfg_app_attr_magnificent_cmd,
+ X(TEST_ATTR_MAGNIFICENT),
+ "app-magnificent",
+ "Magnificent help message\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_app_attr_wonderful,
+ cfg_app_attr_wonderful_cmd,
+ X(TEST_ATTR_WONDERFUL),
+ "app-wonderful",
+ "Wonderful help message\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_app_attr_unbelievable_magnificent,
+ cfg_app_attr_unbelievable_magnificent_cmd,
+ X(TEST_ATTR_UNBELIEVABLE) | X(TEST_ATTR_MAGNIFICENT),
+ "app-unbelievable-magnificent",
+ "Unbelievable & magnificent help message\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN_USRATTR(cfg_app_attr_unbelievable_wonderful,
+ cfg_app_attr_unbelievable_wonderful_cmd,
+ X(TEST_ATTR_UNBELIEVABLE) | X(TEST_ATTR_WONDERFUL),
+ "app-unbelievable-wonderful",
+ "Unbelievable & wonderful help message\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR_USRATTR(cfg_attr_hidden_app_attr_unbelievable,
+ cfg_attr_hidden_app_attr_unbelievable_cmd,
+ CMD_ATTR_HIDDEN, X(TEST_ATTR_UNBELIEVABLE),
+ "app-hidden-unbelievable",
+ "Hidden, but still unbelievable help message\n")
+{
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node nest_a_node = {
+ NEST_A_NODE,
+ "%s(config-a)# ",
+ 1
+};
+
+static struct cmd_node nest_b_node = {
+ NEST_B_NODE,
+ "%s(config-b)# ",
+ 1
+};
+
+static struct cmd_node nest_c_node = {
+ NEST_C_NODE,
+ "%s(config-c)# ",
+ 1
+};
+
+DEFUN(cfg_nest_a, cfg_nest_a_cmd,
+ "nest NAME",
+ "Enter nest level a\n"
+ "Set a name to mark the node's state\n")
+{
+ vty->index = talloc_strdup(root_ctx, argv[0]);
+ vty->node = NEST_A_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nest_b, cfg_nest_b_cmd,
+ "nest NAME",
+ "Enter nest level b\n"
+ "Set a name to mark the node's state\n")
+{
+ vty->index = talloc_strdup(root_ctx, argv[0]);
+ vty->node = NEST_B_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nest_c, cfg_nest_c_cmd,
+ "nest NAME",
+ "Enter nest level c\n"
+ "Set a name to mark the node's state\n")
+{
+ vty->index = talloc_strdup(root_ctx, argv[0]);
+ vty->node = NEST_C_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_nest_state, cfg_nest_state_cmd,
+ "state",
+ "Show this node's mark\n")
+{
+ vty_out(vty, "%s%s", (const char *)vty->index, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+static void init_vty_cmds(void)
{
install_element_ve(&single0_cmd);
install_element_ve(&multi0_cmd);
install_element_ve(&multi1_cmd);
install_element_ve(&multi2_cmd);
+
+ install_element(CONFIG_NODE, &cfg_attr_test_cmd);
+ install_node(&attr_test_node, NULL);
+ install_element(ATTR_TEST_NODE, &cfg_attr_deprecated_cmd);
+ install_element(ATTR_TEST_NODE, &cfg_attr_hidden_cmd);
+ install_element(ATTR_TEST_NODE, &cfg_attr_immediate_cmd);
+ install_element(ATTR_TEST_NODE, &cfg_attr_node_exit_cmd);
+
+ install_element(ATTR_TEST_NODE, &cfg_app_attr_unbelievable_cmd);
+ install_element(ATTR_TEST_NODE, &cfg_app_attr_magnificent_cmd);
+ install_element(ATTR_TEST_NODE, &cfg_app_attr_wonderful_cmd);
+
+ install_element(ATTR_TEST_NODE, &cfg_app_attr_unbelievable_magnificent_cmd);
+ install_element(ATTR_TEST_NODE, &cfg_app_attr_unbelievable_wonderful_cmd);
+ install_element(ATTR_TEST_NODE, &cfg_attr_hidden_app_attr_unbelievable_cmd);
+
+ install_element(CONFIG_NODE, &cfg_nest_a_cmd);
+ install_node(&nest_a_node, NULL);
+ install_element(NEST_A_NODE, &cfg_nest_b_cmd);
+ install_node(&nest_b_node, NULL);
+ install_element(NEST_B_NODE, &cfg_nest_c_cmd);
+ install_node(&nest_c_node, NULL);
+
+ install_element(NEST_A_NODE, &cfg_nest_state_cmd);
+ install_element(NEST_B_NODE, &cfg_nest_state_cmd);
+ install_element(NEST_C_NODE, &cfg_nest_state_cmd);
}
int main(int argc, char **argv)
@@ -217,7 +431,7 @@ int main(int argc, char **argv)
}
}
- rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042);
+ rc = telnet_init_default(root_ctx, NULL, 42042);
if (rc < 0)
return 2;
diff --git a/tests/vty/vty_transcript_test.vty b/tests/vty/vty_transcript_test.vty
index db58830e..7df2a606 100644
--- a/tests/vty/vty_transcript_test.vty
+++ b/tests/vty/vty_transcript_test.vty
@@ -84,3 +84,126 @@ ok argc=1 one
vty_transcript_test> single0
ok argc=0
+
+vty_transcript_test> show vty-attributes
+ Global attributes:
+ ^ This command is hidden (check expert mode)
+ ! This command applies immediately
+ @ This command applies on VTY node exit
+ Library specific attributes:
+ A This command applies on ASP restart
+ I This command applies on IPA link establishment
+ L This command applies on E1 line update
+ Application specific attributes:
+ u Unbelievable: not able to be believed; unlikely to be true
+ m Magnificent: impressively beautiful, elaborate, or extravagant
+ w Wonderful: inspiring delight, pleasure, or admiration
+ n Intentionally unused attribute, ignore me
+
+vty_transcript_test> en
+vty_transcript_test# configure terminal
+vty_transcript_test(config)# attribute-test
+
+vty_transcript_test(config-attr-test)# list
+... !foo-(hidden|deprecated)
+ foo-immediate
+ foo-node-exit
+ app-unbelievable
+ app-magnificent
+ app-wonderful
+ app-unbelievable-magnificent
+ app-unbelievable-wonderful
+... !app-hidden-*
+
+vty_transcript_test(config-attr-test)# list with-flags
+... !foo-(hidden|deprecated)
+ ! ... foo-immediate
+ @ ... foo-node-exit
+ . u.. app-unbelievable
+ . .m. app-magnificent
+ . ..w app-wonderful
+ . um. app-unbelievable-magnificent
+ . u.w app-unbelievable-wonderful
+... !app-hidden-*
+
+vty_transcript_test(config-attr-test)# foo-deprecated?
+% There is no matched command.
+vty_transcript_test(config-attr-test)# foo-hidden?
+% There is no matched command.
+vty_transcript_test(config-attr-test)# app-hidden-unbelievable?
+% There is no matched command.
+
+vty_transcript_test(config-attr-test)# end
+vty_transcript_test# disable
+
+vty_transcript_test> enable?
+ enable Turn on privileged mode command
+vty_transcript_test> enable ?
+ [expert-mode] Enable the expert mode (show hidden commands)
+
+vty_transcript_test> enable expert-mode
+vty_transcript_test# configure terminal
+vty_transcript_test(config)# attribute-test
+
+vty_transcript_test(config-attr-test)# list
+... !foo-deprected
+ foo-hidden [expert-mode]
+ foo-immediate
+ foo-node-exit
+ app-unbelievable
+ app-magnificent
+ app-wonderful
+ app-unbelievable-magnificent
+ app-unbelievable-wonderful
+ app-hidden-unbelievable
+
+vty_transcript_test(config-attr-test)# list with-flags
+... !foo-deprected
+ ^ ... foo-hidden [expert-mode]
+ ! ... foo-immediate
+ @ ... foo-node-exit
+ . u.. app-unbelievable
+ . .m. app-magnificent
+ . ..w app-wonderful
+ . um. app-unbelievable-magnificent
+ . u.w app-unbelievable-wonderful
+ ^ u.. app-hidden-unbelievable
+
+vty_transcript_test(config-attr-test)# foo-deprecated?
+% There is no matched command.
+vty_transcript_test(config-attr-test)# foo-hidden?
+ foo-hidden This command is hidden
+vty_transcript_test(config-attr-test)# foo-hidden ?
+ [expert-mode] But can be seen in the expert mode
+vty_transcript_test(config-attr-test)# app-hidden-unbelievable?
+ app-hidden-unbelievable Hidden, but still unbelievable help message
+
+vty_transcript_test(config-attr-test)# exit
+
+vty_transcript_test(config)# nest A
+vty_transcript_test(config-a)# state
+A
+vty_transcript_test(config-a)# nest B
+vty_transcript_test(config-b)# state
+B
+vty_transcript_test(config-b)# nest C
+vty_transcript_test(config-c)# state
+C
+vty_transcript_test(config-c)# exit
+vty_transcript_test(config-b)# state
+B
+vty_transcript_test(config-b)# exit
+vty_transcript_test(config-a)# state
+A
+vty_transcript_test(config-a)# nest B2
+vty_transcript_test(config-b)# state
+B2
+vty_transcript_test(config-b)# nest C2
+vty_transcript_test(config-c)# state
+C2
+vty_transcript_test(config-c)# exit
+vty_transcript_test(config-b)# state
+B2
+vty_transcript_test(config-b)# exit
+vty_transcript_test(config-a)# state
+A
diff --git a/tests/write_queue/wqueue_test.c b/tests/write_queue/wqueue_test.c
index 827e4e84..d4476f16 100644
--- a/tests/write_queue/wqueue_test.c
+++ b/tests/write_queue/wqueue_test.c
@@ -1,3 +1,14 @@
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH.
+ * Authors: Holger Hans Peter Freyther
+ * Alexander Rehbein
+ *
+ * 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.
+ */
+
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/write_queue.h>
@@ -15,6 +26,7 @@ static void test_wqueue_limit(void)
struct msgb *msg;
struct osmo_wqueue wqueue;
int rc;
+ size_t dropped_msgs;
osmo_wqueue_init(&wqueue, 0);
OSMO_ASSERT(wqueue.max_length == 0);
@@ -63,6 +75,46 @@ static void test_wqueue_limit(void)
OSMO_ASSERT(wqueue.current_length == 2);
msgb_free(msg);
osmo_wqueue_clear(&wqueue);
+
+ /* Update limit */
+ OSMO_ASSERT(osmo_wqueue_set_maxlen(&wqueue, 5) == 0);
+ OSMO_ASSERT(osmo_wqueue_set_maxlen(&wqueue, 1) == 0);
+ OSMO_ASSERT(osmo_wqueue_set_maxlen(&wqueue, 4) == 0);
+
+ /* Add three, update limit to 1 */
+ OSMO_ASSERT(wqueue.max_length == 4);
+ msg = msgb_alloc(4096, "msg6");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 1);
+ msg = msgb_alloc(4096, "msg7");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 2);
+ msg = msgb_alloc(4096, "msg8");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(wqueue.current_length == 3);
+ dropped_msgs = osmo_wqueue_set_maxlen(&wqueue, 1);
+ OSMO_ASSERT(dropped_msgs == 2);
+ osmo_wqueue_clear(&wqueue);
+
+ /* Add three, reduce limit to 3 from 6 */
+ OSMO_ASSERT(osmo_wqueue_set_maxlen(&wqueue, 6) == 0);
+ OSMO_ASSERT(wqueue.max_length == 6);
+ msg = msgb_alloc(4096, "msg9");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 1);
+ msg = msgb_alloc(4096, "msg10");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(rc == 0);
+ OSMO_ASSERT(wqueue.current_length == 2);
+ msg = msgb_alloc(4096, "msg11");
+ rc = osmo_wqueue_enqueue(&wqueue, msg);
+ OSMO_ASSERT(wqueue.current_length == 3);
+ dropped_msgs = osmo_wqueue_set_maxlen(&wqueue, 3);
+ OSMO_ASSERT(dropped_msgs == 0);
+ osmo_wqueue_clear(&wqueue);
}
int main(int argc, char **argv)
@@ -72,7 +124,9 @@ int main(int argc, char **argv)
log_init(&log_info, NULL);
stderr_target = log_target_create_stderr();
log_add_target(stderr_target);
- log_set_print_filename(stderr_target, 0);
+ log_set_print_filename2(stderr_target, LOG_FILENAME_NONE);
+ log_set_print_category_hex(stderr_target, 0);
+ log_set_print_category(stderr_target, 0);
test_wqueue_limit();
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 653b7190..3ec71ea3 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -1,24 +1,46 @@
-if ENABLE_UTILITIES
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include $(TALLOC_CFLAGS)
-AM_CFLAGS = -Wall $(PTHREAD_CFLAGS)
-LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la $(PTHREAD_LIBS)
+bin_PROGRAMS =
+noinst_PROGRAMS =
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS)
+LDADD = $(top_builddir)/src/core/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la $(PTHREAD_LIBS)
+
+if ENABLE_UTILITIES
EXTRA_DIST = conv_gen.py conv_codes_gsm.py
-bin_PROGRAMS = osmo-arfcn osmo-auc-gen osmo-config-merge
+bin_PROGRAMS += osmo-arfcn osmo-auc-gen osmo-config-merge osmo-aka-verify osmo-gsmtap-logsend
osmo_arfcn_SOURCES = osmo-arfcn.c
osmo_auc_gen_SOURCES = osmo-auc-gen.c
+osmo_aka_verify_SOURCES = osmo-aka-verify.c
+
+osmo_gsmtap_logsend_SOURCES = gsmtap-logsend.c
+
osmo_config_merge_SOURCES = osmo-config-merge.c
osmo_config_merge_LDADD = $(LDADD) $(TALLOC_LIBS)
-osmo_config_merge_CFLAGS = $(TALLOC_CFLAGS)
if ENABLE_PCSC
-noinst_PROGRAMS = osmo-sim-test
+noinst_PROGRAMS += osmo-sim-test
osmo_sim_test_SOURCES = osmo-sim-test.c
osmo_sim_test_LDADD = $(LDADD) $(top_builddir)/src/sim/libosmosim.la $(PCSC_LIBS)
-osmo_sim_test_CFLAGS = $(PCSC_CFLAGS)
+osmo_sim_test_CFLAGS = $(AM_CFLAGS) $(PCSC_CFLAGS)
endif
endif
+
+if ENABLE_EXT_TESTS
+SUBDIRS = \
+ osmo-stat-dummy \
+ $(NULL)
+endif
+
+if ENABLE_GB
+noinst_PROGRAMS += osmo-ns-dummy
+osmo_ns_dummy_SOURCES = osmo-ns-dummy.c osmo-ns-dummy-vty.c
+osmo_ns_dummy_LDADD = $(LDADD) $(TALLOC_LIBS) \
+ $(top_builddir)/src/gb/libosmogb.la \
+ $(top_builddir)/src/vty/libosmovty.la \
+ $(top_builddir)/src/ctrl/libosmoctrl.la \
+ $(NULL)
+endif
diff --git a/utils/conv_codes_gsm.py b/utils/conv_codes_gsm.py
index ec776605..7f621b8d 100644
--- a/utils/conv_codes_gsm.py
+++ b/utils/conv_codes_gsm.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from conv_gen import ConvolutionalCode
@@ -42,6 +42,111 @@ conv_codes = [
]
),
+ # TCH/F2.4 definition
+ ConvolutionalCode(
+ 72,
+ [
+ (G1, 1),
+ (G2, 1),
+ (G3, 1),
+ (G1, 1),
+ (G2, 1),
+ (G3, 1),
+ ],
+ name = "tch_f24",
+ description = [
+ "TCH/F2.4 convolutional code:",
+ "72 bits blocks, rate 1/6, k = 5",
+ "G1 = 1 + D + D3 + D4",
+ "G2 = 1 + D2 + D4",
+ "G3 = 1 + D + D2 + D3 + D4",
+ "G1 = 1 + D + D3 + D4",
+ "G2 = 1 + D2 + D4",
+ "G3 = 1 + D + D2 + D3 + D4",
+ ]
+ ),
+
+ # TCH/H2.4 definition
+ ConvolutionalCode(
+ 72,
+ [
+ (G1, 1),
+ (G2, 1),
+ (G3, 1),
+ ],
+ name = "tch_h24",
+ description = [
+ "TCH/H2.4 convolutional code:",
+ "72 bits blocks, rate 1/3, k = 5",
+ "G1 = 1 + D + D3 + D4",
+ "G2 = 1 + D2 + D4",
+ "G3 = 1 + D + D2 + D3 + D4",
+ ]
+ ),
+
+ # TCH/F4.8 definition
+ ConvolutionalCode(
+ 148,
+ [
+ (G1, 1),
+ (G2, 1),
+ (G3, 1),
+ ],
+ name = "tch_f48",
+ description = [
+ "TCH/F4.8 convolutional code:",
+ "148 bits blocks, rate 1/3, k = 5",
+ "G1 = 1 + D + D3 + D4",
+ "G2 = 1 + D2 + D4",
+ "G3 = 1 + D + D2 + D3 + D4",
+ ]
+ ),
+
+ # TCH/F9.6 definition
+ ConvolutionalCode(
+ 240,
+ shared_polys["xcch"],
+ puncture = [
+ 11, 26, 41, 56, 71, 86, 101, 116, 131, 146, 161, 176,
+ 191, 206, 221, 236, 251, 266, 281, 296, 311, 326, 341, 356,
+ 371, 386, 401, 416, 431, 446, 461, 476, -1
+ ],
+ name = "tch_f96",
+ description = [
+ "TCH/F9.6 convolutional code:",
+ "240 bits blocks, rate 1/2, k = 5",
+ "G0 = 1 + D3 + D4",
+ "G1 = 1 + D + D3 + D4",
+ ]
+ ),
+
+ # TCH/F14.4 definition
+ ConvolutionalCode(
+ 290,
+ shared_polys["xcch"],
+ puncture = [
+ 1, 6, 11, 15, 19, 24, 29, 33, 37, 42, 47, 51,
+ 55, 60, 65, 69, 73, 78, 83, 87, 91, 96, 101, 105,
+ 109, 114, 119, 123, 127, 132, 137, 141, 145, 150, 155, 159,
+ 163, 168, 173, 177, 181, 186, 191, 195, 199, 204, 209, 213,
+ 217, 222, 227, 231, 235, 240, 245, 249, 253, 258, 263, 267,
+ 271, 276, 281, 285, 289, 294, 299, 303, 307, 312, 317, 321,
+ 325, 330, 335, 339, 343, 348, 353, 357, 361, 366, 371, 375,
+ 379, 384, 389, 393, 397, 402, 407, 411, 415, 420, 425, 429,
+ 433, 438, 443, 447, 451, 456, 461, 465, 469, 474, 479, 483,
+ 487, 492, 497, 501, 505, 510, 515, 519, 523, 528, 533, 537,
+ 541, 546, 551, 555, 559, 564, 569, 573, 577, 582, 584, 587,
+ -1
+ ],
+ name = "tch_f144",
+ description = [
+ "TCH/F14.4 convolutional code:",
+ "290 bits blocks, rate 1/2, k = 5",
+ "G0 = 1 + D3 + D4",
+ "G1 = 1 + D + D3 + D4",
+ ]
+ ),
+
# RACH definition
ConvolutionalCode(
14,
@@ -50,11 +155,11 @@ conv_codes = [
description = ["RACH convolutional code"]
),
- # Extended RACH definition from 3GPP TS 45.003 §5.3.2
+ # Extended RACH definition from 3GPP TS 45.003 §5.3.2
ConvolutionalCode(
17,
shared_polys["xcch"],
- puncture = [ 0, 2, 5, 37, 39, 41, -1 ],
+ puncture = [ 0, 2, 5, 37, 39, 41, -1 ],
name = "rach_ext",
description = ["Extended RACH (11 bit) convolutional code"]
),
@@ -522,6 +627,25 @@ conv_codes = [
description = ["TCH/AHS 4.75 kbits convolutional code"]
),
+ # TCH_AXS SID UPDATE definition
+ ConvolutionalCode(
+ 49,
+ [
+ ( G1, G3 ),
+ ( G2, G3 ),
+ ( 1, 1 ),
+ ( 1, 1 ),
+ ],
+ name = 'tch_axs_sid_update',
+ description = [
+ "TCH/AFS and TCH/AHS SID UPDATE convolutional code:",
+ "G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4",
+ "G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4",
+ "G3/G3 = 1",
+ "G3/G3 = 1",
+ ]
+ ),
+
# EDGE MCS1_DL_HDR definition
ConvolutionalCode(
36,
diff --git a/utils/conv_gen.py b/utils/conv_gen.py
index 06283d87..27ffe625 100644
--- a/utils/conv_gen.py
+++ b/utils/conv_gen.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
mod_license = """
/*
@@ -16,10 +16,6 @@ mod_license = """
* but WITHOUT 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.
*/
"""
diff --git a/utils/gsmtap-logsend.c b/utils/gsmtap-logsend.c
new file mode 100644
index 00000000..97a838b5
--- /dev/null
+++ b/utils/gsmtap-logsend.c
@@ -0,0 +1,139 @@
+/* Small program to read an input file / stdin and send each line via GSMTAP logging */
+/* (C) 2023 by Harald Welte <laforge@osmocom.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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <getopt.h>
+
+#include <osmocom/core/byteswap.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/core/gsmtap_util.h>
+
+static char *proc_name = "gsmtap-logsend";
+static char *subsys_name = "unknown";
+static char *dest_host = "localhost";
+static int dest_port = GSMTAP_UDP_PORT;
+
+static void help(void)
+{
+ printf("osmo-gsmtap-logsend Usage:\n"
+ "\t[ -r DESTADDR ] [ -p PORTNR ] [ -n PROC_NAME ] [ -s SUBSYS ] [ INFILE ]\n"
+ "\n"
+ " -a --remote-address HOSTNAME Destination IP destination address (default: localhost)\n"
+ " -p --remote-port PORTNR Destination UDP Port number (default: 4729)\n"
+ " -n --process-name PROC_NAME Process name to include in GSMTAP LOG header\n"
+ " -s --subsys-name SUBSYS Subsystem name to include in GSMTAP LOG header\n"
+ " -h --help This help message\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ char buf[1024];
+ int gsmtap_fd;
+ FILE *infile;
+ char *line;
+ int rc;
+
+ while (1) {
+ static const struct option long_options[] = {
+ { "remote-address", 1, 0, 'a' },
+ { "remote-port", 1, 0, 'p' },
+ { "process-name", 1, 0, 'n' },
+ { "subsys-name", 1, 0, 's' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+ int c, option_index;
+
+ c = getopt_long(argc, argv, "a:p:n:s:h", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'a':
+ dest_host = optarg;
+ break;
+ case 'p':
+ dest_port = atoi(optarg);
+ break;
+ case 'n':
+ proc_name = optarg;
+ break;
+ case 's':
+ subsys_name = optarg;
+ break;
+ case 'h':
+ help();
+ exit(0);
+ break;
+ default:
+ help();
+ exit(1);
+ }
+ }
+
+ if (argc <= optind) {
+ infile = stdin;
+ } else {
+ infile = fopen(argv[optind], "r");
+ if (!infile) {
+ fprintf(stderr, "Unable to open %s: %s\n", argv[optind], strerror(errno));
+ exit(2);
+ }
+ }
+
+ gsmtap_fd = gsmtap_source_init_fd(dest_host, dest_port);
+ if (gsmtap_fd < 0) {
+ fprintf(stderr, "Unable to create GSMTAP soicket: %s\n", strerror(errno));
+ exit(2);
+ }
+
+ /* prepare all the data structures that don't change for each line */
+ struct {
+ struct gsmtap_hdr gsmtap;
+ struct gsmtap_osmocore_log_hdr log;
+ } __attribute__ ((packed)) hdr;
+ struct timeval tv;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.gsmtap.version = GSMTAP_VERSION;
+ hdr.gsmtap.hdr_len = sizeof(hdr.gsmtap)/4;
+ hdr.gsmtap.type = GSMTAP_TYPE_OSMOCORE_LOG;
+
+ OSMO_STRLCPY_ARRAY(hdr.log.proc_name, proc_name);
+ OSMO_STRLCPY_ARRAY(hdr.log.subsys, subsys_name);
+ hdr.log.level = LOGL_INFO;
+
+ while ((line = fgets(buf, sizeof(buf), infile))) {
+ struct iovec iov[2] = {
+ { .iov_base = &hdr, .iov_len = sizeof(hdr) },
+ { .iov_base = buf, .iov_len = strlen(line) + 1 },
+ };
+ osmo_gettimeofday(&tv, NULL);
+ hdr.log.ts.sec = osmo_htonl(tv.tv_sec);
+ hdr.log.ts.usec = osmo_htonl(tv.tv_usec);
+
+ rc = writev(gsmtap_fd, iov, ARRAY_SIZE(iov));
+ if (rc <= 0) {
+ fprintf(stderr, "Short write on GSMTAP socket: %d (%s)\n", rc, strerror(errno));
+ exit(1);
+ }
+ }
+}
diff --git a/utils/gsmtap_logread.py b/utils/gsmtap_logread.py
index a29f1491..a29f1491 100644..100755
--- a/utils/gsmtap_logread.py
+++ b/utils/gsmtap_logread.py
diff --git a/utils/osmo-aka-verify.c b/utils/osmo-aka-verify.c
new file mode 100644
index 00000000..f23c349b
--- /dev/null
+++ b/utils/osmo-aka-verify.c
@@ -0,0 +1,249 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <getopt.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bit64gen.h>
+
+/* Utility program for implementing the SIM-side procedures of 3GPP Authentication and Key Agreement
+ * as specified by 3GPP TS 33.102 Section 6.3.3
+ *
+ * (C) 2021 by Harald Welte <laforge@gnumonks.org>
+ * Milenage library code used from libosmocore, which inherited it from wpa_supplicant
+ */
+
+/* FIXME: libosmogsm implements those, but doesn't declare them */
+int milenage_f1(const uint8_t *opc, const uint8_t *k, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_a, uint8_t *mac_s);
+int milenage_f2345(const uint8_t *opc, const uint8_t *k, const uint8_t *_rand,
+ uint8_t *res, uint8_t *ck, uint8_t *ik, uint8_t *ak, uint8_t *akstar);
+int milenage_opc_gen(uint8_t *opc, const uint8_t *k, const uint8_t *op);
+
+static int milenage_check(const uint8_t *opc, const uint8_t *k, const uint8_t *sqn, const uint8_t *_rand,
+ const uint8_t *autn, uint8_t *ck, uint8_t *ik, uint8_t *res, size_t *res_len,
+ uint8_t *auts)
+{
+ int i;
+ uint8_t xmac[8], ak[6], rx_sqn_bin[6];
+ unsigned long long rx_sqn;
+ const uint8_t *amf;
+
+ printf("=== Static SIM parameters:\n");
+ printf("Milenage SIM K: %s\n", osmo_hexdump_nospc(k, 16));
+ printf("Milenage SIM OPc: %s\n", osmo_hexdump_nospc(opc, 16));
+ printf("Milenage SIM SQN: %s\n", osmo_hexdump_nospc(sqn, 6));
+ printf("\n");
+
+ printf("=== Authentication Tuple as received from Network:\n");
+ printf("Milenage Input RAND: %s\n", osmo_hexdump_nospc(_rand, 16));
+ printf("Milenage Input AUTN: %s\n", osmo_hexdump_nospc(autn, 16));
+ printf("\tAUTN(+)AK: %s\n", osmo_hexdump_nospc(autn, 6));
+ printf("\tAMF: %s\n", osmo_hexdump_nospc(autn+6, 2));
+ printf("\tMAC: %s\n", osmo_hexdump_nospc(autn+8, 8));
+ printf("\n");
+
+ if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
+ return -1;
+
+ *res_len = 8;
+ printf("Milenage f2-Computed RES: %s\n", osmo_hexdump_nospc(res, *res_len));
+ printf("Milenage f3-Computed CK: %s\n", osmo_hexdump_nospc(ck, 16));
+ printf("Milenage f4-Computed IK: %s\n", osmo_hexdump_nospc(ik, 16));
+ printf("Milenage f5-Computed AK: %s\n", osmo_hexdump_nospc(ak, 6));
+
+ /* AUTN = (SQN ^ AK) || AMF || MAC */
+ for (i = 0; i < 6; i++)
+ rx_sqn_bin[i] = autn[i] ^ ak[i];
+ rx_sqn = osmo_load64be_ext(rx_sqn_bin, 6);
+ printf("Milenage Computed SQN: %s (%llu)\n", osmo_hexdump_nospc(rx_sqn_bin, 6), rx_sqn);
+
+ if (memcmp(rx_sqn_bin, sqn, 6) <= 0) {
+ printf("Milenage: RX-SQN differs from SIM SQN: Re-Sync!\n");
+ uint8_t auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+ if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+ return -1;
+ printf("Milenage Computed AK*: %s", osmo_hexdump_nospc(ak, 6));
+ for (i = 0; i < 6; i++)
+ auts[i] = sqn[i] ^ ak[i];
+ if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
+ return -1;
+ printf("Milenage AUTS: %s\n", osmo_hexdump_nospc(auts, 14));
+ return -2;
+ }
+
+ amf = autn + 6;
+ if (milenage_f1(opc, k, _rand, rx_sqn_bin, amf, xmac, NULL))
+ return -1;
+
+ printf("Milenage f1-Computed XMAC: %s\n", osmo_hexdump_nospc(xmac, 8));
+
+ if (memcmp(xmac, autn + 8, 8) != 0) {
+ fprintf(stderr, "Milenage: MAC mismatch!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void help(void)
+{
+ printf( "Static SIM card parameters:\n"
+ "-k --key\tSpecify Ki / K\n"
+ "-o --opc\tSpecify OPC\n"
+ "-O --op\tSpecify OP\n"
+ "-f --amf\tSpecify AMF\n"
+ "-s --sqn\tSpecify SQN\n"
+ "\n"
+ "Authentication Tuple by network:\n"
+ //"-i --ind\tSpecify IND slot for new SQN after AUTS\n"
+ //"-l --ind-len\tSpecify IND bit length (default=5)\n"
+ "-r --rand\tSpecify RAND random value\n"
+ "-A --autn\tSpecify AUTN authentication nonce\n"
+ );
+}
+
+static uint8_t g_k[16];
+static uint8_t g_opc[16];
+static uint8_t g_rand[16];
+static uint8_t g_autn[16];
+static uint8_t g_amf[16];
+static unsigned long long g_sqn;
+
+
+static int handle_options(int argc, char **argv)
+{
+ int rc, option_index;
+ bool rand_is_set = false;
+ bool autn_is_set = false;
+ bool sqn_is_set = false;
+ bool k_is_set = false;
+ bool opc_is_set = false;
+ bool amf_is_set = false;
+ bool opc_is_op = false;
+ int64_t val64;
+
+ while (1) {
+ int c;
+ static struct option long_options[] = {
+ { "key", 1, 0, 'k' },
+ { "opc", 1, 0, 'o' },
+ { "op", 1, 0, 'O' },
+ { "amf", 1, 0, 'f' },
+ { "sqn", 1, 0, 's' },
+ { "rand", 1, 0, 'r' },
+ { "autn", 1, 0, 'A' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+
+ rc = 0;
+
+ c = getopt_long(argc, argv, "k:o:O:f:s:r:A:h", long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'k':
+ rc = osmo_hexparse(optarg, g_k, sizeof(g_k));
+ k_is_set = true;
+ break;
+ case 'o':
+ rc = osmo_hexparse(optarg, g_opc, sizeof(g_opc));
+ opc_is_op = false;
+ opc_is_set = true;
+ break;
+ case 'O':
+ rc = osmo_hexparse(optarg, g_opc, sizeof(g_opc));
+ opc_is_op = true;
+ opc_is_set = true;
+ break;
+ case 'A':
+ rc = osmo_hexparse(optarg, g_autn, sizeof(g_autn));
+ autn_is_set = true;
+ break;
+ case 'f':
+ rc = osmo_hexparse(optarg, g_amf, sizeof(g_amf));
+ amf_is_set = true;
+ break;
+ case 's':
+ rc = osmo_str_to_int64(&val64, optarg, 10, 0, INT64_MAX);
+ g_sqn = (unsigned long long)val64;
+ sqn_is_set = true;
+ break;
+ case 'r':
+ rc = osmo_hexparse(optarg, g_rand, sizeof(g_rand));
+ rand_is_set = true;
+ break;
+ case 'h':
+ help();
+ exit(0);
+ default:
+ help();
+ exit(1);
+ }
+
+ if (rc < 0) {
+ help();
+ fprintf(stderr, "\nError parsing argument of option `%c'\n", c);
+ exit(2);
+ }
+ }
+
+ if (!k_is_set || !opc_is_set || !autn_is_set || !rand_is_set) {
+ fprintf(stderr, "Error: K, OP[c], AUTN and RAND are mandatory arguments\n");
+ fprintf(stderr, "\n");
+ help();
+ exit(2);
+ }
+
+ if (!sqn_is_set)
+ printf("Warning: You may want to specify SQN\n");
+
+ if (!amf_is_set)
+ printf("Warning: You may want to specify AMF\n");
+
+ if (opc_is_op) {
+ uint8_t op[16];
+ memcpy(op, g_opc, 16);
+ rc = milenage_opc_gen(g_opc, g_k, op);
+ OSMO_ASSERT(rc == 0);
+ }
+
+ return 0;
+}
+
+
+
+int main(int argc, char **argv)
+{
+ printf("osmo-aka-check (C) 2021 by Harald Welte\n");
+ printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
+
+ handle_options(argc, argv);
+
+ printf("\n");
+
+ uint8_t ck[16];
+ uint8_t ik[16];
+ uint8_t res[16];
+ size_t res_len;
+ uint8_t auts[14];
+ uint8_t sqn_bin[6];
+ int rc;
+
+ osmo_store64be_ext(g_sqn, sqn_bin, 6);
+
+ rc = milenage_check(g_opc, g_k, sqn_bin, g_rand, g_autn, ck, ik, res, &res_len, auts);
+
+ if (rc < 0) {
+ fprintf(stderr, "Authentication FAILED!\n");
+ exit(1);
+ } else {
+ printf("Authentication SUCCEEDED\n");
+ exit(0);
+ }
+}
diff --git a/utils/osmo-arfcn.c b/utils/osmo-arfcn.c
index aee132c7..3d4fb507 100644
--- a/utils/osmo-arfcn.c
+++ b/utils/osmo-arfcn.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <stdio.h>
@@ -97,10 +93,10 @@ int main(int argc, char **argv)
{
int arfcn, freq, pcs = 0, uplink = -1;
int opt;
- char *param;
+ char *param = NULL;
enum program_mode mode = MODE_NONE;
- while ((opt = getopt(argc, argv, "pa:f:ud")) != -1) {
+ while ((opt = getopt(argc, argv, "pa:f:udh")) != -1) {
switch (opt) {
case 'p':
pcs = 1;
@@ -128,6 +124,11 @@ int main(int argc, char **argv)
}
}
+ if (argc > optind) {
+ fprintf(stderr, "Unsupported positional arguments in command line\n");
+ exit(2);
+ }
+
switch (mode) {
case MODE_NONE:
help(argv[0]);
diff --git a/utils/osmo-auc-gen.c b/utils/osmo-auc-gen.c
index ec9bad88..50419a43 100644
--- a/utils/osmo-auc-gen.c
+++ b/utils/osmo-auc-gen.c
@@ -1,7 +1,7 @@
/*! \file osmo-auc-gen.c
* GSM/GPRS/3G authentication testing tool. */
/*
- * (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010-2023 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
@@ -34,8 +30,20 @@
#include <osmocom/crypt/auth.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/base64.h>
#include <osmocom/gsm/gsm_utils.h>
+static void print_base64(const char *fmt, const uint8_t *data, unsigned int len)
+{
+ uint8_t outbuf[256];
+ size_t olen;
+
+ OSMO_ASSERT(osmo_base64_encode(outbuf, sizeof(outbuf), &olen, data, len) == 0);
+ OSMO_ASSERT(sizeof(outbuf) > olen);
+ outbuf[olen] = '\0';
+ printf(fmt, outbuf);
+}
+
static void dump_triplets_dat(struct osmo_auth_vector *vec)
{
if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) {
@@ -53,10 +61,17 @@ static void dump_auth_vec(struct osmo_auth_vector *vec)
printf("RAND:\t%s\n", osmo_hexdump_nospc(vec->rand, sizeof(vec->rand)));
if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) {
+ uint8_t inbuf[sizeof(vec->rand) + sizeof(vec->autn)];
+
printf("AUTN:\t%s\n", osmo_hexdump_nospc(vec->autn, sizeof(vec->autn)));
printf("IK:\t%s\n", osmo_hexdump_nospc(vec->ik, sizeof(vec->ik)));
printf("CK:\t%s\n", osmo_hexdump_nospc(vec->ck, sizeof(vec->ck)));
printf("RES:\t%s\n", osmo_hexdump_nospc(vec->res, vec->res_len));
+
+ memcpy(inbuf, vec->rand, sizeof(vec->rand));
+ memcpy(inbuf + sizeof(vec->rand), vec->autn, sizeof(vec->autn));
+ print_base64("IMS nonce:\t%s\n", inbuf, sizeof(inbuf));
+ print_base64("IMS res:\t%s\n", vec->res, vec->res_len);
}
if (vec->auth_types & OSMO_AUTH_TYPE_GSM) {
@@ -65,12 +80,12 @@ static void dump_auth_vec(struct osmo_auth_vector *vec)
}
}
-static struct osmo_sub_auth_data test_aud = {
+static struct osmo_sub_auth_data2 test_aud = {
.type = OSMO_AUTH_TYPE_NONE,
.algo = OSMO_AUTH_ALG_NONE,
};
-static void help()
+static void help(void)
{
int alg;
printf( "-2 --2g\tUse 2G (GSM) authentication\n"
@@ -83,6 +98,7 @@ static void help()
"-s --sqn\tSpecify SQN (only for 3G)\n"
"-i --ind\tSpecify IND slot for new SQN after AUTS (only for 3G)\n"
"-l --ind-len\tSpecify IND bit length (default=5) (only for 3G)\n"
+ "-L --res-len\tSpecify RES byte length (default=8) (only for 3G)\n"
"-A --auts\tSpecify AUTS (only for 3G)\n"
"-r --rand\tSpecify random value\n"
"-I --ipsec\tOutput in triplets.dat format for strongswan\n");
@@ -108,10 +124,12 @@ int main(int argc, char **argv)
int fmt_triplets_dat = 0;
uint64_t ind_mask = 0;
- printf("osmo-auc-gen (C) 2011-2012 by Harald Welte\n");
+ printf("osmo-auc-gen (C) 2011-2023 by Harald Welte\n");
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
memset(_auts, 0, sizeof(_auts));
+ memset(vec, 0, sizeof(*vec));
+ vec->res_len = 8; /* default */
while (1) {
int c;
@@ -126,6 +144,7 @@ int main(int argc, char **argv)
{ "sqn", 1, 0, 's' },
{ "ind", 1, 0, 'i' },
{ "ind-len", 1, 0, 'l' },
+ { "res-len", 1, 0, 'L' },
{ "rand", 1, 0, 'r' },
{ "auts", 1, 0, 'A' },
{ "help", 0, 0, 'h' },
@@ -134,7 +153,7 @@ int main(int argc, char **argv)
rc = 0;
- c = getopt_long(argc, argv, "23a:k:o:f:s:i:l:r:hO:A:I", long_options,
+ c = getopt_long(argc, argv, "23a:k:o:f:s:i:l:L:r:hO:A:I", long_options,
&option_index);
if (c == -1)
@@ -159,10 +178,21 @@ int main(int argc, char **argv)
case OSMO_AUTH_TYPE_GSM:
rc = osmo_hexparse(optarg, test_aud.u.gsm.ki,
sizeof(test_aud.u.gsm.ki));
+ if (rc != sizeof(test_aud.u.gsm.ki)) {
+ fprintf(stderr, "Invalid Ki length %d\n", rc);
+ exit(2);
+ }
break;
case OSMO_AUTH_TYPE_UMTS:
rc = osmo_hexparse(optarg, test_aud.u.umts.k,
sizeof(test_aud.u.umts.k));
+ /* 3GPP TS 33.102 6.3.7: "The authentication key (K) shall have a length of
+ * 128 bits or 256 bits." */
+ if (rc != 16 && rc != 32) {
+ fprintf(stderr, "Invalid K length %d\n", rc);
+ exit(2);
+ }
+ test_aud.u.umts.k_len = rc;
break;
default:
fprintf(stderr, "please specify 2g/3g first!\n");
@@ -175,6 +205,11 @@ int main(int argc, char **argv)
}
rc = osmo_hexparse(optarg, test_aud.u.umts.opc,
sizeof(test_aud.u.umts.opc));
+ if (rc != 16 && rc != 32) {
+ fprintf(stderr, "Invalid OPC length %d\n", rc);
+ exit(2);
+ }
+ test_aud.u.umts.opc_len = rc;
test_aud.u.umts.opc_is_op = 0;
break;
case 'O':
@@ -184,6 +219,11 @@ int main(int argc, char **argv)
}
rc = osmo_hexparse(optarg, test_aud.u.umts.opc,
sizeof(test_aud.u.umts.opc));
+ if (rc != 16 && rc != 32) {
+ fprintf(stderr, "Invalid OP length %d\n", rc);
+ exit(2);
+ }
+ test_aud.u.umts.opc_len = rc;
test_aud.u.umts.opc_is_op = 1;
break;
case 'A':
@@ -201,13 +241,17 @@ int main(int argc, char **argv)
}
rc = osmo_hexparse(optarg, test_aud.u.umts.amf,
sizeof(test_aud.u.umts.amf));
+ if (rc != 2) {
+ fprintf(stderr, "Invalid AMF length %d\n", rc);
+ exit(2);
+ }
break;
case 's':
if (test_aud.type != OSMO_AUTH_TYPE_UMTS) {
fprintf(stderr, "Only UMTS has SQN\n");
exit(2);
}
- sqn = strtoull(optarg, 0, 10);
+ sqn = strtoull(optarg, 0, 0);
sqn_is_set = 1;
break;
case 'i':
@@ -225,8 +269,20 @@ int main(int argc, char **argv)
}
test_aud.u.umts.ind_bitlen = atoi(optarg);
break;
+ case 'L':
+ rc = atoi(optarg);
+ if (rc != 4 && rc != 8 && rc != 16) {
+ fprintf(stderr, "Invalid RES length %u\n", rc);
+ exit(2);
+ }
+ vec->res_len = rc;
+ break;
case 'r':
rc = osmo_hexparse(optarg, _rand, sizeof(_rand));
+ if (rc != sizeof(_rand)) {
+ fprintf(stderr, "Invalid RAND length %d\n", rc);
+ exit(2);
+ }
rand_is_set = 1;
break;
case 'I':
@@ -247,6 +303,11 @@ int main(int argc, char **argv)
}
}
+ if (argc > optind) {
+ fprintf(stderr, "Unsupported positional arguments in command line\n");
+ exit(2);
+ }
+
if (!rand_is_set) {
rc = osmo_get_rand_id(_rand, 16);
if (rc < 0) {
@@ -264,8 +325,6 @@ int main(int argc, char **argv)
exit(2);
}
- memset(vec, 0, sizeof(*vec));
-
if (test_aud.type == OSMO_AUTH_TYPE_UMTS) {
uint64_t seq_1 = 1LL << test_aud.u.umts.ind_bitlen;
ind_mask = seq_1 - 1;
@@ -296,9 +355,9 @@ int main(int argc, char **argv)
}
if (!auts_is_set)
- rc = osmo_auth_gen_vec(vec, &test_aud, _rand);
+ rc = osmo_auth_gen_vec2(vec, &test_aud, _rand);
else
- rc = osmo_auth_gen_vec_auts(vec, &test_aud, _auts, _rand, _rand);
+ rc = osmo_auth_gen_vec_auts2(vec, &test_aud, _auts, _rand, _rand);
if (rc < 0) {
if (!auts_is_set)
fprintf(stderr, "error generating auth vector\n");
diff --git a/utils/osmo-config-merge.c b/utils/osmo-config-merge.c
index 477bb028..ed2039ac 100644
--- a/utils/osmo-config-merge.c
+++ b/utils/osmo-config-merge.c
@@ -15,10 +15,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
/*
diff --git a/utils/osmo-ns-dummy-vty.c b/utils/osmo-ns-dummy-vty.c
new file mode 100644
index 00000000..2e59b11b
--- /dev/null
+++ b/utils/osmo-ns-dummy-vty.c
@@ -0,0 +1,325 @@
+/* VTY for osmo-ns-dummy */
+
+/* (C) 2021 Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gsm/prim.h>
+#include <osmocom/gprs/gprs_ns2.h>
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/misc.h>
+
+extern struct gprs_ns2_inst *g_nsi;
+static struct llist_head g_ns_traf_gens = LLIST_HEAD_INIT(g_ns_traf_gens);
+int g_mirror_mode;
+
+/* one NS traffic generator instance. You can have as many of these as you want,
+ * just as long as they have unique names */
+struct ns_traf_gen {
+ struct llist_head list;
+ const char *name;
+ struct {
+ uint16_t nsei;
+ uint16_t bvci;
+ /* size of each packet */
+ uint16_t pkt_size;
+ /* interval between packets in us */
+ uint32_t interval_us;
+ /* fixed (false) or random (true) LSP */
+ bool lsp_randomize;
+ /* (fixeD) Link Selector Parameter */
+ uint32_t lsp;
+ } cfg;
+ struct osmo_fd timerfd;
+ bool running;
+};
+
+#define LOGNTG(ntg, lvl, fmt, args ...) \
+ LOGP(DLGLOBAL, lvl, "traf-gen(%s): " fmt, (ntg)->name, ## args)
+
+/* allocate and transmit one NS message */
+static int ntg_tx_one(struct ns_traf_gen *ntg)
+{
+ struct osmo_gprs_ns2_prim nsp = {};
+ struct msgb *msg = msgb_alloc_headroom(3072, 20, "NS traffic gen");
+
+ if (!msg)
+ return -ENOMEM;
+ msgb_put(msg, ntg->cfg.pkt_size);
+ nsp.bvci = ntg->cfg.bvci;
+ nsp.nsei = ntg->cfg.nsei;
+ if (ntg->cfg.lsp_randomize)
+ nsp.u.unitdata.link_selector = rand();
+ else
+ nsp.u.unitdata.link_selector = ntg->cfg.lsp;
+ osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg);
+ return gprs_ns2_recv_prim(g_nsi, &nsp.oph);
+}
+
+/* call-back from transmit timer-fd */
+static int ntg_timerfd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ uint64_t expire_count;
+ struct ns_traf_gen *ntg = ofd->data;
+ unsigned int i;
+ int rc;
+
+ OSMO_ASSERT(what & OSMO_FD_READ);
+
+ rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count));
+ if (rc < 0 && errno == EAGAIN)
+ return 0;
+ OSMO_ASSERT(rc == sizeof(expire_count));
+
+ for (i = 0; i < expire_count; i++)
+ ntg_tx_one(ntg);
+
+ return 0;
+}
+
+static struct ns_traf_gen *ns_traf_gen_find(const char *name)
+{
+ struct ns_traf_gen *ntg;
+
+ llist_for_each_entry(ntg, &g_ns_traf_gens, list) {
+ if (!strcmp(ntg->name, name))
+ return ntg;
+ }
+ return NULL;
+}
+
+static struct ns_traf_gen *ns_traf_gen_find_or_alloc(const char *name)
+{
+ struct ns_traf_gen *ntg;
+ int rc;
+
+ ntg = ns_traf_gen_find(name);
+ if (ntg)
+ return ntg;
+
+ ntg = talloc_zero(g_nsi, struct ns_traf_gen);
+ OSMO_ASSERT(ntg);
+ ntg->name = talloc_strdup(ntg, name);
+ ntg->timerfd.fd = -1;
+ rc = osmo_timerfd_setup(&ntg->timerfd, ntg_timerfd_cb, ntg);
+ OSMO_ASSERT(rc >= 0);
+ llist_add_tail(&ntg->list, &g_ns_traf_gens);
+
+ return ntg;
+}
+
+enum nodes {
+ NTG_NODE = _LAST_OSMOVTY_NODE + 1,
+};
+
+static struct cmd_node ntg_node = {
+ NTG_NODE,
+ "%s(config-ns-traf-gen)# ",
+ 1,
+};
+
+static int config_write_ntg(struct vty *vty)
+{
+ struct ns_traf_gen *ntg;
+
+ llist_for_each_entry(ntg, &g_ns_traf_gens, list) {
+ vty_out(vty, "ns-traffic-generator %s%s", ntg->name, VTY_NEWLINE);
+ vty_out(vty, " nsei %u%s", ntg->cfg.nsei, VTY_NEWLINE);
+ vty_out(vty, " bvci %u%s", ntg->cfg.bvci, VTY_NEWLINE);
+ vty_out(vty, " packet-size %u%s", ntg->cfg.pkt_size, VTY_NEWLINE);
+ vty_out(vty, " interval-us %u%s", ntg->cfg.interval_us, VTY_NEWLINE);
+ vty_out(vty, " lsp %u%s", ntg->cfg.lsp, VTY_NEWLINE);
+ vty_out(vty, " lsp-mode %s%s", ntg->cfg.lsp_randomize ? "randomized" : "fixed", VTY_NEWLINE);
+ }
+ vty_out(vty, "mirror-mode %s%s", g_mirror_mode ? "enable" : "disable", VTY_NEWLINE);
+
+ return 0;
+}
+
+DEFUN(ntg_start, ntg_start_stop_cmd,
+ "ns-traffic-generator (start|stop) NAME",
+ "Control named NS traffic generator\n"
+ "Start generating traffic in this traffic generator\n"
+ "Stop generating traffic in this traffic generator\n"
+ "Name of NS traffic generator to start\n")
+{
+ struct ns_traf_gen *ntg = ns_traf_gen_find(argv[1]);
+ if (!ntg) {
+ vty_out(vty, "NS Traffic generator '%s' doesn't exist%s", argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[0], "start")) {
+ struct timespec interval;
+ if (ntg->running) {
+ vty_out(vty, "NS Traffic generator was already started%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ interval.tv_sec = ntg->cfg.interval_us / 1000000;
+ interval.tv_nsec = (ntg->cfg.interval_us % 1000000) * 1000;
+ osmo_timerfd_schedule(&ntg->timerfd, NULL, &interval);
+ ntg->running = true;
+ } else {
+ if (!ntg->running) {
+ vty_out(vty, "NS Traffic generator was already stopped%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ osmo_timerfd_disable(&ntg->timerfd);
+ ntg->running = false;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(ntg_nsei, ntg_nsei_cmd,
+ "nsei <0-65535>",
+ "NSEI to use when generating traffic\n"
+ "NSEI to use when generating traffic\n")
+{
+ struct ns_traf_gen *ntg = vty->index;
+ ntg->cfg.nsei = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(ntg_bvci, ntg_bvci_cmd,
+ "bvci <0-65535>",
+ "BVCI to use when generating traffic\n"
+ "BVCI to use when generating traffic\n")
+{
+ struct ns_traf_gen *ntg = vty->index;
+ ntg->cfg.bvci = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(ntg_pkt_size, ntg_pkt_size_cmd,
+ "packet-size <0-2048>",
+ "Packet size for generated NS-UNITDATA payload\n"
+ "Packet size for generated NS-UNITDATA payload\n")
+{
+ struct ns_traf_gen *ntg = vty->index;
+ ntg->cfg.pkt_size = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(ntg_pkt_intv_us, ntg_pkt_intv_us_cmd,
+ "interval-us <0-1000000>",
+ "Interval between packets in microseconds\n"
+ "Interval between packets in microseconds\n")
+{
+ struct ns_traf_gen *ntg = vty->index;
+ ntg->cfg.interval_us = atoi(argv[0]);
+ if (ntg->running) {
+ /* TODO: update timer */
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(ntg_lsp, ntg_lsp_cmd,
+ "lsp <0-4294967295>",
+ "Link Selector Parameter (only used in fixed mode)\n"
+ "Link Selector Parameter (only used in fixed mode)\n")
+{
+ struct ns_traf_gen *ntg = vty->index;
+ ntg->cfg.lsp = strtoul(argv[0], NULL, 10);
+ return CMD_SUCCESS;
+}
+
+DEFUN(ntg_lsp_mode, ntg_lsp_mode_cmd,
+ "lsp-mode (fixed|randomized)",
+ "Link Selector Parameter Mode\n"
+ "Fixed / Staic LSP\n"
+ "Randomized LSP\n")
+{
+ struct ns_traf_gen *ntg = vty->index;
+ if (!strcmp(argv[0], "randomized"))
+ ntg->cfg.lsp_randomize = true;
+ else
+ ntg->cfg.lsp_randomize = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN(gen_traffic, gen_traffic_cmd,
+ "ns-traffic-generator NAME",
+ "Configure a given NS traffic generator\n" "Name of NS traffic generator\n")
+{
+ struct ns_traf_gen *ntg = ns_traf_gen_find_or_alloc(argv[0]);
+
+ if (!ntg)
+ return CMD_WARNING;
+
+ vty->index = ntg;
+ vty->node = NTG_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(mirror_mode, mirror_mode_cmd,
+ "mirror-mode (enable|disable)",
+ "Configure mirroring of incoming NS-UNITDATA\n"
+ "Enable mirroring of incoming NS-UNITDATA\n"
+ "Disable mirroring of incoming NS-UNITDATA\n")
+{
+ if (!strcmp(argv[0], "enable"))
+ g_mirror_mode = true;
+ else
+ g_mirror_mode = false;
+
+ return CMD_SUCCESS;
+}
+
+
+int nsdummy_vty_init(void)
+{
+ /* configuration of traffic generators via CONFIG / NTG node */
+ install_element(CONFIG_NODE, &gen_traffic_cmd);
+ install_element(CONFIG_NODE, &mirror_mode_cmd);
+ install_node(&ntg_node, config_write_ntg);
+ install_element(NTG_NODE, &ntg_nsei_cmd);
+ install_element(NTG_NODE, &ntg_bvci_cmd);
+ install_element(NTG_NODE, &ntg_pkt_size_cmd);
+ install_element(NTG_NODE, &ntg_pkt_intv_us_cmd);
+ install_element(NTG_NODE, &ntg_lsp_cmd);
+ install_element(NTG_NODE, &ntg_lsp_mode_cmd);
+
+ /* starting/stopping the traffic generators is in 'enable' mode, not 'config' */
+ install_element(ENABLE_NODE, &ntg_start_stop_cmd);
+
+ return 0;
+}
diff --git a/utils/osmo-ns-dummy.c b/utils/osmo-ns-dummy.c
new file mode 100644
index 00000000..890444cf
--- /dev/null
+++ b/utils/osmo-ns-dummy.c
@@ -0,0 +1,316 @@
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <signal.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/ctrl/control_if.h>
+#include <osmocom/ctrl/control_vty.h>
+#include <osmocom/gprs/gprs_ns2.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/ports.h>
+#include <osmocom/vty/tdef_vty.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/misc.h>
+
+#include "config.h"
+
+void *tall_nsdummy_ctx = NULL;
+static struct log_info log_info = {};
+static bool quit = false;
+static bool config_given = false;
+static bool daemonize = false;
+static int vty_port = 0;
+static int ctrl_port = 0;
+static char *config_file = NULL;
+struct gprs_ns2_inst *g_nsi;
+
+static const char vty_copyright[] =
+ "Copyright (C) 2020 by by sysmocom - s.f.m.c. GmbH\r\n"
+ "Author: Alexander Couzens <lynxis@fe80.eu>\r\n"
+ "License GNU GPL version 2 or later\r\n"
+ "This is free software: you are free to change and redistribute it.\r\n"
+ "There is NO WARRANTY, to the extent permitted by law.\r\n";
+
+static struct vty_app_info vty_info = {
+ .name = "OsmoNSdummy",
+ .version = PACKAGE_VERSION,
+ .copyright = vty_copyright,
+};
+
+static void print_help(void)
+{
+ printf( "Some useful options:\n"
+ " -h --help This text\n"
+ " -c --config-file Specify the filename of the config file\n"
+ " -V --version Print version\n"
+ " -D --daemonize Fork the process into a background daemon\n"
+ " -p --vty-port PORT Set the vty port to listen on.\n"
+ " -r --ctrl-port PORT Set the ctrl port to listen on.\n"
+ "\nVTY reference generation:\n"
+ " --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
+ " --vty-ref-xml Generate the VTY reference XML output and exit.\n"
+ );
+}
+
+static void handle_long_options(const char *prog_name, const int long_option)
+{
+ static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
+
+ switch (long_option) {
+ case 1:
+ vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
+ if (vty_ref_mode < 0) {
+ fprintf(stderr, "%s: Unknown VTY reference generation "
+ "mode '%s'\n", prog_name, optarg);
+ exit(2);
+ }
+ break;
+ case 2:
+ fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
+ get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
+ get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
+ vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
+ exit(0);
+ default:
+ fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
+ exit(2);
+ }
+}
+
+static void handle_options(int argc, char **argv)
+{
+ while (1) {
+ int option_idx = 0, c;
+ static int long_option = 0;
+ static const struct option long_options[] = {
+ { "help", 0, 0, 'h' },
+ { "config-file", 1, 0, 'c' },
+ { "version", 0, 0, 'V' },
+ { "daemonize", 0, 0, 'D' },
+ { "vty-port", 1, 0, 'p' },
+ { "ctrl-port", 1, 0, 'r' },
+ { "vty-ref-mode", 1, &long_option, 1 },
+ { "vty-ref-xml", 0, &long_option, 2 },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "hc:p:r:VD",
+ long_options, &option_idx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 0:
+ handle_long_options(argv[0], long_option);
+ break;
+ case 'c':
+ if (config_file)
+ free(config_file);
+ config_file = optarg;
+ config_given = true;
+ break;
+ case 'p':
+ vty_port = atoi(optarg);
+ if (vty_port < 0 || vty_port > 65535) {
+ fprintf(stderr, "Invalid VTY port %d given!\n", vty_port);
+ exit(1);
+ }
+ break;
+ case 'r':
+ ctrl_port = atoi(optarg);
+ if (ctrl_port < 0 || ctrl_port > 65535) {
+ fprintf(stderr, "Invalid CTRL port %d given!\n", ctrl_port);
+ exit(1);
+ }
+ break;
+ case 'V':
+ print_version(1);
+ exit(0);
+ break;
+ case 'D':
+ daemonize = true;
+ break;
+ default:
+ fprintf(stderr, "Unknown option '%c'\n", c);
+ exit(0);
+ break;
+ }
+ }
+
+ if (!config_file)
+ config_file = "osmo-ns-dummy.cfg";
+ if (!vty_port) {
+ fprintf(stderr, "A vty port need to be specified (-p)\n");
+ exit(1);
+ }
+}
+
+void sighandler(int sigset)
+{
+ if (sigset == SIGPIPE)
+ return;
+
+ fprintf(stderr, "Signal %d received.\n", sigset);
+
+ switch (sigset) {
+ case SIGINT:
+ case SIGTERM:
+ /* If another signal is received afterwards, the program
+ * is terminated without finishing shutdown process.
+ */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGUSR1, SIG_DFL);
+ signal(SIGUSR2, SIG_DFL);
+
+ quit = 1;
+ break;
+ case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report and
+ * then run default SIGABRT handler, who will generate coredump
+ * and abort the process. abort() should do this for us after we
+ * return, but program wouldn't exit if an external SIGABRT is
+ * received.
+ */
+ talloc_report_full(tall_nsdummy_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
+ case SIGUSR1:
+ case SIGUSR2:
+ talloc_report_full(tall_nsdummy_ctx, stderr);
+ break;
+ }
+}
+
+extern int g_mirror_mode;
+
+/* called by the ns layer */
+int gprs_ns_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ struct osmo_gprs_ns2_prim *nsp = container_of(oph, struct osmo_gprs_ns2_prim, oph);
+
+ switch (oph->primitive) {
+ case GPRS_NS2_PRIM_UNIT_DATA:
+ if (g_mirror_mode) {
+ /* simply switch indication->request and resubmit */
+ oph->operation = PRIM_OP_REQUEST;
+ msgb_pull_to_l3(oph->msg);
+ nsp->u.unitdata.link_selector = rand(); /* ensure random distribution */
+ return gprs_ns2_recv_prim(g_nsi, oph);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (oph->msg)
+ msgb_free(oph->msg);
+
+ return 0;
+}
+
+int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
+{
+ return 0;
+}
+
+extern int nsdummy_vty_init(void);
+
+int main (int argc, char *argv[])
+{
+ void *ctx = tall_nsdummy_ctx = talloc_named_const(NULL, 0, "osmo-ns-dummy");
+ struct ctrl_handle *ctrl;
+ int rc = 0;
+
+ osmo_init_logging2(ctx, &log_info);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_log_level(osmo_stderr_target, LOGL_INFO);
+ msgb_talloc_ctx_init(ctx, 0);
+ osmo_stats_init(ctx);
+ rate_ctr_init(ctx);
+
+ vty_info.tall_ctx = ctx;
+ vty_init(&vty_info);
+ ctrl_vty_init(ctx);
+ logging_vty_add_cmds();
+ osmo_stats_vty_add_cmds();
+ osmo_talloc_vty_add_cmds();
+
+ handle_options(argc, argv);
+
+ g_nsi = gprs_ns2_instantiate(ctx, gprs_ns_prim_cb, NULL);
+ if (!g_nsi) {
+ LOGP(DLNS, LOGL_ERROR, "Failed to create NS instance\n");
+ exit(1);
+ }
+
+ gprs_ns2_vty_init(g_nsi);
+ nsdummy_vty_init();
+ rc = vty_read_config_file(config_file, NULL);
+ if (rc < 0 && config_given) {
+ fprintf(stderr, "Failed to parse the config file: '%s'\n",
+ config_file);
+ exit(1);
+ }
+ if (rc < 0)
+ fprintf(stderr, "No config file: '%s' Using default config.\n",
+ config_file);
+
+ rc = telnet_init_default(ctx, NULL, vty_port);
+ if (rc < 0) {
+ fprintf(stderr, "Error initializing telnet\n");
+ exit(1);
+ }
+
+ if (ctrl_port > 0) {
+ ctrl = ctrl_interface_setup(NULL, ctrl_port, NULL);
+ if (!ctrl) {
+ fprintf(stderr, "Failed to initialize control interface. Exiting.\n");
+ exit(1);
+ }
+ }
+
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+ signal(SIGPIPE, sighandler);
+ signal(SIGABRT, sighandler);
+ signal(SIGUSR1, sighandler);
+ signal(SIGUSR2, sighandler);
+ osmo_init_ignore_signals();
+
+ if (daemonize) {
+ rc = osmo_daemonize();
+ if (rc < 0) {
+ perror("Error during daemonize");
+ exit(1);
+ }
+ }
+
+ while (!quit) {
+ osmo_select_main(0);
+ }
+
+ telnet_exit();
+ gprs_ns2_free(g_nsi);
+
+ talloc_report_full(tall_nsdummy_ctx, stderr);
+ talloc_free(tall_nsdummy_ctx);
+
+ return 0;
+}
diff --git a/utils/osmo-sim-test.c b/utils/osmo-sim-test.c
index 5588294a..2c6c7641 100644
--- a/utils/osmo-sim-test.c
+++ b/utils/osmo-sim-test.c
@@ -1,5 +1,5 @@
/* libosmosim test application - currently simply dumps a USIM */
-/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2012-2020 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -12,18 +12,21 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License 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 <errno.h>
#include <string.h>
+#include <getopt.h>
#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <limits.h>
+
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/sim/sim.h>
@@ -32,6 +35,9 @@
/* FIXME: this needs to be moved to card_fs_uicc.c */
+static uint8_t g_class = 0x00; /* UICC/USIM */
+static const char *g_output_dir;
+
/* 11.1.1 */
static struct msgb *_select_file(struct osim_chan_hdl *st, uint8_t p1, uint8_t p2,
const uint8_t *data, uint8_t data_len)
@@ -39,7 +45,7 @@ static struct msgb *_select_file(struct osim_chan_hdl *st, uint8_t p1, uint8_t p
struct msgb *msg;
uint8_t *dst;
- msg = osim_new_apdumsg(0x00, 0xA4, p1, p2, data_len, 256);
+ msg = osim_new_apdumsg(g_class, 0xA4, p1, p2, data_len, 256);
dst = msgb_put(msg, data_len);
memcpy(dst, data, data_len);
@@ -58,10 +64,16 @@ static struct msgb *select_adf(struct osim_chan_hdl *st, const uint8_t *adf, uin
static struct msgb *select_file(struct osim_chan_hdl *st, uint16_t fid)
{
uint16_t cfid = htons(fid);
+ uint8_t p2 = 0x04;
- return _select_file(st, 0x00, 0x04, (uint8_t *)&cfid, 2);
+ /* Classic SIM cards don't support 0x04 (Return FCP) */
+ if (g_class == 0xA0)
+ p2 = 0x00;
+
+ return _select_file(st, 0x00, p2, (uint8_t *)&cfid, 2);
}
+#if 0
/* 11.1.9 */
static int verify_pin(struct osim_chan_hdl *st, uint8_t pin_nr, char *pin)
{
@@ -71,7 +83,7 @@ static int verify_pin(struct osim_chan_hdl *st, uint8_t pin_nr, char *pin)
if (strlen(pin) > 8)
return -EINVAL;
- msg = osim_new_apdumsg(0x00, 0x20, 0x00, pin_nr, 8, 0);
+ msg = osim_new_apdumsg(g_class, 0x20, 0x00, pin_nr, 8, 0);
pindst = (char *) msgb_put(msg, 8);
memset(pindst, 0xFF, 8);
/* Do not copy the terminating \0 */
@@ -79,13 +91,14 @@ static int verify_pin(struct osim_chan_hdl *st, uint8_t pin_nr, char *pin)
return osim_transceive_apdu(st, msg);
}
+#endif
/* 11.1.5 */
static struct msgb *read_record_nr(struct osim_chan_hdl *st, uint8_t rec_nr, uint16_t rec_size)
{
struct msgb *msg;
- msg = osim_new_apdumsg(0x00, 0xB2, rec_nr, 0x04, 0, rec_size);
+ msg = osim_new_apdumsg(g_class, 0xB2, rec_nr, 0x04, 0, rec_size);
osim_transceive_apdu(st, msg);
@@ -100,7 +113,7 @@ static struct msgb *read_binary(struct osim_chan_hdl *st, uint16_t offset, uint1
if (offset > 0x7fff || len > 256)
return NULL;
- msg = osim_new_apdumsg(0x00, 0xB0, offset >> 8, offset & 0xff, 0, len & 0xff);
+ msg = osim_new_apdumsg(g_class, 0xB0, offset >> 8, offset & 0xff, 0, len & 0xff);
osim_transceive_apdu(st, msg);
@@ -174,39 +187,111 @@ static int osim_fcp_fd_decode(struct osim_fcp_fd_decoded *ofd, const uint8_t *fc
return 0;
}
-extern struct osim_card_profile *osim_cprof_usim(void *ctx);
+/* TS 51.011 Section 9.3 Type of File */
+static const enum osim_file_type sim2ftype[8] = {
+ [1] = TYPE_MF,
+ [2] = TYPE_DF,
+ [4] = TYPE_EF,
+};
-static struct msgb *try_select_adf_usim(struct osim_chan_hdl *st)
+/* TS 51.011 Section 9.3 Structure of File */
+static const enum osim_ef_type sim2eftype[8] = {
+ [0] = EF_TYPE_TRANSP,
+ [1] = EF_TYPE_RECORD_FIXED,
+ [3] = EF_TYPE_RECORD_CYCLIC,
+};
+
+/* TS 51.011 Section 9.2.1 */
+static int osim_fcp_fd_decode_sim(struct osim_fcp_fd_decoded *ofd, const uint8_t *fcp, int fcp_len)
+{
+ memset(ofd, 0, sizeof(*ofd));
+
+ if (fcp_len < 14)
+ return -EINVAL;
+
+ ofd->type = sim2ftype[fcp[6] & 7];
+ switch (ofd->type) {
+ case TYPE_EF:
+ ofd->ef_type = sim2eftype[fcp[13] & 7];
+ if (fcp_len < 13 + fcp[12])
+ return -EINVAL;
+ switch (ofd->ef_type) {
+ case EF_TYPE_RECORD_FIXED:
+ case EF_TYPE_RECORD_CYCLIC:
+ if (fcp_len < 15)
+ return -EINVAL;
+ ofd->rec_len = fcp[14];
+ ofd->num_rec = ntohs(*(uint16_t *)(fcp+2)) / ofd->rec_len;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TYPE_MF:
+ case TYPE_DF:
+ if (fcp_len < 22)
+ return -EINVAL;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*! scan an UICC for all installed apps; allocate osim_card_app_hdl for each of them */
+static int osim_uicc_scan_apps(struct osim_chan_hdl *st)
{
struct tlv_parsed tp;
struct osim_fcp_fd_decoded ofd;
- struct msgb *msg, *msg2;
+ struct msgb *msg;
uint8_t *cur;
int rc, i;
+ /* we don't know where we currently might be; go back to MF */
+ msg = select_file(st, 0x3f00);
+ if (!msg)
+ return -EIO;
+ if (msgb_apdu_sw(msg) != 0x9000)
+ return -msgb_apdu_sw(msg);
+
+ /* select EF.DIR */
msg = select_file(st, 0x2f00);
+ if (!msg)
+ return -EIO;
+ /* return status word in case of error */
+ if (msgb_apdu_sw(msg) != 0x9000)
+ return -msgb_apdu_sw(msg);
+
+ /* various FCP related sanity checks */
rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0);
- if (rc < 0)
- return NULL;
+ if (rc < 0) {
+ fprintf(stderr, "Error decoding EF.DIR FCP TLV\n");
+ msgb_free(msg);
+ return -EINVAL;
+ }
dump_fcp_template(&tp);
if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) ||
TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 5) {
+ fprintf(stderr, "No EF.DIR FCP file description\n");
msgb_free(msg);
- return NULL;
+ return -EINVAL;
}
rc = osim_fcp_fd_decode(&ofd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC),
TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC));
if (rc < 0) {
+ fprintf(stderr, "Error decoding EF.DIR FCP file description\n");
msgb_free(msg);
- return NULL;
+ return -EINVAL;
}
if (ofd.type != TYPE_EF || ofd.ef_type != EF_TYPE_RECORD_FIXED) {
+ fprintf(stderr, "EF.DIR is not a fixed record EF!?!\n");
msgb_free(msg);
- return NULL;
+ return -EINVAL;
}
msgb_free(msg);
@@ -214,109 +299,147 @@ static struct msgb *try_select_adf_usim(struct osim_chan_hdl *st)
printf("ofd rec_len = %u, num_rec = %u\n", ofd.rec_len, ofd.num_rec);
for (i = 0; i < ofd.num_rec; i++) {
+ const uint8_t *aid;
+ uint8_t aid_len;
msg = read_record_nr(st, i+1, ofd.rec_len);
- if (!msg)
- return NULL;
+ if (!msg) {
+ fprintf(stderr, "Error reading Record %u of EF.DIR, skipping\n", i+1);
+ continue;
+ }
+
+ /* Entries look like this:
+ * 61194f10 a0000000871002ffffffff8907090000 5005 5553696d31 ffffffffffffffffffffff */
cur = msgb_apdu_de(msg);
if (msgb_apdu_le(msg) < 5) {
+ fprintf(stderr, "Record length %u too short for EF.DIR, skipping\n", msgb_apdu_le(msg));
msgb_free(msg);
- return NULL;
+ continue;
}
if (cur[0] != 0x61 || cur[1] < 0x03 || cur[1] > 0x7f ||
cur[2] != 0x4F || cur[3] < 0x01 || cur[3] > 0x10) {
+ fprintf(stderr, "Unexpected/unknown record in EF.DIR: %s, skipping\n",
+ osmo_hexdump_nospc(msgb_apdu_de(msg), msgb_apdu_le(msg)));
msgb_free(msg);
- return NULL;
+ continue;
}
+ aid_len = cur[3];
+ aid = cur+4;
- /* FIXME: actually check if it is an AID that we support, or
- * iterate until we find one that we support */
-
- msg2 = select_adf(st, cur+4, cur[3]);
-
- /* attach the USIM profile, FIXME: do this based on AID match */
- st->card->prof = osim_cprof_usim(st->card);
- st->cwd = osim_file_desc_find_name(st->card->prof->mf, "ADF.USIM");
-
- msgb_free(msg);
-
- return msg2;
+ /* FIXME: parse / pass label*/
+ printf("Detected AID %s\n", osmo_hexdump_nospc(aid, aid_len));
+ osim_card_hdl_add_app(st->card, aid, aid_len, NULL);
}
- return NULL;
+ return i;
}
-static int dump_file(struct osim_chan_hdl *chan, uint16_t fid)
+
+extern struct osim_card_profile *osim_cprof_sim(void *ctx);
+extern struct osim_card_profile *osim_cprof_uicc(void *ctx, bool have_df_gsm);
+
+static int dump_file(struct osim_chan_hdl *chan, const char *short_name, uint16_t fid)
{
struct tlv_parsed tp;
struct osim_fcp_fd_decoded ffdd;
struct msgb *msg, *rmsg;
int rc, i, offset;
+ FILE *f_data = NULL;
+ /* Select the file */
msg = select_file(chan, fid);
if (!msg) {
- printf("Unable to select file\n");
+ fprintf(stderr, "Unable to select file\n");
return -EIO;
}
- printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg)));
if (msgb_apdu_sw(msg) != 0x9000) {
- printf("status 0x%04x selecting file\n", msgb_apdu_sw(msg));
+ fprintf(stderr, "status 0x%04x selecting file\n", msgb_apdu_sw(msg));
goto out;
}
- rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0);
- if (rc < 0) {
- printf("Unable to parse FCP\n");
- goto out;
- }
+ if (g_class != 0xA0) {
+ rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0);
+ if (rc < 0) {
+ fprintf(stderr, "Unable to parse FCP: %s\n", msgb_hexdump(msg));
+ goto out;
+ }
- if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) ||
- TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 2) {
- printf("No file descriptor present ?!?\n");
- goto out;
+ if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) ||
+ TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 2) {
+ fprintf(stderr, "No file descriptor present ?!?\n");
+ goto out;
+ }
+
+ rc = osim_fcp_fd_decode(&ffdd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC),
+ TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC));
+ } else {
+ rc = osim_fcp_fd_decode_sim(&ffdd, msgb_apdu_de(msg), msgb_apdu_le(msg));
}
- rc = osim_fcp_fd_decode(&ffdd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC),
- TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC));
if (rc < 0) {
- printf("Unable to decode File Descriptor\n");
+ fprintf(stderr, "Unable to decode File Descriptor\n");
goto out;
}
if (ffdd.type != TYPE_EF) {
- printf("File Type != EF\n");
+ fprintf(stderr, "File Type != EF\n");
goto out;
}
+ if (g_output_dir) {
+ f_data = fopen(short_name, "w");
+ if (!f_data) {
+ fprintf(stderr, "Couldn't create '%s': %s\n", short_name, strerror(errno));
+ goto out;
+ }
+ }
+
printf("EF type: %u\n", ffdd.ef_type);
switch (ffdd.ef_type) {
case EF_TYPE_RECORD_FIXED:
for (i = 0; i < ffdd.num_rec; i++) {
+ const char *hex;
rmsg = read_record_nr(chan, i+1, ffdd.rec_len);
- if (!rmsg)
+ if (!rmsg) {
+ if (f_data)
+ fclose(f_data);
return -EIO;
- printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg)));
- printf("Rec %03u: %s\n", i+1,
- osmo_hexdump(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg)));
+ }
+ printf("SW: %s\n", osim_print_sw(chan, msgb_apdu_sw(msg)));
+
+ hex = osmo_hexdump_nospc(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg));
+ printf("Rec %03u: %s\n", i+1, hex);
+ if (f_data)
+ fprintf(f_data, "%s\n", hex);
}
break;
case EF_TYPE_TRANSP:
- if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_SIZE))
+ if (g_class != 0xA0) {
+ if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_SIZE))
+ goto out;
+ i = ntohs(*(uint16_t *)TLVP_VAL(&tp, UICC_FCP_T_FILE_SIZE));
+ printf("File size: %d bytes\n", i);
+ } else {
+ fprintf(stderr, "Can not determine file size, invalid EF-type!\n");
goto out;
- i = ntohs(*(uint16_t *)TLVP_VAL(&tp, UICC_FCP_T_FILE_SIZE));
- printf("File size: %d bytes\n", i);
-
+ }
for (offset = 0; offset < i-1; ) {
uint16_t remain_len = i - offset;
uint16_t read_len = OSMO_MIN(remain_len, 256);
+ const char *hex;
rmsg = read_binary(chan, offset, read_len);
- if (!rmsg)
+ if (!rmsg) {
+ if (f_data)
+ fclose(f_data);
return -EIO;
+ }
offset += read_len;
- printf("Content: %s\n",
- osmo_hexdump(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg)));
+ hex = osmo_hexdump_nospc(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg));
+ printf("Content: %s\n", hex);
+ if (f_data)
+ fprintf(f_data, "%s", hex);
}
break;
default:
@@ -324,18 +447,191 @@ static int dump_file(struct osim_chan_hdl *chan, uint16_t fid)
}
out:
+ if (f_data)
+ fclose(f_data);
msgb_free(msg);
return -EINVAL;
+
+}
+
+static void print_help(void)
+{
+ printf( "osmo-sim-test Usage:\n"
+ " -h --help This message\n"
+ " -n --reader-num NR Open reader number NR\n"
+ " -o --output-dir DIR To-be-created output directory for filesystem dump\n"
+ );
}
+static int readernum = 0;
+
+static void handle_options(int argc, char **argv)
+{
+ while (1) {
+ int option_index = 0, c;
+ const struct option long_options[] = {
+ { "help", 0, 0, 'h' },
+ { "reader-num", 1, 0, 'n' },
+ { "output-dir", 1, 0, 'o' },
+ {0,0,0,0}
+ };
+
+ c = getopt_long(argc, argv, "hn:o:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 'n':
+ readernum = atoi(optarg);
+ break;
+ case 'o':
+ g_output_dir = optarg;
+ break;
+ default:
+ exit(2);
+ break;
+ }
+ }
+
+ if (argc > optind) {
+ fprintf(stderr, "Unsupported positional arguments on command line\n");
+ exit(2);
+ }
+}
+
+
+static void mkdir_and_chdir(const char *name, mode_t mode)
+{
+ int rc;
+ rc = mkdir(name, mode);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot create '%s': %s\n", name, strerror(errno));
+ exit(24);
+ }
+ rc = chdir(name);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot change to just-created '%s': %s\n", name, strerror(errno));
+ exit(24);
+ }
+}
+
+
+static void iterate_fs(struct osim_chan_hdl *chan)
+{
+ const struct osim_file_desc *prev_cwd;
+ struct osim_file_desc *ofd;
+
+ /* iterate over all files in current working directory */
+ llist_for_each_entry(ofd, &chan->cwd->child_list, list) {
+ struct msgb *m;
+ char prev_dir[PATH_MAX];
+
+ printf("\n\n================ %s (%s) ==================\n",
+ ofd->short_name, ofd->long_name);
+
+ m = select_file(chan, ofd->fid);
+ if (msgb_apdu_sw(m) != 0x9000) {
+ msgb_free(m);
+ continue;
+ }
+ dump_fcp_template_msg(m);
+ msgb_free(m);
+
+ /* If this is a DF, recurse into it */
+ switch (ofd->type) {
+ case TYPE_DF:
+ /* the select above has just changed into this directory */
+ prev_cwd = chan->cwd;
+ chan->cwd = ofd;
+ if (g_output_dir) {
+ if (!getcwd(prev_dir, sizeof(prev_dir))) {
+ fprintf(stderr, "Cannot determine cwd: %s\n", strerror(errno));
+ exit(23);
+ continue;
+ }
+ mkdir_and_chdir(ofd->short_name, 0750);
+ }
+ iterate_fs(chan);
+ /* "pop" the directory from the stack */
+ chan->cwd = prev_cwd;
+ if (g_output_dir)
+ OSMO_ASSERT(chdir("..") == 0);
+ break;
+ default:
+ dump_file(chan, ofd->short_name, ofd->fid);
+ break;
+ }
+ }
+}
+
+static void iterate_apps(struct osim_chan_hdl *chan)
+{
+ struct osim_card_app_hdl *cah;
+
+ llist_for_each_entry(cah, &chan->card->apps, list) {
+ const struct osim_card_app_profile *cap = cah->prof;
+ struct msgb *msg;
+
+ if (!cap) {
+ fprintf(stderr, "Unknown AID %s; skipping\n",
+ osmo_hexdump_nospc(cah->aid, cah->aid_len));
+ continue;
+ }
+
+ msg = select_adf(chan, cah->aid, cah->aid_len);
+ if (!msg) {
+ fprintf(stderr, "Error selectiong ADF for AID %s; skipping\n",
+ osmo_hexdump_nospc(cah->aid, cah->aid_len));
+ continue;
+ }
+ printf("SW: %s\n", osim_print_sw(chan, msgb_apdu_sw(msg)));
+ chan->cur_app = cah;
+ chan->cwd = cap->adf;
+
+ if (g_output_dir)
+ mkdir_and_chdir(cap->adf->short_name, 0750);
+
+ iterate_fs(chan);
+
+ if (g_output_dir)
+ OSMO_ASSERT(chdir("..") == 0);
+ }
+}
+
+
int main(int argc, char **argv)
{
struct osim_reader_hdl *reader;
struct osim_card_hdl *card;
struct osim_chan_hdl *chan;
- struct msgb *msg;
+ int rc;
- reader = osim_reader_open(OSIM_READER_DRV_PCSC, 0, "", NULL);
+ handle_options(argc, argv);
+
+ osim_init(NULL);
+
+ if (g_output_dir) {
+ int rc;
+ rc = mkdir(g_output_dir, 0750);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot create directory '%s': %s\n", g_output_dir,
+ strerror(errno));
+ exit(5);
+ }
+ rc = chdir(g_output_dir);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot change to just-created directory '%s': %s\n",
+ g_output_dir, strerror(errno));
+ exit(5);
+ }
+ }
+
+ reader = osim_reader_open(OSIM_READER_DRV_PCSC, readernum, "", NULL);
if (!reader)
exit(1);
card = osim_card_open(reader, OSIM_PROTO_T0);
@@ -345,36 +641,26 @@ int main(int argc, char **argv)
if (!chan)
exit(3);
- msg = try_select_adf_usim(chan);
- if (!msg || msgb_apdu_sw(msg) != 0x9000)
+ //verify_pin(chan, 1, "1653");
+
+ rc = osim_uicc_scan_apps(chan);
+ if (rc >= 0) {
+ chan->card->prof = osim_cprof_uicc(chan->card, true);
+ chan->cwd = chan->card->prof->mf;
+ } else if (rc == -0x6e00) {
+ /* CLA not supported: must be classic SIM, not USIM */
+ g_class = 0xA0;
+ chan->card->prof = osim_cprof_sim(chan->card);
+ chan->cwd = chan->card->prof->mf;
+ } else if (rc < 0) {
exit(4);
- dump_fcp_template_msg(msg);
- msgb_free(msg);
-
- msg = select_file(chan, 0x6fc5);
- dump_fcp_template_msg(msg);
- printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg)));
- msgb_free(msg);
-
- verify_pin(chan, 1, "1653");
-
- msg = select_file(chan, 0x6f06);
- dump_fcp_template_msg(msg);
- msgb_free(msg);
+ }
- {
- struct osim_file_desc *ofd;
- llist_for_each_entry(ofd, &chan->cwd->child_list, list) {
- struct msgb *m;
- printf("\n\n================ %s (%s) ==================\n",
- ofd->short_name, ofd->long_name);
+ /* first iterate over normal file system */
+ iterate_fs(chan);
- m = select_file(chan, ofd->fid);
- dump_fcp_template_msg(m);
- msgb_free(m);
- dump_file(chan, ofd->fid);
- }
- }
+ /* then itereate over all apps and their file system */
+ iterate_apps(chan);
exit(0);
}
diff --git a/utils/osmo-stat-dummy/Makefile.am b/utils/osmo-stat-dummy/Makefile.am
new file mode 100644
index 00000000..0232736b
--- /dev/null
+++ b/utils/osmo-stat-dummy/Makefile.am
@@ -0,0 +1,10 @@
+if ENABLE_UTILITIES
+noinst_PROGRAMS = osmo-stat-dummy
+osmo_stat_dummy_SOURCES = osmo-stat-dummy.c
+osmo_stat_dummy_LDADD = $(TALLOC_LIBS) \
+ $(top_builddir)/src/vty/libosmovty.la \
+ $(top_builddir)/src/ctrl/libosmoctrl.la \
+ $(top_builddir)/src/core/libosmocore.la
+osmo_stat_dummy_CFLAGS = -Wall $(TALLOC_CFLAGS)
+osmo_stat_dummy_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
+endif
diff --git a/utils/osmo-stat-dummy/README.md b/utils/osmo-stat-dummy/README.md
new file mode 100644
index 00000000..34ffbb4c
--- /dev/null
+++ b/utils/osmo-stat-dummy/README.md
@@ -0,0 +1,12 @@
+# Osmocom utilities
+
+* osmo-stat-dummy: utility for rate counter and statsd testing
+
+It has 2 rate counters: one ticks twice a seconds, another one can be manually updated with 'update-rate-ctr' command via vty.
+
+The raw value is sent via statsd protocol. If you install "netdata" monitoring tool than you can open http://localhost:19999 in browser
+and observe live counters monitoring under "StatsD dummy" without any additional setup.
+
+Opening osmo-stat-dummy.html in browser while both netdata and osmo-stat-dummy are running will show dimensioned (per min/hour/day) rate counters as well as raw data.
+
+The latter is handy for troubleshooting and comparing libosmocore's internal rate counter computation.
diff --git a/utils/osmo-stat-dummy/osmo-stat-dummy.c b/utils/osmo-stat-dummy/osmo-stat-dummy.c
new file mode 100644
index 00000000..37da7b32
--- /dev/null
+++ b/utils/osmo-stat-dummy/osmo-stat-dummy.c
@@ -0,0 +1,335 @@
+/* Rate counter and statsd test application */
+/* (C) 2022 by by sysmocom - s.f.m.c. GmbH
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/ctrl/control_if.h>
+#include <osmocom/ctrl/control_vty.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/ports.h>
+#include <osmocom/vty/tdef_vty.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/misc.h>
+
+#include "config.h"
+
+void *tall_statdummy_ctx = NULL;
+static bool quit = false;
+static bool config_given = false;
+struct rate_ctr_group *g_ctrg;
+
+enum dummy_rate_ctr_idx {
+ DUMMY_VTY = 0,
+ DUMMY_AUTO,
+};
+
+static void print_help(void)
+{
+ printf("Some useful options:\n"
+ " -h --help This text\n"
+ " -c --config-file Specify the filename of the config file\n"
+ " -V --version Print version\n"
+ "\nVTY reference generation:\n"
+ " --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
+ " --vty-ref-xml Generate the VTY reference XML output and exit.\n"
+ );
+}
+
+static void handle_long_options(const char *prog_name, const int long_option)
+{
+ static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
+
+ switch (long_option) {
+ case 1:
+ vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
+ if (vty_ref_mode < 0) {
+ fprintf(stderr, "%s: Unknown VTY reference generation mode '%s'\n", prog_name, optarg);
+ exit(2);
+ }
+ break;
+ case 2:
+ fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
+ get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
+ get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
+ vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
+ exit(0);
+ default:
+ fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
+ exit(2);
+ }
+}
+
+static char *handle_options(int argc, char **argv)
+{
+ char *config_file = NULL;
+
+ while (1) {
+ int option_idx = 0, c;
+ static int long_option = 0;
+ static const struct option long_options[] = {
+ { "help", 0, 0, 'h' },
+ { "config-file", 1, 0, 'c' },
+ { "version", 0, 0, 'V' },
+ { "vty-ref-mode", 1, &long_option, 1 },
+ { "vty-ref-xml", 0, &long_option, 2 },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "hc:V", long_options, &option_idx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 0:
+ handle_long_options(argv[0], long_option);
+ break;
+ case 'c':
+ if (config_file)
+ free(config_file);
+ config_file = optarg;
+ config_given = true;
+ break;
+ case 'V':
+ print_version(1);
+ exit(0);
+ break;
+ default:
+ fprintf(stderr, "Unknown option '%c'\n", c);
+ exit(0);
+ break;
+ }
+ }
+
+ if (!config_file)
+ return "osmo-stat-dummy.cfg";
+
+ return config_file;
+}
+
+void sighandler(int sigset)
+{
+ if (sigset == SIGPIPE)
+ return;
+
+ fprintf(stderr, "Signal %d received.\n", sigset);
+
+ switch (sigset) {
+ case SIGINT:
+ case SIGTERM:
+ /* If another signal is received afterwards, the program
+ * is terminated without finishing shutdown process.
+ */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGABRT, SIG_DFL);
+ signal(SIGUSR1, SIG_DFL);
+ signal(SIGUSR2, SIG_DFL);
+
+ quit = 1;
+ break;
+ case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report and
+ * then run default SIGABRT handler, who will generate coredump
+ * and abort the process. abort() should do this for us after we
+ * return, but program wouldn't exit if an external SIGABRT is
+ * received.
+ */
+ talloc_report_full(tall_statdummy_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
+ case SIGUSR1:
+ case SIGUSR2:
+ talloc_report_full(tall_statdummy_ctx, stderr);
+ break;
+ }
+}
+
+static int rate_ctr_timer_cb(struct osmo_fd *ofd, unsigned int what)
+{
+ uint64_t expire_count;
+ int rc;
+
+ /* check that the timer has actually expired */
+ if (!(what & OSMO_FD_READ))
+ return 0;
+
+ /* read from timerfd: number of expirations of periodic timer */
+ rc = read(ofd->fd, (void *) &expire_count, sizeof(expire_count));
+ if (rc < 0 && errno == EAGAIN)
+ return 0;
+
+ OSMO_ASSERT(rc == sizeof(expire_count));
+
+ if (expire_count > 1)
+ LOGP(DLGLOBAL, LOGL_NOTICE, "Stats timer expire_count=%" PRIu64 ": We missed %" PRIu64 " timers\n",
+ expire_count, expire_count-1);
+
+ /* Increment the counter value */
+ rate_ctr_inc(rate_ctr_group_get_ctr(g_ctrg, DUMMY_AUTO));
+
+ return 0;
+}
+
+DEFUN(update_rate_ctr, update_rate_ctr_cmd,
+ "update-rate-ctr <0-100000>",
+ "Update dummy rate counter\n"
+ "Value to add to rate counter\n")
+{
+ rate_ctr_add(rate_ctr_group_get_ctr(g_ctrg, DUMMY_VTY), atoi(argv[0]));
+
+ return CMD_SUCCESS;
+}
+
+static int statdummy_vty_init(void)
+{
+ install_element_ve(&update_rate_ctr_cmd);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct log_info log_info = {};
+ char *config_file;
+ void *ctx = tall_statdummy_ctx = talloc_named_const(NULL, 0, "osmo-stat-dummy");
+ struct ctrl_handle *ctrl;
+ struct osmo_fd rate_ctr_timer = { .fd = -1 };
+ struct timespec ts_interval = { .tv_sec = 0, .tv_nsec = 500000000 }; /* 0.5 seconds */
+ int rc = 0;
+
+ const char vty_copyright[] =
+ "Copyright (C) 2022 by by sysmocom - s.f.m.c. GmbH\r\n"
+ "Author: Max Suraev <msuraev@sysmocom.de>\r\n"
+ "License GNU GPL version 3 or later\r\n"
+ "This is free software: you are free to change and redistribute it.\r\n"
+ "There is NO WARRANTY, to the extent permitted by law.\r\n";
+
+ struct vty_app_info vty_info = {
+ .name = "OsmoSTATdummy",
+ .version = PACKAGE_VERSION,
+ .copyright = vty_copyright,
+ .tall_ctx = ctx
+ };
+
+ const struct rate_ctr_desc dummy_ctr_desc[] = {
+ [DUMMY_VTY] = { "dummy:vty", "Dummy counter updated via VTY" },
+ [DUMMY_AUTO] = { "dummy:auto", "Dummy counter autoupdated via timer" },
+ };
+
+ const struct rate_ctr_group_desc dummy_ctrg_desc = {
+ "dummy",
+ "dummy stat tester",
+ OSMO_STATS_CLASS_GLOBAL,
+ ARRAY_SIZE(dummy_ctr_desc),
+ dummy_ctr_desc,
+ };
+
+ osmo_init_logging2(ctx, &log_info);
+ log_set_use_color(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
+ log_set_log_level(osmo_stderr_target, LOGL_INFO);
+
+ msgb_talloc_ctx_init(ctx, 0);
+
+ vty_init(&vty_info);
+ ctrl_vty_init(ctx);
+ logging_vty_add_cmds();
+ osmo_stats_vty_add_cmds();
+ osmo_talloc_vty_add_cmds();
+
+ config_file = handle_options(argc, argv);
+
+ statdummy_vty_init();
+ rc = vty_read_config_file(config_file, NULL);
+ if (rc < 0) {
+ if (config_given) {
+ fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
+ exit(1);
+ }
+ fprintf(stderr, "No config file: '%s' Using default config.\n", config_file);
+ }
+
+ rc = telnet_init_default(ctx, NULL, -1);
+ if (rc < 0) {
+ fprintf(stderr, "Error initializing telnet\n");
+ exit(1);
+ }
+
+ ctrl = ctrl_interface_setup(NULL, 1234, NULL);
+ if (!ctrl) {
+ fprintf(stderr, "Failed to initialize control interface. Exiting.\n");
+ exit(1);
+ }
+
+ g_ctrg = rate_ctr_group_alloc(ctx, &dummy_ctrg_desc, 0);
+ if (!g_ctrg) {
+ fprintf(stderr, "Failed to initialize rate counters. Exiting.\n");
+ return -1;
+ }
+
+ osmo_stats_init(ctx);
+ rate_ctr_init(ctx);
+
+ rc = osmo_timerfd_setup(&rate_ctr_timer, rate_ctr_timer_cb, NULL);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Failed to setup the timer with error code %d (fd=%d)\n",
+ rc, rate_ctr_timer.fd);
+ return rc;
+ }
+
+ rc = osmo_timerfd_schedule(&rate_ctr_timer, NULL, &ts_interval);
+ if (rc < 0) {
+ LOGP(DLGLOBAL, LOGL_ERROR, "Failed to schedule the timer with error code %d (fd=%d)\n",
+ rc, rate_ctr_timer.fd);
+ }
+
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+ signal(SIGPIPE, sighandler);
+ signal(SIGABRT, sighandler);
+ signal(SIGUSR1, sighandler);
+ signal(SIGUSR2, sighandler);
+ osmo_init_ignore_signals();
+
+ while (!quit) {
+ osmo_select_main(0);
+ }
+
+ telnet_exit();
+
+ talloc_report_full(tall_statdummy_ctx, stderr);
+ talloc_free(tall_statdummy_ctx);
+
+ return 0;
+}
diff --git a/utils/osmo-stat-dummy/osmo-stat-dummy.cfg b/utils/osmo-stat-dummy/osmo-stat-dummy.cfg
new file mode 100644
index 00000000..559fe4ed
--- /dev/null
+++ b/utils/osmo-stat-dummy/osmo-stat-dummy.cfg
@@ -0,0 +1,40 @@
+log stderr
+ logging filter all 1
+ logging color 0
+ logging timestamp 0
+ logging print extended-timestamp 0
+ logging print category-hex 0
+ logging print category 1
+ !logging print file basename
+ ! log-levels defined by libosmocore and hence available everywhere, can be overridden by inidividual per-app configs below
+ logging level lstats notice
+ logging level lglobal notice
+ logging level llapd notice
+ logging level linp notice
+ logging level lmux notice
+ logging level lmi notice
+ logging level lmib notice
+ logging level lsms notice
+ logging level lctrl notice
+ logging level lgtp notice
+ logging level lgsup notice
+ logging level loap notice
+ logging level lss7 error
+ logging level lsccp notice
+ logging level lsua notice
+ logging level lm3ua notice
+ logging level lmgcp notice
+ logging level ljibuf notice
+ logging level lrspro notice
+stats reporter statsd
+ !use default https://learn.netdata.cloud/ statsd plugin:
+ remote-ip 127.0.0.1
+ remote-port 8125
+ level global
+ no prefix
+ enable
+stats interval 1
+line vty
+ bind 127.0.0.2 6969
+ctrl
+ bind 127.0.0.11 1234
diff --git a/utils/osmo-stat-dummy/osmo-stat-dummy.html b/utils/osmo-stat-dummy/osmo-stat-dummy.html
new file mode 100644
index 00000000..fc772573
--- /dev/null
+++ b/utils/osmo-stat-dummy/osmo-stat-dummy.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+</head>
+<body>
+ <div data-netdata="statsd_dummy.0.dummy.auto_counter"
+ data-chart-library="dygraph"
+ data-title="raw data (timerfd ticks twice per second)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ ></div>
+ <div data-netdata="statsd_dummy.0.dummy.auto_counter"
+ data-chart-library="dygraph"
+ data-title="aggregated per minute (timerfd ticks twice per second)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ data-method="average"
+ data-gtime="60"
+ data-units="events/min"
+ ></div>
+ <div data-netdata="statsd_dummy.0.dummy.auto_counter"
+ data-chart-library="dygraph"
+ data-title="aggregated per hour (timerfd ticks twice per second)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ data-method="average"
+ data-gtime="3600"
+ data-units="events/hour"
+ ></div>
+ <div data-netdata="statsd_dummy.0.dummy.auto_counter"
+ data-chart-library="dygraph"
+ data-title="aggregated per day (timerfd ticks twice per second)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ data-method="average"
+ data-gtime="86400"
+ data-units="events/day"
+ ></div>
+
+ <div data-netdata="statsd_dummy.0.dummy.vty_counter"
+ data-chart-library="dygraph"
+ data-title="raw data (updated manually via vty command)"
+ data-width="600"
+ data-height="200"
+ data-after="-600"
+ ></div>
+</body>
+<script type="text/javascript" src="http://localhost:19999/dashboard.js"></script>
+</html>