aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--TODO-RELEASE19
-rw-r--r--contrib/libosmocore.spec.in14
-rw-r--r--debian/changelog250
-rw-r--r--debian/control6
-rw-r--r--debian/libosmocore22.install (renamed from debian/libosmocore21.install)0
-rw-r--r--include/osmocom/codec/codec.h3
-rw-r--r--include/osmocom/core/Makefile.am1
-rw-r--r--include/osmocom/core/jhash.h171
-rw-r--r--include/osmocom/core/osmo_io.h2
-rw-r--r--include/osmocom/gprs/gprs_bssgp.h6
-rw-r--r--include/osmocom/gsm/Makefile.am1
-rw-r--r--include/osmocom/gsm/bts_features.h2
-rw-r--r--include/osmocom/gsm/gsm48.h2
-rw-r--r--include/osmocom/gsm/protocol/gsm_08_08.h3
-rw-r--r--include/osmocom/gsm/protocol/gsm_08_58.h1
-rw-r--r--include/osmocom/gsm/rtp_extensions.h23
-rw-r--r--osmo-release.mk9
-rwxr-xr-xosmo-release.sh105
-rw-r--r--src/codec/Makefile.am3
-rw-r--r--src/codec/gsm620.c33
-rw-r--r--src/codec/hr_sid_class.c176
-rw-r--r--src/coding/Makefile.am2
-rw-r--r--src/core/Makefile.am2
-rw-r--r--src/core/socket.c53
-rw-r--r--src/ctrl/Makefile.am2
-rw-r--r--src/gb/Makefile.am2
-rw-r--r--src/gb/gprs_bssgp.c35
-rw-r--r--src/gb/libosmogb.map2
-rw-r--r--src/gsm/Makefile.am2
-rw-r--r--src/gsm/bts_features.c4
-rw-r--r--src/gsm/gsm0808.c3
-rw-r--r--src/gsm/gsm23236.c4
-rw-r--r--src/gsm/gsm48.c28
-rw-r--r--src/gsm/iuup.c6
-rw-r--r--src/gsm/libosmogsm.map2
-rw-r--r--src/gsm/rsl.c1
-rw-r--r--src/isdn/Makefile.am2
-rw-r--r--src/sim/Makefile.am2
-rw-r--r--src/sim/class_tables.c52
-rw-r--r--src/vty/Makefile.am2
-rw-r--r--src/vty/fsm_vty.c39
-rw-r--r--tests/Makefile.am32
-rw-r--r--tests/codec/codec_efr_sid_test.c97
-rw-r--r--tests/codec/codec_efr_sid_test.in183
-rw-r--r--tests/codec/codec_efr_sid_test.ok175
-rw-r--r--tests/codec/codec_fr_sid_test.c97
-rw-r--r--tests/codec/codec_fr_sid_test.in184
-rw-r--r--tests/codec/codec_fr_sid_test.ok175
-rw-r--r--tests/codec/codec_hr_sid_test.c144
-rw-r--r--tests/codec/codec_hr_sid_test.in61
-rw-r--r--tests/codec/codec_hr_sid_test.ok40
-rw-r--r--tests/jhash/jhash_test.c56
-rw-r--r--tests/jhash/jhash_test.ok25
-rw-r--r--tests/sim/sim_test.c8
-rw-r--r--tests/sim/sim_test.ok4
-rw-r--r--tests/testsuite.at24
57 files changed, 2260 insertions, 122 deletions
diff --git a/README.md b/README.md
index ed9bcd6b..4c914621 100644
--- a/README.md
+++ b/README.md
@@ -70,7 +70,7 @@ Contributing
Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
-We us a gerrit based patch submission/review process for managing
+We use a Gerrit based patch submission/review process for managing
contributions. Please see
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
more details
diff --git a/TODO-RELEASE b/TODO-RELEASE
index ce041627..8a1e0dc9 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -3,22 +3,9 @@
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# LIBVERSION=c:r:a
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
-# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
+# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
# 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 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.
+gb ADD add bssgp_parse_cell_id2/bssgp_create_cell_id2
+gsm ADD add osmo_rai_to_gprs/gprs_rai_to_osmo
diff --git a/contrib/libosmocore.spec.in b/contrib/libosmocore.spec.in
index 60ca3977..f16f7999 100644
--- a/contrib/libosmocore.spec.in
+++ b/contrib/libosmocore.spec.in
@@ -115,13 +115,13 @@ transcoding routines.
This subpackage contains libraries and header files for developing
applications that want to make use of libosmocoding.
-%package -n libosmocore21
+%package -n libosmocore22
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
+%description -n libosmocore22
libosmocore is a library with various utility functions shared
between OpenBSC and OsmocomBB.
@@ -130,7 +130,7 @@ 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: libosmocore22 = %version
Requires: libtalloc-devel
Requires: lksctp-tools-devel
@@ -367,8 +367,8 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%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 libosmocore22 -p /sbin/ldconfig
+%postun -n libosmocore22 -p /sbin/ldconfig
%post -n libosmoctrl0 -p /sbin/ldconfig
%postun -n libosmoctrl0 -p /sbin/ldconfig
%post -n libosmogb14 -p /sbin/ldconfig
@@ -412,9 +412,9 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%_libdir/libosmocoding.so
%_libdir/pkgconfig/libosmocoding.pc
-%files -n libosmocore21
+%files -n libosmocore22
%defattr(-,root,root)
-%_libdir/libosmocore.so.21*
+%_libdir/libosmocore.so.22*
%files -n libosmocore-devel
%defattr(-,root,root)
diff --git a/debian/changelog b/debian/changelog
index c9184df0..f80c4e49 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,253 @@
+libosmocore (1.10.0) unstable; urgency=medium
+
+ [ Mychaela N. Falconia ]
+ * gsm/protocol/gsm_04_11.h: add SMSC-address length limit definitions
+ * gsm0808: add knowledge of TW-TS-003 BSSMAP IE
+ * bts_features: add feature flags for TWTS001 and TWTS002
+ * rsl: define RSL_IE_OSMO_RTP_EXTENSIONS
+ * include/osmocom/gsm: add rtp_extensions.h
+ * codec: make osmo_hr_check_sid() more efficient
+
+ [ arehbein ]
+ * write_queue: Enable updating max_length field
+ * osmo_io: Clean up code
+ * gsmtap: Hide implementation of gsmtap_inst
+ * osmo_io_poll: Handle -EAGAIN in case of OSMO_FD_WRITE
+ * write_queue: Fix Doxygen comment
+ * gsmtap_util: Use Osmo IO instead of Osmo write queues
+ * gsmtap_util: Simplify sink
+ * gmstap_util: Fix sending out gsmtap messages
+ * ns2: Improve code consistency
+
+ [ Manawyrm ]
+ * gsmtap.h: Add definitions for ISDN PPP sub-type
+ * gsm48_ie.c: add 3.1kHz audio bearer capability for CSD calls
+ * gsm48_ie.c: change bearer cap structure in outgoing CSD calls
+ * logging: ensure ANSI color escape is sent in same line/before newline
+
+ [ Karsten Ohme ]
+ * APDU parsing support for GlobalPlatform GET RESPONSE
+
+ [ Vadim Yanitskiy ]
+ * gsm: add gsm0502_fn_compare() for comparing TDMA FNs
+ * gsm: rename s/gsm0502_fn_compare/gsm0502_fncmp/
+ * msgb: fix doxygen docs for msgb_pull_u{8,16,32): end -> front
+ * coding: gsm0503_tch_a[fh]s_encode(): improve cmr/ft checks
+ * coding: gsm0503_tch_a[fh]s_encode(): make *codec const
+ * tests/testsuite.at: remove copy-pasted 'touch experr'
+ * soft_uart: add osmo_soft_uart_free()
+ * soft_uart: add doxygen documentation
+ * soft_uart: split osmo_soft_uart_enable()
+ * soft_uart: make osmo_soft_uart_alloc() accept *cfg
+ * soft_uart: rework osmo_uart_rx_bit() to use flow state
+ * soft_uart: implement parity checking for the receiver
+ * soft_uart: implement the transmitter
+ * soft_uart: allow manually flushing the receive buffer
+ * soft_uart: add unit tests for the receiver and transmitter
+ * soft_uart: fix Rx buffer flushing logic in suart_rx_ch()
+ * soft_uart: fix handling of num_data_bits < 8
+ * soft_uart: implement OSMO_SUART_PARITY_{MARK,SPACE}
+ * soft_uart: demonstrate a problem with osmo_soft_uart_tx_ubits()
+ * soft_uart: fix pulling a small number of Tx bits
+ * soft_uart: check n_bits against 0 in osmo_soft_uart_tx_ubits()
+ * soft_uart: fix spelling in doxygen docs
+ * soft_uart: add osmo_soft_uart_{get,set}_name()
+ * soft_uart: fix doxygen doc for osmo_soft_uart_tx_ubits()
+ * soft_uart: check Rx/Tx state once in osmo_soft_uart_{rx,tx}_ubits()
+ * soft_uart: cosmetic: do not use 'osmo_' prefix for static symbols
+ * soft_uart: improve doxygen documentation
+ * soft_uart: osmo_soft_uart_tx_ubits(): return number of bits pulled
+ * soft_uart: implement modem status lines and flow control
+ * coding: fix doxygen docs for gsm0503_pdtch[_egprs]_decode()
+ * coding: clarify the USF decoding for PDCH blocks
+ * coding: gsm0503_pdtch_decode(): implement USF decoding for CS1
+ * tests/coding: fix -Wmaybe-uninitialized in test_pdtch()
+ * tests/it_q: add tc_enqueue/dequeue testcase
+ * core: fix wrong logic in _osmo_it_q_dequeue()
+ * tests/soft_uart: assert that osmo_soft_uart_rx_ubits() returns 0
+ * tests/soft_uart: cosmetic: improve readability of the test output
+ * soft_uart: cosmetic: use consistent naming for the Rx buffer msgb
+ * logging: fix NULL pointer dereference in _output_buf()
+ * soft_uart: fix the Rx flushing logic, add a unit test
+ * soft_uart: demonstrate a problem with manual flush()ing
+ * soft_uart: demonstrate a problem with inefficient polling
+ * tests/lapd: fix wrong size passed in test_lapdm_contention_resolution()
+ * tests/gsm0808: fix assert()s in test_gsm0808_dec_cell_id_list_srvcc()
+ * tests/iuup: fix assert()s in test_decode_passive_init_2_rfci_no_iptis()
+ * tests/fsm: also test .onenter and .onleave callbacks
+ * tests: fix update-exp: soft_uart_test overwrites rlp_test.ok
+ * utils: fix OSMO_STRBUF_REMAIN to handle sb.pos == NULL correctly
+ * utils: improve readability of OSMO_STRBUF_CHAR_COUNT
+ * utils: fix -Wsign-compare in definition of OSMO_STRBUF_CHAR_COUNT
+ * utils: osmo_bcd2str(): fix applying non-zero offset to null pointer
+ * tests/utils: do not test strbuf_example2() with buf=NULL
+ * bitvec: bitvec_to_string_r(): drop unused variable
+ * tests/{gb,iuup}: also match stderr
+ * tests/tdef: also test OSMO_TDEF_US and negative T values
+ * tests/tdef: tune logging, also match stderr
+ * pseudotalloc: add talloc_memdup(), use it in talloc_strdup()
+ * gsm0808_utils: use osmo_strbuf API, drop APPEND_{THING,STR,CELL_ID_U}
+ * vty: suppress warnings about len being set but not used
+ * gsm48_ie: fix various issues in doxygen docs
+ * gsm: fix osmo_mobile_identity_decode(): init *mi on error
+ * core: osmo_tdef_fsm_inst_state_chg(): allow millisecond precision
+ * tests/tdef: improve test output (use OSMO_T_FMT[_ARGS])
+ * fsm: fix OSMO_T_FMT_ARGS: add missing braces
+ * isdn: initial implementation of the V.110 TA
+ * isdn: add a lookup table with E1/E2/E3 bits from Table 5/V.110
+ * tests/gsm0408: cosmetic: adjust coding style (make linter happy)
+ * tests/gsm0408: add two more samples to bcap_tests[]
+ * isdn/v110_ta: avoid redundant .status_update_cb() calls
+ * gsm: add more definitions from Table 10.5.112/3GPP TS 24.008
+ * tests/iuup: fix duplicate assignment in def_configure_req
+ * tests/a5: fix bit-wise vs logical and in test_a5[34]()
+ * tests/sockaddr_str: fix dead code, print some errno values
+ * tests/sockaddr_str: rc_name(): also handle -EAFNOSUPPORT
+ * osmo-release.sh: make it a bit more user friendly
+ * tests/Makefile.am: do not add files to EXTRA_DIST conditionally
+ * utils/conv_codes_gsm.py: fix inconsistent formatting
+ * coding: fix a typo in docs for gsm0503_pdtch_decode()
+ * coding: improve readability in osmo_conv_decode_ber_punctured()
+ * coding: fix artificial bit errors for PDTCH CS2 and CS3
+ * coding: fix wrong n_bits_total reported for PDTCH CS2 and CS3
+ * core: fix missing '\n' in iofd_uring_connected_cb()
+ * tests: do not copy *.cfg files to the build directory
+ * tests: rename logging_test_gsmtap -> logging_gsmtap_test
+ * vty: fix memleak in host_config_set()
+ * {fsm,vty}: add a VTY command to generate FSM state graphs
+ * core: fix LOGPIO(): add missing space after 'iofd(...)'
+ * README.md: cosmetic: fix a typo
+
+ [ Pau Espin Pedrol ]
+ * sockaddr_str.h Fix OSMO_SOCKADDR_STR_FMT_ARGS_NOT_NULL syntax error
+ * socket: Introduce APIs osmo_sock_multiaddr_{add,del}_local_addr()
+ * Fix typo in libosmocore.map
+ * socket.c: Fix compilation with --disable-libsctp
+ * tests/socket: Avoid keeping unneeded sockets open during next test cases
+ * socket: Reimplement osmo_sock_init2_multiaddr()
+ * socket: osmo_sock_init2_multiaddr2(): Apply params too if no OSMO_SOCK_F_BIND flag set
+ * socket: Introduce API osmo_sock_multiaddr_get_ip_and_port()
+ * socket: Introduce API osmo_sock_multiaddr_get_name_buf()
+ * socket: Fix uninitialized mem ptr free in osmo_sock_init2_multiaddr2()
+ * socket: Introduce API osmo_sock_sctp_get_peer_addr_info()
+ * socket: Introduce defines OSMO_SOCK_MULTIADDR_{PEER_STR,NAME}_MAXLEN
+ * ipa_ccm_tlv_to_unitdata(): free previous string if present before allocating new one
+ * cosmetic: gsup.h: fix whitespace formatting
+ * gsm_04_08_gprs.h: Introduce packed struct gsm48_pdp_address
+ * gsup: Fail decoding if len of PDP Type IE is less than 2 bytes
+ * gsup: Deprecate field pdp_type in favour of pdp_type_nr and pdp_type_org
+ * gsup: Convert PDP-Type IE to PDP-Address IE
+ * cosmetic: tests/gsup/gsup_test: Move send_e_send_end_signal_res to correct place
+ * socket: Support AF_UNIX in osmo_sock_get_name_buf()
+ * socket: Add remote PID and local FD to AF_UNIX sockname
+ * osmo_io: Add iofd param to segmentation_cb
+
+ [ Daniel Willmann ]
+ * osmo_io: Init struct msghdr to zero
+ * osmo_io: Only allow reading/writing if the relevant callback is set
+ * logging_gsmtap: Temporarily disable logging when sending the logs
+ * osmo_io: Assert that iofd mode is correct when calling *_write_msgb
+ * Disable uring when building for embedded
+ * osmo_io: Factor out and use common send function from backend
+ * tests: Test gsmtap logging if write queue fills up
+
+ [ Andreas Eversberg ]
+ * LAPDm: Correctly count expiry of T200 during estabishment/release
+ * ASCI: Add primitive to L1-SAP to switch uplink access detection on or off
+ * LAPD: Prepare lapd_send_i() for RTS support
+ * LAPD: Flush TX queue, if remote peer enters busy condition or rejects
+ * LAPD: Always update N(R) in pending TX frames if V(R) is incremented
+ * LAPD: Add support for RTS based polling and T200
+ * LAPDm: Add support for RTS based polling
+ * LAPDm: Add an extra queue for UI frames
+ * LAPDm: Add a flag to enable suppression of subsequent REJ frame
+ * LAPD: Indicate sequence error after indicating received data
+ * coding: gsm0503_tch_{afs,ahs}_encode(): add ability to emit BFI
+ * Fix union abis_rsl_chan_nr and abis_rsl_link_id
+ * Add flag to enable RTS based polling
+ * Prevent poll() in select.c to timeout too early
+ * Make socket.c compile without libsctp support (--disable-libsctp)
+ * osmo_io: Move notify_connected function to backend
+ * osmo_io: Use poll/select to notify socket connection at osmo_io_uring.c
+ * osmo_io: Reject writing messages with length of 0
+ * osmo_io_poll: Use -errno as result on read error
+ * osmo_io_uring: Cancel pending request, free msghdr on completion
+ * osmo_io_uring: Detach msghdr from iofd before calling iofd_handle_send_completion()
+ * osmo_io_poll: Declare local functions "static"
+ * osmo_io: Assign const name when stealing TX msg from iofd ctx
+ * osmo_io_uring: Check if osmo_fd_register fails at iofd_uring_notify_connected()
+ * osmo_io: do check_mode_callback_compat() only if ioops is set at osmo_iofd_setup()
+ * osmo_io_poll: Use -errno as result on write error
+ * Fix file descriptor that is passed to io_uring_register_eventfd()
+ * osmo_io_uring: Run check of tests/osmo_io with io_uring also
+
+ [ Harald Welte ]
+ * core: Add software UART implementation
+ * add new osmo_sockaddr_from_str_and_uint() function
+ * io_uring: add some more source code comments/docs
+ * osmo_io: rename unsupported SCTP mode to OSMO_IO_FD_MODE_SCTP_RECVMSG_SEND
+ * osmo_io: Reject unknown/unsupported modes in osmo_iofd_setup()
+ * sim/class_tables: Prevent out-of-bounds access
+ * libosmosim: Support Microsoft smart card discovery process
+ * Add a GSM RLP decoder and encoder
+ * rlp: Add support for 576bit RLP frames
+ * osmo_io: Change struct osmo_io_ops to contain struct, not union
+ * osmo_io: sendmsg/recvmsg support
+ * osmo_io: Add osmo_io_get_ioops() function
+ * ctrl: Don't expose write_queue in ctrl_cmd_send() api
+ * cbsp: Add osmo_cbsp_segmentation_cb for message segmentation
+ * osmo_io: Guard osmo_iofd_register() with invalid file descriptor
+ * osmo_io: Log error message in case call-backs incompatible with mode
+ * osmo_io: Don't pretend to support backends without close_cb
+ * osmo_io: avoid OSMO_ASSERT one each API call
+ * osmo_io: Avoid implementing non-existant situations
+ * ctrl: re-introduce duplicate declaration of ctrl_cmd_send()
+ * io_uring: more verbose error messages if io_uring setup fails
+ * io_uring: check all operations in osmo_iofd_uring_init()
+ * osmo_io: Dont use __linux__ but !EMBEDDED
+ * osmo_io: Massive improvement of API documentation
+ * osmo_io: Make {write,sendto,sendmsg} completion callback optional
+ * Add funding link to github mirror
+ * libosmosim: class_tables: Resolve conflicting CLA=8x INS=F2 definitions
+ * libosmosim: class_tables: Fix GlobalPlatform CLA=8x INS=CA/CB GET DATA
+ * [cosmetic] libosmosim/class_tables: Add a quick reminder about the cases
+
+ [ Alexander Couzens ]
+ * gsup: add message type for osmo-epdg CEAI interface
+ * gsup: fix error log message
+ * gsup.h: define newly added PCO IE
+
+ [ Neels Hofmeyr ]
+ * util: add OSMO_STRBUF_REMAIN()
+ * util: add osmo_strbuf macros to manipulate the strbuf tail
+ * logging: fix nul octets in log output / use osmo_strbuf
+ * comment: gsm_04_08.h: add a spec hint
+ * sockaddr_str: add conversion to,from osmo_sockaddr
+ * fix update_exp: s/soft_uart.ok/soft_uart_test.ok
+ * add jhash.h, copied from linux/jhash.h
+
+ [ Philipp Maier ]
+ * ecu: fix alignment of fr_ecu_state
+
+ [ Eric ]
+ * logging: add log level cache
+
+ [ Hoernchen ]
+ * Revert "logging: add log level cache"
+ * Revert "Revert "logging: add log level cache""
+
+ [ Matan Perelman ]
+ * ctrl: Add lchan node
+
+ [ Oliver Smith ]
+ * osmo-release: don't default to REL=patch
+ * osmo-release: use script from PWD if available
+ * osmo-release: use colored output
+ * osmo-release: rework cleaning of TODO-RELEASE
+ * osmo-release: fix libversion updating comment
+
+ -- Oliver Smith <osmith@sysmocom.de> Wed, 24 Jul 2024 10:19:45 +0200
+
libosmocore (1.9.0) unstable; urgency=medium
[ Oliver Smith ]
diff --git a/debian/control b/debian/control
index 5fb26cbc..ae8900fc 100644
--- a/debian/control
+++ b/debian/control
@@ -33,7 +33,7 @@ Architecture: any
Multi-Arch: foreign
Depends: libosmocodec4 (= ${binary:Version}),
libosmocoding0 (= ${binary:Version}),
- libosmocore21 (= ${binary:Version}),
+ libosmocore22 (= ${binary:Version}),
libosmogb14 (= ${binary:Version}),
libosmogsm20 (= ${binary:Version}),
libosmoisdn0 (= ${binary:Version}),
@@ -118,7 +118,7 @@ Description: Documentation for the osmo coding library
.
This package contains the documentation for the libosmocoding library.
-Package: libosmocore21
+Package: libosmocore22
Section: libs
Architecture: any
Multi-Arch: same
@@ -139,7 +139,7 @@ Package: libosmocore-doc
Architecture: all
Section: doc
Depends: ${misc:Depends},
- libosmocore21,
+ libosmocore22,
libjs-jquery,
libosmocodec-doc,
libosmocoding-doc,
diff --git a/debian/libosmocore21.install b/debian/libosmocore22.install
index b73331b9..b73331b9 100644
--- a/debian/libosmocore21.install
+++ b/debian/libosmocore22.install
diff --git a/include/osmocom/codec/codec.h b/include/osmocom/codec/codec.h
index c5981f89..7a23e7f0 100644
--- a/include/osmocom/codec/codec.h
+++ b/include/osmocom/codec/codec.h
@@ -100,6 +100,9 @@ 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);
+enum osmo_gsm631_sid_class osmo_hr_sid_classify(const uint8_t *rtp_payload,
+ bool bci_flag,
+ bool *bfi_from_bci);
/*! Check if given FR codec frame is any kind of SID, valid or invalid
* \param[in] rtp_payload Buffer with RTP payload
diff --git a/include/osmocom/core/Makefile.am b/include/osmocom/core/Makefile.am
index 7c29ca10..980f8134 100644
--- a/include/osmocom/core/Makefile.am
+++ b/include/osmocom/core/Makefile.am
@@ -27,6 +27,7 @@ osmocore_HEADERS = \
hashtable.h \
isdnhdlc.h \
it_q.h \
+ jhash.h \
linuxlist.h \
linuxrbtree.h \
log2.h \
diff --git a/include/osmocom/core/jhash.h b/include/osmocom/core/jhash.h
new file mode 100644
index 00000000..763fcd7d
--- /dev/null
+++ b/include/osmocom/core/jhash.h
@@ -0,0 +1,171 @@
+#pragma once
+#include <osmocom/core/bit32gen.h>
+
+/* Below is a partial copy of
+ * https://raw.githubusercontent.com/torvalds/linux/3eb3c33c1d87029a3832e205eebd59cfb56ba3a4/tools/include/linux/bitops.h
+ * with an osmo_ prefix applied to avoid any collisions.
+ */
+/* SPDX-License-Identifier: GPL-2.0 */
+/**
+ * rol32 - rotate a 32-bit value left
+ * @word: value to rotate
+ * @shift: bits to roll
+ */
+static inline uint32_t osmo_rol32(uint32_t word, unsigned int shift)
+{
+ return (word << shift) | (word >> ((-shift) & 31));
+}
+
+/* Below is a partial copy of
+ * https://raw.githubusercontent.com/torvalds/linux/22c033989c3eb9731ad0c497dfab4231b8e367d6/include/linux/unaligned/packed_struct.h
+ * with an osmo_ prefix applied to avoid any collisions.
+ */
+struct osmo_unaligned_cpu32 {
+ uint32_t x;
+} __attribute__((__packed__));
+
+static inline uint32_t osmo_get_unaligned_cpu32(const void *p)
+{
+ const struct osmo_unaligned_cpu32 *ptr = (const struct osmo_unaligned_cpu32 *)p;
+ return ptr->x;
+}
+
+/* Below is a partial copy of
+ * https://raw.githubusercontent.com/torvalds/linux/79e3ea5aab48c83de9410e43b52895406847eca7/tools/include/linux/jhash.h
+ * with an osmo_ prefix applied to avoid any collisions.
+ */
+/* jhash.h: Jenkins hash support.
+ *
+ * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net)
+ *
+ * https://burtleburtle.net/bob/hash/
+ *
+ * These are the credits from Bob's sources:
+ *
+ * lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+ *
+ * These are functions for producing 32-bit hashes for hash table lookup.
+ * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+ * are externally useful functions. Routines to test the hash are included
+ * if SELF_TEST is defined. You can use this free for any purpose. It's in
+ * the public domain. It has no warranty.
+ *
+ * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * I've modified Bob's hash to be useful in the Linux kernel, and
+ * any bugs present are my fault.
+ * Jozsef
+ */
+
+/* OSMO_JHASH_MIX -- mix 3 32-bit values reversibly. */
+#define OSMO_JHASH_MIX(a, b, c) \
+{ \
+ a -= c; a ^= osmo_rol32(c, 4); c += b; \
+ b -= a; b ^= osmo_rol32(a, 6); a += c; \
+ c -= b; c ^= osmo_rol32(b, 8); b += a; \
+ a -= c; a ^= osmo_rol32(c, 16); c += b; \
+ b -= a; b ^= osmo_rol32(a, 19); a += c; \
+ c -= b; c ^= osmo_rol32(b, 4); b += a; \
+}
+
+/* OSMO_JHASH_FINAL - final mixing of 3 32-bit values (a,b,c) into c */
+#define OSMO_JHASH_FINAL(a, b, c) \
+{ \
+ c ^= b; c -= osmo_rol32(b, 14); \
+ a ^= c; a -= osmo_rol32(c, 11); \
+ b ^= a; b -= osmo_rol32(a, 25); \
+ c ^= b; c -= osmo_rol32(b, 16); \
+ a ^= c; a -= osmo_rol32(c, 4); \
+ b ^= a; b -= osmo_rol32(a, 14); \
+ c ^= b; c -= osmo_rol32(b, 24); \
+}
+
+/* An arbitrary initial parameter */
+#define JHASH_INITVAL 0xdeadbeef
+
+/* osmo_jhash - hash an arbitrary key
+ * @k: sequence of bytes as key
+ * @length: the length of the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * The generic version, hashes an arbitrary sequence of bytes.
+ * No alignment or length assumptions are made about the input key.
+ *
+ * Returns the hash value of the key. The result depends on endianness.
+ */
+static inline uint32_t osmo_jhash(const void *key, uint32_t length, uint32_t initval)
+{
+ uint32_t a, b, c;
+ const uint8_t *k = key;
+
+ /* Set up the internal state */
+ a = b = c = JHASH_INITVAL + length + initval;
+
+ /* All but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12) {
+ a += osmo_get_unaligned_cpu32(k);
+ b += osmo_get_unaligned_cpu32(k + 4);
+ c += osmo_get_unaligned_cpu32(k + 8);
+ OSMO_JHASH_MIX(a, b, c);
+ length -= 12;
+ k += 12;
+ }
+ /* Last block: affect all 32 bits of (c) */
+ /* All the case statements fall through */
+ switch (length) {
+ case 12: c += (uint32_t)k[11]<<24;
+ case 11: c += (uint32_t)k[10]<<16;
+ case 10: c += (uint32_t)k[9]<<8;
+ case 9: c += k[8];
+ case 8: b += (uint32_t)k[7]<<24;
+ case 7: b += (uint32_t)k[6]<<16;
+ case 6: b += (uint32_t)k[5]<<8;
+ case 5: b += k[4];
+ case 4: a += (uint32_t)k[3]<<24;
+ case 3: a += (uint32_t)k[2]<<16;
+ case 2: a += (uint32_t)k[1]<<8;
+ case 1: a += k[0];
+ OSMO_JHASH_FINAL(a, b, c);
+ case 0: /* Nothing left to add */
+ break;
+ }
+
+ return c;
+}
+
+/* osmo_jhash2 - hash an array of uint32_t's
+ * @k: the key which must be an array of uint32_t's
+ * @length: the number of uint32_t's in the key
+ * @initval: the previous hash, or an arbitray value
+ *
+ * Returns the hash value of the key.
+ */
+static inline uint32_t osmo_jhash2(const uint32_t *k, uint32_t length, uint32_t initval)
+{
+ uint32_t a, b, c;
+
+ /* Set up the internal state */
+ a = b = c = JHASH_INITVAL + (length<<2) + initval;
+
+ /* Handle most of the key */
+ while (length > 3) {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ OSMO_JHASH_MIX(a, b, c);
+ length -= 3;
+ k += 3;
+ }
+
+ /* Handle the last 3 uint32_t's: all the case statements fall through */
+ switch (length) {
+ case 3: c += k[2];
+ case 2: b += k[1];
+ case 1: a += k[0];
+ OSMO_JHASH_FINAL(a, b, c);
+ case 0: /* Nothing left to add */
+ break;
+ }
+
+ return c;
+}
diff --git a/include/osmocom/core/osmo_io.h b/include/osmocom/core/osmo_io.h
index 6f4dfa8a..fa1f9c3c 100644
--- a/include/osmocom/core/osmo_io.h
+++ b/include/osmocom/core/osmo_io.h
@@ -55,7 +55,7 @@
* \param[in] args arguments to the format string
*/
#define LOGPIO(iofd, level, fmt, args...) \
- LOGP(DLIO, level, "iofd(%s)" fmt, iofd->name, ## args)
+ LOGP(DLIO, level, "iofd(%s) " fmt, iofd->name, ## args)
struct osmo_io_fd;
diff --git a/include/osmocom/gprs/gprs_bssgp.h b/include/osmocom/gprs/gprs_bssgp.h
index 6c043327..dff7268c 100644
--- a/include/osmocom/gprs/gprs_bssgp.h
+++ b/include/osmocom/gprs/gprs_bssgp.h
@@ -182,6 +182,12 @@ uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf);
int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
uint16_t cid);
+int bssgp_parse_cell_id2(struct osmo_routing_area_id *raid, uint16_t *cid,
+ const uint8_t *buf, size_t buf_len);
+int bssgp_create_cell_id2(uint8_t *buf, size_t buf_len,
+ const struct osmo_routing_area_id *raid,
+ uint16_t cid);
+
/* Wrapper around TLV parser to parse BSSGP IEs */
static inline int bssgp_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len)
{
diff --git a/include/osmocom/gsm/Makefile.am b/include/osmocom/gsm/Makefile.am
index 5678a51e..e42ffeca 100644
--- a/include/osmocom/gsm/Makefile.am
+++ b/include/osmocom/gsm/Makefile.am
@@ -50,6 +50,7 @@ osmogsm_HEADERS = \
oap_client.h \
rlp.h \
rsl.h \
+ rtp_extensions.h \
rxlev_stat.h \
sysinfo.h \
tlv.h \
diff --git a/include/osmocom/gsm/bts_features.h b/include/osmocom/gsm/bts_features.h
index cf1db4ac..8da08d83 100644
--- a/include/osmocom/gsm/bts_features.h
+++ b/include/osmocom/gsm/bts_features.h
@@ -36,6 +36,8 @@ enum osmo_bts_features {
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 */
+ BTS_FEAT_TWTS001, /* TW-TS-001: enhanced RTP transport for FR & EFR */
+ BTS_FEAT_TWTS002, /* TW-TS-002: enhanced RTP transport for HRv1 */
_NUM_BTS_FEAT
};
diff --git a/include/osmocom/gsm/gsm48.h b/include/osmocom/gsm/gsm48.h
index 00fb6f40..d36b85cb 100644
--- a/include/osmocom/gsm/gsm48.h
+++ b/include/osmocom/gsm/gsm48.h
@@ -42,6 +42,8 @@ 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);
char *osmo_rai_name_c(const void *ctx, const struct gprs_ra_id *rai);
+void osmo_rai_to_gprs(struct gprs_ra_id *dest, const struct osmo_routing_area_id *src);
+void gprs_rai_to_osmo(struct osmo_routing_area_id *dest, const struct gprs_ra_id *src);
int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc,
uint16_t *mnc, uint16_t *lac)
diff --git a/include/osmocom/gsm/protocol/gsm_08_08.h b/include/osmocom/gsm/protocol/gsm_08_08.h
index 1e211dc9..45396566 100644
--- a/include/osmocom/gsm/protocol/gsm_08_08.h
+++ b/include/osmocom/gsm/protocol/gsm_08_08.h
@@ -332,9 +332,10 @@ enum GSM0808_IE_CODING {
GSM0808_IE_PS_REGISTERED_OPERATOR = 0x99,
GSM0808_IE_CS_REGISTERED_OPERATOR = 0x9a,
- /* Osmocom extensions: */
+ /* Osmocom and Themyscira extensions: */
GSM0808_IE_OSMO_OSMUX_SUPPORT = 0xf0,
GSM0808_IE_OSMO_OSMUX_CID = 0xf1,
+ GSM0808_IE_THEMWI_RTP_EXTENSIONS = 0xf2,
};
/* 3GPP TS 48.008 3.2.3 Signalling Field Element Coding.
diff --git a/include/osmocom/gsm/protocol/gsm_08_58.h b/include/osmocom/gsm/protocol/gsm_08_58.h
index e8758df0..13e84bad 100644
--- a/include/osmocom/gsm/protocol/gsm_08_58.h
+++ b/include/osmocom/gsm/protocol/gsm_08_58.h
@@ -373,6 +373,7 @@ enum abis_rsl_ie {
RSL_IE_OSMO_TRAINING_SEQUENCE = 0x61,
RSL_IE_OSMO_TEMP_OVP_ACCH_CAP = 0x62,
RSL_IE_OSMO_OSMUX_CID = 0x63,
+ RSL_IE_OSMO_RTP_EXTENSIONS = 0x64,
/* ip.access */
RSL_IE_IPAC_SRTP_CONFIG = 0xe0,
diff --git a/include/osmocom/gsm/rtp_extensions.h b/include/osmocom/gsm/rtp_extensions.h
new file mode 100644
index 00000000..edea4316
--- /dev/null
+++ b/include/osmocom/gsm/rtp_extensions.h
@@ -0,0 +1,23 @@
+/*
+ * Themyscira Wireless Technical Specification TW-TS-003 defines a BSSMAP
+ * extension whereby a CN implementation and a BSS implementation can
+ * negotiate the use of non-3GPP-standard extensions to RTP user plane,
+ * extensions that modify RTP formats counter to the stipulations of
+ * 3GPP TS 48.103. There is also a private Osmocom-defined IE in Abis RSL
+ * that communicates the same RTP extensions from OsmoBSC to OsmoBTS.
+ *
+ * This header file defines the meaning of the bits in the first (and currently
+ * only) value octet of the TLV IE added to BSSMAP and RSL interfaces,
+ * namely, GSM0808_IE_THEMWI_RTP_EXTENSIONS and RSL_IE_OSMO_RTP_EXTENSIONS.
+ * It is based on this authoritative definition:
+ *
+ * https://www.freecalypso.org/specs/tw-ts-003-v010002.txt
+ *
+ * Section 5.3 in the above specification defines the assignment of
+ * individual bits in the single value octet.
+ */
+
+#pragma once
+
+#define OSMO_RTP_EXT_TWTS001 0x01
+#define OSMO_RTP_EXT_TWTS002 0x02
diff --git a/osmo-release.mk b/osmo-release.mk
index 01285b13..a28372c7 100644
--- a/osmo-release.mk
+++ b/osmo-release.mk
@@ -1,9 +1,2 @@
-ifndef REL
- REL := patch
-endif
-
release:
-ifeq ($(origin REL), file)
- @echo "No REL value specified, defaulting to 'patch' release"
-endif
- @osmo-release.sh $(VERSION) $(REL)
+ @PATH="$$PWD:$$PATH" osmo-release.sh $(VERSION) $(REL)
diff --git a/osmo-release.sh b/osmo-release.sh
index e947fe43..61adf579 100755
--- a/osmo-release.sh
+++ b/osmo-release.sh
@@ -3,8 +3,17 @@ VERSION=$1
REL=$2
if [ "z$REL" = "z" ]; then
- echo "No REL value specified, defaulting to 'patch' release"
- REL="patch"
+ echo "usage: make REL=patch|minor|major release"
+ echo
+ echo "optional environment variables:"
+ echo " DRY_RUN=1 run checks but make no modifications"
+ echo " ALLOW_NO_LIBVERSION_CHANGE=1 skip LIBVERSION in Makefile.am fchecks"
+ echo " ALLOW_NO_LIBVERSION_DEB_MATCH=1 skip LIBVERSION in debian packaging checks"
+ echo " ALLOW_NO_LIBVERSION_RPM_MATCH=1 skip LIBVERSION in rpm packaging checks"
+ echo
+ echo "See also:"
+ echo "https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release"
+ exit 1
fi
ALLOW_NO_LIBVERSION_CHANGE="${ALLOW_NO_LIBVERSION_CHANGE:-0}"
@@ -13,6 +22,23 @@ ALLOW_NO_LIBVERSION_RPM_MATCH="${ALLOW_NO_LIBVERSION_RPM_MATCH:-0}"
# Test stuff but don't modify stuff:
DRY_RUN="${DRY_RUN:-0}"
+RESET="\033[1;0m"
+RED="\033[1;31m"
+GREEN="\033[1;32m"
+YELLOW="\033[1;33m"
+
+ok() {
+ echo "${GREEN}OK:${RESET} $@"
+}
+
+warn() {
+ echo "${YELLOW}WARN:${RESET} $@"
+}
+
+error() {
+ echo "${RED}ERROR:${RESET} $@"
+}
+
libversion_to_lib_major() {
libversion="$1"
current="$(echo "$libversion" | cut -d ":" -f 1)"
@@ -43,16 +69,16 @@ check_configureac_debctrl_deps_match() {
if [ "z$debctrl_match_count" != "z0" ]; then
#echo "Dependency <$dep, $ver> from configure.ac matched in debian/control! ($debctrl_match_count)"
if [ "z$debctrl_match_count" != "z1" ]; then
- echo "WARN: configure.ac <$dep, $ver> matches debian/control $debctrl_match_count times, manual check required!"
+ 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 " " | 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
- echo "ERROR: configure.ac <$dep, $ver> does NOT match debian/control <$debctrl_dep, $debctrl_ver>!"
+ error "configure.ac <$dep, $ver> does NOT match debian/control <$debctrl_dep, $debctrl_ver>!"
return_error=1
#else
- # echo "OK: configure.ac <$dep, $ver> matches debian/control <$debctrl_dep, $debctrl_ver>"
+ # ok "configure.ac <$dep, $ver> matches debian/control <$debctrl_dep, $debctrl_ver>"
fi
fi
fi
@@ -64,10 +90,10 @@ check_configureac_debctrl_deps_match() {
# catch and forward exit from pipe subshell "while read":
if [ $? -ne 0 ]; then
- echo "ERROR: exiting due to previous errors"
+ error "exiting due to previous errors"
exit 1
fi
- echo "OK: dependency specific versions in configure.ac and debian/control match"
+ 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.
@@ -88,16 +114,16 @@ check_configureac_rpmspecin_deps_match() {
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!"
+ 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>!"
+ 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>"
+ # ok "configure.ac <$dep, $ver> matches contrib/*.spec.in <$debctrl_dep, $debctrl_ver>"
fi
fi
fi
@@ -109,10 +135,10 @@ check_configureac_rpmspecin_deps_match() {
# catch and forward exit from pipe subshell "while read":
if [ $? -ne 0 ]; then
- echo "ERROR: exiting due to previous errors"
+ error "exiting due to previous errors"
exit 1
fi
- echo "OK: dependency specific versions in configure.ac and contrib/*.spec.in match"
+ ok "dependency specific versions in configure.ac and contrib/*.spec.in match"
}
# Make sure that patches under debian/patches/ apply:
@@ -123,10 +149,10 @@ check_debian_patch_apply() {
for patch in ${GIT_TOPDIR}/debian/patches/*.patch; do
git apply --check $patch
if [ $? -ne 0 ]; then
- echo "ERROR: patch no longer applies! $patch"
+ error "patch no longer applies! $patch"
exit 1
else
- echo "OK: patch applies: $patch"
+ ok "patch applies: $patch"
fi
done
}
@@ -137,34 +163,34 @@ libversion_deb_match() {
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"
+ 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"
+ 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!"
+ 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"
+ 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"
+ 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!"
+ 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"
+ 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"
+ 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!"
+ 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
@@ -187,22 +213,22 @@ libversion_rpmspecin_match() {
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"
+ 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"
+ 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!"
+ 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"
+ 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"
+ 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!"
+ 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":
@@ -211,6 +237,20 @@ libversion_rpmspecin_match() {
fi
}
+clean_todo_release() {
+ rm -f TODO-RELEASE
+ echo "# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install" >> TODO-RELEASE
+ echo "# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release" >> TODO-RELEASE
+ echo "# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info" >> TODO-RELEASE
+ echo "# LIBVERSION=c:r:a" >> TODO-RELEASE
+ echo "# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a." >> TODO-RELEASE
+ echo "# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a." >> TODO-RELEASE
+ echo "# If any interfaces have been added since the last public release: c:r:a + 1." >> TODO-RELEASE
+ echo "# If any interfaces have been removed or changed since the last public release: c:r:0." >> TODO-RELEASE
+ echo "#library what description / commit summary line" >> TODO-RELEASE
+ git add TODO-RELEASE
+}
+
BUMPVER=`command -v bumpversion`
if [ "z$BUMPVER" = "z" ]; then
@@ -235,7 +275,7 @@ 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:"
+ 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:"
@@ -255,10 +295,9 @@ if [ "z$DRY_RUN" != "z0" ]; then
fi
set -e
+
if [ -f "TODO-RELEASE" ]; then
- grep '#' TODO-RELEASE > TODO-RELEASE.clean || true
- mv TODO-RELEASE.clean TODO-RELEASE
- git add TODO-RELEASE
+ clean_todo_release
fi
# Add missing epoch (OS#5046)
diff --git a/src/codec/Makefile.am b/src/codec/Makefile.am
index bb01b9d3..17227b67 100644
--- a/src/codec/Makefile.am
+++ b/src/codec/Makefile.am
@@ -1,7 +1,7 @@
# 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:0
+LIBVERSION=4:1:0
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
@@ -18,6 +18,7 @@ libosmocodec_la_SOURCES = \
gsm620.c \
gsm660.c \
gsm690.c \
+ hr_sid_class.c \
ecu.c \
ecu_fr.c \
ecu_fr_old.c \
diff --git a/src/codec/gsm620.c b/src/codec/gsm620.c
index ef1d3b9b..49ee7243 100644
--- a/src/codec/gsm620.c
+++ b/src/codec/gsm620.c
@@ -23,8 +23,6 @@
#include <stdbool.h>
#include <string.h>
-#include <osmocom/core/bitvec.h>
-#include <osmocom/core/utils.h>
#include <osmocom/codec/codec.h>
/* GSM HR unvoiced (mode=0) frames - subjective importance bit ordering */
@@ -270,21 +268,32 @@ const uint16_t gsm620_voiced_bitorder[112] = {
* \param[in] rtp_payload Buffer with RTP payload
* \param[in] payload_len Length of payload
* \returns true if code word is found, false otherwise
+ *
+ * Note that this function checks only for a perfect, error-free SID.
+ * Unlike GSM 06.31 for FR or GSM 06.81 for EFR, GSM 06.41 spec for HR
+ * does not prescribe exact bit counting rules, hence detection of
+ * partially corrupted SID frames in downstream network elements
+ * without out-of-band indication is not possible.
*/
bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
{
- struct bitvec bv = {
- .data = (uint8_t *)rtp_payload,
- .data_len = payload_len,
- };
+ static const uint8_t all_ff_bytes[9] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF};
- /* 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;
+ if (payload_len < GSM_HR_BYTES)
+ return false;
- return true;
+ /* 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. However, given the position of
+ * these bits in the frame, the most efficient way to perform
+ * this check does not use any bit-level operations. */
+ if ((rtp_payload[4] & 0x7F) != 0x7F)
+ return false;
+ if (memcmp(rtp_payload + 5, all_ff_bytes, 9) == 0)
+ return true;
+ else
+ return false;
}
/*! Reset the SID field of a potentially corrupted, but still valid GSM-HR
diff --git a/src/codec/hr_sid_class.c b/src/codec/hr_sid_class.c
new file mode 100644
index 00000000..6c5ba3d2
--- /dev/null
+++ b/src/codec/hr_sid_class.c
@@ -0,0 +1,176 @@
+/*
+ * This module implements osmo_hr_sid_classify() function - an independent
+ * reimplementation of the logic that was recommended (but not stipulated
+ * as normative) by ETSI for classifying received TCH/HS frames as
+ * valid SID, invalid SID or non-SID speech.
+ *
+ * Author: Mychaela N. Falconia <falcon@freecalypso.org>, 2024 - however,
+ * Mother Mychaela's contributions are NOT subject to copyright.
+ * No rights reserved, all rights relinquished.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/codec/codec.h>
+
+/*
+ * Input to the table: any 4-bit nibble.
+ * Output from the table: number of 1 bits in that nibble.
+ */
+static const uint8_t ones_in_nibble[16] = {0, 1, 1, 2, 1, 2, 2, 3,
+ 1, 2, 2, 3, 2, 3, 3, 4};
+
+/*
+ * This helper function takes two byte arrays of equal length (data and mask),
+ * applies the mask to the data, then counts how many bits are set to 1
+ * under the mask, and returns that number.
+ */
+static unsigned count_ones_under_mask(const uint8_t *data, const uint8_t *mask,
+ unsigned nbytes)
+{
+ unsigned n, accum;
+ uint8_t and;
+
+ accum = 0;
+ for (n = 0; n < nbytes; n++) {
+ and = *data++ & *mask++;
+ accum += ones_in_nibble[and >> 4] + ones_in_nibble[and & 0xF];
+ }
+ return accum;
+}
+
+/*
+ * When a GSM-HR SID frame has been decoded correctly in voiced mode,
+ * the 79 bits of the SID field will be the last bits in the frame.
+ * In the packed format of TS 101 318, the bits of interest will be
+ * in the last 10 bytes. The following array is the mask to be applied.
+ */
+static const uint8_t sid_field_last10_mask[10] = {
+ 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/*
+ * When a GSM-HR SID frame has been incorrectly decoded in unvoiced mode
+ * (both mode bits got flipped to 0 by channel errors), the 79 bits
+ * of the SID field will be badly misordered all over the frame.
+ * However, they can still be counted for the purpose of SID detection.
+ * The following array is the mask to be applied to the whole frame
+ * (14 bytes) to locate the misordered SID field.
+ */
+static const uint8_t sid_field_misordered[14] = {
+ 0x08, 0xEF, 0x1F, 0x3F, 0xF3, 0xFC, 0xA4,
+ 0xFF, 0xFA, 0x3F, 0xFF, 0x47, 0xFF, 0xEC
+};
+
+/*
+ * In the channel coding scheme on TCH/HS, the HR codec frame of 112 bits
+ * is divided into 95 class 1 bits and 17 class 2 bits. In the packed
+ * format of TS 101 318, all 17 class 2 bits will always be in the last
+ * 4 bytes; however, the specific bits will be different depending on
+ * whether the frame was decoded in voiced or unvoiced mode.
+ * The following two arrays are masks to be applied to the last 4 bytes.
+ */
+static const uint8_t class2_mask_voiced[4] = {0x7F, 0x80, 0x3F, 0xE0};
+static const uint8_t class2_mask_unvoiced[4] = {0x07, 0x07, 0xFF, 0xE0};
+
+/*
+ * osmo_hr_sid_classify() - this function is an independent reimplementation
+ * of the logic that was recommended (but not stipulated as normative) by ETSI
+ * for classifying received TCH/HS frames as valid SID, invalid SID or non-SID
+ * speech. ETSI's original version is swSidDetection() function in reid.c
+ * in GSM 06.06 source; the present version implements exactly the same
+ * logic (same inputs will produce same output), but differs in the following
+ * ways:
+ *
+ * - The frame of channel-decoded 112 payload bits was passed in the form
+ * of an array of 18 codec parameters in ETSI's version; the present version
+ * uses the packed format of TS 101 318 instead.
+ *
+ * - The C code implementation was written anew by Mother Mychaela; no code
+ * in this file has been copied directly from GSM 06.06 code drop.
+ *
+ * This function is meant to be used only in the same network element
+ * that performs GSM 05.03 channel decoding (OsmoBTS, new implementations
+ * of GSM MS), _*NOT*_ in programs or network elements that receive
+ * HRv1 codec frames from other elements via RTP or Abis-E1 etc!
+ *
+ * The BCI logic recommended by ETSI and implemented in practice by at least
+ * one vendor whose implementation has been reverse-engineered (TI Calypso)
+ * is included in this function. To understand this logic, please refer
+ * to this wiki description:
+ *
+ * https://osmocom.org/projects/retro-gsm/wiki/HRv1_error_flags
+ */
+enum osmo_gsm631_sid_class osmo_hr_sid_classify(const uint8_t *rtp_payload,
+ bool bci_flag,
+ bool *bfi_from_bci)
+{
+ uint8_t mode_bits = rtp_payload[4] & 0x30;
+ unsigned sid_field_ones, class1_ones, class2_ones;
+ unsigned sid_field_zeros, class1_zeros;
+ unsigned invalid_sid_threshold;
+ enum osmo_gsm631_sid_class sidc;
+
+ if (mode_bits != 0) { /* decoded as voiced */
+ sid_field_ones = count_ones_under_mask(rtp_payload + 4,
+ sid_field_last10_mask, 10);
+ class2_ones = count_ones_under_mask(rtp_payload + 10,
+ class2_mask_voiced, 4);
+ } else { /* decoded as unvoiced */
+ sid_field_ones = count_ones_under_mask(rtp_payload,
+ sid_field_misordered, 14);
+ class2_ones = count_ones_under_mask(rtp_payload + 10,
+ class2_mask_unvoiced, 4);
+ }
+ /* All class 2 bits are in SID field, hence class2_ones can never
+ * be greater than sid_field_ones. */
+ class1_ones = sid_field_ones - class2_ones;
+ /* 79 is the total number of bits in the SID field */
+ sid_field_zeros = 79 - sid_field_ones;
+ /* 62 is the total number of class 1 bits in TCH/HS frame */
+ class1_zeros = 62 - class1_ones;
+
+ /* frame classification logic recommended by ETSI */
+ if (bci_flag)
+ invalid_sid_threshold = 16;
+ else
+ invalid_sid_threshold = 11;
+
+ if (class1_zeros < 3)
+ sidc = OSMO_GSM631_SID_CLASS_VALID;
+ else if (sid_field_zeros < invalid_sid_threshold)
+ sidc = OSMO_GSM631_SID_CLASS_INVALID;
+ else
+ sidc = OSMO_GSM631_SID_CLASS_SPEECH;
+
+ /* If the mode bits got corrupted and the frame was channel-decoded
+ * as unvoiced, it cannot be taken as valid SID because the bits
+ * that hold CN parameters have been misordered. Therefore,
+ * we have to turn it into invalid SID classification.
+ */
+ if (mode_bits == 0 && sidc == OSMO_GSM631_SID_CLASS_VALID)
+ sidc = OSMO_GSM631_SID_CLASS_INVALID;
+
+ /* ETSI's peculiar logic that "upgrades" BCI error flag to BFI
+ * (from lowest to highest error severity) when the decoded bit
+ * pattern matches a set criterion. We leave it up to applications
+ * whether they choose to apply this logic or not. If this logic
+ * is not wanted, pass NULL pointer as the last argument.
+ */
+ if (bci_flag && bfi_from_bci &&
+ sid_field_zeros >= 16 && sid_field_zeros <= 25)
+ *bfi_from_bci = true;
+
+ return sidc;
+}
diff --git a/src/coding/Makefile.am b/src/coding/Makefile.am
index 0cab66ff..c2a02d5b 100644
--- a/src/coding/Makefile.am
+++ b/src/coding/Makefile.am
@@ -1,7 +1,7 @@
# 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=3:0:3
+LIBVERSION=3:1:3
AM_CPPFLAGS = \
-I"$(top_srcdir)/include" \
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 2efebd8d..96e64602 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -1,7 +1,7 @@
# 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
+LIBVERSION=22: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)
diff --git a/src/core/socket.c b/src/core/socket.c
index 80a9d0ec..90cf4cab 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -172,8 +172,8 @@ static int socket_helper(const struct addrinfo *rp, unsigned int flags)
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));
+ LOGP(DLGLOBAL, LOGL_ERROR, "unable to create socket(%d, %d, %d): %s\n",
+ rp->ai_family, rp->ai_socktype, rp->ai_protocol, strerror(errno));
return sfd;
}
@@ -278,6 +278,24 @@ static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags)
return 0;
}
+/*! Determine if the system supports AF_INET6 sockets at all.
+ * We call socket(AF_INET6) once and cache the result value. */
+static bool system_supports_inet6(void)
+{
+ static int cached_val = -1;
+
+ if (cached_val < 0) {
+ int rc = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (rc < 0 && errno == EAFNOSUPPORT) {
+ cached_val = 0;
+ } else if (rc >= 0) {
+ cached_val = 1;
+ close(rc);
+ }
+ }
+ return cached_val == 1;
+}
+
/*! 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
@@ -343,26 +361,31 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
/* 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) {
+ if (!system_supports_inet6()) {
+ /* if no AF_INET6 is supported, then the decision is easy...*/
+ local_ipv4 = true;
+ remote_ipv4 = true;
+ family = AF_INET;
+ } else 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;
+ 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;
+ case AF_INET:
+ remote_ipv4 = true;
+ break;
+ case AF_INET6:
+ remote_ipv6 = true;
+ break;
}
}
diff --git a/src/ctrl/Makefile.am b/src/ctrl/Makefile.am
index f9e34333..9d3254c1 100644
--- a/src/ctrl/Makefile.am
+++ b/src/ctrl/Makefile.am
@@ -1,7 +1,7 @@
# 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:1:8
+LIBVERSION=9:0:9
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
diff --git a/src/gb/Makefile.am b/src/gb/Makefile.am
index 2f7ad5eb..0aa5b131 100644
--- a/src/gb/Makefile.am
+++ b/src/gb/Makefile.am
@@ -1,7 +1,7 @@
# 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=16:0:2
+LIBVERSION=16:1:2
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
AM_CFLAGS = -Wall -fno-strict-aliasing \
diff --git a/src/gb/gprs_bssgp.c b/src/gb/gprs_bssgp.c
index 7abef804..d436f0aa 100644
--- a/src/gb/gprs_bssgp.c
+++ b/src/gb/gprs_bssgp.c
@@ -354,6 +354,24 @@ uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf)
return osmo_load16be(buf+6);
}
+/*! Parse the value of a BSSGP Cell identity (04.08 RAI + Cell Id) */
+int bssgp_parse_cell_id2(struct osmo_routing_area_id *raid, uint16_t *cid,
+ const uint8_t *buf, size_t buf_len)
+{
+ if (buf_len < 8)
+ return -EINVAL;
+
+ /* 6 octets RAC */
+ if (raid)
+ osmo_routing_area_id_decode(raid, buf, buf_len);
+
+ /* 2 octets CID */
+ if (cid)
+ *cid = osmo_load16be(buf + sizeof(struct gsm48_ra_id));
+
+ return 0;
+}
+
int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
uint16_t cid)
{
@@ -365,6 +383,23 @@ int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
return 8;
}
+/*! Encode the 04.08 RAI, Cell Id into BSSGP Cell identity */
+int bssgp_create_cell_id2(uint8_t *buf, size_t buf_len,
+ const struct osmo_routing_area_id *raid,
+ const uint16_t cid)
+{
+ if (buf_len < 8)
+ return -ENOMEM;
+
+ /* 6 octets RAC */
+ osmo_routing_area_id_encode_buf(buf, buf_len, raid);
+
+ /* 2 octets CID */
+ osmo_store16be(cid, buf + sizeof(struct gsm48_ra_id));
+
+ return 8;
+}
+
/* Chapter 8.4 BVC-Reset Procedure */
static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
uint16_t ns_bvci)
diff --git a/src/gb/libosmogb.map b/src/gb/libosmogb.map
index e5a5c8fd..f1a6bd69 100644
--- a/src/gb/libosmogb.map
+++ b/src/gb/libosmogb.map
@@ -2,6 +2,7 @@ LIBOSMOGB_1.0 {
global:
bssgp_cause_str;
bssgp_create_cell_id;
+bssgp_create_cell_id2;
bssgp_create_rim_ri;
bssgp_dec_app_err_cont_nacc;
bssgp_dec_ran_inf_ack_rim_cont;
@@ -32,6 +33,7 @@ bssgp_msgb_tlli_put;
bssgp_msgb_ra_put;
bssgp_nacc_cause_strs;
bssgp_parse_cell_id;
+bssgp_parse_cell_id2;
bssgp_parse_rim_pdu;
bssgp_parse_rim_ri;
bssgp_parse_rim_ra;
diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am
index e6b687d2..8c70473e 100644
--- a/src/gsm/Makefile.am
+++ b/src/gsm/Makefile.am
@@ -1,7 +1,7 @@
# 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=20:0:0
+LIBVERSION=21:0:1
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
diff --git a/src/gsm/bts_features.c b/src/gsm/bts_features.c
index b6cd82ec..158937a9 100644
--- a/src/gsm/bts_features.c
+++ b/src/gsm/bts_features.c
@@ -47,6 +47,8 @@ const struct value_string osmo_bts_features_descs[] = {
{ BTS_FEAT_OSMUX, "Osmux (Osmocom RTP multiplexing)" },
{ BTS_FEAT_VBS, "Voice Broadcast Service" },
{ BTS_FEAT_VGCS, "Voice Group Call Service" },
+ { BTS_FEAT_TWTS001, "TW-TS-001 RTP format" },
+ { BTS_FEAT_TWTS002, "TW-TS-002 RTP format" },
{ 0, NULL }
};
@@ -88,6 +90,8 @@ const struct value_string osmo_bts_features_names[] = {
{ BTS_FEAT_OSMUX, "OSMUX" },
{ BTS_FEAT_VBS, "VBS" },
{ BTS_FEAT_VGCS, "VGCS" },
+ { BTS_FEAT_TWTS001, "TWTS001" },
+ { BTS_FEAT_TWTS002, "TWTS002" },
{}
};
diff --git a/src/gsm/gsm0808.c b/src/gsm/gsm0808.c
index 529dbdfe..85c2aef1 100644
--- a/src/gsm/gsm0808.c
+++ b/src/gsm/gsm0808.c
@@ -2310,9 +2310,10 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0808_IE_PS_REGISTERED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
[GSM0808_IE_CS_REGISTERED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
- /* Osmocom extensions */
+ /* Osmocom and Themyscira extensions */
[GSM0808_IE_OSMO_OSMUX_SUPPORT] = { TLV_TYPE_T },
[GSM0808_IE_OSMO_OSMUX_CID] = { TLV_TYPE_TV },
+ [GSM0808_IE_THEMWI_RTP_EXTENSIONS] = { TLV_TYPE_TLV },
},
};
diff --git a/src/gsm/gsm23236.c b/src/gsm/gsm23236.c
index 4a83ec82..a80ecf46 100644
--- a/src/gsm/gsm23236.c
+++ b/src/gsm/gsm23236.c
@@ -487,7 +487,7 @@ static int osmo_nri_parse_range(struct osmo_nri_range *nri_range, const char *fi
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;
+ 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;
@@ -523,7 +523,7 @@ int osmo_nri_ranges_vty_add(const char **message, struct osmo_nri_range *added_r
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;
+ 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;
diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c
index 64b17658..1201595e 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -222,6 +222,34 @@ char *osmo_rai_name_c(const void *ctx, const struct gprs_ra_id *rai)
return osmo_rai_name_buf(buf, 32, rai);
}
+/*! Convert osmo_routing_area_id (new) into gprs_ra_id (old) */
+void osmo_rai_to_gprs(struct gprs_ra_id *dest, const struct osmo_routing_area_id *src)
+{
+ OSMO_ASSERT(src);
+ OSMO_ASSERT(dest);
+
+ dest->mcc = src->lac.plmn.mcc;
+ dest->mnc = src->lac.plmn.mnc;
+ dest->mnc_3_digits = src->lac.plmn.mnc_3_digits;
+
+ dest->lac = src->lac.lac;
+ dest->rac = src->rac;
+}
+
+/*! Convert gprs_ra_id (old) into osmo_routing_area_id (new) */
+void gprs_rai_to_osmo(struct osmo_routing_area_id *dest, const struct gprs_ra_id *src)
+{
+ OSMO_ASSERT(src);
+ OSMO_ASSERT(dest);
+
+ dest->lac.plmn.mcc = src->mcc;
+ dest->lac.plmn.mnc = src->mnc;
+ dest->lac.plmn.mnc_3_digits = src->mnc_3_digits;
+
+ dest->lac.lac = src->lac;
+ dest->rac = src->rac;
+}
+
/* FIXME: convert to value_string */
static const char *cc_state_names[32] = {
"NULL",
diff --git a/src/gsm/iuup.c b/src/gsm/iuup.c
index 16a6f5e0..49912131 100644
--- a/src/gsm/iuup.c
+++ b/src/gsm/iuup.c
@@ -1,5 +1,9 @@
/*! \file iu_up.c
- * IuUP (Iu User Plane) according to 3GPP TS 25.415 */
+ * IuUP (Iu User Plane) according to 3GPP TS 25.415
+ * See also 3GPP TS 25.414 regarding data transport.
+ * See also 3GPP TS 29.414 and 3GPP TS 29.415 regarding Nb counterparts
+ * of the above specs.
+ */
/*
* (C) 2017 by Harald Welte <laforge@gnumonks.org>
*
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 2c4c621c..3bc8309c 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -487,6 +487,8 @@ osmo_rai_name_c;
osmo_rai_name2;
osmo_rai_name2_buf;
osmo_rai_name2_c;
+osmo_rai_to_gprs;
+gprs_rai_to_osmo;
osmo_cgi_cmp;
osmo_cgi_name;
osmo_cgi_name_buf;
diff --git a/src/gsm/rsl.c b/src/gsm/rsl.c
index fbba982a..c7d69a14 100644
--- a/src/gsm/rsl.c
+++ b/src/gsm/rsl.c
@@ -126,6 +126,7 @@ const struct tlv_definition rsl_att_tlvdef = {
[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_OSMO_RTP_EXTENSIONS] = { 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 },
diff --git a/src/isdn/Makefile.am b/src/isdn/Makefile.am
index 3e7f86eb..51455452 100644
--- a/src/isdn/Makefile.am
+++ b/src/isdn/Makefile.am
@@ -1,7 +1,7 @@
# 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
+LIBVERSION=2:0:2
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
AM_CFLAGS = -Wall $(TALLOC_CFLAGS)
diff --git a/src/sim/Makefile.am b/src/sim/Makefile.am
index 0f6be576..01c51757 100644
--- a/src/sim/Makefile.am
+++ b/src/sim/Makefile.am
@@ -1,7 +1,7 @@
# 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=3:2:1
+LIBVERSION=3:3:1
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
AM_CFLAGS = -fPIC -Wall $(TALLOC_CFLAGS)
diff --git a/src/sim/class_tables.c b/src/sim/class_tables.c
index 29c1e40e..7500fc5a 100644
--- a/src/sim/class_tables.c
+++ b/src/sim/class_tables.c
@@ -19,6 +19,14 @@
#include <osmocom/core/utils.h>
#include <osmocom/sim/class_tables.h>
+/* Quick reminder about the "cases" as per ISO7816-3 / ETSI TS 102 221 Section 7.3.1.1
+ *
+ * Case 1: Command Length 0, Response Length 0
+ * Case 2: Command Length 0, Response Length from P3
+ * Case 3: Command Length P3, Response Length 0
+ * Case 4: Command Length P3, followed by 61xx for response length + GET RESPONSE
+ */
+
static const uint8_t iso7816_ins_tbl[256] = {
[0xB0] = 2, /* READ BIN */
[0xD0] = 3, /* WRITE BIN */
@@ -178,6 +186,7 @@ static int gp_cla_ins_helper(const struct osim_cla_ins_case *cic,
{
uint8_t ins = hdr[1];
uint8_t p1 = hdr[2];
+ uint8_t p3 = hdr[4];
switch (ins) {
case 0xE2: /* STORE DATA */
@@ -187,6 +196,26 @@ static int gp_cla_ins_helper(const struct osim_cla_ins_case *cic,
default:
return 3;
}
+ break;
+ case 0xF2:
+ /* in their infinite wisdom, GlobalPlatform re-defined the CLA 8x / INS F2 command, so one can
+ * take a guess if it's GlobalPlatform or ETSI. Lucikly, the P1 coding of ETSI [so far]
+ * states all the four upper bits must be 0, while GP always has one of those bits set */
+ if (p1 & 0xF0)
+ return 4; /* GlobalPlatform v2.2 11.4.2 */
+ else
+ return 2; /* ETSI TS 102 221 V16.2.0 11.1.2 */
+ break;
+ case 0xCA:
+ case 0xCB:
+ /* in their infinite wisdom, GlobalPlatform made GET DATA a command that can be either APDU
+ * case 2 or case 4. As the specify Le must be 0x00, we can conclude that P3 == 0x00 must be
+ * Le, while P3 != 0x00 must be Lc and hence case 4 */
+ if (p3 == 0x00)
+ return 2;
+ else
+ return 4;
+ break;
}
return 0;
}
@@ -215,9 +244,9 @@ static const uint8_t uicc_ins_tbl_80[256] = {
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 */
+ [0xCA] = 0x80, /* GET DATA */
+ [0xCB] = 0x80, /* GET DATA */
+ [0xF2] = 0x80, /* GET STATUS */
[0xE6] = 4, /* INSTALL */
[0xE8] = 4, /* LOAD */
[0xD8] = 4, /* PUT KEY */
@@ -246,6 +275,12 @@ static const struct osim_cla_ins_case uicc_ins_case[] = {
.helper = uicc046_cla_ins_helper,
.ins_tbl = uicc_ins_tbl_046,
}, {
+ /* must be before uicc_ins_tbl_8ce below with same CLA+mask */
+ .cla = 0x80,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
.cla = 0x80,
.cla_mask = 0xF0,
.ins_tbl = uicc_ins_tbl_8ce,
@@ -258,11 +293,6 @@ static const struct osim_cla_ins_case uicc_ins_case[] = {
.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,
@@ -308,6 +338,12 @@ static const struct osim_cla_ins_case uicc_sim_ins_case[] = {
.helper = uicc046_cla_ins_helper,
.ins_tbl = uicc_ins_tbl_046,
}, {
+ /* must be before uicc_ins_tbl_8ce below with same CLA+mask */
+ .cla = 0x80,
+ .cla_mask = 0xF0,
+ .helper = gp_cla_ins_helper,
+ .ins_tbl = gp_ins_tbl_8ce,
+ }, {
.cla = 0x80,
.cla_mask = 0xF0,
.ins_tbl = uicc_ins_tbl_8ce,
diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am
index c314a141..4a4f16ba 100644
--- a/src/vty/Makefile.am
+++ b/src/vty/Makefile.am
@@ -1,7 +1,7 @@
# 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=13:1:0
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir)
AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS)
diff --git a/src/vty/fsm_vty.c b/src/vty/fsm_vty.c
index da6038fa..19c35daa 100644
--- a/src/vty/fsm_vty.c
+++ b/src/vty/fsm_vty.c
@@ -158,6 +158,44 @@ DEFUN(show_fsm, show_fsm_cmd,
return CMD_SUCCESS;
}
+DEFUN(show_fsm_state_graph, show_fsm_state_graph_cmd,
+ "show fsm-state-graph NAME",
+ SHOW_STR "Generate a state transition graph (using DOT language)\n"
+ "FSM name\n")
+{
+ const struct osmo_fsm *fsm;
+
+ fsm = osmo_fsm_find_by_name(argv[0]);
+ if (!fsm) {
+ vty_out(vty, "Error: FSM with name '%s' doesn't exist!%s",
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "digraph \"%s\" {%s", fsm->name, VTY_NEWLINE);
+
+ for (unsigned int i = 0; i < fsm->num_states; i++) {
+ const struct osmo_fsm_state *st = &fsm->states[i];
+
+ vty_out(vty, "\t\"%s\"; # out_state_mask=0x%08x%s",
+ st->name, st->out_state_mask, VTY_NEWLINE);
+
+ for (unsigned int j = 0; j < sizeof(st->out_state_mask) * 8; j++) {
+ if (~st->out_state_mask & (1 << j))
+ continue;
+ vty_out(vty, "\t\"%s\" -> \"%s\";%s",
+ st->name, osmo_fsm_state_name(fsm, j),
+ VTY_NEWLINE);
+ }
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ vty_out(vty, "}%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(show_fsm_insts, show_fsm_insts_cmd,
"show fsm-instances all",
SH_FSMI_STR
@@ -214,6 +252,7 @@ void osmo_fsm_vty_add_cmds(void)
install_lib_element_ve(&show_fsm_cmd);
install_lib_element_ve(&show_fsms_cmd);
+ install_lib_element_ve(&show_fsm_state_graph_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/tests/Makefile.am b/tests/Makefile.am
index 3b4325ee..48242c35 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -26,7 +26,9 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_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 \
+ codec/codec_ecu_fr_test codec/codec_efr_sid_test \
+ codec/codec_fr_sid_test codec/codec_hr_sid_test \
+ timer/clk_override_test \
oap/oap_client_test gsm29205/gsm29205_test \
logging/logging_vty_test \
vty/vty_transcript_test \
@@ -59,6 +61,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
osmo_io/osmo_io_test \
soft_uart/soft_uart_test \
rlp/rlp_test \
+ jhash/jhash_test \
$(NULL)
if ENABLE_MSGFILE
@@ -264,6 +267,15 @@ 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 = $(top_builddir)/src/codec/libosmocodec.la $(LDADD)
+codec_codec_efr_sid_test_SOURCES = codec/codec_efr_sid_test.c
+codec_codec_efr_sid_test_LDADD = $(top_builddir)/src/codec/libosmocodec.la $(LDADD)
+
+codec_codec_fr_sid_test_SOURCES = codec/codec_fr_sid_test.c
+codec_codec_fr_sid_test_LDADD = $(top_builddir)/src/codec/libosmocodec.la $(LDADD)
+
+codec_codec_hr_sid_test_SOURCES = codec/codec_hr_sid_test.c
+codec_codec_hr_sid_test_LDADD = $(top_builddir)/src/codec/libosmocodec.la $(LDADD)
+
loggingrb_loggingrb_test_SOURCES = loggingrb/loggingrb_test.c
loggingrb_loggingrb_test_LDADD = $(LDADD)
@@ -391,6 +403,7 @@ 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)
+jhash_jhash_test_SOURCES = jhash/jhash_test.c
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
@@ -436,6 +449,9 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
loggingrb/logging_test.err strrb/strrb_test.ok \
codec/codec_test.ok \
codec/codec_ecu_fr_test.ok \
+ codec/codec_efr_sid_test.ok codec/codec_efr_sid_test.in \
+ codec/codec_fr_sid_test.ok codec/codec_fr_sid_test.in \
+ codec/codec_hr_sid_test.ok codec/codec_hr_sid_test.in \
vty/vty_test.ok vty/vty_test.err \
vty/fail_not_de-indented.cfg \
vty/fail_tabs_and_spaces.cfg \
@@ -505,6 +521,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
soft_uart/soft_uart_test.ok \
rlp/rlp_test.ok \
socket/socket_sctp_test.ok socket/socket_sctp_test.err \
+ jhash/jhash_test.ok \
$(NULL)
if ENABLE_LIBSCTP
@@ -595,6 +612,12 @@ endif
>$(srcdir)/codec/codec_test.ok
codec/codec_ecu_fr_test \
>$(srcdir)/codec/codec_ecu_fr_test.ok
+ codec/codec_efr_sid_test $(srcdir)/codec/codec_efr_sid_test.in \
+ >$(srcdir)/codec/codec_efr_sid_test.ok
+ codec/codec_fr_sid_test $(srcdir)/codec/codec_fr_sid_test.in \
+ >$(srcdir)/codec/codec_fr_sid_test.ok
+ codec/codec_hr_sid_test $(srcdir)/codec/codec_hr_sid_test.in \
+ >$(srcdir)/codec/codec_hr_sid_test.ok
if ENABLE_GB
fr/fr_test \
>$(srcdir)/fr/fr_test.ok
@@ -727,10 +750,11 @@ endif
>$(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
+ >$(srcdir)/soft_uart/soft_uart_test.ok
rlp/rlp_test \
- >$(srcdir)/rlp/rlp_test.ok \
- $(NULL)
+ >$(srcdir)/rlp/rlp_test.ok
+ jhash/jhash_test \
+ >$(srcdir)/jhash/jhash_test.ok
check-local: atconfig $(TESTSUITE)
diff --git a/tests/codec/codec_efr_sid_test.c b/tests/codec/codec_efr_sid_test.c
new file mode 100644
index 00000000..08cc94b5
--- /dev/null
+++ b/tests/codec/codec_efr_sid_test.c
@@ -0,0 +1,97 @@
+/*
+ * This program is a test for osmo_efr_sid_classify(). It reads a set of
+ * EFR codec frames in hex format (TS 101 318 RTP format represented in hex,
+ * each frame as its own hex line) and feeds each test frame to
+ * osmo_efr_sid_classify(). It then prints the output next to each input.
+ *
+ * Author: Mychaela N. Falconia <falcon@freecalypso.org>, 2024 - however,
+ * Mother Mychaela's contributions are NOT subject to copyright.
+ * No rights reserved, all rights relinquished.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+
+static void process_record(const char *hex_str)
+{
+ uint8_t frame_bytes[GSM_EFR_BYTES];
+ enum osmo_gsm631_sid_class sidc;
+
+ osmo_hexparse(hex_str, frame_bytes, GSM_EFR_BYTES);
+ sidc = osmo_efr_sid_classify(frame_bytes);
+ printf("%s ==> %d\n", hex_str, (int) sidc);
+}
+
+static void process_line(char *linebuf, const char *infname, int lineno)
+{
+ char *cp = linebuf, *hex_str;
+ int ndig;
+
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0' || *cp == '#')
+ return;
+ /* expect string of 62 hex digits */
+ hex_str = cp;
+ for (ndig = 0; ndig < GSM_EFR_BYTES * 2; ndig++) {
+ if (!isxdigit(*cp))
+ goto inv;
+ cp++;
+ }
+ if (*cp) {
+ if (!isspace(*cp))
+ goto inv;
+ *cp++ = '\0';
+ }
+ /* must be end of non-comment line */
+ while (isspace(*cp))
+ cp++;
+ if (*cp != '\0' && *cp != '#')
+ goto inv;
+
+ process_record(hex_str);
+ return;
+
+inv: fprintf(stderr, "%s line %d: invalid syntax\n", infname, lineno);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ const char *infname;
+ FILE *inf;
+ char linebuf[128];
+ int lineno;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s input-file\n", argv[0]);
+ exit(1);
+ }
+ infname = argv[1];
+ inf = fopen(infname, "r");
+ if (!inf) {
+ perror(infname);
+ exit(1);
+ }
+ for (lineno = 1; fgets(linebuf, sizeof(linebuf), inf); lineno++)
+ process_line(linebuf, infname, lineno);
+ fclose(inf);
+ exit(0);
+}
diff --git a/tests/codec/codec_efr_sid_test.in b/tests/codec/codec_efr_sid_test.in
new file mode 100644
index 00000000..af95b5b2
--- /dev/null
+++ b/tests/codec/codec_efr_sid_test.in
@@ -0,0 +1,183 @@
+# This file is input for the EFR SID classifier test program.
+# It has been generated here:
+#
+# https://www.freecalypso.org/hg/vband-misc/file/tip/efr-sid
+#
+# Unit-test-desc file in the above directory contains
+# a detailed description.
+
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5802FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5804FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B58067FFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806BFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806DFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806EFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806F7FFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FBFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FDFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FEFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FF7FF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFBFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFDFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFEFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFF7F80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFBF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFDF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFEF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFF780001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFB80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFD80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFE80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF00001E3BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E1BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E2BFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E33FFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E39FFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3AFFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3B7FFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BBFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BDFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BEFFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BF7FFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFBFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFDFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFEFFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFF7FE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFBFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFDFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFEFE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFF7E0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFBE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFDE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFEE0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFF60000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFA0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFC0000800FFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE00008007FFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800BFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800DFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800EFFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800F7FFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FBFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FDFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FEFFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FF7FFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFBFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFDFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFEFFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFF7FF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFBFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFDFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFEFF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFF7F000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFBF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFDF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFEF000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFF7000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFB000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFD000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFE000040FFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF0000407FFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040BFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040DFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040EFFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040F7FCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FBFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FDFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FEFCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FF7CFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFBCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFDCFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFECFFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFF4FFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFF8FFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFC7FC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCBFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCDFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCEFC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCF7C00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFBC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFDC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFEC00010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFF400010
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFF800010
+C286DD29B5802FFFFF80001E3BFFFFE00008007FFFFF000040FFFCFFC00010
+C286DD29B5804FFFFF80001E3BFFFFE0000800BFFFFF000040FFFCFFC00010
+C286DD29B58067FFFF80001E3BFFFFE0000800DFFFFF000040FFFCFFC00010
+C286DD29B5806BFFFF80001E3BFFFFE0000800EFFFFF000040FFFCFFC00010
+C286DD29B5806DFFFF80001E3BFFFFE0000800F7FFFF000040FFFCFFC00010
+C286DD29B5806EFFFF80001E3BFFFFE0000800FBFFFF000040FFFCFFC00010
+C286DD29B5806F7FFF80001E3BFFFFE0000800FDFFFF000040FFFCFFC00010
+C286DD29B5806FBFFF80001E3BFFFFE0000800FEFFFF000040FFFCFFC00010
+C286DD29B5806FDFFF80001E3BFFFFE0000800FF7FFF000040FFFCFFC00010
+C286DD29B5806FEFFF80001E3BFFFFE0000800FFBFFF000040FFFCFFC00010
+C286DD29B5806FF7FF80001E3BFFFFE0000800FFDFFF000040FFFCFFC00010
+C286DD29B5806FFBFF80001E3BFFFFE0000800FFEFFF000040FFFCFFC00010
+C286DD29B5806FFDFF80001E3BFFFFE0000800FFF7FF000040FFFCFFC00010
+C286DD29B5806FFEFF80001E3BFFFFE0000800FFFBFF000040FFFCFFC00010
+C286DD29B5806FFF7F80001E3BFFFFE0000800FFFDFF000040FFFCFFC00010
+C286DD29B5806FFFBF80001E3BFFFFE0000800FFFEFF000040FFFCFFC00010
+C286DD29B5806FFFDF80001E3BFFFFE0000800FFFF7F000040FFFCFFC00010
+C286DD29B5806FFFEF80001E3BFFFFE0000800FFFFBF000040FFFCFFC00010
+C286DD29B5806FFFF780001E3BFFFFE0000800FFFFDF000040FFFCFFC00010
+C286DD29B5806FFFFB80001E3BFFFFE0000800FFFFEF000040FFFCFFC00010
+C286DD29B5806FFFFD80001E3BFFFFE0000800FFFFF7000040FFFCFFC00010
+C286DD29B5806FFFFE80001E3BFFFFE0000800FFFFFB000040FFFCFFC00010
+C286DD29B5806FFFFF00001E3BFFFFE0000800FFFFFD000040FFFCFFC00010
+C286DD29B5806FFFFF80001E1BFFFFE0000800FFFFFE000040FFFCFFC00010
+C286DD29B5806FFFFF80001E2BFFFFE0000800FFFFFF0000407FFCFFC00010
+C286DD29B5806FFFFF80001E33FFFFE0000800FFFFFF000040BFFCFFC00010
+C286DD29B5806FFFFF80001E39FFFFE0000800FFFFFF000040DFFCFFC00010
+C286DD29B5806FFFFF80001E3AFFFFE0000800FFFFFF000040EFFCFFC00010
+C286DD29B5806FFFFF80001E3B7FFFE0000800FFFFFF000040F7FCFFC00010
+C286DD29B5806FFFFF80001E3BBFFFE0000800FFFFFF000040FBFCFFC00010
+C286DD29B5806FFFFF80001E3BDFFFE0000800FFFFFF000040FDFCFFC00010
+C286DD29B5806FFFFF80001E3BEFFFE0000800FFFFFF000040FEFCFFC00010
+C286DD29B5806FFFFF80001E3BF7FFE0000800FFFFFF000040FF7CFFC00010
+C286DD29B5806FFFFF80001E3BFBFFE0000800FFFFFF000040FFBCFFC00010
+C286DD29B5806FFFFF80001E3BFDFFE0000800FFFFFF000040FFDCFFC00010
+C286DD29B5806FFFFF80001E3BFEFFE0000800FFFFFF000040FFECFFC00010
+C286DD29B5806FFFFF80001E3BFF7FE0000800FFFFFF000040FFF4FFC00010
+C286DD29B5806FFFFF80001E3BFFBFE0000800FFFFFF000040FFF8FFC00010
+C286DD29B5806FFFFF80001E3BFFDFE0000800FFFFFF000040FFFC7FC00010
+C286DD29B5806FFFFF80001E3BFFEFE0000800FFFFFF000040FFFCBFC00010
+C286DD29B5806FFFFF80001E3BFFF7E0000800FFFFFF000040FFFCDFC00010
+C286DD29B5806FFFFF80001E3BFFFBE0000800FFFFFF000040FFFCEFC00010
+C286DD29B5806FFFFF80001E3BFFFDE0000800FFFFFF000040FFFCF7C00010
+C286DD29B5806FFFFF80001E3BFFFEE0000800FFFFFF000040FFFCFBC00010
+C286DD29B5806FFFFF80001E3BFFFF60000800FFFFFF000040FFFCFDC00010
+C286DD29B5806FFFFF80001E3BFFFFA0000800FFFFFF000040FFFCFEC00010
+C286DD29B5806FFFFF80001E3BFFFFC0000800FFFFFF000040FFFCFF400010
+C286DD29B5806FFFFF80001E3BFFFFE00008007FFFFF000040FFFCFF800010
+C286DD29B5802F7DF780001E2BDF7DE0000800BEFBEF000040BEF8FFC00010
+C286DD29B5804FBEFB80001E33EFBEE0000800DF7DF7000040DF7C7FC00010
+C286DD29B58067DF7D80001E39F7DF60000800EFBEFB000040EFBCBFC00010
+C286DD29B5806BEFBE80001E3AFBEFA0000800F7DF7D000040F7DCDFC00010
+C286DD29B5806DF7DF00001E3B7DF7C0000800FBEFBE000040FBECEFC00010
+C286DD29B5806EFBEF80001E1BBEFBE00008007DF7DF0000407DF4F7C00010
+C286DD29B5806F7DF780001E2BDF7DE0000800BEFBEF000040BEF8FBC00010
+C286DD29B5806FBEFB80001E33EFBEE0000800DF7DF7000040DF7C7DC00010
+C286DD29B5806FDF7D80001E39F7DF60000800EFBEFB000040EFBCBEC00010
+C286DD29B5806FEFBE80001E3AFBEFA0000800F7DF7D000040F7DCDF400010
+C286DD29B5806FF7DF00001E3B7DF7C0000800FBEFBE000040FBECEF800010
+C286DD29B5802EF7BD80001E33DEF7A0000800EF7BDE000040F7FCFFC00010
+C286DD29B5804F7BDE80001E39EF7BC0000800F7BDEF0000407BFCFFC00010
+C286DD29B58067BDEF00001E3AF7BDE00008007BDEF7000040BDFCFFC00010
+C286DD29B5806BDEF780001E1B7BDEE0000800BDEF7B000040DEFCFFC00010
+C286DD29B5806DEF7B80001E2BBDEF60000800DEF7BD000040EF7CFFC00010
+C286DD29B5806EF7BD80001E33DEF7A0000800EF7BDE000040F7BCFFC00010
+C286DD29B5806F7BDE80001E39EF7BC0000800F7BDEF0000407BDCFFC00010
+C286DD29B5806FBDEF00001E3AF7BDE00008007BDEF7000040BDECFFC00010
+C286DD29B5806FDEF780001E1B7BDEE0000800BDEF7B000040DEF4FFC00010
+C286DD29B5806FEF7B80001E2BBDEF60000800DEF7BD000040EF78FFC00010
+C286DD29B5806FF7BD80001E33DEF7A0000800EF7BDE000040F7BC7FC00010
+C286DD29B5806FFBDE80001E39EF7BC0000800F7BDEF0000407BDCBFC00010
+C286DD29B5806FFDEF00001E3AF7BDE00008007BDEF7000040BDECDFC00010
+C286DD29B5806FFEF780001E1B7BDEE0000800BDEF7B000040DEF4EFC00010
+C286DD29B5806FFF7B80001E2BBDEF60000800DEF7BD000040EF78F7C00010
+C286DD29B5806FFFBD80001E33DEF7A0000800EF7BDE000040F7BC7BC00010
+C286DD29B5806FFFDE80001E39EF7BC0000800F7BDEF0000407BDCBDC00010
+C286DD29B5806FFFEF00001E3AF7BDE00008007BDEF7000040BDECDEC00010
+C286DD29B5806FFFF780001E1B7BDEE0000800BDEF7B000040DEF4EF400010
+C286DD29B5806FFFFB80001E2BBDEF60000800DEF7BD000040EF78F7800010
diff --git a/tests/codec/codec_efr_sid_test.ok b/tests/codec/codec_efr_sid_test.ok
new file mode 100644
index 00000000..6b94cf4e
--- /dev/null
+++ b/tests/codec/codec_efr_sid_test.ok
@@ -0,0 +1,175 @@
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5802FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5804FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B58067FFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806BFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806DFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806EFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806F7FFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FBFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FDFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FEFFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FF7FF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFBFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFDFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFEFF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFF7F80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFBF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFDF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFEF80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFF780001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFB80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFD80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFE80001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF00001E3BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E1BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E2BFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E33FFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E39FFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3AFFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3B7FFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BBFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BDFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BEFFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BF7FFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFBFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFDFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFEFFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFF7FE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFBFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFDFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFEFE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFF7E0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFBE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFDE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFEE0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFF60000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFA0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFC0000800FFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE00008007FFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800BFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800DFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800EFFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800F7FFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FBFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FDFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FEFFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FF7FFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFBFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFDFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFEFFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFF7FF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFBFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFDFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFEFF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFF7F000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFBF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFDF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFEF000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFF7000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFB000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFD000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFE000040FFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF0000407FFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040BFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040DFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040EFFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040F7FCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FBFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FDFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FEFCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FF7CFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFBCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFDCFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFECFFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFF4FFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFF8FFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFC7FC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCBFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCDFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCEFC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCF7C00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFBC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFDC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFEC00010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFF400010 ==> 2
+C286DD29B5806FFFFF80001E3BFFFFE0000800FFFFFF000040FFFCFF800010 ==> 2
+C286DD29B5802FFFFF80001E3BFFFFE00008007FFFFF000040FFFCFFC00010 ==> 1
+C286DD29B5804FFFFF80001E3BFFFFE0000800BFFFFF000040FFFCFFC00010 ==> 1
+C286DD29B58067FFFF80001E3BFFFFE0000800DFFFFF000040FFFCFFC00010 ==> 1
+C286DD29B5806BFFFF80001E3BFFFFE0000800EFFFFF000040FFFCFFC00010 ==> 1
+C286DD29B5806DFFFF80001E3BFFFFE0000800F7FFFF000040FFFCFFC00010 ==> 1
+C286DD29B5806EFFFF80001E3BFFFFE0000800FBFFFF000040FFFCFFC00010 ==> 1
+C286DD29B5806F7FFF80001E3BFFFFE0000800FDFFFF000040FFFCFFC00010 ==> 1
+C286DD29B5806FBFFF80001E3BFFFFE0000800FEFFFF000040FFFCFFC00010 ==> 1
+C286DD29B5806FDFFF80001E3BFFFFE0000800FF7FFF000040FFFCFFC00010 ==> 1
+C286DD29B5806FEFFF80001E3BFFFFE0000800FFBFFF000040FFFCFFC00010 ==> 1
+C286DD29B5806FF7FF80001E3BFFFFE0000800FFDFFF000040FFFCFFC00010 ==> 1
+C286DD29B5806FFBFF80001E3BFFFFE0000800FFEFFF000040FFFCFFC00010 ==> 1
+C286DD29B5806FFDFF80001E3BFFFFE0000800FFF7FF000040FFFCFFC00010 ==> 1
+C286DD29B5806FFEFF80001E3BFFFFE0000800FFFBFF000040FFFCFFC00010 ==> 1
+C286DD29B5806FFF7F80001E3BFFFFE0000800FFFDFF000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFBF80001E3BFFFFE0000800FFFEFF000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFDF80001E3BFFFFE0000800FFFF7F000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFEF80001E3BFFFFE0000800FFFFBF000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFF780001E3BFFFFE0000800FFFFDF000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFFB80001E3BFFFFE0000800FFFFEF000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFFD80001E3BFFFFE0000800FFFFF7000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFFE80001E3BFFFFE0000800FFFFFB000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFFF00001E3BFFFFE0000800FFFFFD000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E1BFFFFE0000800FFFFFE000040FFFCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E2BFFFFE0000800FFFFFF0000407FFCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E33FFFFE0000800FFFFFF000040BFFCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E39FFFFE0000800FFFFFF000040DFFCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3AFFFFE0000800FFFFFF000040EFFCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3B7FFFE0000800FFFFFF000040F7FCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BBFFFE0000800FFFFFF000040FBFCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BDFFFE0000800FFFFFF000040FDFCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BEFFFE0000800FFFFFF000040FEFCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BF7FFE0000800FFFFFF000040FF7CFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFBFFE0000800FFFFFF000040FFBCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFDFFE0000800FFFFFF000040FFDCFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFEFFE0000800FFFFFF000040FFECFFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFF7FE0000800FFFFFF000040FFF4FFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFBFE0000800FFFFFF000040FFF8FFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFDFE0000800FFFFFF000040FFFC7FC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFEFE0000800FFFFFF000040FFFCBFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFF7E0000800FFFFFF000040FFFCDFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFFBE0000800FFFFFF000040FFFCEFC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFFDE0000800FFFFFF000040FFFCF7C00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFFEE0000800FFFFFF000040FFFCFBC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFFF60000800FFFFFF000040FFFCFDC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFFFA0000800FFFFFF000040FFFCFEC00010 ==> 1
+C286DD29B5806FFFFF80001E3BFFFFC0000800FFFFFF000040FFFCFF400010 ==> 1
+C286DD29B5806FFFFF80001E3BFFFFE00008007FFFFF000040FFFCFF800010 ==> 1
+C286DD29B5802F7DF780001E2BDF7DE0000800BEFBEF000040BEF8FFC00010 ==> 1
+C286DD29B5804FBEFB80001E33EFBEE0000800DF7DF7000040DF7C7FC00010 ==> 1
+C286DD29B58067DF7D80001E39F7DF60000800EFBEFB000040EFBCBFC00010 ==> 1
+C286DD29B5806BEFBE80001E3AFBEFA0000800F7DF7D000040F7DCDFC00010 ==> 1
+C286DD29B5806DF7DF00001E3B7DF7C0000800FBEFBE000040FBECEFC00010 ==> 1
+C286DD29B5806EFBEF80001E1BBEFBE00008007DF7DF0000407DF4F7C00010 ==> 1
+C286DD29B5806F7DF780001E2BDF7DE0000800BEFBEF000040BEF8FBC00010 ==> 1
+C286DD29B5806FBEFB80001E33EFBEE0000800DF7DF7000040DF7C7DC00010 ==> 1
+C286DD29B5806FDF7D80001E39F7DF60000800EFBEFB000040EFBCBEC00010 ==> 1
+C286DD29B5806FEFBE80001E3AFBEFA0000800F7DF7D000040F7DCDF400010 ==> 1
+C286DD29B5806FF7DF00001E3B7DF7C0000800FBEFBE000040FBECEF800010 ==> 1
+C286DD29B5802EF7BD80001E33DEF7A0000800EF7BDE000040F7FCFFC00010 ==> 0
+C286DD29B5804F7BDE80001E39EF7BC0000800F7BDEF0000407BFCFFC00010 ==> 0
+C286DD29B58067BDEF00001E3AF7BDE00008007BDEF7000040BDFCFFC00010 ==> 0
+C286DD29B5806BDEF780001E1B7BDEE0000800BDEF7B000040DEFCFFC00010 ==> 0
+C286DD29B5806DEF7B80001E2BBDEF60000800DEF7BD000040EF7CFFC00010 ==> 0
+C286DD29B5806EF7BD80001E33DEF7A0000800EF7BDE000040F7BCFFC00010 ==> 0
+C286DD29B5806F7BDE80001E39EF7BC0000800F7BDEF0000407BDCFFC00010 ==> 0
+C286DD29B5806FBDEF00001E3AF7BDE00008007BDEF7000040BDECFFC00010 ==> 0
+C286DD29B5806FDEF780001E1B7BDEE0000800BDEF7B000040DEF4FFC00010 ==> 0
+C286DD29B5806FEF7B80001E2BBDEF60000800DEF7BD000040EF78FFC00010 ==> 0
+C286DD29B5806FF7BD80001E33DEF7A0000800EF7BDE000040F7BC7FC00010 ==> 0
+C286DD29B5806FFBDE80001E39EF7BC0000800F7BDEF0000407BDCBFC00010 ==> 0
+C286DD29B5806FFDEF00001E3AF7BDE00008007BDEF7000040BDECDFC00010 ==> 0
+C286DD29B5806FFEF780001E1B7BDEE0000800BDEF7B000040DEF4EFC00010 ==> 0
+C286DD29B5806FFF7B80001E2BBDEF60000800DEF7BD000040EF78F7C00010 ==> 0
+C286DD29B5806FFFBD80001E33DEF7A0000800EF7BDE000040F7BC7BC00010 ==> 0
+C286DD29B5806FFFDE80001E39EF7BC0000800F7BDEF0000407BDCBDC00010 ==> 0
+C286DD29B5806FFFEF00001E3AF7BDE00008007BDEF7000040BDECDEC00010 ==> 0
+C286DD29B5806FFFF780001E1B7BDEE0000800BDEF7B000040DEF4EF400010 ==> 0
+C286DD29B5806FFFFB80001E2BBDEF60000800DEF7BD000040EF78F7800010 ==> 0
diff --git a/tests/codec/codec_fr_sid_test.c b/tests/codec/codec_fr_sid_test.c
new file mode 100644
index 00000000..25c1b3f3
--- /dev/null
+++ b/tests/codec/codec_fr_sid_test.c
@@ -0,0 +1,97 @@
+/*
+ * This program is a test for osmo_fr_sid_classify(). It reads a set of
+ * FRv1 codec frames in hex format (TS 101 318 RTP format represented in hex,
+ * each frame as its own hex line) and feeds each test frame to
+ * osmo_fr_sid_classify(). It then prints the output next to each input.
+ *
+ * Author: Mychaela N. Falconia <falcon@freecalypso.org>, 2024 - however,
+ * Mother Mychaela's contributions are NOT subject to copyright.
+ * No rights reserved, all rights relinquished.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+
+static void process_record(const char *hex_str)
+{
+ uint8_t frame_bytes[GSM_FR_BYTES];
+ enum osmo_gsm631_sid_class sidc;
+
+ osmo_hexparse(hex_str, frame_bytes, GSM_FR_BYTES);
+ sidc = osmo_fr_sid_classify(frame_bytes);
+ printf("%s ==> %d\n", hex_str, (int) sidc);
+}
+
+static void process_line(char *linebuf, const char *infname, int lineno)
+{
+ char *cp = linebuf, *hex_str;
+ int ndig;
+
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0' || *cp == '#')
+ return;
+ /* expect string of 66 hex digits */
+ hex_str = cp;
+ for (ndig = 0; ndig < GSM_FR_BYTES * 2; ndig++) {
+ if (!isxdigit(*cp))
+ goto inv;
+ cp++;
+ }
+ if (*cp) {
+ if (!isspace(*cp))
+ goto inv;
+ *cp++ = '\0';
+ }
+ /* must be end of non-comment line */
+ while (isspace(*cp))
+ cp++;
+ if (*cp != '\0' && *cp != '#')
+ goto inv;
+
+ process_record(hex_str);
+ return;
+
+inv: fprintf(stderr, "%s line %d: invalid syntax\n", infname, lineno);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ const char *infname;
+ FILE *inf;
+ char linebuf[128];
+ int lineno;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s input-file\n", argv[0]);
+ exit(1);
+ }
+ infname = argv[1];
+ inf = fopen(infname, "r");
+ if (!inf) {
+ perror(infname);
+ exit(1);
+ }
+ for (lineno = 1; fgets(linebuf, sizeof(linebuf), inf); lineno++)
+ process_line(linebuf, infname, lineno);
+ fclose(inf);
+ exit(0);
+}
diff --git a/tests/codec/codec_fr_sid_test.in b/tests/codec/codec_fr_sid_test.in
new file mode 100644
index 00000000..d5930e25
--- /dev/null
+++ b/tests/codec/codec_fr_sid_test.in
@@ -0,0 +1,184 @@
+# This file is input for the FRv1 SID classifier test program.
+# It has been generated here:
+#
+# https://www.freecalypso.org/hg/vband-misc/file/tip/fr-sid
+#
+# It is based on the same principle as the EFR version:
+#
+# https://www.freecalypso.org/hg/vband-misc/file/tip/efr-sid/Unit-test-desc
+
+DAE6DB659B00010000000000000100000000000001000000000000010000000000
+DAE6DB659B00014000000000000100000000000001000000000000010000000000
+DAE6DB659B00012000000000000100000000000001000000000000010000000000
+DAE6DB659B00010800000000000100000000000001000000000000010000000000
+DAE6DB659B00010400000000000100000000000001000000000000010000000000
+DAE6DB659B00010100000000000100000000000001000000000000010000000000
+DAE6DB659B00010080000000000100000000000001000000000000010000000000
+DAE6DB659B00010020000000000100000000000001000000000000010000000000
+DAE6DB659B00010010000000000100000000000001000000000000010000000000
+DAE6DB659B00010004000000000100000000000001000000000000010000000000
+DAE6DB659B00010002000000000100000000000001000000000000010000000000
+DAE6DB659B00010000800000000100000000000001000000000000010000000000
+DAE6DB659B00010000400000000100000000000001000000000000010000000000
+DAE6DB659B00010000100000000100000000000001000000000000010000000000
+DAE6DB659B00010000080000000100000000000001000000000000010000000000
+DAE6DB659B00010000020000000100000000000001000000000000010000000000
+DAE6DB659B00010000010000000100000000000001000000000000010000000000
+DAE6DB659B00010000004000000100000000000001000000000000010000000000
+DAE6DB659B00010000002000000100000000000001000000000000010000000000
+DAE6DB659B00010000000800000100000000000001000000000000010000000000
+DAE6DB659B00010000000400000100000000000001000000000000010000000000
+DAE6DB659B00010000000100000100000000000001000000000000010000000000
+DAE6DB659B00010000000080000100000000000001000000000000010000000000
+DAE6DB659B00010000000020000100000000000001000000000000010000000000
+DAE6DB659B00010000000010000100000000000001000000000000010000000000
+DAE6DB659B00010000000004000100000000000001000000000000010000000000
+DAE6DB659B00010000000002000100000000000001000000000000010000000000
+DAE6DB659B00010000000000000140000000000001000000000000010000000000
+DAE6DB659B00010000000000000120000000000001000000000000010000000000
+DAE6DB659B00010000000000000108000000000001000000000000010000000000
+DAE6DB659B00010000000000000104000000000001000000000000010000000000
+DAE6DB659B00010000000000000101000000000001000000000000010000000000
+DAE6DB659B00010000000000000100800000000001000000000000010000000000
+DAE6DB659B00010000000000000100200000000001000000000000010000000000
+DAE6DB659B00010000000000000100100000000001000000000000010000000000
+DAE6DB659B00010000000000000100040000000001000000000000010000000000
+DAE6DB659B00010000000000000100020000000001000000000000010000000000
+DAE6DB659B00010000000000000100008000000001000000000000010000000000
+DAE6DB659B00010000000000000100004000000001000000000000010000000000
+DAE6DB659B00010000000000000100001000000001000000000000010000000000
+DAE6DB659B00010000000000000100000800000001000000000000010000000000
+DAE6DB659B00010000000000000100000200000001000000000000010000000000
+DAE6DB659B00010000000000000100000100000001000000000000010000000000
+DAE6DB659B00010000000000000100000040000001000000000000010000000000
+DAE6DB659B00010000000000000100000020000001000000000000010000000000
+DAE6DB659B00010000000000000100000008000001000000000000010000000000
+DAE6DB659B00010000000000000100000004000001000000000000010000000000
+DAE6DB659B00010000000000000100000001000001000000000000010000000000
+DAE6DB659B00010000000000000100000000800001000000000000010000000000
+DAE6DB659B00010000000000000100000000200001000000000000010000000000
+DAE6DB659B00010000000000000100000000100001000000000000010000000000
+DAE6DB659B00010000000000000100000000040001000000000000010000000000
+DAE6DB659B00010000000000000100000000020001000000000000010000000000
+DAE6DB659B00010000000000000100000000000001400000000000010000000000
+DAE6DB659B00010000000000000100000000000001200000000000010000000000
+DAE6DB659B00010000000000000100000000000001080000000000010000000000
+DAE6DB659B00010000000000000100000000000001040000000000010000000000
+DAE6DB659B00010000000000000100000000000001010000000000010000000000
+DAE6DB659B00010000000000000100000000000001008000000000010000000000
+DAE6DB659B00010000000000000100000000000001002000000000010000000000
+DAE6DB659B00010000000000000100000000000001001000000000010000000000
+DAE6DB659B00010000000000000100000000000001000400000000010000000000
+DAE6DB659B00010000000000000100000000000001000200000000010000000000
+DAE6DB659B00010000000000000100000000000001000080000000010000000000
+DAE6DB659B00010000000000000100000000000001000040000000010000000000
+DAE6DB659B00010000000000000100000000000001000010000000010000000000
+DAE6DB659B00010000000000000100000000000001000008000000010000000000
+DAE6DB659B00010000000000000100000000000001000002000000010000000000
+DAE6DB659B00010000000000000100000000000001000001000000010000000000
+DAE6DB659B00010000000000000100000000000001000000400000010000000000
+DAE6DB659B00010000000000000100000000000001000000200000010000000000
+DAE6DB659B00010000000000000100000000000001000000080000010000000000
+DAE6DB659B00010000000000000100000000000001000000040000010000000000
+DAE6DB659B00010000000000000100000000000001000000010000010000000000
+DAE6DB659B00010000000000000100000000000001000000008000010000000000
+DAE6DB659B00010000000000000100000000000001000000002000010000000000
+DAE6DB659B00010000000000000100000000000001000000001000010000000000
+DAE6DB659B00010000000000000100000000000001000000000400010000000000
+DAE6DB659B00010000000000000100000000000001000000000200010000000000
+DAE6DB659B00010000000000000100000000000001000000000000014000000000
+DAE6DB659B00010000000000000100000000000001000000000000012000000000
+DAE6DB659B00010000000000000100000000000001000000000000010800000000
+DAE6DB659B00010000000000000100000000000001000000000000010400000000
+DAE6DB659B00010000000000000100000000000001000000000000010100000000
+DAE6DB659B00010000000000000100000000000001000000000000010080000000
+DAE6DB659B00010000000000000100000000000001000000000000010020000000
+DAE6DB659B00010000000000000100000000000001000000000000010010000000
+DAE6DB659B00010000000000000100000000000001000000000000010004000000
+DAE6DB659B00010000000000000100000000000001000000000000010000800000
+DAE6DB659B00010000000000000100000000000001000000000000010000100000
+DAE6DB659B00010000000000000100000000000001000000000000010000020000
+DAE6DB659B00010000000000000100000000000001000000000000010000004000
+DAE6DB659B00010000000000000100000000000001000000000000010000000800
+DAE6DB659B00010000000000000100000000000001000000000000010000000100
+DAE6DB659B00010000000000000100000000000001000000000000010000000020
+DAE6DB659B00010000000000000100000000000001000000000000010000000004
+DAE6DB659B00014000000000000100000000800001000000000000010000000000
+DAE6DB659B00012000000000000100000000200001000000000000010000000000
+DAE6DB659B00010800000000000100000000100001000000000000010000000000
+DAE6DB659B00010400000000000100000000040001000000000000010000000000
+DAE6DB659B00010100000000000100000000020001000000000000010000000000
+DAE6DB659B00010080000000000100000000000001400000000000010000000000
+DAE6DB659B00010020000000000100000000000001200000000000010000000000
+DAE6DB659B00010010000000000100000000000001080000000000010000000000
+DAE6DB659B00010004000000000100000000000001040000000000010000000000
+DAE6DB659B00010002000000000100000000000001010000000000010000000000
+DAE6DB659B00010000800000000100000000000001008000000000010000000000
+DAE6DB659B00010000400000000100000000000001002000000000010000000000
+DAE6DB659B00010000100000000100000000000001001000000000010000000000
+DAE6DB659B00010000080000000100000000000001000400000000010000000000
+DAE6DB659B00010000020000000100000000000001000200000000010000000000
+DAE6DB659B00010000010000000100000000000001000080000000010000000000
+DAE6DB659B00010000004000000100000000000001000040000000010000000000
+DAE6DB659B00010000002000000100000000000001000010000000010000000000
+DAE6DB659B00010000000800000100000000000001000008000000010000000000
+DAE6DB659B00010000000400000100000000000001000002000000010000000000
+DAE6DB659B00010000000100000100000000000001000001000000010000000000
+DAE6DB659B00010000000080000100000000000001000000400000010000000000
+DAE6DB659B00010000000020000100000000000001000000200000010000000000
+DAE6DB659B00010000000010000100000000000001000000080000010000000000
+DAE6DB659B00010000000004000100000000000001000000040000010000000000
+DAE6DB659B00010000000002000100000000000001000000010000010000000000
+DAE6DB659B00010000000000000140000000000001000000008000010000000000
+DAE6DB659B00010000000000000120000000000001000000002000010000000000
+DAE6DB659B00010000000000000108000000000001000000001000010000000000
+DAE6DB659B00010000000000000104000000000001000000000400010000000000
+DAE6DB659B00010000000000000101000000000001000000000200010000000000
+DAE6DB659B00010000000000000100800000000001000000000000014000000000
+DAE6DB659B00010000000000000100200000000001000000000000012000000000
+DAE6DB659B00010000000000000100100000000001000000000000010800000000
+DAE6DB659B00010000000000000100040000000001000000000000010400000000
+DAE6DB659B00010000000000000100020000000001000000000000010100000000
+DAE6DB659B00010000000000000100008000000001000000000000010080000000
+DAE6DB659B00010000000000000100004000000001000000000000010020000000
+DAE6DB659B00010000000000000100001000000001000000000000010010000000
+DAE6DB659B00010000000000000100000800000001000000000000010004000000
+DAE6DB659B00010000000000000100000200000001000000000000010000800000
+DAE6DB659B00010000000000000100000100000001000000000000010000100000
+DAE6DB659B00010000000000000100000040000001000000000000010000020000
+DAE6DB659B00010000000000000100000020000001000000000000010000004000
+DAE6DB659B00010000000000000100000008000001000000000000010000000800
+DAE6DB659B00010000000000000100000004000001000000000000010000000100
+DAE6DB659B00010000000000000100000001000001000000000000010000000020
+DAE6DB659B00010000000000000100000000800001000000000000010000000004
+DAE6DB659B00014020100804000101008040200001080402010000014020000000
+DAE6DB659B00012010080402000100804020100001040201008000012010000000
+DAE6DB659B00010804020100000140201008040001010080402000010804000000
+DAE6DB659B00010402010080000120100804020001008040201000010400800000
+DAE6DB659B00010100804020000108040201000001402010080400010100100000
+DAE6DB659B00010080402010000104020100800001201008040200010080020000
+DAE6DB659B00010020100804000101008040200001080402010000014020004000
+DAE6DB659B00010010080402000100804020100001040201008000012010000800
+DAE6DB659B00010004020100000140201008040001010080402000010804000100
+DAE6DB659B00010002010080000120100804020001008040201000010400800020
+DAE6DB659B00010000804020000108040201000001402010080400010100100004
+DAE6DB659B00014080810102000101020204040001040408081000010000000000
+DAE6DB659B00012020404080000140808101020001010202040400010000000000
+DAE6DB659B00010810102020000120204040800001408081010200010000000000
+DAE6DB659B00010404080810000108101020200001202040408000014000000000
+DAE6DB659B00010102020404000104040808100001081010202000012000000000
+DAE6DB659B00010080810102000101020204040001040408081000010800000000
+DAE6DB659B00010020404080000140808101020001010202040400010400000000
+DAE6DB659B00010010102020000120204040800001408081010200010100000000
+DAE6DB659B00010004080810000108101020200001202040408000014080000000
+DAE6DB659B00010002020404000104040808100001081010202000012020000000
+DAE6DB659B00010000810102000101020204040001040408081000010810000000
+DAE6DB659B00010000404080000140808101020001010202040400010404000000
+DAE6DB659B00010000102020000120204040800001408081010200010100800000
+DAE6DB659B00010000080810000108101020200001202040408000014080100000
+DAE6DB659B00010000020404000104040808100001081010202000012020020000
+DAE6DB659B00010000010102000101020204040001040408081000010810004000
+DAE6DB659B00010000004080000140808101020001010202040400010404000800
+DAE6DB659B00010000002020000120204040800001408081010200010100800100
+DAE6DB659B00010000000810000108101020200001202040408000014080100020
+DAE6DB659B00010000000404000104040808100001081010202000012020020004
diff --git a/tests/codec/codec_fr_sid_test.ok b/tests/codec/codec_fr_sid_test.ok
new file mode 100644
index 00000000..8dcaa345
--- /dev/null
+++ b/tests/codec/codec_fr_sid_test.ok
@@ -0,0 +1,175 @@
+DAE6DB659B00010000000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00014000000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00012000000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010800000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010400000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010100000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010080000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010020000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010010000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010004000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010002000000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000800000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000400000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000100000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000080000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000020000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000010000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000004000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000002000000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000800000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000400000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000100000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000080000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000020000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000010000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000004000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000002000100000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000140000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000120000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000108000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000104000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000101000000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100800000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100200000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100100000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100040000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100020000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100008000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100004000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100001000000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000800000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000200000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000100000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000040000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000020000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000008000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000004000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000001000001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000800001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000200001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000100001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000040001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000020001000000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001400000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001200000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001080000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001040000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001010000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001008000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001002000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001001000000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000400000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000200000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000080000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000040000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000010000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000008000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000002000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000001000000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000400000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000200000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000080000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000040000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000010000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000008000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000002000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000001000010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000400010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000200010000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000014000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000012000000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010800000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010400000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010100000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010080000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010020000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010010000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010004000000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010000800000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010000100000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010000020000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010000004000 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010000000800 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010000000100 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010000000020 ==> 2
+DAE6DB659B00010000000000000100000000000001000000000000010000000004 ==> 2
+DAE6DB659B00014000000000000100000000800001000000000000010000000000 ==> 1
+DAE6DB659B00012000000000000100000000200001000000000000010000000000 ==> 1
+DAE6DB659B00010800000000000100000000100001000000000000010000000000 ==> 1
+DAE6DB659B00010400000000000100000000040001000000000000010000000000 ==> 1
+DAE6DB659B00010100000000000100000000020001000000000000010000000000 ==> 1
+DAE6DB659B00010080000000000100000000000001400000000000010000000000 ==> 1
+DAE6DB659B00010020000000000100000000000001200000000000010000000000 ==> 1
+DAE6DB659B00010010000000000100000000000001080000000000010000000000 ==> 1
+DAE6DB659B00010004000000000100000000000001040000000000010000000000 ==> 1
+DAE6DB659B00010002000000000100000000000001010000000000010000000000 ==> 1
+DAE6DB659B00010000800000000100000000000001008000000000010000000000 ==> 1
+DAE6DB659B00010000400000000100000000000001002000000000010000000000 ==> 1
+DAE6DB659B00010000100000000100000000000001001000000000010000000000 ==> 1
+DAE6DB659B00010000080000000100000000000001000400000000010000000000 ==> 1
+DAE6DB659B00010000020000000100000000000001000200000000010000000000 ==> 1
+DAE6DB659B00010000010000000100000000000001000080000000010000000000 ==> 1
+DAE6DB659B00010000004000000100000000000001000040000000010000000000 ==> 1
+DAE6DB659B00010000002000000100000000000001000010000000010000000000 ==> 1
+DAE6DB659B00010000000800000100000000000001000008000000010000000000 ==> 1
+DAE6DB659B00010000000400000100000000000001000002000000010000000000 ==> 1
+DAE6DB659B00010000000100000100000000000001000001000000010000000000 ==> 1
+DAE6DB659B00010000000080000100000000000001000000400000010000000000 ==> 1
+DAE6DB659B00010000000020000100000000000001000000200000010000000000 ==> 1
+DAE6DB659B00010000000010000100000000000001000000080000010000000000 ==> 1
+DAE6DB659B00010000000004000100000000000001000000040000010000000000 ==> 1
+DAE6DB659B00010000000002000100000000000001000000010000010000000000 ==> 1
+DAE6DB659B00010000000000000140000000000001000000008000010000000000 ==> 1
+DAE6DB659B00010000000000000120000000000001000000002000010000000000 ==> 1
+DAE6DB659B00010000000000000108000000000001000000001000010000000000 ==> 1
+DAE6DB659B00010000000000000104000000000001000000000400010000000000 ==> 1
+DAE6DB659B00010000000000000101000000000001000000000200010000000000 ==> 1
+DAE6DB659B00010000000000000100800000000001000000000000014000000000 ==> 1
+DAE6DB659B00010000000000000100200000000001000000000000012000000000 ==> 1
+DAE6DB659B00010000000000000100100000000001000000000000010800000000 ==> 1
+DAE6DB659B00010000000000000100040000000001000000000000010400000000 ==> 1
+DAE6DB659B00010000000000000100020000000001000000000000010100000000 ==> 1
+DAE6DB659B00010000000000000100008000000001000000000000010080000000 ==> 1
+DAE6DB659B00010000000000000100004000000001000000000000010020000000 ==> 1
+DAE6DB659B00010000000000000100001000000001000000000000010010000000 ==> 1
+DAE6DB659B00010000000000000100000800000001000000000000010004000000 ==> 1
+DAE6DB659B00010000000000000100000200000001000000000000010000800000 ==> 1
+DAE6DB659B00010000000000000100000100000001000000000000010000100000 ==> 1
+DAE6DB659B00010000000000000100000040000001000000000000010000020000 ==> 1
+DAE6DB659B00010000000000000100000020000001000000000000010000004000 ==> 1
+DAE6DB659B00010000000000000100000008000001000000000000010000000800 ==> 1
+DAE6DB659B00010000000000000100000004000001000000000000010000000100 ==> 1
+DAE6DB659B00010000000000000100000001000001000000000000010000000020 ==> 1
+DAE6DB659B00010000000000000100000000800001000000000000010000000004 ==> 1
+DAE6DB659B00014020100804000101008040200001080402010000014020000000 ==> 1
+DAE6DB659B00012010080402000100804020100001040201008000012010000000 ==> 1
+DAE6DB659B00010804020100000140201008040001010080402000010804000000 ==> 1
+DAE6DB659B00010402010080000120100804020001008040201000010400800000 ==> 1
+DAE6DB659B00010100804020000108040201000001402010080400010100100000 ==> 1
+DAE6DB659B00010080402010000104020100800001201008040200010080020000 ==> 1
+DAE6DB659B00010020100804000101008040200001080402010000014020004000 ==> 1
+DAE6DB659B00010010080402000100804020100001040201008000012010000800 ==> 1
+DAE6DB659B00010004020100000140201008040001010080402000010804000100 ==> 1
+DAE6DB659B00010002010080000120100804020001008040201000010400800020 ==> 1
+DAE6DB659B00010000804020000108040201000001402010080400010100100004 ==> 1
+DAE6DB659B00014080810102000101020204040001040408081000010000000000 ==> 0
+DAE6DB659B00012020404080000140808101020001010202040400010000000000 ==> 0
+DAE6DB659B00010810102020000120204040800001408081010200010000000000 ==> 0
+DAE6DB659B00010404080810000108101020200001202040408000014000000000 ==> 0
+DAE6DB659B00010102020404000104040808100001081010202000012000000000 ==> 0
+DAE6DB659B00010080810102000101020204040001040408081000010800000000 ==> 0
+DAE6DB659B00010020404080000140808101020001010202040400010400000000 ==> 0
+DAE6DB659B00010010102020000120204040800001408081010200010100000000 ==> 0
+DAE6DB659B00010004080810000108101020200001202040408000014080000000 ==> 0
+DAE6DB659B00010002020404000104040808100001081010202000012020000000 ==> 0
+DAE6DB659B00010000810102000101020204040001040408081000010810000000 ==> 0
+DAE6DB659B00010000404080000140808101020001010202040400010404000000 ==> 0
+DAE6DB659B00010000102020000120204040800001408081010200010100800000 ==> 0
+DAE6DB659B00010000080810000108101020200001202040408000014080100000 ==> 0
+DAE6DB659B00010000020404000104040808100001081010202000012020020000 ==> 0
+DAE6DB659B00010000010102000101020204040001040408081000010810004000 ==> 0
+DAE6DB659B00010000004080000140808101020001010202040400010404000800 ==> 0
+DAE6DB659B00010000002020000120204040800001408081010200010100800100 ==> 0
+DAE6DB659B00010000000810000108101020200001202040408000014080100020 ==> 0
+DAE6DB659B00010000000404000104040808100001081010202000012020020004 ==> 0
diff --git a/tests/codec/codec_hr_sid_test.c b/tests/codec/codec_hr_sid_test.c
new file mode 100644
index 00000000..ed4aa9bc
--- /dev/null
+++ b/tests/codec/codec_hr_sid_test.c
@@ -0,0 +1,144 @@
+/*
+ * This program is a test for osmo_hr_sid_classify(). It reads a set of
+ * TCH/HS Rx bit patterns in TI DSP format (originally captured from a
+ * Calypso MS under conditions of induced radio errors), converts each
+ * bit pattern to TS 101 318 format (using same bit reordering function
+ * as libosmocoding gsm0503 implementation), and feeds each test line
+ * to osmo_hr_sid_classify(). It then prints the output next to each input.
+ *
+ * Author: Mychaela N. Falconia <falcon@freecalypso.org>, 2024 - however,
+ * Mother Mychaela's contributions are NOT subject to copyright.
+ * No rights reserved, all rights relinquished.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+
+#define HR_CODEC_BITS (GSM_HR_BYTES * 8)
+#define HR_BYTES_TIDSP (GSM_HR_BYTES + 1)
+
+/* re-arrange according to TS 05.03 Table 3a (receiver) */
+/* function copied from src/coding/gsm0503_coding.c */
+static void tch_hr_d_to_b(ubit_t *b_bits, const ubit_t *d_bits)
+{
+ int i;
+
+ const uint16_t *map;
+
+ if (!d_bits[93] && !d_bits[94])
+ map = gsm620_unvoiced_bitorder;
+ else
+ map = gsm620_voiced_bitorder;
+
+ for (i = 0; i < 112; i++)
+ b_bits[map[i]] = d_bits[i];
+}
+
+static void process_record(const char *hex_str, bool bci_flag)
+{
+ uint8_t dsp_rx_bytes[HR_BYTES_TIDSP];
+ ubit_t bits_transmission_order[HR_BYTES_TIDSP * 8];
+ ubit_t bits_codec_order[HR_CODEC_BITS];
+ uint8_t hr_bytes_ts101318[GSM_HR_BYTES];
+ bool bfi_flag = false;
+ enum osmo_gsm631_sid_class sidc;
+
+ osmo_hexparse(hex_str, dsp_rx_bytes, HR_BYTES_TIDSP);
+ osmo_pbit2ubit(bits_transmission_order, dsp_rx_bytes,
+ HR_BYTES_TIDSP * 8);
+ /* TI DSP format has a gap of 4 bits between class 1 and class 2
+ * portions - get rid of it. 95 is the number of class 1 bits,
+ * 17 is the number of class 2 bits. */
+ memmove(bits_transmission_order + 95,
+ bits_transmission_order + 95 + 4, 17);
+ tch_hr_d_to_b(bits_codec_order, bits_transmission_order);
+ osmo_ubit2pbit(hr_bytes_ts101318, bits_codec_order, HR_CODEC_BITS);
+
+ sidc = osmo_hr_sid_classify(hr_bytes_ts101318, bci_flag, &bfi_flag);
+ printf("%s %d ==> %d %d\n", hex_str, (int) bci_flag,
+ (int) sidc, (int) bfi_flag);
+}
+
+static void process_line(char *linebuf, const char *infname, int lineno)
+{
+ char *cp = linebuf, *hex_str;
+ int ndig;
+ bool bci_flag;
+
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0' || *cp == '#')
+ return;
+ /* expect string of 30 hex digits */
+ hex_str = cp;
+ for (ndig = 0; ndig < HR_BYTES_TIDSP * 2; ndig++) {
+ if (!isxdigit(*cp))
+ goto inv;
+ cp++;
+ }
+ if (!isspace(*cp))
+ goto inv;
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ /* 0 or 1 must follow, giving BCI flag */
+ if (*cp == '0')
+ bci_flag = false;
+ else if (*cp == '1')
+ bci_flag = true;
+ else
+ goto inv;
+ cp++;
+ /* must be end of non-comment line */
+ while (isspace(*cp))
+ cp++;
+ if (*cp != '\0' && *cp != '#')
+ goto inv;
+
+ process_record(hex_str, bci_flag);
+ return;
+
+inv: fprintf(stderr, "%s line %d: invalid syntax\n", infname, lineno);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ const char *infname;
+ FILE *inf;
+ char linebuf[128];
+ int lineno;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s input-file\n", argv[0]);
+ exit(1);
+ }
+ infname = argv[1];
+ inf = fopen(infname, "r");
+ if (!inf) {
+ perror(infname);
+ exit(1);
+ }
+ for (lineno = 1; fgets(linebuf, sizeof(linebuf), inf); lineno++)
+ process_line(linebuf, infname, lineno);
+ fclose(inf);
+ exit(0);
+}
diff --git a/tests/codec/codec_hr_sid_test.in b/tests/codec/codec_hr_sid_test.in
new file mode 100644
index 00000000..e8ad9662
--- /dev/null
+++ b/tests/codec/codec_hr_sid_test.in
@@ -0,0 +1,61 @@
+# This file is input for the HR SID classifier test program. All TCH/HS
+# Rx bit strings contained in this file have been taken from hr-test1.dl
+# or hr-test2.dl, attached to this wiki page:
+#
+# https://osmocom.org/projects/retro-gsm/wiki/HRv1_error_flags
+#
+# Each Rx bit string (15 hex bytes) is from TI DSP; the 0 or 1 that follows
+# is the BCI flag to be fed to the SID classifier. The original DSP status
+# word is noted in the comments; running the unit test program will show
+# whether or not our SID classification agrees with that produced by
+# TI Calypso DSP.
+
+# perfect, error-free SID: hr-test2.dl line 171, C010
+FFFFFFFFFFFFFFFFFFFFFFFE1FFFF0 0
+
+# mode bits cleared to 0, all other bits still 1s
+FFFFFFFFFFFFFFFFFFFFFFF81FFFF0 0
+
+# selected error lines
+FFFFFFFFFFFFFFFFFFFFFFFE0FFF70 0 # hr-test2.dl line 4226, C010
+FFFFFFFFFFFFFFFFFFFFFFFE13FFF0 1 # hr-test2.dl line 4256, C012
+FFFFFFFFFFFFFFFFFFFFFFFE17FFF0 1 # hr-test2.dl line 4258, C012
+FFFD01FFFFFFFFFFFFFFFFFE0FFFF0 1 # hr-test2.dl line 4598, C00A
+FFFFFFFFFFFCC296452940FE1FFEF0 1 # hr-test2.dl line 5141, C00F
+FFFFFFFFFFFCC296452940FE1FFEF0 0 # same bits without BCI
+FF6E76F40C276FFFFFFFFFFE0FFFF0 1 # hr-test2.dl line 5173, C003
+FF6E76F40C276FFFFFFFFFFE0FFFF0 0 # same bits without BCI
+FFFFFFFFFDFFFFFFFFFFFFFE0FFFF0 1 # hr-test2.dl line 5206, C012
+FFFFFFFFFDFFFFFFFFFFFFFE0FFFF0 0 # same bits without BCI
+FFFFFFFFFFD0DFFFFFFFFFFE07FEF0 1 # hr-test2.dl line 5261, C00A
+FFFFFFFFFFD0DFFFFFFFFFFE07FEF0 0 # same bits without BCI
+FFFFFFFFFFFF41EAC9676FFE1F7DF0 1 # hr-test2.dl line 5738, C00F
+FFFFFFFFFFFF41EAC9676FFE1F7DF0 0 # same bits without BCI
+
+FFFFFFFFFFFF847D5B9DBFFE1937B0 1 # hr-test2.dl line 6175, C00F
+FFFFFFFFFFFF847D5B9DBFFE1937B0 0 # same bits without BCI
+FFFFFFFFFFFFFFFFFFFFFFD01FFEB0 1 # hr-test2.dl line 6188, C017
+FFFFFFFFFFFFFFFFFFFFFFD01FFEB0 0 # same bits without BCI
+FDBD7D552CB25FFFFFFFFFFE1DFBB0 1 # hr-test2.dl line 6191, C002
+FDBD7D552CB25FFFFFFFFFFE1DFBB0 0 # same bits without BCI
+FFD2F0A52B8FFFFFFFFEDE600FFDF0 1 # hr-test2.dl line 6195, C007
+FFD2F0A52B8FFFFFFFFEDE600FFDF0 0 # same bits without BCI
+FFFFFFFFCC7FFFFFFFF4EE601C31D0 1 # hr-test2.dl line 6318, C007
+FFFFFFFFCC7FFFFFFFF4EE601C31D0 0 # same bits without BCI
+FFFFFFFFFFFFFFFFFFFFA5901BEFD0 1 # hr-test2.dl line 6545, C00F
+FFFFFFFFFFFFFFFFFFFFA5901BEFD0 0 # same bits without BCI
+FFFFFFFFEA97FFFFFFE765901FFFB0 1 # hr-test2.dl line 6973, C00F
+FFFFFFFFEA97FFFFFFE765901FFFB0 0 # same bits without BCI
+
+FFFFFFFFF8C8A5E29DA0DFFE07FFF0 1 # hr-test2.dl line 7158, C00F
+FFFFFFFFF8C8A5E29DA0DFFE07FFF0 0 # same bits without BCI
+FFFD01853B7206E63FFFFFFE1FBFD0 1 # hr-test2.dl line 7175, C003
+FFFD01853B7206E63FFFFFFE1FBFD0 0 # same bits without BCI
+E6ACC7FFFF40FFFFFFFFFFFE1FF770 1 # hr-test2.dl line 7195, C00B
+E6ACC7FFFF40FFFFFFFFFFFE1FF770 0 # same bits without BCI
+
+# hr-test1.dl, PRBS without major errors
+55A5404BFAED58A3BE33A978092A40 0 # hr-test1.dl line 1051, C000
+CFE44B516ED5D1F54E4615AA101260 0 # hr-test1.dl line 2710, C000
+D7881D40AA0F68106195DCD41568C0 0 # hr-test1.dl line 4306, C000
+D4CFB4961F8F9F11313454560690E0 1 # hr-test1.dl line 4631, C002
diff --git a/tests/codec/codec_hr_sid_test.ok b/tests/codec/codec_hr_sid_test.ok
new file mode 100644
index 00000000..3ea1fedf
--- /dev/null
+++ b/tests/codec/codec_hr_sid_test.ok
@@ -0,0 +1,40 @@
+FFFFFFFFFFFFFFFFFFFFFFFE1FFFF0 0 ==> 2 0
+FFFFFFFFFFFFFFFFFFFFFFF81FFFF0 0 ==> 1 0
+FFFFFFFFFFFFFFFFFFFFFFFE0FFF70 0 ==> 2 0
+FFFFFFFFFFFFFFFFFFFFFFFE13FFF0 1 ==> 2 0
+FFFFFFFFFFFFFFFFFFFFFFFE17FFF0 1 ==> 2 0
+FFFD01FFFFFFFFFFFFFFFFFE0FFFF0 1 ==> 1 0
+FFFFFFFFFFFCC296452940FE1FFEF0 1 ==> 1 0
+FFFFFFFFFFFCC296452940FE1FFEF0 0 ==> 0 0
+FF6E76F40C276FFFFFFFFFFE0FFFF0 1 ==> 0 1
+FF6E76F40C276FFFFFFFFFFE0FFFF0 0 ==> 0 0
+FFFFFFFFFDFFFFFFFFFFFFFE0FFFF0 1 ==> 2 0
+FFFFFFFFFDFFFFFFFFFFFFFE0FFFF0 0 ==> 2 0
+FFFFFFFFFFD0DFFFFFFFFFFE07FEF0 1 ==> 1 0
+FFFFFFFFFFD0DFFFFFFFFFFE07FEF0 0 ==> 1 0
+FFFFFFFFFFFF41EAC9676FFE1F7DF0 1 ==> 1 0
+FFFFFFFFFFFF41EAC9676FFE1F7DF0 0 ==> 0 0
+FFFFFFFFFFFF847D5B9DBFFE1937B0 1 ==> 1 0
+FFFFFFFFFFFF847D5B9DBFFE1937B0 0 ==> 0 0
+FFFFFFFFFFFFFFFFFFFFFFD01FFEB0 1 ==> 1 0
+FFFFFFFFFFFFFFFFFFFFFFD01FFEB0 0 ==> 1 0
+FDBD7D552CB25FFFFFFFFFFE1DFBB0 1 ==> 0 1
+FDBD7D552CB25FFFFFFFFFFE1DFBB0 0 ==> 0 0
+FFD2F0A52B8FFFFFFFFEDE600FFDF0 1 ==> 0 1
+FFD2F0A52B8FFFFFFFFEDE600FFDF0 0 ==> 0 0
+FFFFFFFFCC7FFFFFFFF4EE601C31D0 1 ==> 0 1
+FFFFFFFFCC7FFFFFFFF4EE601C31D0 0 ==> 0 0
+FFFFFFFFFFFFFFFFFFFFA5901BEFD0 1 ==> 1 0
+FFFFFFFFFFFFFFFFFFFFA5901BEFD0 0 ==> 1 0
+FFFFFFFFEA97FFFFFFE765901FFFB0 1 ==> 1 0
+FFFFFFFFEA97FFFFFFE765901FFFB0 0 ==> 0 0
+FFFFFFFFF8C8A5E29DA0DFFE07FFF0 1 ==> 1 0
+FFFFFFFFF8C8A5E29DA0DFFE07FFF0 0 ==> 0 0
+FFFD01853B7206E63FFFFFFE1FBFD0 1 ==> 0 1
+FFFD01853B7206E63FFFFFFE1FBFD0 0 ==> 0 0
+E6ACC7FFFF40FFFFFFFFFFFE1FF770 1 ==> 1 0
+E6ACC7FFFF40FFFFFFFFFFFE1FF770 0 ==> 0 0
+55A5404BFAED58A3BE33A978092A40 0 ==> 0 0
+CFE44B516ED5D1F54E4615AA101260 0 ==> 0 0
+D7881D40AA0F68106195DCD41568C0 0 ==> 0 0
+D4CFB4961F8F9F11313454560690E0 1 ==> 0 0
diff --git a/tests/jhash/jhash_test.c b/tests/jhash/jhash_test.c
new file mode 100644
index 00000000..e2c8c71e
--- /dev/null
+++ b/tests/jhash/jhash_test.c
@@ -0,0 +1,56 @@
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/hashtable.h>
+#include <osmocom/core/jhash.h>
+
+struct item {
+ const char blob[32];
+ struct hlist_node node;
+};
+
+struct item items[] = {
+ { "blob one", },
+ { "blob two and five are the same", },
+ { "third blob", },
+ { "fourth blob", },
+ { "blob two and five are the same", },
+};
+
+uint32_t item_hash(const struct item *item)
+{
+ return osmo_jhash(item->blob, strlen(item->blob), 0);
+}
+
+int main(void)
+{
+ int i;
+ struct item *item;
+
+ DECLARE_HASHTABLE(haystack, 5);
+ hash_init(haystack);
+
+ printf("add:\n");
+ for (i = 0; i < ARRAY_SIZE(items); i++) {
+ uint32_t hash;
+ item = &items[i];
+ hash_add(haystack, &item->node, hash = item_hash(item));
+ printf("- adding items[%d]#%x = %s\n", i, hash, item->blob);
+ }
+
+ printf("list:\n");
+ hash_for_each (haystack, i, item, node)
+ printf("- %s [%d]\n", item->blob, (int)(item - items));
+
+ printf("find:\n");
+ for (i = 0; i < ARRAY_SIZE(items); i++) {
+ uint32_t hash;
+ struct item *needle = &items[i];
+ hash = item_hash(needle);
+ printf("- looking up items[%d]#%x = %s\n", i, hash, needle->blob);
+ hash_for_each_possible (haystack, item, node, hash)
+ printf(" - %s items[%d]\n",
+ (item == needle) ? "found" : "not",
+ (int)(item - items));
+ }
+
+ return 0;
+}
diff --git a/tests/jhash/jhash_test.ok b/tests/jhash/jhash_test.ok
new file mode 100644
index 00000000..e28ccc46
--- /dev/null
+++ b/tests/jhash/jhash_test.ok
@@ -0,0 +1,25 @@
+add:
+- adding items[0]#b286bb61 = blob one
+- adding items[1]#ad4eb7b = blob two and five are the same
+- adding items[2]#6b8eae61 = third blob
+- adding items[3]#24a546dc = fourth blob
+- adding items[4]#ad4eb7b = blob two and five are the same
+list:
+- blob two and five are the same [4]
+- blob two and five are the same [1]
+- blob one [0]
+- fourth blob [3]
+- third blob [2]
+find:
+- looking up items[0]#b286bb61 = blob one
+ - found items[0]
+- looking up items[1]#ad4eb7b = blob two and five are the same
+ - not items[4]
+ - found items[1]
+- looking up items[2]#6b8eae61 = third blob
+ - found items[2]
+- looking up items[3]#24a546dc = fourth blob
+ - found items[3]
+- looking up items[4]#ad4eb7b = blob two and five are the same
+ - found items[4]
+ - not items[1]
diff --git a/tests/sim/sim_test.c b/tests/sim/sim_test.c
index 2e2eec58..ab5d2be3 100644
--- a/tests/sim/sim_test.c
+++ b/tests/sim/sim_test.c
@@ -27,6 +27,10 @@ const uint8_t uicc_tprof[] = { 0x80, 0x10, 0x00, 0x00, 0x02, 0x01, 0x02 };
const uint8_t uicc_tprof_wrong_class[] = { 0x00, 0x10, 0x00, 0x00, 0x02, 0x01, 0x02 };
const uint8_t uicc_read[] = { 0x00, 0xB0, 0x00, 0x00, 0x10 };
const uint8_t uicc_upd[] = { 0x00, 0xD6, 0x00, 0x00, 0x02, 0x01, 0x02 };
+const uint8_t uicc_get_status[] = { 0x80, 0xf2, 0x00, 0x02, 0x10 };
+const uint8_t euicc_m2m_get_status[] = { 0x81, 0xf2, 0x40, 0x02, 0x02, 0x4f, 0x00 };
+const uint8_t gp_get_data2[] = { 0x81, 0xCA, 0x00, 0x5A, 0x00 };
+const uint8_t gp_get_data4[] = { 0x81, 0xCA, 0x00, 0x5A, 0x12 };
#define APDU_CASE_ASSERT(x, y) \
do { \
@@ -45,6 +49,10 @@ static void test_cla_ins_tbl(void)
APDU_CASE_ASSERT(uicc_tprof_wrong_class, 0);
APDU_CASE_ASSERT(uicc_read, 2);
APDU_CASE_ASSERT(uicc_upd, 3);
+ APDU_CASE_ASSERT(uicc_get_status, 2);
+ APDU_CASE_ASSERT(euicc_m2m_get_status, 4);
+ APDU_CASE_ASSERT(gp_get_data2, 2);
+ APDU_CASE_ASSERT(gp_get_data4, 4);
}
int main(int argc, char **argv)
diff --git a/tests/sim/sim_test.ok b/tests/sim/sim_test.ok
index 7d3f986d..3abfb718 100644
--- a/tests/sim/sim_test.ok
+++ b/tests/sim/sim_test.ok
@@ -4,3 +4,7 @@ Testing uicc_tprof
Testing uicc_tprof_wrong_class
Testing uicc_read
Testing uicc_upd
+Testing uicc_get_status
+Testing euicc_m2m_get_status
+Testing gp_get_data2
+Testing gp_get_data4
diff --git a/tests/testsuite.at b/tests/testsuite.at
index e721b937..f22ca5a5 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -218,6 +218,24 @@ cat $abs_srcdir/codec/codec_ecu_fr_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/codec/codec_ecu_fr_test], [0], [expout], [ignore])
AT_CLEANUP
+AT_SETUP([codec_efr_sid])
+AT_KEYWORDS([codec_efr_sid])
+cat $abs_srcdir/codec/codec_efr_sid_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/codec/codec_efr_sid_test $abs_srcdir/codec/codec_efr_sid_test.in], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([codec_fr_sid])
+AT_KEYWORDS([codec_fr_sid])
+cat $abs_srcdir/codec/codec_fr_sid_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/codec/codec_fr_sid_test $abs_srcdir/codec/codec_fr_sid_test.in], [0], [expout], [ignore])
+AT_CLEANUP
+
+AT_SETUP([codec_hr_sid])
+AT_KEYWORDS([codec_hr_sid])
+cat $abs_srcdir/codec/codec_hr_sid_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/codec/codec_hr_sid_test $abs_srcdir/codec/codec_hr_sid_test.in], [0], [expout], [ignore])
+AT_CLEANUP
+
AT_SETUP([fr])
AT_KEYWORDS([fr])
cat $abs_srcdir/fr/fr_test.ok > expout
@@ -547,3 +565,9 @@ 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
+
+AT_SETUP([jhash])
+AT_KEYWORDS([jhash])
+cat $abs_srcdir/jhash/jhash_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/jhash/jhash_test], [0], [expout], [ignore])
+AT_CLEANUP