forked from cellular-infrastructure/osmo-pcu
Compare commits
104 Commits
master
...
radisys/eg
Author | SHA1 | Date |
---|---|---|
Pravin Kumarvel | b6da682060 | |
Pravin Kumarvel | 03a4786a22 | |
Pravin Kumarvel | 75b8f8efd8 | |
pravin | dcb2f6b3de | |
pravin | f15edb81ff | |
pravin | 99e78ab60e | |
pravin | 07b05394bd | |
pravin | 92e8b8122f | |
pravin | c73ae31306 | |
pravin | 93605f3b4b | |
pravin | d97a7c5921 | |
Neels Hofmeyr | fd9e16ce97 | |
bhargava | 465f5bbb6f | |
bhargava | 628dcfbc97 | |
Aravind Sirsikar | 0ee31cfa38 | |
Aravind Sirsikar | 8e70bb5bb4 | |
Aravind Sirsikar | 22a901905c | |
Aravind Sirsikar | 3463bd4adc | |
Aravind Sirsikar | e26ee01d56 | |
Neels Hofmeyr | 0241526836 | |
Aravind Sirsikar | 9f5f008aed | |
Aravind Sirsikar | 8d2d9e8985 | |
Max | 9bbe1600cc | |
Minh-Quang Nguyen | 16ddc90eab | |
Aravind Sirsikar | 7c7a86c080 | |
Aravind Sirsikar | a35c911a91 | |
Aravind Sirsikar | 3c2eaebd21 | |
Aravind Sirsikar | fd71384104 | |
Aravind Sirsikar | b119198992 | |
Neels Hofmeyr | 01826c13b1 | |
bhargava | 959d1dee67 | |
Aravind Sirsikar | eebcb1e3e8 | |
Aravind Sirsikar | 02352b487a | |
Aravind Sirsikar | 50b097003b | |
Aravind Sirsikar | e6cadb4e3c | |
Aravind Sirsikar | 1ec4d80176 | |
Neels Hofmeyr | 9876f4bb21 | |
Neels Hofmeyr | 7fd177b91c | |
Neels Hofmeyr | 2d91260ea4 | |
Neels Hofmeyr | 6bae2d11f1 | |
Neels Hofmeyr | 0b4da058ad | |
Max | 79cb245157 | |
Max | cbf9a721d6 | |
Aravind Sirsikar | 505a86d396 | |
Aravind Sirsikar | 36bdc5f7a4 | |
Max | d572054ca7 | |
Max | 878bd1f296 | |
Max | 1d7644b23a | |
Max | 2ec6b8e758 | |
Tom Tsou | df69809b82 | |
Holger Hans Peter Freyther | 5d94b5455f | |
Aravind Sirsikar | 1a679127af | |
Aravind Sirsikar | cf2152b24c | |
Aravind Sirsikar | e8ccafc63d | |
Aravind Sirsikar | 914955209e | |
Harald Welte | 899d36d813 | |
Neels Hofmeyr | d32aa03520 | |
Aravind Sirsikar | 2c9f980163 | |
Aravind Sirsikar | 99ab0a8fa0 | |
Aravind Sirsikar | 550a54184b | |
Aravind Sirsikar | 23617c001d | |
Aravind Sirsikar | 189742b66c | |
Alexander Couzens | e04fd0cf0f | |
Alexander Couzens | 6922bcd929 | |
Alexander Couzens | 7fdbf89ef3 | |
Alexander Couzens | 6f0dc96929 | |
Alexander Couzens | 1a5066112f | |
Alexander Couzens | d302e4fb28 | |
Alexander Couzens | 68e2c6375e | |
Alexander Couzens | cb846ecbbc | |
Alexander Couzens | b82bd92e57 | |
Alexander Couzens | 2fcfc29020 | |
Alexander Couzens | ce936f3cd4 | |
Alexander Couzens | c1c9d6a9d8 | |
Alexander Couzens | c8fd4b7c42 | |
Alexander Couzens | f929e62525 | |
Alexander Couzens | 4acb6b7251 | |
Alexander Couzens | 95e379241a | |
Alexander Couzens | 543756adbe | |
Alexander Couzens | 2cb1547993 | |
Alexander Couzens | 9736d00b12 | |
Yves Godin | 660709dc7c | |
Max | 58b6646750 | |
Alexander Couzens | ed3ae4a392 | |
Yves Godin | f0bb25450c | |
Max | de810f2005 | |
Max | cad867ec8d | |
Max | 280448ba7b | |
Holger Hans Peter Freyther | 1aa7527302 | |
Holger Hans Peter Freyther | ca025c02ef | |
Holger Hans Peter Freyther | 97e48a3252 | |
Harald Welte | 63d33ad2d7 | |
Aravind Sirsikar | 7952282b78 | |
Aravind Sirsikar | a859a21800 | |
Aravind Sirsikar | 7a05b039c8 | |
Bhargava Abhyankar | e44383baa4 | |
Aravind Sirsikar | 5a5d2b7a27 | |
Saurabh Sharan | 2b09c39c9c | |
Saurabh Sharan | bacb65b48b | |
Saurabh Sharan | 656eed5975 | |
Holger Hans Peter Freyther | 173ef90a53 | |
Holger Hans Peter Freyther | fd263b0dfd | |
Holger Hans Peter Freyther | 99db40ad2d | |
Max | 22d7e75e1f |
|
@ -14,10 +14,11 @@ config.guess
|
|||
config.sub
|
||||
config.status
|
||||
configure
|
||||
compile
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
libtool
|
||||
*libtool
|
||||
ltmain.sh
|
||||
|
||||
core
|
||||
|
@ -43,3 +44,17 @@ tests/codel/codel_test
|
|||
tests/emu/pcu_emu
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
tests/edge/EdgeTest
|
||||
tests/llc/LlcTest
|
||||
|
||||
# ignore debian files
|
||||
.tarball-version
|
||||
debian/autoreconf.after
|
||||
debian/autoreconf.before
|
||||
debian/files
|
||||
debian/*.debhelper*
|
||||
debian/*.substvars
|
||||
debian/osmo-pcu-dbg/
|
||||
debian/osmo-pcu.substvars
|
||||
debian/osmo-pcu/
|
||||
debian/tmp/
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=osmo-pcu
|
9
README
9
README
|
@ -2,14 +2,19 @@ This is an implementation of Packet Control Unit (PCU) according to TS 04.60
|
|||
|
||||
The PCU is part of BSS, so it connects directly to SGSN.
|
||||
|
||||
For this PCU the dependent libosmocore patch is in contrib which has to be
|
||||
applied on top of libosmmocore a23817622b28cb1969a73ffd36da501eb29b9cd7.
|
||||
|
||||
== Current limitations ==
|
||||
This PCU enables
|
||||
* Fix for Flexible window calculation for EGPRS in UL
|
||||
* Correction for CRBB generation in PUAN
|
||||
|
||||
== Crrent limitations ==
|
||||
|
||||
* No PFC support
|
||||
* No fixed allocation support
|
||||
* No extended dynamic allocation support
|
||||
* No unacknowledged mode operation
|
||||
* No PCCCH/PBCCH support
|
||||
* Only single slot assignment on uplink direction
|
||||
* No half-duplex class support (only semi-duplex)
|
||||
* No handover support
|
||||
|
|
19
configure.ac
19
configure.ac
|
@ -35,6 +35,25 @@ AC_ARG_ENABLE(sysmocom-dsp,
|
|||
AC_MSG_RESULT([$enable_sysmocom_dsp])
|
||||
AM_CONDITIONAL(ENABLE_SYSMODSP, test "x$enable_sysmocom_dsp" = "xyes")
|
||||
|
||||
AC_MSG_CHECKING([whether to enable direct PHY access for PDCH of NuRAN Wireless Litecell 1.5 BTS])
|
||||
AC_ARG_ENABLE(lc15bts-phy,
|
||||
AC_HELP_STRING([--enable-lc15bts-phy],
|
||||
[enable code for Litecell 1.5 PHY [default=no]]),
|
||||
[enable_lc15bts_phy="$enableval"],[enable_lc15bts_phy="no"])
|
||||
AC_ARG_WITH([litecell15], [AS_HELP_STRING([--with-litecell15=INCLUDE_DIR], [Location of the litecell 1.5 API header files])],
|
||||
[litecell15_incdir="$withval"],[litecell15_incdir="$incdir"])
|
||||
AC_SUBST([LITECELL15_INCDIR], $litecell15_incdir)
|
||||
AC_MSG_RESULT([$enable_lc15bts_phy])
|
||||
AM_CONDITIONAL(ENABLE_LC15BTS_PHY, test "x$enable_lc15bts_phy" = "xyes")
|
||||
if test "$enable_litecell15" = "yes"; then
|
||||
oldCPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS -I$LITECELL15_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
|
||||
AC_CHECK_HEADER([nrw/litecell15/litecell15.h],[],
|
||||
[AC_MSG_ERROR([nrw/litecell15/litecell15.h can not be found in $litecell15_incdir])],
|
||||
[#include <nrw/litecell15/litecell15.h>])
|
||||
CPPFLAGS=$oldCPPFLAGS
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([vty_tests],
|
||||
AC_HELP_STRING([--enable-vty-tests],
|
||||
[Include the VTY tests in make check [default=no]]),
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
From 369a4edfc661a82e0766a63f423b7d2665115183 Mon Sep 17 00:00:00 2001
|
||||
From: Pravin Kumarvel <pmanohar@radisys.com>
|
||||
Date: Thu, 13 Oct 2016 16:35:28 +0530
|
||||
Subject: [PATCH] Add function to get uninterrupted bit run
|
||||
|
||||
Function bitvec_rl_curbit added to get number of uninterrupted
|
||||
bits run in vector starting from the current bit till max number
|
||||
of bits.
|
||||
---
|
||||
include/osmocom/core/bitvec.h | 1 +
|
||||
src/bitvec.c | 44 +++++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 45 insertions(+)
|
||||
|
||||
diff --git a/include/osmocom/core/bitvec.h b/include/osmocom/core/bitvec.h
|
||||
index 19e2af8..0e17ba7 100644
|
||||
--- a/include/osmocom/core/bitvec.h
|
||||
+++ b/include/osmocom/core/bitvec.h
|
||||
@@ -89,6 +89,7 @@ char bit_value_to_char(enum bit_value v);
|
||||
void bitvec_to_string_r(const struct bitvec *bv, char *str);
|
||||
void bitvec_zero(struct bitvec *bv);
|
||||
unsigned bitvec_rl(const struct bitvec *bv, bool b);
|
||||
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits);
|
||||
void bitvec_shiftl(struct bitvec *bv, unsigned int n);
|
||||
int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits);
|
||||
unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array,
|
||||
diff --git a/src/bitvec.c b/src/bitvec.c
|
||||
index 38148ac..d366eb9 100644
|
||||
--- a/src/bitvec.c
|
||||
+++ b/src/bitvec.c
|
||||
@@ -575,6 +575,50 @@ unsigned bitvec_rl(const struct bitvec *bv, bool b)
|
||||
return bv->cur_bit;
|
||||
}
|
||||
|
||||
+/*! \brief Return number (bits) of uninterrupted bit run in vector starting from the current bit
|
||||
+ * \param[in] bv The boolean vector to work on
|
||||
+ * \param[in] b The boolean, sequence of 1's or 0's to be checked
|
||||
+ * \returns Number of consecutive bits of \p b in \p bv
|
||||
+ */
|
||||
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits)
|
||||
+{
|
||||
+ unsigned i = 0;
|
||||
+ int temp_res = 0;
|
||||
+ int count = 0;
|
||||
+ unsigned readIndex = bv->cur_bit;
|
||||
+ if (bv->cur_bit % 8 == 0) {
|
||||
+ for (i = (bv->cur_bit/8); i < (max_bits % 8 ? max_bits/8 + 1 : max_bits/8); i++, count++) {
|
||||
+ if ((b ? 0xFF : 0) != bv->data[i]) {
|
||||
+ bv->cur_bit = (count * 8 + leading_bits(bv->data[i], b) + readIndex);
|
||||
+ return count * 8 + leading_bits(bv->data[i], b);
|
||||
+ }
|
||||
+ }
|
||||
+ bv->cur_bit = (count * 8) + readIndex ;
|
||||
+ if (bv->cur_bit > max_bits)
|
||||
+ bv->cur_bit = max_bits;
|
||||
+ return (bv->cur_bit - readIndex);
|
||||
+ } else {
|
||||
+ int pos = bv->cur_bit/8;
|
||||
+ while (readIndex < max_bits && bitvec_read_field(bv, &readIndex, 1) == b) {
|
||||
+ if( bv->cur_bit % 8 >= 0)
|
||||
+ temp_res++;
|
||||
+ else {
|
||||
+ pos++;
|
||||
+ for (i = pos; i < (max_bits % 8 ? max_bits/8 + 1 : max_bits/8); i++, count++) {
|
||||
+ if ((b ? 0xFF : 0) != bv->data[i]) {
|
||||
+ bv->cur_bit = (count * 8 + leading_bits(bv->data[i], b) + temp_res) + readIndex ;
|
||||
+ return count * 8 + leading_bits(bv->data[i], b) + temp_res;
|
||||
+ }
|
||||
+ }
|
||||
+ bv->cur_bit = (temp_res + (count * 8)) + readIndex;
|
||||
+ return temp_res + (count * 8);
|
||||
+ }
|
||||
+ }
|
||||
+ bv->cur_bit--;
|
||||
+ return temp_res;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/*! \brief Shifts bitvec to the left, n MSB bits lost */
|
||||
void bitvec_shiftl(struct bitvec *bv, unsigned n)
|
||||
{
|
||||
--
|
||||
1.9.1
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
if [ -z "$MAKE" ]; then
|
||||
echo 'The $MAKE variable is not defined, cannot build'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
base="$PWD"
|
||||
deps="$base/deps"
|
||||
inst="$deps/install"
|
||||
|
||||
rm -rf "$inst"
|
||||
mkdir -p "$deps"
|
||||
|
||||
# Collect configure options for osmo-pcu
|
||||
PCU_CONFIG=""
|
||||
if [ "$with_dsp" = sysmo ]; then
|
||||
PCU_CONFIG="$PCU_CONFIG --enable-sysmocom-dsp"
|
||||
|
||||
# For direct sysmo DSP access, provide the SysmoBTS Layer 1 API
|
||||
cd "$deps"
|
||||
if [ ! -d layer1-api ]; then
|
||||
git clone git://git.sysmocom.de/sysmo-bts/layer1-api.git layer1-api
|
||||
fi
|
||||
cd layer1-api
|
||||
git fetch origin
|
||||
git reset --hard origin/master
|
||||
api_incl="$inst/include/sysmocom/femtobts/"
|
||||
mkdir -p "$api_incl"
|
||||
cp include/*.h "$api_incl"
|
||||
|
||||
elif [ -z "$with_dsp" -o "$with_dsp" = none ]; then
|
||||
echo "Direct DSP access disabled"
|
||||
else
|
||||
echo 'Invalid $with_dsp value:' $with_dsp
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$with_vty" = "yes" ]; then
|
||||
PCU_CONFIG="$PCU_CONFIG --enable-vty-tests"
|
||||
elif [ -z "$with_vty" -o "$with_vty" = "no" ]; then
|
||||
echo "VTY tests disabled"
|
||||
else
|
||||
echo 'Invalid $with_vty value:' $with_vty
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build deps
|
||||
cd "$deps"
|
||||
osmo-deps.sh libosmocore
|
||||
cd libosmocore
|
||||
autoreconf --install --force
|
||||
./configure --prefix="$inst"
|
||||
$MAKE $PARALLEL_MAKE install
|
||||
|
||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
|
||||
# Build osmo-pcu
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure $PCU_CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" AM_DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" $MAKE distcheck
|
|
@ -3,7 +3,7 @@ Description=sysmocom sysmoPCU
|
|||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg -e
|
||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
RestartPreventExitStatus=1
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
osmo-pcu (0.3) UNRELEASED; urgency=medium
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@moiji-mobile.com> Fri, 01 Apr 2016 18:59:00 +0200
|
|
@ -0,0 +1 @@
|
|||
7
|
|
@ -0,0 +1,24 @@
|
|||
Source: osmo-pcu
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
|
||||
Build-Depends: debhelper (>= 7.0.0~), dh-autoreconf, dh-systemd (>= 1.5), autotools-dev, pkg-config, libosmocore-dev
|
||||
Standards-Version: 3.8.4
|
||||
Homepage: http://osmocom.org/projects/osmopcu
|
||||
Vcs-Git: git://git.osmocom.org/osmo-pcu
|
||||
Vcs-Browser: http://git.osmocom.org/osmo-pcu/
|
||||
|
||||
Package: osmo-pcu
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: osmo-pcu GSM PCU for GPRS and EDGE
|
||||
osmo-pcu for GPRS and EDGE support in the network
|
||||
|
||||
Package: osmo-pcu-dbg
|
||||
Architecture: any
|
||||
Section: debug
|
||||
Priority: extra
|
||||
Depends: osmo-pcu (= ${binary:Version}), ${misc:Depends}
|
||||
Description: Debug symbols for the osmo-pcu
|
||||
Make debugging possible
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
etc/osmocom/osmo-pcu.cfg
|
||||
usr/bin/osmo-pcu
|
|
@ -0,0 +1,15 @@
|
|||
[Unit]
|
||||
Description=Osmocom osmo-pcu
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
# Read quickly enough
|
||||
CPUSchedulingPolicy=rr
|
||||
CPUSchedulingPriority=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2)
|
||||
DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
|
||||
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
|
||||
|
||||
#export DH_VERBOSE=1
|
||||
export DEB_BUILD_HARDENING=1
|
||||
|
||||
|
||||
%:
|
||||
dh $@ --with=systemd --with autoreconf --fail-missing
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=osmo-pcu-dbg
|
||||
|
||||
override_dh_autoreconf:
|
||||
echo $(VERSION) > .tarball-version
|
||||
dh_autoreconf
|
|
@ -0,0 +1 @@
|
|||
3.0 (native)
|
|
@ -1,6 +1,24 @@
|
|||
pcu
|
||||
egprs only
|
||||
flow-control-interval 10
|
||||
cs 2
|
||||
alloc-algorithm dynamic
|
||||
flow-control force-bvc-bucket-size 596000
|
||||
flow-control force-bvc-leak-rate 59600
|
||||
flow-control force-ms-bucket-size 596000
|
||||
flow-control force-ms-leak-rate 59600
|
||||
cs 1
|
||||
cs max 1
|
||||
no cs threshold
|
||||
no cs downgrade-threshold
|
||||
cs link-quality-ranges cs1 6 cs2 5 8 cs3 7 13 cs4 12
|
||||
mcs 9 9
|
||||
mcs max 9 9
|
||||
window-size 64 104
|
||||
queue lifetime infinite
|
||||
queue idle-ack-delay 10
|
||||
no queue codel
|
||||
alloc-algorithm b
|
||||
two-phase-access
|
||||
alpha 0
|
||||
gamma 0
|
||||
dl-tbf-idle-time 2000
|
||||
maximise-direction dl
|
||||
|
|
|
@ -18,10 +18,15 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
|
||||
|
||||
if ENABLE_SYSMODSP
|
||||
AM_CPPFLAGS += -DENABLE_SYSMODSP
|
||||
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
|
||||
endif
|
||||
|
||||
if ENABLE_LC15BTS_PHY
|
||||
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
|
||||
endif
|
||||
|
||||
AM_CXXFLAGS = -Wall -ldl -pthread
|
||||
|
@ -57,18 +62,14 @@ libgprs_la_SOURCES = \
|
|||
rlc.cpp \
|
||||
osmobts_sock.cpp \
|
||||
gprs_codel.c \
|
||||
gprs_coding_scheme.cpp
|
||||
gprs_coding_scheme.cpp \
|
||||
egprs_rlc_compression.cpp
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-pcu
|
||||
|
||||
noinst_PROGRAMS =
|
||||
|
||||
if ENABLE_SYSMODSP
|
||||
noinst_PROGRAMS += \
|
||||
osmo-pcu-remote
|
||||
endif
|
||||
|
||||
noinst_HEADERS = \
|
||||
gprs_debug.h \
|
||||
csn1.h \
|
||||
|
@ -83,8 +84,6 @@ noinst_HEADERS = \
|
|||
bitvector.h \
|
||||
pcu_vty.h \
|
||||
pcu_vty_functions.h \
|
||||
sysmo_l1_if.h \
|
||||
femtobts.h \
|
||||
tbf.h \
|
||||
bts.h \
|
||||
poll_controller.h \
|
||||
|
@ -96,19 +95,65 @@ noinst_HEADERS = \
|
|||
pcu_utils.h \
|
||||
cxx_linuxlist.h \
|
||||
gprs_codel.h \
|
||||
gprs_coding_scheme.h
|
||||
gprs_coding_scheme.h \
|
||||
egprs_rlc_compression.h
|
||||
|
||||
osmo_pcu_SOURCES = pcu_main.cpp
|
||||
|
||||
if ENABLE_SYSMODSP
|
||||
osmo_pcu_SOURCES += sysmo_l1_if.c \
|
||||
sysmo_l1_hw.c \
|
||||
femtobts.c
|
||||
AM_CPPFLAGS += -I$(srcdir)/osmo-bts-sysmo
|
||||
|
||||
osmo_pcu_remote_SOURCES = pcu_main.cpp \
|
||||
sysmo_l1_if.c \
|
||||
sysmo_l1_fwd.c \
|
||||
femtobts.c
|
||||
EXTRA_DIST = \
|
||||
osmo-bts-sysmo/sysmo_l1_if.c \
|
||||
osmo-bts-sysmo/sysmo_l1_if.h \
|
||||
osmo-bts-sysmo/sysmo_l1_hw.c \
|
||||
osmo-bts-sysmo/femtobts.c \
|
||||
osmo-bts-sysmo/femtobts.h
|
||||
|
||||
noinst_HEADERS += \
|
||||
osmo-bts-sysmo/sysmo_l1_if.h \
|
||||
osmo-bts-sysmo/femtobts.h
|
||||
|
||||
noinst_PROGRAMS += \
|
||||
osmo-pcu-remote
|
||||
|
||||
osmo_pcu_SOURCES += \
|
||||
osmo-bts-sysmo/sysmo_l1_if.c \
|
||||
osmo-bts-sysmo/sysmo_l1_hw.c \
|
||||
osmo-bts-sysmo/femtobts.c
|
||||
|
||||
osmo_pcu_remote_SOURCES = \
|
||||
pcu_main.cpp \
|
||||
osmo-bts-sysmo/sysmo_l1_if.c \
|
||||
osmo-bts-sysmo/sysmo_l1_fwd.c \
|
||||
osmo-bts-sysmo/femtobts.c
|
||||
|
||||
osmo_pcu_remote_LDADD = \
|
||||
libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(COMMON_LA)
|
||||
endif
|
||||
|
||||
if ENABLE_LC15BTS_PHY
|
||||
AM_CPPFLAGS += -I$(LITECELL15_INCDIR) -I$(srcdir)/osmo-bts-litecell15
|
||||
|
||||
EXTRA_DIST = \
|
||||
osmo-bts-litecell15/lc15_l1_if.c \
|
||||
osmo-bts-litecell15/lc15_l1_if.h \
|
||||
osmo-bts-litecell15/lc15_l1_hw.c \
|
||||
osmo-bts-litecell15/lc15bts.c \
|
||||
osmo-bts-litecell15/lc15bts.h
|
||||
|
||||
noinst_HEADERS += \
|
||||
osmo-bts-litecell15/lc15_l1_if.h \
|
||||
osmo-bts-litecell15/lc15bts.h
|
||||
|
||||
osmo_pcu_SOURCES += \
|
||||
osmo-bts-litecell15/lc15_l1_if.c \
|
||||
osmo-bts-litecell15/lc15_l1_hw.c \
|
||||
osmo-bts-litecell15/lc15bts.c
|
||||
endif
|
||||
|
||||
osmo_pcu_LDADD = \
|
||||
|
@ -118,13 +163,4 @@ osmo_pcu_LDADD = \
|
|||
$(LIBOSMOGSM_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
if ENABLE_SYSMODSP
|
||||
osmo_pcu_remote_LDADD = \
|
||||
libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(COMMON_LA)
|
||||
endif
|
||||
|
||||
#MOSTLYCLEANFILES += testSource testDestination
|
||||
|
|
|
@ -101,23 +101,6 @@ uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len
|
|||
}
|
||||
|
||||
|
||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc;
|
||||
bv->cur_bit = write_index;
|
||||
for (i = 0; i < len; i++) {
|
||||
int bit = 0;
|
||||
if (val & ((uint64_t)1 << (len - i - 1)))
|
||||
bit = 1;
|
||||
rc = bitvec_set_bit(bv, (bit_value)bit);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
write_index += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bitvec_write_field_lh(struct bitvec *bv, unsigned& write_index,
|
||||
uint64_t val, unsigned len)
|
||||
{
|
||||
|
|
|
@ -38,9 +38,15 @@ int bitvec_unhex(struct bitvec *bv, const char* src);
|
|||
unsigned int bitvec_pack(struct bitvec *bv, uint8_t *buffer);
|
||||
unsigned int bitvec_unpack(struct bitvec *bv, uint8_t *buffer);
|
||||
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len);
|
||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
|
||||
int bitvec_write_field_lh(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
|
||||
|
||||
|
||||
static inline int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len)
|
||||
{
|
||||
/* Call the libosmocore variant */
|
||||
return ::bitvec_write_field(bv, &write_index, val, len);
|
||||
}
|
||||
|
||||
/*! }@ */
|
||||
|
||||
#endif // BITVECTOR_H
|
||||
|
|
123
src/bts.cpp
123
src/bts.cpp
|
@ -33,6 +33,7 @@ extern "C" {
|
|||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
}
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
@ -71,6 +72,12 @@ static const struct rate_ctr_desc bts_ctr_description[] = {
|
|||
{ "rlc.ack.failed", "RLC Ack Failed "},
|
||||
{ "rlc.rel.timedout", "RLC Release Timeout "},
|
||||
{ "rlc.late-block", "RLC Late Block "},
|
||||
{ "rlc.sent-dummy", "RLC Sent Dummy "},
|
||||
{ "rlc.sent-control", "RLC Sent Control "},
|
||||
{ "rlc.dl_bytes", "RLC DL Bytes "},
|
||||
{ "rlc.dl_payload_bytes", "RLC DL Payload Bytes "},
|
||||
{ "rlc.ul_bytes", "RLC UL Bytes "},
|
||||
{ "rlc.ul_payload_bytes", "RLC UL Payload Bytes "},
|
||||
{ "decode.errors", "Decode Errors "},
|
||||
{ "sba.allocated", "SBA Allocated "},
|
||||
{ "sba.freed", "SBA Freed "},
|
||||
|
@ -78,6 +85,8 @@ static const struct rate_ctr_desc bts_ctr_description[] = {
|
|||
{ "llc.timeout", "Timedout Frames "},
|
||||
{ "llc.dropped", "Dropped Frames "},
|
||||
{ "llc.scheduled", "Scheduled Frames "},
|
||||
{ "llc.dl_bytes", "RLC encapsulated PDUs"},
|
||||
{ "llc.ul_bytes", "full PDUs received "},
|
||||
{ "rach.requests", "RACH requests "},
|
||||
};
|
||||
|
||||
|
@ -459,7 +468,8 @@ int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
|
||||
int BTS::rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit,
|
||||
enum ph_burst_type burst_type)
|
||||
{
|
||||
struct gprs_rlcmac_ul_tbf *tbf = NULL;
|
||||
uint8_t trx_no, ts_no = 0;
|
||||
|
@ -470,20 +480,16 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
|
|||
uint8_t usf = 7;
|
||||
uint8_t tsc;
|
||||
uint16_t ta;
|
||||
uint16_t ms_class = 0;
|
||||
uint16_t priority = 0;
|
||||
|
||||
rach_frame();
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide "
|
||||
"one:\n");
|
||||
if ((ra & 0xf8) == 0x70) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
|
||||
"allocation\n");
|
||||
sb = 1;
|
||||
} else if (m_bts.force_two_phase) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single phase access, "
|
||||
"but we force two phase access\n");
|
||||
sb = 1;
|
||||
}
|
||||
|
||||
sb = is_single_block(ra, burst_type, is_11bit, &ms_class, &priority);
|
||||
|
||||
if (qta < 0)
|
||||
qta = 0;
|
||||
if (qta > 252)
|
||||
|
@ -505,9 +511,17 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
|
|||
tsc = m_bts.trx[trx_no].pdch[ts_no].tsc;
|
||||
} else {
|
||||
// Create new TBF
|
||||
#warning "Copy and pate with other routines.."
|
||||
/* set class to 0, since we don't know the multislot class yet */
|
||||
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0, 0, 1);
|
||||
#warning "Copy and paste with other routines.."
|
||||
|
||||
if (is_11bit) {
|
||||
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0,
|
||||
ms_class, 1);
|
||||
} else {
|
||||
/* set class to 0, since we don't know the multislot
|
||||
* class yet */
|
||||
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0, 0, 1);
|
||||
}
|
||||
|
||||
if (!tbf) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
|
||||
/* FIXME: send reject */
|
||||
|
@ -542,7 +556,7 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
|
|||
plen = Encoding::write_immediate_assignment(
|
||||
tbf, immediate_assignment, 0, ra, Fn, ta,
|
||||
m_bts.trx[trx_no].arfcn, ts_no, tsc, usf, 0, sb_fn,
|
||||
m_bts.alpha, m_bts.gamma, -1);
|
||||
m_bts.alpha, m_bts.gamma, -1, burst_type, sb);
|
||||
|
||||
if (plen >= 0) {
|
||||
pcu_l1if_tx_agch(immediate_assignment, plen);
|
||||
|
@ -555,6 +569,73 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint8_t BTS::is_single_block(uint16_t ra, enum ph_burst_type burst_type,
|
||||
uint8_t is_11bit, uint16_t *ms_class, uint16_t *priority)
|
||||
{
|
||||
uint8_t sb = 0, val = 0;
|
||||
|
||||
if (!is_11bit && (burst_type == GSM_L1_BURST_TYPE_ACCESS_0)) {
|
||||
|
||||
if ((ra & 0xf8) == 0x70) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
|
||||
"allocation\n");
|
||||
sb = 1;
|
||||
} else if (m_bts.force_two_phase) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single "
|
||||
"phase access, but we force two phase "
|
||||
"access\n");
|
||||
sb = 1;
|
||||
}
|
||||
|
||||
} else if (is_11bit &&
|
||||
((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
||||
|
||||
val = !!(ra & (1 << 10));
|
||||
|
||||
if (!val) {
|
||||
if (m_bts.force_two_phase) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS 11 bit RACH "
|
||||
"received. MS requests single phase "
|
||||
"access but we force two phase "
|
||||
"access\n");
|
||||
sb = 1;
|
||||
} else {
|
||||
sb = 0;
|
||||
*ms_class = (ra & 0x3e0) >> 5;
|
||||
*priority = (ra & 0x18) >> 3;
|
||||
}
|
||||
|
||||
} else {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS 11 bit RACH received."
|
||||
"MS requests single block allocation\n");
|
||||
sb = 1;
|
||||
}
|
||||
|
||||
} else if (is_11bit &&
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_0)) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR,
|
||||
"Error: GPRS 11 bit RACH not supported\n");
|
||||
|
||||
} else if (burst_type == GSM_L1_BURST_TYPE_NONE) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "pcu has not received burst type "
|
||||
"from bts \n");
|
||||
|
||||
if ((ra & 0xf8) == 0x70) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
|
||||
"allocation\n");
|
||||
sb = 1;
|
||||
} else if (m_bts.force_two_phase) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single "
|
||||
"phase access, but we force two phase "
|
||||
"access\n");
|
||||
sb = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
/* depending on the current TBF, we assign on PACCH or AGCH */
|
||||
void BTS::trigger_dl_ass(
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf,
|
||||
|
@ -1120,7 +1201,7 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
|
|||
uint32_t tlli = request->ID.u.TLLI;
|
||||
uint8_t ms_class = 0;
|
||||
uint8_t egprs_ms_class = 0;
|
||||
uint8_t ta = 0;
|
||||
uint8_t ta = GSM48_TA_INVALID;
|
||||
struct pcu_l1_meas meas;
|
||||
|
||||
GprsMs *ms = bts()->ms_by_tlli(tlli);
|
||||
|
@ -1300,6 +1381,8 @@ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
bts()->rlc_ul_bytes(len);
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "Got RLC block, coding scheme: %s, "
|
||||
"length: %d (%d))\n", cs.name(), len, cs.usedSizeUL());
|
||||
|
||||
|
@ -1315,6 +1398,7 @@ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! \brief process egprs and gprs data blocks */
|
||||
int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint32_t fn,
|
||||
struct pcu_l1_meas *meas, GprsCodingScheme cs)
|
||||
{
|
||||
|
@ -1333,15 +1417,6 @@ int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint32_t fn,
|
|||
cs.name());
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!cs.isEgprsGmsk()) {
|
||||
LOGP(DRLCMACUL, LOGL_ERROR,
|
||||
"Got %s RLC block but EGPRS is not implemented "
|
||||
"for 8PSK yet\n",
|
||||
cs.name());
|
||||
bts()->decode_error();
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, " UL data: %s\n", osmo_hexdump(data, len));
|
||||
|
|
49
src/bts.h
49
src/bts.h
|
@ -28,6 +28,7 @@ extern "C" {
|
|||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/gsm/l1sap.h>
|
||||
}
|
||||
|
||||
#include "poll_controller.h"
|
||||
|
@ -41,6 +42,12 @@ extern "C" {
|
|||
|
||||
#define LLC_CODEL_DISABLE 0
|
||||
#define LLC_CODEL_USE_DEFAULT (-1)
|
||||
#define MAX_GPRS_CS 9
|
||||
|
||||
enum maximise_direction {
|
||||
DL_ONLY,
|
||||
NO_MAXIMISE
|
||||
};
|
||||
|
||||
struct BTS;
|
||||
struct GprsMs;
|
||||
|
@ -184,11 +191,17 @@ struct gprs_rlcmac_bts {
|
|||
uint8_t alpha, gamma;
|
||||
uint8_t egprs_enabled;
|
||||
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
|
||||
|
||||
/* 0 to support resegmentation in DL, 1 for no reseg */
|
||||
uint8_t dl_arq_type;
|
||||
|
||||
enum maximise_direction maximise_dir;
|
||||
|
||||
uint32_t ms_idle_sec;
|
||||
uint8_t cs_adj_enabled;
|
||||
uint8_t cs_adj_upper_limit;
|
||||
uint8_t cs_adj_lower_limit;
|
||||
struct {int16_t low; int16_t high;} cs_lqual_ranges[4];
|
||||
struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
|
||||
uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
|
||||
uint16_t ws_base;
|
||||
uint16_t ws_pdch; /* increase WS by this value per PDCH */
|
||||
|
@ -233,6 +246,12 @@ public:
|
|||
CTR_RLC_ACK_FAILED,
|
||||
CTR_RLC_REL_TIMEDOUT,
|
||||
CTR_RLC_LATE_BLOCK,
|
||||
CTR_RLC_SENT_DUMMY,
|
||||
CTR_RLC_SENT_CONTROL,
|
||||
CTR_RLC_DL_BYTES,
|
||||
CTR_RLC_DL_PAYLOAD_BYTES,
|
||||
CTR_RLC_UL_BYTES,
|
||||
CTR_RLC_UL_PAYLOAD_BYTES,
|
||||
CTR_DECODE_ERRORS,
|
||||
CTR_SBA_ALLOCATED,
|
||||
CTR_SBA_FREED,
|
||||
|
@ -240,6 +259,8 @@ public:
|
|||
CTR_LLC_FRAME_TIMEDOUT,
|
||||
CTR_LLC_FRAME_DROPPED,
|
||||
CTR_LLC_FRAME_SCHED,
|
||||
CTR_LLC_DL_BYTES,
|
||||
CTR_LLC_UL_BYTES,
|
||||
CTR_RACH_REQUESTS,
|
||||
};
|
||||
|
||||
|
@ -275,7 +296,10 @@ public:
|
|||
int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx);
|
||||
|
||||
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
|
||||
int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
|
||||
uint8_t is_single_block(uint16_t ra, enum ph_burst_type burst_type,
|
||||
uint8_t is_11bit, uint16_t *ms_class, uint16_t *priority);
|
||||
int rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit,
|
||||
enum ph_burst_type burst_type);
|
||||
|
||||
void trigger_dl_ass(gprs_rlcmac_dl_tbf *tbf, gprs_rlcmac_tbf *old_tbf);
|
||||
void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
|
||||
|
@ -309,6 +333,12 @@ public:
|
|||
void rlc_ack_failed();
|
||||
void rlc_rel_timedout();
|
||||
void rlc_late_block();
|
||||
void rlc_sent_dummy();
|
||||
void rlc_sent_control();
|
||||
void rlc_dl_bytes(int bytes);
|
||||
void rlc_dl_payload_bytes(int bytes);
|
||||
void rlc_ul_bytes(int bytes);
|
||||
void rlc_ul_payload_bytes(int bytes);
|
||||
void decode_error();
|
||||
void sba_allocated();
|
||||
void sba_freed();
|
||||
|
@ -316,6 +346,8 @@ public:
|
|||
void llc_timedout_frame();
|
||||
void llc_dropped_frame();
|
||||
void llc_frame_sched();
|
||||
void llc_dl_bytes(int bytes);
|
||||
void llc_ul_bytes(int bytes);
|
||||
void rach_frame();
|
||||
|
||||
void ms_present(int32_t n);
|
||||
|
@ -423,6 +455,11 @@ inline struct osmo_stat_item_group *BTS::stat_items() const
|
|||
return m_statg;
|
||||
}
|
||||
|
||||
#define CREATE_COUNT_ADD_INLINE(func_name, ctr_name) \
|
||||
inline void BTS::func_name(int inc) {\
|
||||
rate_ctr_add(&m_ratectrs->ctr[ctr_name], inc); \
|
||||
}
|
||||
|
||||
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
|
||||
inline void BTS::func_name() {\
|
||||
rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
|
||||
|
@ -449,6 +486,12 @@ CREATE_COUNT_INLINE(rlc_ack_timedout, CTR_RLC_ACK_TIMEDOUT);
|
|||
CREATE_COUNT_INLINE(rlc_ack_failed, CTR_RLC_ACK_FAILED);
|
||||
CREATE_COUNT_INLINE(rlc_rel_timedout, CTR_RLC_REL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_late_block, CTR_RLC_LATE_BLOCK);
|
||||
CREATE_COUNT_INLINE(rlc_sent_dummy, CTR_RLC_SENT_DUMMY);
|
||||
CREATE_COUNT_INLINE(rlc_sent_control, CTR_RLC_SENT_CONTROL);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_dl_bytes, CTR_RLC_DL_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_dl_payload_bytes, CTR_RLC_DL_PAYLOAD_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_ul_bytes, CTR_RLC_UL_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_ul_payload_bytes, CTR_RLC_UL_PAYLOAD_BYTES);
|
||||
CREATE_COUNT_INLINE(decode_error, CTR_DECODE_ERRORS)
|
||||
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
|
||||
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)
|
||||
|
@ -456,6 +499,8 @@ CREATE_COUNT_INLINE(sba_timedout, CTR_SBA_TIMEDOUT)
|
|||
CREATE_COUNT_INLINE(llc_timedout_frame, CTR_LLC_FRAME_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(llc_dropped_frame, CTR_LLC_FRAME_DROPPED);
|
||||
CREATE_COUNT_INLINE(llc_frame_sched, CTR_LLC_FRAME_SCHED);
|
||||
CREATE_COUNT_ADD_INLINE(llc_dl_bytes, CTR_LLC_DL_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(llc_ul_bytes, CTR_LLC_UL_BYTES);
|
||||
CREATE_COUNT_INLINE(rach_frame, CTR_RACH_REQUESTS);
|
||||
|
||||
#undef CREATE_COUNT_INLINE
|
||||
|
|
19
src/csn1.cpp
19
src/csn1.cpp
|
@ -1110,22 +1110,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
|
|||
|
||||
{ /* extract bits */
|
||||
guint8* pui8 = pui8DATA(data, pDescr->offset);
|
||||
gint16 nB1 = no_of_bits & 0x07;/* no_of_bits Mod 8 */
|
||||
|
||||
while (no_of_bits > 0)
|
||||
while (no_of_bits >= 8)
|
||||
{
|
||||
*pui8 = bitvec_read_field(vector, readIndex, 8);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
|
||||
pui8++;
|
||||
no_of_bits -= 8;
|
||||
}
|
||||
if (nB1 > 0)
|
||||
if (no_of_bits > 0)
|
||||
{
|
||||
*pui8 = bitvec_read_field(vector, readIndex, nB1);
|
||||
*pui8 = bitvec_read_field(vector, readIndex, no_of_bits);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
|
||||
pui8++;
|
||||
no_of_bits -= nB1;
|
||||
bit_offset += nB1; /* (nB1 is no_of_bits Mod 8) */
|
||||
bit_offset += no_of_bits;
|
||||
no_of_bits = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2400,7 +2399,12 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||
guint8 bits_to_handle = remaining_bits_len%8;
|
||||
if (bits_to_handle > 0)
|
||||
{
|
||||
guint8 fl = filler&(0xff>>(8-bits_to_handle));
|
||||
/* section 11 of 44.060
|
||||
* The padding bits may be the 'null' string. Otherwise, the
|
||||
* padding bits starts with bit '0', followed by 'spare padding'
|
||||
* < padding bits > ::= { null | 0 < spare padding > ! < Ignore : 1 bit** = < no string > > } ;
|
||||
*/
|
||||
guint8 fl = filler&(0xff>>(8-bits_to_handle + 1));
|
||||
bitvec_write_field(vector, writeIndex, fl, bits_to_handle);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%u|", fl);
|
||||
remaining_bits_len -= bits_to_handle;
|
||||
|
@ -2499,6 +2503,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||
bitvec_write_field(vector, writeIndex, !Tag, 1);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(!Tag));
|
||||
bit_offset++;
|
||||
remaining_bits_len--;
|
||||
|
||||
pDescr++;
|
||||
break;
|
||||
|
|
287
src/decoding.cpp
287
src/decoding.cpp
|
@ -20,10 +20,12 @@
|
|||
#include <decoding.h>
|
||||
#include <rlc.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <egprs_rlc_compression.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/bitcomp.h>
|
||||
#include <osmocom/gprs/protocol/gsm_04_60.h>
|
||||
}
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
@ -32,7 +34,7 @@ extern "C" {
|
|||
#include <string.h>
|
||||
|
||||
#define LENGTH_TO_END 255
|
||||
/*
|
||||
/*!
|
||||
* \returns num extensions fields (num frames == offset) on success,
|
||||
* -errno otherwise.
|
||||
*/
|
||||
|
@ -66,14 +68,11 @@ static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
|
|||
"but no more chunks possible\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
if (li->li == 0 && num_chunks == 0 && li->e == 0) {
|
||||
if (li->li == 0 && num_chunks == 0) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 2a */
|
||||
/* TS 44.060, table 10.4.14a.1, row 4 */
|
||||
chunks[num_chunks].length = 0;
|
||||
chunks[num_chunks].is_complete = true;
|
||||
} else if (li->li == 0 && num_chunks == 0 && li->e == 1) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 4 */
|
||||
chunks[num_chunks].length = LENGTH_TO_END;
|
||||
chunks[num_chunks].is_complete = is_last_block;
|
||||
} else if (li->li == 127 && li->e == 1) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 3 & 5 */
|
||||
/* only filling bytes left */
|
||||
|
@ -197,6 +196,7 @@ int Decoding::rlc_data_from_ul_data(
|
|||
e = rdbi->e;
|
||||
if (e) {
|
||||
if (chunks_size > 0) {
|
||||
/* Block without LI means it only contains data of one LLC PDU */
|
||||
chunks[num_chunks].offset = offs;
|
||||
chunks[num_chunks].length = LENGTH_TO_END;
|
||||
chunks[num_chunks].is_complete = is_last_block;
|
||||
|
@ -266,7 +266,7 @@ int Decoding::rlc_data_from_ul_data(
|
|||
* so drop it (this may happen with EGPRS since
|
||||
* there is no M flag. */
|
||||
num_chunks -= 1;
|
||||
break;;
|
||||
break;
|
||||
}
|
||||
chunks[i].length = data_len - offs;
|
||||
}
|
||||
|
@ -344,78 +344,20 @@ void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
|
|||
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, GprsCodingScheme cs)
|
||||
{
|
||||
const struct gprs_rlc_ul_header_egprs_3 *egprs3;
|
||||
const struct rlc_ul_header *gprs;
|
||||
unsigned int e_ti_header;
|
||||
unsigned int cur_bit = 0;
|
||||
int punct, punct2, with_padding, cps;
|
||||
unsigned int offs;
|
||||
|
||||
switch(cs.headerTypeData()) {
|
||||
case GprsCodingScheme::HEADER_GPRS_DATA:
|
||||
gprs = static_cast<struct rlc_ul_header *>
|
||||
((void *)data);
|
||||
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, false);
|
||||
|
||||
rlc->r = gprs->r;
|
||||
rlc->si = gprs->si;
|
||||
rlc->tfi = gprs->tfi;
|
||||
rlc->cps = 0;
|
||||
rlc->rsb = 0;
|
||||
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = gprs->cv;
|
||||
rlc->block_info[0].pi = gprs->pi;
|
||||
rlc->block_info[0].bsn = gprs->bsn;
|
||||
rlc->block_info[0].e = gprs->e;
|
||||
rlc->block_info[0].ti = gprs->ti;
|
||||
rlc->block_info[0].spb = 0;
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0];
|
||||
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
case GprsCodingScheme::HEADER_GPRS_DATA :
|
||||
cur_bit = rlc_parse_ul_data_header_gprs(rlc, data, cs);
|
||||
break;
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3:
|
||||
egprs3 = static_cast<struct gprs_rlc_ul_header_egprs_3 *>
|
||||
((void *)data);
|
||||
|
||||
cps = (egprs3->cps_a << 0) | (egprs3->cps_b << 2);
|
||||
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||
|
||||
rlc->r = egprs3->r;
|
||||
rlc->si = egprs3->si;
|
||||
rlc->tfi = (egprs3->tfi_a << 0) | (egprs3->tfi_b << 2);
|
||||
rlc->cps = cps;
|
||||
rlc->rsb = egprs3->rsb;
|
||||
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = egprs3->cv;
|
||||
rlc->block_info[0].pi = egprs3->pi;
|
||||
rlc->block_info[0].spb = egprs3->spb;
|
||||
rlc->block_info[0].bsn =
|
||||
(egprs3->bsn1_a << 0) | (egprs3->bsn1_b << 5);
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||
|
||||
offs = rlc->data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1);
|
||||
|
||||
e_ti_header = (data[offs-1] + (data[offs] << 8)) >> 7;
|
||||
rlc->block_info[0].e = !!(e_ti_header & 0x01);
|
||||
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3 :
|
||||
cur_bit = rlc_parse_ul_data_header_egprs_type_3(rlc, data, cs);
|
||||
break;
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2 :
|
||||
cur_bit = rlc_parse_ul_data_header_egprs_type_2(rlc, data, cs);
|
||||
break;
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1 :
|
||||
cur_bit = rlc_parse_ul_data_header_egprs_type_1(rlc, data, cs);
|
||||
break;
|
||||
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1:
|
||||
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2:
|
||||
/* TODO: Support both header types */
|
||||
/* fall through */
|
||||
default:
|
||||
LOGP(DRLCMACDL, LOGL_ERROR,
|
||||
"Decoding of uplink %s data blocks not yet supported.\n",
|
||||
|
@ -426,6 +368,181 @@ int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
|||
return cur_bit;
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header_egprs_type_3(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs)
|
||||
{
|
||||
int punct, punct2, with_padding, cps;
|
||||
unsigned int e_ti_header, offs, cur_bit = 0;
|
||||
const struct gprs_rlc_ul_header_egprs_3 *egprs3;
|
||||
|
||||
egprs3 = static_cast < struct gprs_rlc_ul_header_egprs_3 * >
|
||||
((void *)data);
|
||||
|
||||
cps = (egprs3->cps_hi << 0) | (egprs3->cps_lo << 2);
|
||||
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||
|
||||
rlc->r = egprs3->r;
|
||||
rlc->si = egprs3->si;
|
||||
rlc->tfi = (egprs3->tfi_hi << 0) | (egprs3->tfi_lo << 2);
|
||||
rlc->cps = cps;
|
||||
rlc->rsb = egprs3->rsb;
|
||||
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = egprs3->cv;
|
||||
rlc->block_info[0].pi = egprs3->pi;
|
||||
rlc->block_info[0].spb = egprs3->spb;
|
||||
rlc->block_info[0].bsn =
|
||||
(egprs3->bsn1_hi << 0) | (egprs3->bsn1_lo << 5);
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||
offs = rlc->data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1);
|
||||
e_ti_header = (data[offs-1] + (data[offs] << 8)) >> 7;
|
||||
rlc->block_info[0].e = !!(e_ti_header & 0x01);
|
||||
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header_egprs_type_2(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs)
|
||||
{
|
||||
const struct gprs_rlc_ul_header_egprs_2 *egprs2;
|
||||
unsigned int e_ti_header, offs, cur_bit = 0;
|
||||
int punct, punct2, with_padding, cps;
|
||||
|
||||
egprs2 = static_cast < struct gprs_rlc_ul_header_egprs_2 * >
|
||||
((void *)data);
|
||||
|
||||
cps = (egprs2->cps_hi << 0) | (egprs2->cps_lo << 2);
|
||||
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||
|
||||
rlc->r = egprs2->r;
|
||||
rlc->si = egprs2->si;
|
||||
rlc->tfi = (egprs2->tfi_hi << 0) | (egprs2->tfi_lo << 2);
|
||||
rlc->cps = cps;
|
||||
rlc->rsb = egprs2->rsb;
|
||||
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = egprs2->cv;
|
||||
rlc->block_info[0].pi = egprs2->pi;
|
||||
rlc->block_info[0].bsn =
|
||||
(egprs2->bsn1_hi << 0) | (egprs2->bsn1_lo << 5);
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||
|
||||
offs = rlc->data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 7);
|
||||
|
||||
e_ti_header = (data[offs] & 0x60) >> 5;
|
||||
rlc->block_info[0].e = !!(e_ti_header & 0x01);
|
||||
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header_egprs_type_1(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, const GprsCodingScheme &cs)
|
||||
{
|
||||
struct gprs_rlc_ul_header_egprs_1 *egprs1;
|
||||
unsigned int e_ti_header, cur_bit = 0, offs;
|
||||
int punct, punct2, with_padding;
|
||||
|
||||
egprs1 = static_cast < struct gprs_rlc_ul_header_egprs_1 * >
|
||||
((void *)data);
|
||||
gprs_rlc_mcs_cps_decode(egprs1->cps, cs, &punct, &punct2,
|
||||
&with_padding);
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||
|
||||
rlc->r = egprs1->r;
|
||||
rlc->si = egprs1->si;
|
||||
rlc->tfi = (egprs1->tfi_hi << 0) | (egprs1->tfi_lo << 2);
|
||||
rlc->cps = egprs1->cps;
|
||||
rlc->rsb = egprs1->rsb;
|
||||
rlc->num_data_blocks = 2;
|
||||
rlc->block_info[0].cv = egprs1->cv;
|
||||
rlc->block_info[0].pi = egprs1->pi;
|
||||
rlc->block_info[0].bsn =
|
||||
(egprs1->bsn1_hi << 0) | (egprs1->bsn1_lo << 5);
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||
offs = rlc->data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 0);
|
||||
|
||||
e_ti_header = data[offs - 1] >> 6;
|
||||
rlc->block_info[0].e = (e_ti_header & 0x01);
|
||||
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
|
||||
rlc->block_info[1].cv = egprs1->cv;
|
||||
rlc->block_info[1].pi = egprs1->pi;
|
||||
rlc->block_info[1].bsn = rlc->block_info[0].bsn +
|
||||
((egprs1->bsn2_hi << 0) | (egprs1->bsn2_lo << 2));
|
||||
rlc->block_info[1].bsn = rlc->block_info[1].bsn & (RLC_EGPRS_SNS - 1);
|
||||
|
||||
if ((rlc->block_info[1].bsn != rlc->block_info[0].bsn) &&
|
||||
(rlc->block_info[0].cv == 0))
|
||||
rlc->block_info[0].cv = 1;
|
||||
|
||||
cur_bit = rlc->data_offs_bits[1] - 2;
|
||||
|
||||
offs = rlc->data_offs_bits[1] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[1] % 8 == 2);
|
||||
|
||||
e_ti_header = (data[offs] & (0x03));
|
||||
rlc->block_info[1].e = (e_ti_header & 0x01);
|
||||
rlc->block_info[1].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, const GprsCodingScheme &cs)
|
||||
{
|
||||
const struct rlc_ul_header *gprs;
|
||||
unsigned int cur_bit = 0;
|
||||
|
||||
gprs = static_cast < struct rlc_ul_header * >
|
||||
((void *)data);
|
||||
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, false);
|
||||
|
||||
rlc->r = gprs->r;
|
||||
rlc->si = gprs->si;
|
||||
rlc->tfi = gprs->tfi;
|
||||
rlc->cps = 0;
|
||||
rlc->rsb = 0;
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = gprs->cv;
|
||||
rlc->block_info[0].pi = gprs->pi;
|
||||
rlc->block_info[0].bsn = gprs->bsn;
|
||||
rlc->block_info[0].e = gprs->e;
|
||||
rlc->block_info[0].ti = gprs->ti;
|
||||
rlc->block_info[0].spb = 0;
|
||||
cur_bit += rlc->data_offs_bits[0];
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Copy LSB bitstream RLC data block to byte aligned buffer.
|
||||
*
|
||||
|
@ -576,21 +693,17 @@ int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc,
|
|||
|
||||
if (crbb_len > 0) {
|
||||
int old_len = bits->cur_bit;
|
||||
struct bitvec crbb;
|
||||
|
||||
crbb.data = (uint8_t *)desc->CRBB;
|
||||
crbb.data_len = sizeof(desc->CRBB);
|
||||
crbb.cur_bit = desc->CRBB_LENGTH;
|
||||
|
||||
rc = osmo_t4_decode(&crbb, desc->CRBB_STARTING_COLOR_CODE,
|
||||
bits);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "Compress bitmap exists, "
|
||||
"CRBB LEN = %d and Starting color code = %d",
|
||||
desc->CRBB_LENGTH, desc->CRBB_STARTING_COLOR_CODE);
|
||||
rc = egprs_compress::decompress_crbb(desc->CRBB_LENGTH,
|
||||
desc->CRBB_STARTING_COLOR_CODE, desc->CRBB, bits);
|
||||
if (rc < 0) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"Failed to decode CRBB: "
|
||||
"length %d, data '%s'\n",
|
||||
desc->CRBB_LENGTH,
|
||||
osmo_hexdump(crbb.data, crbb.data_len));
|
||||
"Failed to decode CRBB: length %d, data '%s'\n",
|
||||
desc->CRBB_LENGTH, osmo_hexdump(
|
||||
desc->CRBB, (desc->CRBB_LENGTH + 7)/8));
|
||||
/* We don't know the SSN offset for the URBB,
|
||||
* return what we have so far and assume the
|
||||
* bitmap has stopped here */
|
||||
|
|
|
@ -28,10 +28,11 @@ struct bitvec;
|
|||
|
||||
class Decoding {
|
||||
public:
|
||||
/* represents (parts) LLC PDUs within one RLC Data block */
|
||||
struct RlcData {
|
||||
uint8_t offset;
|
||||
uint8_t length;
|
||||
bool is_complete;
|
||||
bool is_complete; /* if this PDU ends in this block */
|
||||
};
|
||||
|
||||
static int rlc_data_from_ul_data(
|
||||
|
@ -43,7 +44,22 @@ public:
|
|||
|
||||
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
|
||||
static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
|
||||
|
||||
static int rlc_parse_ul_data_header_egprs_type_3(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header_egprs_type_2(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header_egprs_type_1(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header_gprs(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, GprsCodingScheme cs);
|
||||
static unsigned int rlc_copy_to_aligned_buffer(
|
||||
|
|
|
@ -0,0 +1,669 @@
|
|||
/* egprs_rlc_compression.h
|
||||
* Routines for EGPRS RLC bitmap compression handling
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <decoding.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <egprs_rlc_compression.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
}
|
||||
|
||||
#define EGPRS_CODEWORDS 79 /* total number of codewords */
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
extern const char *one_run_len_code_list[EGPRS_CODEWORDS];
|
||||
extern const char *zero_run_len_code_list[EGPRS_CODEWORDS];
|
||||
|
||||
egprs_compress *egprs_compress::s_instance = 0;
|
||||
|
||||
egprs_compress_node *egprs_compress::create_tree_node(void *parent)
|
||||
{
|
||||
egprs_compress_node *new_node;
|
||||
|
||||
new_node = talloc_zero(parent, egprs_compress_node);
|
||||
new_node->left = NULL;
|
||||
new_node->right = NULL;
|
||||
new_node->run_length = -1;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
/* The function expands the given tree by incorporating
|
||||
* the given codewords.
|
||||
* \param root[in] Root of Ones or Zeros tree
|
||||
* \param cdwd[in] Code word
|
||||
*/
|
||||
void egprs_compress::build_codewords(egprs_compress_node *root, const char *cdwd[])
|
||||
{
|
||||
egprs_compress_node *iter;
|
||||
int len;
|
||||
int i;
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < EGPRS_CODEWORDS; idx++) {
|
||||
len = strlen((const char *)cdwd[idx]);
|
||||
iter = root;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (cdwd[idx][i] == '0') {
|
||||
if (!iter->left)
|
||||
iter->left = create_tree_node(root);
|
||||
iter = iter->left;
|
||||
} else {
|
||||
if (!iter->right)
|
||||
iter->right = create_tree_node(root);
|
||||
iter = iter->right;
|
||||
}
|
||||
}
|
||||
if (iter) {
|
||||
if (idx < 64)
|
||||
iter->run_length = idx;
|
||||
else
|
||||
/* The first 64 run lengths are 0, 1, 2, ..., 63
|
||||
* and the following ones are 64, 128, 192 described in
|
||||
* section 9.1.10 of 3gpp 44.060 */
|
||||
iter->run_length = (idx - 63) * 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminating codes for uninterrupted sequences of 0 and 1 up to 64 bit length
|
||||
* according to TS 44.060 9.1.10
|
||||
*/
|
||||
static const unsigned t4_term[2][64] = {
|
||||
{
|
||||
0b0000110111,
|
||||
0b10,
|
||||
0b11,
|
||||
0b010,
|
||||
0b011,
|
||||
0b0011,
|
||||
0b0010,
|
||||
0b00011,
|
||||
0b000101,
|
||||
0b000100,
|
||||
0b0000100,
|
||||
0b0000101,
|
||||
0b0000111,
|
||||
0b00000100,
|
||||
0b00000111,
|
||||
0b000011000,
|
||||
0b0000010111,
|
||||
0b0000011000,
|
||||
0b0000001000,
|
||||
0b00001100111,
|
||||
0b00001101000,
|
||||
0b00001101100,
|
||||
0b00000110111,
|
||||
0b00000101000,
|
||||
0b00000010111,
|
||||
0b00000011000,
|
||||
0b000011001010,
|
||||
0b000011001011,
|
||||
0b000011001100,
|
||||
0b000011001101,
|
||||
0b000001101000,
|
||||
0b000001101001,
|
||||
0b000001101010,
|
||||
0b000001101011,
|
||||
0b000011010010,
|
||||
0b000011010011,
|
||||
0b000011010100,
|
||||
0b000011010101,
|
||||
0b000011010110,
|
||||
0b000011010111,
|
||||
0b000001101100,
|
||||
0b000001101101,
|
||||
0b000011011010,
|
||||
0b000011011011,
|
||||
0b000001010100,
|
||||
0b000001010101,
|
||||
0b000001010110,
|
||||
0b000001010111,
|
||||
0b000001100100,
|
||||
0b000001100101,
|
||||
0b000001010010,
|
||||
0b000001010011,
|
||||
0b000000100100,
|
||||
0b000000110111,
|
||||
0b000000111000,
|
||||
0b000000100111,
|
||||
0b000000101000,
|
||||
0b000001011000,
|
||||
0b000001011001,
|
||||
0b000000101011,
|
||||
0b000000101100,
|
||||
0b000001011010,
|
||||
0b000001100110,
|
||||
0b000001100111
|
||||
|
||||
},
|
||||
{
|
||||
0b00110101,
|
||||
0b000111,
|
||||
0b0111,
|
||||
0b1000,
|
||||
0b1011,
|
||||
0b1100,
|
||||
0b1110,
|
||||
0b1111,
|
||||
0b10011,
|
||||
0b10100,
|
||||
0b00111,
|
||||
0b01000,
|
||||
0b001000,
|
||||
0b000011,
|
||||
0b110100,
|
||||
0b110101,
|
||||
0b101010,
|
||||
0b101011,
|
||||
0b0100111,
|
||||
0b0001100,
|
||||
0b0001000,
|
||||
0b0010111,
|
||||
0b0000011,
|
||||
0b0000100,
|
||||
0b0101000,
|
||||
0b0101011,
|
||||
0b0010011,
|
||||
0b0100100,
|
||||
0b0011000,
|
||||
0b00000010,
|
||||
0b00000011,
|
||||
0b00011010,
|
||||
0b00011011,
|
||||
0b00010010,
|
||||
0b00010011,
|
||||
0b00010100,
|
||||
0b00010101,
|
||||
0b00010110,
|
||||
0b00010111,
|
||||
0b00101000,
|
||||
0b00101001,
|
||||
0b00101010,
|
||||
0b00101011,
|
||||
0b00101100,
|
||||
0b00101101,
|
||||
0b00000100,
|
||||
0b00000101,
|
||||
0b00001010,
|
||||
0b00001011,
|
||||
0b01010010,
|
||||
0b01010011,
|
||||
0b01010100,
|
||||
0b01010101,
|
||||
0b00100100,
|
||||
0b00100101,
|
||||
0b01011000,
|
||||
0b01011001,
|
||||
0b01011010,
|
||||
0b01011011,
|
||||
0b01001010,
|
||||
0b01001011,
|
||||
0b00110010,
|
||||
0b00110011,
|
||||
0b00110100
|
||||
}
|
||||
};
|
||||
static const unsigned t4_term_length[2][64] = {
|
||||
{10, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
|
||||
{8, 6, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
|
||||
};
|
||||
|
||||
static const unsigned t4_min_term_length[] = {2, 4};
|
||||
static const unsigned t4_min_make_up_length[] = {10, 5};
|
||||
|
||||
static const unsigned t4_max_term_length[] = {12, 8};
|
||||
static const unsigned t4_max_make_up_length[] = {13, 9};
|
||||
|
||||
static const unsigned t4_make_up_length[2][15] = {
|
||||
{10, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13},
|
||||
{5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9}
|
||||
};
|
||||
|
||||
static const unsigned t4_make_up_ind[15] = {64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960};
|
||||
|
||||
static const unsigned t4_make_up[2][15] = {
|
||||
{
|
||||
0b0000001111,
|
||||
0b000011001000,
|
||||
0b000011001001,
|
||||
0b000001011011,
|
||||
0b000000110011,
|
||||
0b000000110100,
|
||||
0b000000110101,
|
||||
0b0000001101100,
|
||||
0b0000001101101,
|
||||
0b0000001001010,
|
||||
0b0000001001011,
|
||||
0b0000001001100,
|
||||
0b0000001001101,
|
||||
0b0000001110010,
|
||||
0b0000001110011
|
||||
},
|
||||
{
|
||||
0b11011,
|
||||
0b10010,
|
||||
0b010111,
|
||||
0b0110111,
|
||||
0b00110110,
|
||||
0b00110111,
|
||||
0b01100100,
|
||||
0b01100101,
|
||||
0b01101000,
|
||||
0b01100111,
|
||||
0b011001100,
|
||||
0b011001101,
|
||||
0b011010010,
|
||||
0b011010011,
|
||||
0b011010100
|
||||
}
|
||||
};
|
||||
|
||||
/* The code words for one run length and zero
|
||||
* run length are described in table 9.1.10.1
|
||||
* of 3gpp 44.060
|
||||
*/
|
||||
const char *one_run_len_code_list[EGPRS_CODEWORDS] = {
|
||||
"00110101",
|
||||
"000111",
|
||||
"0111",
|
||||
"1000",
|
||||
"1011",
|
||||
"1100",
|
||||
"1110",
|
||||
"1111",
|
||||
"10011",
|
||||
"10100",
|
||||
"00111",
|
||||
"01000",
|
||||
"001000",
|
||||
"000011",
|
||||
"110100",
|
||||
"110101",
|
||||
"101010",
|
||||
"101011",
|
||||
"0100111",
|
||||
"0001100",
|
||||
"0001000",
|
||||
"0010111",
|
||||
"0000011",
|
||||
"0000100",
|
||||
"0101000",
|
||||
"0101011",
|
||||
"0010011",
|
||||
"0100100",
|
||||
"0011000",
|
||||
"00000010",
|
||||
"00000011",
|
||||
"00011010",
|
||||
"00011011",
|
||||
"00010010",
|
||||
"00010011",
|
||||
"00010100",
|
||||
"00010101",
|
||||
"00010110",
|
||||
"00010111",
|
||||
"00101000",
|
||||
"00101001",
|
||||
"00101010",
|
||||
"00101011",
|
||||
"00101100",
|
||||
"00101101",
|
||||
"00000100",
|
||||
"00000101",
|
||||
"00001010",
|
||||
"00001011",
|
||||
"01010010",
|
||||
"01010011",
|
||||
"01010100",
|
||||
"01010101",
|
||||
"00100100",
|
||||
"00100101",
|
||||
"01011000",
|
||||
"01011001",
|
||||
"01011010",
|
||||
"01011011",
|
||||
"01001010",
|
||||
"01001011",
|
||||
"00110010",
|
||||
"00110011",
|
||||
"00110100",
|
||||
"11011",
|
||||
"10010",
|
||||
"010111",
|
||||
"0110111",
|
||||
"00110110",
|
||||
"00110111",
|
||||
"01100100",
|
||||
"01100101",
|
||||
"01101000",
|
||||
"01100111",
|
||||
"011001100",
|
||||
"011001101",
|
||||
"011010010",
|
||||
"011010011",
|
||||
"011010100"
|
||||
};
|
||||
|
||||
const char *zero_run_len_code_list[EGPRS_CODEWORDS] = {
|
||||
"0000110111",
|
||||
"10",
|
||||
"11",
|
||||
"010",
|
||||
"011",
|
||||
"0011",
|
||||
"0010",
|
||||
"00011",
|
||||
"000101",
|
||||
"000100",
|
||||
"0000100",
|
||||
"0000101",
|
||||
"0000111",
|
||||
"00000100",
|
||||
"00000111",
|
||||
"000011000",
|
||||
"0000010111",
|
||||
"0000011000",
|
||||
"0000001000",
|
||||
"00001100111",
|
||||
"00001101000",
|
||||
"00001101100",
|
||||
"00000110111",
|
||||
"00000101000",
|
||||
"00000010111",
|
||||
"00000011000",
|
||||
"000011001010",
|
||||
"000011001011",
|
||||
"000011001100",
|
||||
"000011001101",
|
||||
"000001101000",
|
||||
"000001101001",
|
||||
"000001101010",
|
||||
"000001101011",
|
||||
"000011010010",
|
||||
"000011010011",
|
||||
"000011010100",
|
||||
"000011010101",
|
||||
"000011010110",
|
||||
"000011010111",
|
||||
"000001101100",
|
||||
"000001101101",
|
||||
"000011011010",
|
||||
"000011011011",
|
||||
"000001010100",
|
||||
"000001010101",
|
||||
"000001010110",
|
||||
"000001010111",
|
||||
"000001100100",
|
||||
"000001100101",
|
||||
"000001010010",
|
||||
"000001010011",
|
||||
"000000100100",
|
||||
"000000110111",
|
||||
"000000111000",
|
||||
"000000100111",
|
||||
"000000101000",
|
||||
"000001011000",
|
||||
"000001011001",
|
||||
"000000101011",
|
||||
"000000101100",
|
||||
"000001011010",
|
||||
"000001100110",
|
||||
"000001100111",
|
||||
"0000001111",
|
||||
"000011001000",
|
||||
"000011001001",
|
||||
"000001011011",
|
||||
"000000110011",
|
||||
"000000110100",
|
||||
"000000110101",
|
||||
"0000001101100",
|
||||
"0000001101101",
|
||||
"0000001001010",
|
||||
"0000001001011",
|
||||
"0000001001100",
|
||||
"0000001001101",
|
||||
"0000001110010",
|
||||
"0000001110011"
|
||||
};
|
||||
|
||||
/* Calculate runlength of a codeword
|
||||
* \param root[in] Root of Ones or Zeros tree
|
||||
* \param bmbuf[in] Received compressed bitmap buf
|
||||
* \param bit_pos[in] The start bit pos to read codeword
|
||||
* \param len_codewd[in] Length of code word
|
||||
* \param rlen[out] Caluculate run length
|
||||
*/
|
||||
static int search_runlen(
|
||||
egprs_compress_node *root,
|
||||
const uint8_t *bmbuf,
|
||||
uint8_t bit_pos,
|
||||
uint8_t *len_codewd,
|
||||
uint16_t *rlen)
|
||||
{
|
||||
egprs_compress_node *iter;
|
||||
uint8_t dir;
|
||||
|
||||
iter = root;
|
||||
*len_codewd = 0;
|
||||
|
||||
while (iter->run_length == -1) {
|
||||
if ((!iter->left) && (!iter->right))
|
||||
return -1;
|
||||
/* get the bit value at the bitpos and put it in right most of dir */
|
||||
dir = ((bmbuf[BITS_TO_BYTES(bit_pos) - 1]
|
||||
>> (7 - MOD8(bit_pos))) & 0x01);
|
||||
(bit_pos)++;
|
||||
(*len_codewd)++;
|
||||
if (((dir&0x01) == 0) && (iter->left != NULL))
|
||||
iter = iter->left;
|
||||
else if (((dir&0x01) == 1) && (iter->right != NULL))
|
||||
iter = iter->right;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "Run_length = %d\n", iter->run_length);
|
||||
(*rlen) = (iter->run_length);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Function to decompress compressed received block bitmap
|
||||
* \param compress_bmap_len[in] Compressed bitmap length
|
||||
* \param color_code_bit[in] Color code 1 for Ones runlength 0 for Zero runlength
|
||||
* \param orig_crbb_buf[in] Received block crbb bitmap
|
||||
* \param dest[out] Uncompressed bitvector
|
||||
*/
|
||||
int egprs_compress::decompress_crbb(
|
||||
int8_t compress_bmap_len,
|
||||
bool color_code_bit,
|
||||
const uint8_t *orig_crbb_buf,
|
||||
bitvec *dest)
|
||||
{
|
||||
|
||||
uint8_t bit_pos = 0;
|
||||
uint8_t data = 0x0;
|
||||
egprs_compress_node *list = NULL;
|
||||
uint8_t nbits = 0; /* number of bits of codeword */
|
||||
uint16_t run_length = 0;
|
||||
uint16_t cbmaplen = 0; /* compressed bitmap part after decompression */
|
||||
unsigned wp = dest->cur_bit;
|
||||
int rc = 0;
|
||||
egprs_compress *compress = egprs_compress::instance();
|
||||
|
||||
while (compress_bmap_len > 0) {
|
||||
if (color_code_bit == 1) {
|
||||
data = 0xff;
|
||||
list = compress->ones_list;
|
||||
} else {
|
||||
data = 0x00;
|
||||
list = compress->zeros_list;
|
||||
}
|
||||
rc = search_runlen(list, orig_crbb_buf,
|
||||
bit_pos, &nbits, &run_length);
|
||||
if (rc == -1)
|
||||
return -1;
|
||||
/* If run length > 64, need makeup and terminating code */
|
||||
if (run_length < 64)
|
||||
color_code_bit ? color_code_bit = 0 : color_code_bit = 1;
|
||||
cbmaplen = cbmaplen + run_length;
|
||||
/* put run length of Ones in uncompressed bitmap */
|
||||
while (run_length != 0) {
|
||||
if (run_length > 8) {
|
||||
bitvec_write_field(dest, wp, data, 8);
|
||||
run_length = run_length - 8;
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, data, run_length);
|
||||
run_length = 0;
|
||||
}
|
||||
}
|
||||
bit_pos = bit_pos + nbits;
|
||||
compress_bmap_len = compress_bmap_len - nbits;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void egprs_compress::decode_tree_init()
|
||||
{
|
||||
ones_list = create_tree_node(tall_pcu_ctx);
|
||||
zeros_list = create_tree_node(tall_pcu_ctx);
|
||||
build_codewords(ones_list, one_run_len_code_list);
|
||||
build_codewords(zeros_list, zero_run_len_code_list);
|
||||
}
|
||||
void compress_bitmap(
|
||||
uint16_t *run_len_cnt, /* cnt: run length count */
|
||||
uint16_t *codewrd_bitmap, /* code word */
|
||||
int16_t *codewrd_len, /* number of bits in the code word */
|
||||
bitvec *crbb_vec, /* bitmap buffer to put code word in */
|
||||
uint8_t clr_code)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned writeIndex = crbb_vec->cur_bit;
|
||||
|
||||
*codewrd_bitmap = 0;
|
||||
*codewrd_len = 0;
|
||||
if ((*run_len_cnt) >= 64) {
|
||||
for (i = 0; i < 15; i++) {
|
||||
if (t4_make_up_ind[i] == *run_len_cnt) {
|
||||
*codewrd_bitmap = t4_make_up[clr_code][i];
|
||||
*codewrd_len = t4_make_up_length[clr_code][i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*codewrd_bitmap = t4_term[clr_code][*run_len_cnt];
|
||||
*codewrd_len = t4_term_length[clr_code][*run_len_cnt];
|
||||
}
|
||||
bitvec_write_field(crbb_vec, writeIndex, (*codewrd_bitmap), (*codewrd_len));
|
||||
}
|
||||
|
||||
int osmo_t4_compress(struct bitvec *bv)
|
||||
{
|
||||
uint8_t crbb_len = 0;
|
||||
uint8_t uclen_crbb = 0;
|
||||
uint8_t crbb_bitmap[127] = {'\0'};
|
||||
uint8_t color_code = (bv->data[0] & 0x80)>>7;
|
||||
struct bitvec crbb_vec;
|
||||
|
||||
crbb_vec.data = crbb_bitmap;
|
||||
crbb_vec.cur_bit = 0;
|
||||
crbb_vec.data_len = 127;
|
||||
bv->data_len = bv->cur_bit;
|
||||
bv->cur_bit = 0;
|
||||
if (compress_rbb(bv, &crbb_vec, &uclen_crbb, 23*8)) {
|
||||
memcpy(bv->data, crbb_bitmap, (crbb_len+7)/8);
|
||||
bv->cur_bit = crbb_len;
|
||||
bv->data_len = (crbb_len+7)/8;
|
||||
return color_code;
|
||||
}
|
||||
else
|
||||
printf("Encode failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! \brief compression algorithm using T4 encoding
|
||||
* the compressed bitmap's are copied in crbb_bitmap
|
||||
* \param[in] rbb_vec bit vector to be encoded
|
||||
* \return 1 if compression is success or 0 for failure
|
||||
*/
|
||||
int compress_rbb(
|
||||
struct bitvec *urbb_vec,
|
||||
struct bitvec *crbb_vec,
|
||||
uint8_t *uclen_crbb, /* Uncompressed bitmap len in CRBB */
|
||||
uint8_t max_bits) /* max remaining bits */
|
||||
{
|
||||
bool run_len_bit;
|
||||
int buflen = urbb_vec->cur_bit;
|
||||
int total_bits = urbb_vec->cur_bit;
|
||||
uint16_t rlen;
|
||||
uint16_t temprl = 0;
|
||||
uint16_t cbmap = 0; /* Compressed code word */
|
||||
int16_t nbits; /* Length of code word */
|
||||
uint16_t uclen = 0;
|
||||
int16_t clen = 0;
|
||||
uint8_t clr_code = 0;
|
||||
urbb_vec->cur_bit = 0;
|
||||
run_len_bit = (urbb_vec->data[0] & 0x80)>>7;
|
||||
while (buflen > 0) {
|
||||
temprl = 0;
|
||||
/* Find Run length */
|
||||
if (run_len_bit == 1)
|
||||
rlen = bitvec_rl_curbit(urbb_vec, true, total_bits);
|
||||
else
|
||||
rlen = bitvec_rl_curbit(urbb_vec, false, total_bits);
|
||||
buflen = buflen - rlen;
|
||||
/* if rlen> 64 need 2 code words */
|
||||
/*Compress the bits */
|
||||
if (run_len_bit == 0) {
|
||||
clr_code = 0;
|
||||
if (rlen >= 64) {
|
||||
temprl = (rlen/64)*64;
|
||||
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||
crbb_vec, clr_code);
|
||||
clen = clen + nbits;
|
||||
}
|
||||
temprl = MOD64(rlen);
|
||||
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||
crbb_vec, clr_code);
|
||||
/* next time the run length will be Ones */
|
||||
run_len_bit = 1;
|
||||
} else {
|
||||
clr_code = 1;
|
||||
if (rlen >= 64) {
|
||||
temprl = (rlen/64)*64;
|
||||
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||
crbb_vec, clr_code);
|
||||
clen = clen + nbits;
|
||||
}
|
||||
temprl = MOD64(rlen);
|
||||
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||
crbb_vec, clr_code);
|
||||
|
||||
/* next time the run length will be Zeros */
|
||||
run_len_bit = 0;
|
||||
}
|
||||
uclen = uclen + (rlen);
|
||||
clen = clen + nbits;
|
||||
/*compressed bitmap exceeds the buffer space */
|
||||
if (clen > max_bits) {
|
||||
uclen = uclen - rlen;
|
||||
clen = clen - nbits;
|
||||
break;
|
||||
}
|
||||
}
|
||||
crbb_vec->cur_bit = clen;
|
||||
*uclen_crbb = uclen;
|
||||
if (clen >= uclen)
|
||||
/* No Gain is observed, So no need to compress */
|
||||
return 0;
|
||||
else
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "CRBB bitmap = %s\n", osmo_hexdump(crbb_vec->data, (crbb_vec->cur_bit+7)/8));
|
||||
/* Add compressed bitmap to final buffer */
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/* egprs_rlc_compression.h
|
||||
* Routines for EGPRS RLC bitmap compression handling
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define BITS_TO_BYTES(X) (((X) ? (X)/8 : 0) + 1)
|
||||
#define MOD8(X) ((X) & (0x07))
|
||||
#define MOD64(X) ((X + 64) & 0x3F)
|
||||
|
||||
typedef struct egprs_compress_node{
|
||||
struct egprs_compress_node *left;
|
||||
struct egprs_compress_node *right;
|
||||
int run_length;
|
||||
} egprs_compress_node;
|
||||
|
||||
int decompress_crbb(int8_t compress_bmap_len, uint8_t clr_code_bit,
|
||||
const uint8_t *orig_buf, bitvec *dest);
|
||||
int osmo_t4_compress(struct bitvec *bv);
|
||||
int compress_rbb(
|
||||
struct bitvec *urbb_vec,/*!< input bitvector to compress */
|
||||
struct bitvec *crbb_vec, /*!< Compressed bitmap len */
|
||||
uint8_t *uclen_crbb, /*!< Uncompressed bitmap len in CRBB */
|
||||
uint8_t max_bits /*!< Maximum remaining bits */
|
||||
);
|
||||
|
||||
/* Singleton to manage the EGPRS compression algorithm. */
|
||||
class egprs_compress
|
||||
{
|
||||
public:
|
||||
static int decompress_crbb(int8_t compress_bmap_len,
|
||||
bool color_code_bit, const uint8_t *orig_buf,
|
||||
bitvec *dest);
|
||||
|
||||
private:
|
||||
static egprs_compress *s_instance;
|
||||
egprs_compress_node *ones_list;
|
||||
egprs_compress_node *zeros_list;
|
||||
|
||||
void decode_tree_init(void);
|
||||
|
||||
static egprs_compress *instance()
|
||||
{
|
||||
if (!s_instance)
|
||||
s_instance = new egprs_compress;
|
||||
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
egprs_compress()
|
||||
{
|
||||
decode_tree_init();
|
||||
}
|
||||
egprs_compress_node *create_tree_node(void *);
|
||||
void build_codewords(egprs_compress_node *root, const char *cdwd[]);
|
||||
/* singleton class, so this private destructor is left unimplemented. */
|
||||
~egprs_compress();
|
||||
};
|
359
src/encoding.cpp
359
src/encoding.cpp
|
@ -24,14 +24,22 @@
|
|||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <egprs_rlc_compression.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/gprs/protocol/gsm_04_60.h>
|
||||
}
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
static int write_ia_rest_downlink(
|
||||
gprs_rlcmac_dl_tbf *tbf,
|
||||
bitvec * dest, unsigned& wp,
|
||||
uint8_t polling, uint32_t fn,
|
||||
uint8_t polling, bool ta_valid, uint32_t fn,
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
|
||||
{
|
||||
if (!tbf) {
|
||||
|
@ -54,7 +62,7 @@ static int write_ia_rest_downlink(
|
|||
}
|
||||
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
|
||||
bitvec_write_field(dest, wp,polling,1); // Polling Bit
|
||||
bitvec_write_field(dest, wp,!polling,1); // TA_VALID ???
|
||||
bitvec_write_field(dest, wp, ta_valid, 1); // N. B: NOT related to TAI!
|
||||
if (ta_idx < 0) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
|
||||
} else {
|
||||
|
@ -140,11 +148,71 @@ static int write_ia_rest_egprs_uplink(
|
|||
gprs_rlcmac_ul_tbf *tbf,
|
||||
bitvec * dest, unsigned& wp,
|
||||
uint8_t usf, uint32_t fn,
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
|
||||
enum ph_burst_type burst_type, uint16_t ra)
|
||||
{
|
||||
LOGP(DRLCMACUL, LOGL_ERROR,
|
||||
"EGPRS Packet Uplink Assignment is not yet implemented\n");
|
||||
return -EINVAL;
|
||||
unsigned int ws_enc = 0;
|
||||
uint8_t extended_ra = 0;
|
||||
|
||||
extended_ra = (ra & 0x1F);
|
||||
|
||||
bitvec_write_field(dest, wp, 1, 2); /* LH */
|
||||
bitvec_write_field(dest, wp, 0, 2); /* 0 EGPRS Uplink Assignment */
|
||||
bitvec_write_field(dest, wp, extended_ra, 5); /* Extended RA */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* Access technology Request */
|
||||
|
||||
if (tbf == NULL) {
|
||||
|
||||
bitvec_write_field(dest, wp, 0, 1); /* multiblock allocation */
|
||||
|
||||
if (alpha) {
|
||||
bitvec_write_field(dest, wp, 0x1, 1); /* ALPHA =yes */
|
||||
bitvec_write_field(dest, wp, alpha, 4); /* ALPHA */
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 0x0, 1); /* ALPHA = no */
|
||||
}
|
||||
|
||||
bitvec_write_field(dest, wp, gamma, 5); /* GAMMA power contrl */
|
||||
bitvec_write_field(dest, wp, (fn / (26 * 51)) % 32, 5);/* T1' */
|
||||
bitvec_write_field(dest, wp, fn % 51, 6); /* T3 */
|
||||
bitvec_write_field(dest, wp, fn % 26, 5); /* T2 */
|
||||
bitvec_write_field(dest, wp, 0, 2); /* Radio block allocation */
|
||||
|
||||
bitvec_write_field(dest, wp, 0, 1);
|
||||
|
||||
} else {
|
||||
|
||||
ws_enc = (tbf->m_window.ws() - 64) / 32;
|
||||
|
||||
bitvec_write_field(dest, wp, 1, 1); /* single block alloc */
|
||||
bitvec_write_field(dest, wp, tbf->tfi(), 5);/* TFI assignment */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* polling bit */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* constant */
|
||||
bitvec_write_field(dest, wp, usf, 3); /* USF bit */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* USF granularity */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* P0 */
|
||||
/* MCS */
|
||||
bitvec_write_field(dest, wp, tbf->current_cs().to_num()-1, 4);
|
||||
/* tlli channel block */
|
||||
bitvec_write_field(dest, wp, tbf->tlli(), 1);
|
||||
bitvec_write_field(dest, wp, 0, 1); /* BEP period present */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* resegmentation */
|
||||
bitvec_write_field(dest, wp, ws_enc, 5);/* egprs window_size */
|
||||
|
||||
if (alpha) {
|
||||
bitvec_write_field(dest, wp, 0x1, 1); /* ALPHA =yes */
|
||||
bitvec_write_field(dest, wp, alpha, 4); /* ALPHA */
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 0x0, 1); /* ALPHA = no */
|
||||
}
|
||||
|
||||
bitvec_write_field(dest, wp, gamma, 5); /* GAMMA power contrl */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* TIMING_ADVANCE_INDEX */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* TBF_STARTING_TIME_FLAG */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* NULL */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -153,10 +221,10 @@ static int write_ia_rest_egprs_uplink(
|
|||
*/
|
||||
int Encoding::write_immediate_assignment(
|
||||
struct gprs_rlcmac_tbf *tbf,
|
||||
bitvec * dest, uint8_t downlink, uint8_t ra,
|
||||
bitvec * dest, uint8_t downlink, uint16_t ra,
|
||||
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
|
||||
uint8_t usf, uint8_t polling, uint32_t fn, uint8_t alpha,
|
||||
uint8_t gamma, int8_t ta_idx)
|
||||
uint8_t gamma, int8_t ta_idx, enum ph_burst_type burst_type, uint8_t sb)
|
||||
{
|
||||
unsigned wp = 0;
|
||||
int plen;
|
||||
|
@ -182,7 +250,13 @@ int Encoding::write_immediate_assignment(
|
|||
bitvec_write_field(dest, wp,arfcn,10); // ARFCN
|
||||
|
||||
//10.5.2.30 Request Reference
|
||||
bitvec_write_field(dest, wp,ra,8); // RA
|
||||
if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
||||
bitvec_write_field(dest, wp, 0x7f, 8); /* RACH value */
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, ra, 8); /* RACH value */
|
||||
}
|
||||
|
||||
bitvec_write_field(dest, wp,(ref_fn / (26 * 51)) % 32,5); // T1'
|
||||
bitvec_write_field(dest, wp,ref_fn % 51,6); // T3
|
||||
bitvec_write_field(dest, wp,ref_fn % 26,5); // T2
|
||||
|
@ -204,12 +278,13 @@ int Encoding::write_immediate_assignment(
|
|||
|
||||
if (downlink)
|
||||
rc = write_ia_rest_downlink(as_dl_tbf(tbf), dest, wp,
|
||||
polling, fn,
|
||||
polling, gsm48_ta_is_valid(ta), fn,
|
||||
alpha, gamma, ta_idx);
|
||||
else if (as_ul_tbf(tbf) && as_ul_tbf(tbf)->is_egprs_enabled())
|
||||
else if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2)))
|
||||
rc = write_ia_rest_egprs_uplink(as_ul_tbf(tbf), dest, wp,
|
||||
usf, fn,
|
||||
alpha, gamma, ta_idx);
|
||||
alpha, gamma, ta_idx, burst_type, ra);
|
||||
else
|
||||
rc = write_ia_rest_uplink(as_ul_tbf(tbf), dest, wp,
|
||||
usf, fn,
|
||||
|
@ -258,7 +333,7 @@ void Encoding::write_packet_uplink_assignment(
|
|||
|
||||
if (!use_egprs) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // Message escape
|
||||
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 2); // CHANNEL_CODING_COMMAND
|
||||
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 2); // CHANNEL_CODING_COMMAND
|
||||
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
|
||||
bitvec_write_field(dest, wp,tbf->ta(),6); // TIMING_ADVANCE_VALUE
|
||||
|
@ -276,11 +351,12 @@ void Encoding::write_packet_uplink_assignment(
|
|||
bitvec_write_field(dest, wp,0x0,1); // No CONTENTION_RESOLUTION_TLLI
|
||||
bitvec_write_field(dest, wp,0x0,1); // No COMPACT reduced MA
|
||||
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 4); // EGPRS Modulation and Coding IE
|
||||
bitvec_write_field(dest, wp,0x0,1); // No RESEGMENT
|
||||
/* 0: no RESEGMENT, 1: Segmentation*/
|
||||
bitvec_write_field(dest, wp, 0x1, 1);
|
||||
bitvec_write_field(dest, wp,ws_enc,5); // EGPRS Window Size
|
||||
bitvec_write_field(dest, wp,0x0,1); // No Access Technologies Request
|
||||
bitvec_write_field(dest, wp,0x0,1); // No ARAC RETRANSMISSION REQUEST
|
||||
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
|
||||
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
|
||||
bitvec_write_field(dest, wp,0x0,1); // No BEP_PERIOD2
|
||||
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
|
||||
|
@ -534,17 +610,44 @@ static void write_packet_uplink_ack_gprs(
|
|||
|
||||
static void write_packet_ack_nack_desc_egprs(
|
||||
struct gprs_rlcmac_bts *bts, bitvec * dest, unsigned& wp,
|
||||
gprs_rlc_ul_window *window, bool is_final)
|
||||
gprs_rlc_ul_window *window, bool is_final, unsigned& rest_bits)
|
||||
{
|
||||
int urbb_len = 0;
|
||||
int crbb_len = 0;
|
||||
int len;
|
||||
uint8_t crbb_len = 0;
|
||||
uint8_t len;
|
||||
bool bow = true;
|
||||
bool eow = true;
|
||||
int ssn = window->mod_sns(window->v_q() + 1);
|
||||
int num_blocks = window->mod_sns(window->v_r() - window->v_q());
|
||||
int esn_crbb = window->mod_sns(ssn - 1);
|
||||
int rest_bits = dest->data_len * 8 - wp;
|
||||
static uint8_t rbb[RLC_EGPRS_MAX_WS] = {'\0'};
|
||||
uint8_t iter = 0;
|
||||
int is_compressed = 0;
|
||||
bool try_compression = false;
|
||||
uint8_t ucmp_bmplen;
|
||||
uint8_t crbb_bitmap[23] = {'\0'};
|
||||
bitvec ucmp_vec;
|
||||
bitvec crbb_vec;
|
||||
uint8_t uclen_crbb = 0;
|
||||
bool len_coded = true;
|
||||
uint8_t crbb_start_clr_code;
|
||||
uint8_t i;
|
||||
#if 0
|
||||
/* static size of 16 bits*/
|
||||
..0. .... = ACKNACK: (Union)
|
||||
Desc
|
||||
|
||||
...0 .... = FINAL_ACK_INDICATION: False
|
||||
|
||||
.... 1... = BEGINNING_OF_WINDOW: 1
|
||||
|
||||
.... .1.. = END_OF_WINDOW: 1
|
||||
|
||||
.... ..10 0101 0001 1... .... = STARTING_SEQUENCE_NUMBER: 1187
|
||||
|
||||
.0.. .... = CRBB Exist: 0
|
||||
#endif
|
||||
rest_bits -= 16;
|
||||
|
||||
if (num_blocks > 0)
|
||||
/* V(Q) is NACK and omitted -> SSN = V(Q) + 1 */
|
||||
|
@ -553,31 +656,69 @@ static void write_packet_ack_nack_desc_egprs(
|
|||
if (num_blocks > window->ws())
|
||||
num_blocks = window->ws();
|
||||
|
||||
/* Try Compression as number of blocks does not fit */
|
||||
if (num_blocks > rest_bits) {
|
||||
eow = false;
|
||||
urbb_len = rest_bits;
|
||||
/* TODO: use compression, start encoding bits and stop when the
|
||||
* space is exhausted. Use the first combination that encodes
|
||||
* all bits. If there is none, use the combination that encodes
|
||||
* the largest number of bits (e.g. by setting num_blocks to the
|
||||
* max and repeating the construction).
|
||||
*/
|
||||
} else if (num_blocks > rest_bits - 9) {
|
||||
/* union bit and length field take 9 bits */
|
||||
eow = false;
|
||||
urbb_len = rest_bits - 9;
|
||||
/* TODO: use compression (see above) */
|
||||
try_compression = true;
|
||||
}
|
||||
if (try_compression == true) {
|
||||
ucmp_bmplen = window->update_egprs_rbb(rbb);
|
||||
ucmp_vec.data = rbb;
|
||||
ucmp_vec.cur_bit = ucmp_bmplen;
|
||||
ucmp_vec.data_len = 127;
|
||||
crbb_vec.data = crbb_bitmap;
|
||||
crbb_vec.cur_bit = 0;
|
||||
crbb_vec.data_len = 127;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"rest_bits=%d uncompressed len %d and uncompressed bitmap = %s\n",
|
||||
rest_bits, ucmp_bmplen,
|
||||
osmo_hexdump(ucmp_vec.data, (ucmp_bmplen+7)/8));
|
||||
|
||||
is_compressed = compress_rbb(&ucmp_vec, /* Uncompressed bitmap*/
|
||||
&crbb_vec, /*Compressed bitmap vector */
|
||||
&uclen_crbb,
|
||||
(rest_bits - 8 - 8));/* CRBBlength:7 colourcode:1 dissector length:8*/
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"the ucmp len=%d uclen_crbb=%d num_blocks=%d crbb length %d, "
|
||||
"and the CRBB bitmap = %s\n",
|
||||
ucmp_bmplen, uclen_crbb, num_blocks, crbb_vec.cur_bit,
|
||||
osmo_hexdump(crbb_bitmap, (crbb_vec.cur_bit+7)/8));
|
||||
crbb_len = crbb_vec.cur_bit;
|
||||
}
|
||||
|
||||
if (urbb_len + crbb_len == rest_bits)
|
||||
len = -1;
|
||||
else if (crbb_len == 0)
|
||||
if (is_compressed == 0) {
|
||||
/* length field takes 8 bits*/
|
||||
if (num_blocks > rest_bits - 8) {
|
||||
eow = false;
|
||||
urbb_len = rest_bits;
|
||||
len_coded = false;
|
||||
} else if (num_blocks == rest_bits) {
|
||||
urbb_len = rest_bits;
|
||||
len_coded = false;
|
||||
} else
|
||||
urbb_len = num_blocks;
|
||||
|
||||
len = urbb_len + 15;
|
||||
else
|
||||
} else {
|
||||
if (num_blocks > uclen_crbb) {
|
||||
eow = false;
|
||||
urbb_len = num_blocks - uclen_crbb;
|
||||
}
|
||||
/* Union bit takes 1 bit */
|
||||
/* Other fields in descr of compresed bitmap takes 23 bits*/
|
||||
if (urbb_len > (rest_bits - crbb_len - 8)) {
|
||||
eow = false;
|
||||
len_coded = false;
|
||||
urbb_len = rest_bits - crbb_len - 8;
|
||||
} else if (urbb_len > (rest_bits - crbb_len - 8 - 8)) {
|
||||
eow = false;
|
||||
len_coded = false;
|
||||
urbb_len = rest_bits - crbb_len - 8 - 8;
|
||||
}
|
||||
len = urbb_len + crbb_len + 23;
|
||||
}
|
||||
|
||||
/* EGPRS Ack/Nack Description IE */
|
||||
if (len < 0) {
|
||||
if (len_coded == false) {
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have length
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 1, 1); // 1: have length
|
||||
|
@ -588,17 +729,37 @@ static void write_packet_ack_nack_desc_egprs(
|
|||
bitvec_write_field(dest, wp, bow, 1); // BEGINNING_OF_WINDOW
|
||||
bitvec_write_field(dest, wp, eow, 1); // END_OF_WINDOW
|
||||
bitvec_write_field(dest, wp, ssn, 11); // STARTING_SEQUENCE_NUMBER
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have CRBB
|
||||
|
||||
/* TODO: Add CRBB support */
|
||||
|
||||
if (is_compressed) {
|
||||
bitvec_write_field(dest, wp, 1, 1); // CRBB_Exist
|
||||
bitvec_write_field(dest, wp, crbb_len, 7); // CRBB_LENGTH
|
||||
crbb_start_clr_code = (0x80 & ucmp_vec.data[0])>>7;
|
||||
bitvec_write_field(dest, wp, crbb_start_clr_code, 1); // CRBB_clr_code
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"EGPRS CRBB, crbb_len = %d, crbb_start_clr_code = %d\n",
|
||||
crbb_len, crbb_start_clr_code);
|
||||
while (crbb_len != 0) {
|
||||
if (crbb_len > 8) {
|
||||
bitvec_write_field(dest, wp, crbb_bitmap[iter], 8);
|
||||
crbb_len = crbb_len - 8;
|
||||
iter++;
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, crbb_bitmap[iter], crbb_len);
|
||||
crbb_len = 0;
|
||||
}
|
||||
}
|
||||
esn_crbb = window->mod_sns(esn_crbb + uclen_crbb);
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 0, 1); // CRBB_Exist
|
||||
}
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
" - EGPRS URBB, len = %d, SSN = %d, ESN_CRBB = %d, "
|
||||
"EGPRS URBB, urbb len = %d, SSN = %d, ESN_CRBB = %d, "
|
||||
"len present = %s,desc len = %d, "
|
||||
"SNS = %d, WS = %d, V(Q) = %d, V(R) = %d%s%s\n",
|
||||
urbb_len, ssn, esn_crbb,
|
||||
urbb_len, ssn, esn_crbb, len_coded ? "yes" : "No" ,len,
|
||||
window->sns(), window->ws(), window->v_q(), window->v_r(),
|
||||
bow ? ", BOW" : "", eow ? ", EOW" : "");
|
||||
for (int i = urbb_len; i > 0; i--) {
|
||||
|
||||
for (i = urbb_len; i > 0; i--) {
|
||||
/* Set bit at the appropriate position (see 3GPP TS 04.60 12.3.1) */
|
||||
bool is_ack = window->m_v_n.is_received(esn_crbb + i);
|
||||
bitvec_write_field(dest, wp, is_ack, 1);
|
||||
|
@ -610,9 +771,11 @@ static void write_packet_uplink_ack_egprs(
|
|||
struct gprs_rlcmac_ul_tbf *tbf, bool is_final)
|
||||
{
|
||||
bitvec_write_field(dest, wp, 0, 2); // fixed 00
|
||||
bitvec_write_field(dest, wp, 2, 4); // CHANNEL_CODING_COMMAND: MCS-3
|
||||
// bitvec_write_field(dest, wp, tbf->current_cs() - 1, 4); // CHANNEL_CODING_COMMAND
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: no RESEGMENT (nyi)
|
||||
/* CHANNEL_CODING_COMMAND */
|
||||
bitvec_write_field(dest, wp,
|
||||
tbf->current_cs().to_num() - 1, 4);
|
||||
/* 0: no RESEGMENT, 1: Segmentation*/
|
||||
bitvec_write_field(dest, wp, 1, 1);
|
||||
bitvec_write_field(dest, wp, 1, 1); // PRE_EMPTIVE_TRANSMISSION, TODO: This resembles GPRS, change it?
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: no PRR_RETRANSMISSION_REQUEST, TODO: clarify
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: no ARAC_RETRANSMISSION_REQUEST, TODO: clarify
|
||||
|
@ -624,7 +787,9 @@ static void write_packet_uplink_ack_egprs(
|
|||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have Power Control Parameters
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have Extension Bits
|
||||
|
||||
write_packet_ack_nack_desc_egprs(bts, dest, wp, &tbf->m_window, is_final);
|
||||
/* -2 for last bit 0 mandatory and REL5 not supported */
|
||||
unsigned bits_ack_nack = dest->data_len * 8 - wp - 2;
|
||||
write_packet_ack_nack_desc_egprs(bts, dest, wp, &tbf->m_window, is_final, bits_ack_nack);
|
||||
|
||||
bitvec_write_field(dest, wp, 0, 1); // fixed 0
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have REL 5
|
||||
|
@ -744,20 +909,20 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
|
|||
egprs1->usf = rlc->usf;
|
||||
egprs1->es_p = rlc->es_p;
|
||||
egprs1->rrbp = rlc->rrbp;
|
||||
egprs1->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs1->tfi_b = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs1->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs1->tfi_lo = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs1->pr = rlc->pr;
|
||||
egprs1->cps = rlc->cps;
|
||||
|
||||
egprs1->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs1->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs1->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
egprs1->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs1->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs1->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
|
||||
bsn_delta = (rlc->block_info[1].bsn - rlc->block_info[0].bsn) &
|
||||
(RLC_EGPRS_SNS - 1);
|
||||
|
||||
egprs1->bsn2_a = bsn_delta >> 0; /* 7 bits LSB */
|
||||
egprs1->bsn2_b = bsn_delta >> 7; /* 3 bits */
|
||||
egprs1->bsn2_hi = bsn_delta >> 0; /* 7 bits LSB */
|
||||
egprs1->bsn2_lo = bsn_delta >> 7; /* 3 bits */
|
||||
|
||||
/* first FBI/E header */
|
||||
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
|
||||
|
@ -783,14 +948,14 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
|
|||
egprs2->usf = rlc->usf;
|
||||
egprs2->es_p = rlc->es_p;
|
||||
egprs2->rrbp = rlc->rrbp;
|
||||
egprs2->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs2->tfi_b = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs2->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs2->tfi_lo = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs2->pr = rlc->pr;
|
||||
egprs2->cps = rlc->cps;
|
||||
|
||||
egprs2->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs2->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs2->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
egprs2->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs2->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs2->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
|
||||
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
|
||||
e_fbi_header |= rlc->block_info[0].cv == 0 ? 0x02 : 0; /* FBI */
|
||||
|
@ -807,14 +972,14 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
|
|||
egprs3->usf = rlc->usf;
|
||||
egprs3->es_p = rlc->es_p;
|
||||
egprs3->rrbp = rlc->rrbp;
|
||||
egprs3->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs3->tfi_b = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs3->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs3->tfi_lo = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs3->pr = rlc->pr;
|
||||
egprs3->cps = rlc->cps;
|
||||
|
||||
egprs3->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs3->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs3->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
egprs3->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs3->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs3->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
|
||||
egprs3->spb = rlc->block_info[0].spb;
|
||||
|
||||
|
@ -891,11 +1056,21 @@ unsigned int Encoding::rlc_copy_from_aligned_buffer(
|
|||
return rdbi->data_len;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief (GPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
|
||||
* \param rdbi rlc/mac block info
|
||||
* \param llc llc pdu
|
||||
* \param offset given offset within the rlc/mac block
|
||||
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
|
||||
* \param data_block buffer holds rlc/mac data
|
||||
* \param is_final if this is the last rlc/mac within a TBF
|
||||
* \param count_payload if not NULL save the written size of payload in bytes into it
|
||||
* \return the state of the rlc/mac like if there is more space for another chunk
|
||||
*/
|
||||
static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
||||
struct gprs_rlc_data_block_info *rdbi,
|
||||
gprs_llc *llc, int *offset, int *num_chunks,
|
||||
uint8_t *data_block,
|
||||
bool is_final)
|
||||
uint8_t *data_block, bool is_final, int *count_payload)
|
||||
{
|
||||
int chunk;
|
||||
int space;
|
||||
|
@ -920,6 +1095,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
*e_pointer |= 0x01;
|
||||
/* fill only space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
/* return data block as message */
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
|
@ -937,6 +1114,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
*e_pointer |= 0x01;
|
||||
/* fill space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
rdbi->cv = 0;
|
||||
|
@ -964,6 +1143,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
// no need to set e_pointer nor increase delimiter
|
||||
/* fill only space, which is 1 octet less than chunk */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
/* return data block as message */
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
|
@ -989,6 +1170,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
(*num_chunks)++;
|
||||
/* copy (rest of) LLC frame to space and reset later */
|
||||
llc->consume(data, chunk);
|
||||
if (count_payload)
|
||||
*count_payload = chunk;
|
||||
data += chunk;
|
||||
space -= chunk;
|
||||
(*offset) += chunk;
|
||||
|
@ -1012,11 +1195,22 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief (EGPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
|
||||
* \param rdbi rlc/mac block info
|
||||
* \param llc llc pdu
|
||||
* \param offset given offset within the rlc/mac block
|
||||
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
|
||||
* \param data_block buffer holds rlc/mac data
|
||||
* \param is_final if this is the last rlc/mac within a TBF
|
||||
* \param count_payload if not NULL save the written size of payload in bytes into it
|
||||
* \return the state of the rlc/mac like if there is more space for another chunk
|
||||
*/
|
||||
static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
||||
struct gprs_rlc_data_block_info *rdbi,
|
||||
gprs_llc *llc, int *offset, int *num_chunks,
|
||||
uint8_t *data_block,
|
||||
bool is_final)
|
||||
bool is_final, int *count_payload)
|
||||
{
|
||||
int chunk;
|
||||
int space;
|
||||
|
@ -1040,6 +1234,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
chunk, space);
|
||||
/* fill only space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
/* return data block as message */
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
|
@ -1054,6 +1250,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
"header, and we are done\n", chunk, space);
|
||||
/* fill space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
rdbi->cv = 0;
|
||||
|
@ -1068,6 +1266,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
chunk, space);
|
||||
/* fill space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
return Encoding::AR_NEED_MORE_BLOCKS;
|
||||
|
@ -1098,6 +1298,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
(*num_chunks)++;
|
||||
/* copy (rest of) LLC frame to space and reset later */
|
||||
llc->consume(data, chunk);
|
||||
if (count_payload)
|
||||
*count_payload = chunk;
|
||||
data += chunk;
|
||||
space -= chunk;
|
||||
(*offset) += chunk;
|
||||
|
@ -1154,19 +1356,32 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Encoding::rlc_data_to_dl_append
|
||||
* \param rdbi rlc/mac block info
|
||||
* \param cs the coding scheme to use
|
||||
* \param llc llc pdu
|
||||
* \param offset given offset within the rlc/mac block
|
||||
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
|
||||
* \param data_block buffer holds rlc/mac data
|
||||
* \param is_final if this is the last rlc/mac within a TBF
|
||||
* \param count_payload if not NULL save the written size of payload in bytes into it
|
||||
* \return the state of the rlc/mac like if there is more space for another chunk
|
||||
*/
|
||||
Encoding::AppendResult Encoding::rlc_data_to_dl_append(
|
||||
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
|
||||
gprs_llc *llc, int *offset, int *num_chunks,
|
||||
uint8_t *data_block,
|
||||
bool is_final)
|
||||
uint8_t *data_block, bool is_final, int *count_payload)
|
||||
{
|
||||
if (cs.isGprs())
|
||||
return rlc_data_to_dl_append_gprs(rdbi,
|
||||
llc, offset, num_chunks, data_block, is_final);
|
||||
llc, offset, num_chunks, data_block, is_final,
|
||||
count_payload);
|
||||
|
||||
if (cs.isEgprs())
|
||||
return rlc_data_to_dl_append_egprs(rdbi,
|
||||
llc, offset, num_chunks, data_block, is_final);
|
||||
llc, offset, num_chunks, data_block, is_final,
|
||||
count_payload);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_ERROR, "%s data block encoding not implemented\n",
|
||||
cs.name());
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
#include <stdint.h>
|
||||
#include <gsm_rlcmac.h>
|
||||
#include <gprs_coding_scheme.h>
|
||||
extern "C" {
|
||||
#include <osmocom/gsm/l1sap.h>
|
||||
}
|
||||
|
||||
struct gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_tbf;
|
||||
|
@ -40,11 +43,14 @@ class Encoding {
|
|||
public:
|
||||
static int write_immediate_assignment(
|
||||
struct gprs_rlcmac_tbf *tbf,
|
||||
bitvec * dest, uint8_t downlink, uint8_t ra,
|
||||
bitvec * dest, uint8_t downlink, uint16_t ra,
|
||||
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts,
|
||||
uint8_t tsc, uint8_t usf, uint8_t polling,
|
||||
uint32_t fn, uint8_t alpha, uint8_t gamma,
|
||||
int8_t ta_idx);
|
||||
int8_t ta_idx,
|
||||
enum ph_burst_type burst_type =
|
||||
GSM_L1_BURST_TYPE_ACCESS_0,
|
||||
uint8_t sb = 1);
|
||||
|
||||
static void write_packet_uplink_assignment(
|
||||
struct gprs_rlcmac_bts *bts,
|
||||
|
@ -91,6 +97,5 @@ public:
|
|||
static AppendResult rlc_data_to_dl_append(
|
||||
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
|
||||
gprs_llc *llc, int *offset, int *num_chunks,
|
||||
uint8_t *data,
|
||||
bool is_final);
|
||||
uint8_t *data, bool is_final, int *count_payload);
|
||||
};
|
||||
|
|
|
@ -21,6 +21,38 @@
|
|||
|
||||
#include "gprs_coding_scheme.h"
|
||||
|
||||
/*
|
||||
* 44.060 Table 8.1.1.1 and Table 8.1.1.2
|
||||
* It has 3 level indexing. 0th level is ARQ type
|
||||
* 1st level is Original MCS( index 0 corresponds to MCS1 and so on)
|
||||
* 2nd level is MS MCS (index 0 corresponds to MCS1 and so on)
|
||||
*/
|
||||
enum GprsCodingScheme::Scheme GprsCodingScheme::egprs_mcs_retx_tbl[MAX_NUM_ARQ]
|
||||
[MAX_NUM_MCS][MAX_NUM_MCS] = {
|
||||
{
|
||||
{MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1},
|
||||
{MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3},
|
||||
{MCS1, MCS1, MCS1, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4},
|
||||
{MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9},
|
||||
{MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS8, MCS8},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9}
|
||||
},
|
||||
{
|
||||
{MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1},
|
||||
{MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3},
|
||||
{MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4},
|
||||
{MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7},
|
||||
{MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9},
|
||||
{MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7},
|
||||
{MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS8, MCS8},
|
||||
{MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9}
|
||||
}
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
unsigned int bytes;
|
||||
|
|
|
@ -26,6 +26,12 @@
|
|||
|
||||
class GprsCodingScheme {
|
||||
public:
|
||||
|
||||
#define MAX_NUM_ARQ 2 /* max. number of ARQ */
|
||||
#define MAX_NUM_MCS 9 /* max. number of MCS */
|
||||
#define EGPRS_ARQ1 0x0
|
||||
#define EGPRS_ARQ2 0x1
|
||||
|
||||
enum Scheme {
|
||||
UNKNOWN,
|
||||
CS1, CS2, CS3, CS4,
|
||||
|
@ -64,6 +70,7 @@ public:
|
|||
unsigned int to_num() const;
|
||||
|
||||
GprsCodingScheme& operator =(Scheme s);
|
||||
bool operator == (Scheme s) const;
|
||||
GprsCodingScheme& operator =(GprsCodingScheme o);
|
||||
|
||||
bool isValid() const {return UNKNOWN <= m_scheme && m_scheme <= MCS9;}
|
||||
|
@ -105,6 +112,12 @@ public:
|
|||
static GprsCodingScheme getEgprsByNum(unsigned num);
|
||||
|
||||
static const char *modeName(Mode mode);
|
||||
static Scheme get_retx_mcs(const GprsCodingScheme mcs,
|
||||
const GprsCodingScheme retx_mcs,
|
||||
const unsigned arq_type);
|
||||
|
||||
static enum Scheme egprs_mcs_retx_tbl[MAX_NUM_ARQ]
|
||||
[MAX_NUM_MCS][MAX_NUM_MCS];
|
||||
private:
|
||||
GprsCodingScheme(int s); /* fail on use */
|
||||
GprsCodingScheme& operator =(int s); /* fail on use */
|
||||
|
@ -188,6 +201,11 @@ inline bool operator ==(GprsCodingScheme a, GprsCodingScheme b)
|
|||
return GprsCodingScheme::Scheme(a) == GprsCodingScheme::Scheme(b);
|
||||
}
|
||||
|
||||
inline bool GprsCodingScheme::operator == (Scheme scheme) const
|
||||
{
|
||||
return this->m_scheme == scheme;
|
||||
}
|
||||
|
||||
inline bool operator !=(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return !(a == b);
|
||||
|
@ -213,4 +231,11 @@ inline bool operator >=(GprsCodingScheme a, GprsCodingScheme b)
|
|||
{
|
||||
return a == b || a > b;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme::Scheme GprsCodingScheme::get_retx_mcs(
|
||||
const GprsCodingScheme mcs,
|
||||
const GprsCodingScheme demanded_mcs,
|
||||
const unsigned arq_type)
|
||||
{
|
||||
return egprs_mcs_retx_tbl[arq_type][mcs.to_num() - 1]
|
||||
[demanded_mcs.to_num() - 1];
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
}
|
||||
|
||||
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
|
||||
|
@ -95,7 +96,7 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
|
|||
m_tlli(tlli),
|
||||
m_new_ul_tlli(0),
|
||||
m_new_dl_tlli(0),
|
||||
m_ta(0),
|
||||
m_ta(GSM48_TA_INVALID),
|
||||
m_ms_class(0),
|
||||
m_egprs_ms_class(0),
|
||||
m_is_idle(true),
|
||||
|
@ -237,7 +238,7 @@ void GprsMs::set_mode(GprsCodingScheme::Mode mode)
|
|||
if (!m_current_cs_ul.isEgprs()) {
|
||||
m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
|
||||
m_bts->bts_data()->initial_mcs_ul);
|
||||
if (!m_current_cs_dl.isValid())
|
||||
if (!m_current_cs_ul.isValid())
|
||||
m_current_cs_ul = GprsCodingScheme::MCS1;
|
||||
}
|
||||
if (!m_current_cs_dl.isEgprs()) {
|
||||
|
@ -464,11 +465,15 @@ void GprsMs::set_ta(uint8_t ta_)
|
|||
if (ta_ == m_ta)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
|
||||
tlli(), m_ta, ta_);
|
||||
|
||||
m_ta = ta_;
|
||||
if (gsm48_ta_is_valid(ta_)) {
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
|
||||
tlli(), m_ta, ta_);
|
||||
m_ta = ta_;
|
||||
} else
|
||||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
|
||||
"value %d kept)\n", tlli(), ta_, m_ta);
|
||||
}
|
||||
|
||||
void GprsMs::set_ms_class(uint8_t ms_class_)
|
||||
|
@ -574,6 +579,11 @@ GprsCodingScheme GprsMs::max_cs_ul() const
|
|||
return GprsCodingScheme(GprsCodingScheme::MCS4);
|
||||
}
|
||||
|
||||
void GprsMs::set_current_cs_dl(GprsCodingScheme::Scheme scheme)
|
||||
{
|
||||
m_current_cs_dl = scheme;
|
||||
}
|
||||
|
||||
GprsCodingScheme GprsMs::max_cs_dl() const
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
|
@ -632,8 +642,8 @@ void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
|
|||
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
|
||||
} else if (m_current_cs_ul.isEgprs()) {
|
||||
/* TODO, use separate table */
|
||||
if (current_cs_num > 4)
|
||||
current_cs_num = 4;
|
||||
if (current_cs_num > MAX_GPRS_CS)
|
||||
current_cs_num = MAX_GPRS_CS;
|
||||
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
|
||||
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
|
||||
} else {
|
||||
|
|
|
@ -86,6 +86,7 @@ public:
|
|||
uint8_t egprs_ms_class() const;
|
||||
void set_ms_class(uint8_t ms_class);
|
||||
void set_egprs_ms_class(uint8_t ms_class);
|
||||
void set_current_cs_dl(GprsCodingScheme::Scheme scheme);
|
||||
|
||||
GprsCodingScheme current_cs_ul() const;
|
||||
GprsCodingScheme current_cs_dl() const;
|
||||
|
|
|
@ -90,7 +90,7 @@ int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
|
|||
const char *imsi);
|
||||
|
||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||
uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint8_t trx, uint8_t ts,
|
||||
uint32_t fn, uint8_t block_nr);
|
||||
|
||||
int gprs_alloc_max_dl_slots_per_ms(struct gprs_rlcmac_bts *bts,
|
||||
|
|
|
@ -132,7 +132,7 @@ static struct msgb *sched_select_ctrl_msg(
|
|||
|
||||
/*
|
||||
* Assignments for the same direction have lower precedence,
|
||||
* because they may kill the TBF when the CONTOL ACK is
|
||||
* because they may kill the TBF when the CONTROL ACK is
|
||||
* received, thus preventing the others from being processed.
|
||||
*/
|
||||
|
||||
|
@ -289,7 +289,7 @@ static struct msgb *sched_dummy(void)
|
|||
}
|
||||
|
||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||
uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint8_t trx, uint8_t ts,
|
||||
uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
|
@ -300,8 +300,6 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
|||
struct msgb *msg = NULL;
|
||||
uint32_t poll_fn, sba_fn;
|
||||
|
||||
#warning "ARFCN... it is already in the TRX..... is it consistent with it?"
|
||||
|
||||
if (trx >= 8 || ts >= 8)
|
||||
return -EINVAL;
|
||||
pdch = &bts->trx[trx].pdch[ts];
|
||||
|
@ -339,18 +337,28 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
|||
/* Prio 1: select control message */
|
||||
msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
|
||||
dl_ass_tbf, ul_ack_tbf);
|
||||
if (msg)
|
||||
bts->bts->rlc_sent_control();
|
||||
|
||||
/* Prio 2: select data message for downlink */
|
||||
if (!msg)
|
||||
if (!msg) {
|
||||
msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
|
||||
if (msg)
|
||||
bts->bts->rlc_sent();
|
||||
}
|
||||
|
||||
/* Prio 3: send dummy contol message */
|
||||
if (!msg)
|
||||
if (!msg) {
|
||||
/* increase counter */
|
||||
msg = sched_dummy();
|
||||
if (msg)
|
||||
bts->bts->rlc_sent_dummy();
|
||||
}
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
/* msg is now available */
|
||||
bts->bts->rlc_dl_bytes(msg->data_len);
|
||||
|
||||
/* set USF */
|
||||
OSMO_ASSERT(msgb_length(msg) > 0);
|
||||
|
@ -360,7 +368,7 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
|||
gprs_bssgp_update_frames_sent();
|
||||
|
||||
/* send PDTCH/PACCH to L1 */
|
||||
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
|
||||
pcu_l1if_tx_pdtch(msg, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -775,9 +775,13 @@ static int find_multi_slots(struct gprs_rlcmac_bts *bts,
|
|||
rx_window & tx_window, 'C'),
|
||||
capacity);
|
||||
#endif
|
||||
|
||||
if (capacity <= max_capacity)
|
||||
continue;
|
||||
if (bts->maximise_dir == DL_ONLY) {
|
||||
if (rx_window < max_dl_slots)
|
||||
continue;
|
||||
} else {
|
||||
if (capacity <= max_capacity)
|
||||
continue;
|
||||
}
|
||||
|
||||
max_capacity = capacity;
|
||||
max_ul_slots = tx_window;
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/* Interface handler for Nuran Wireless Litecell 1.5 L1 (real hardware) */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* femto_l1_hw.c
|
||||
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <nrw/litecell15/litecell15.h>
|
||||
#include <nrw/litecell15/gsml1prim.h>
|
||||
#include <nrw/litecell15/gsml1const.h>
|
||||
#include <nrw/litecell15/gsml1types.h>
|
||||
|
||||
#include "gprs_debug.h"
|
||||
#include "lc15bts.h"
|
||||
#include "lc15_l1_if.h"
|
||||
|
||||
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/litecell15_dsp2arm_trx"
|
||||
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/litecell15_arm2dsp_trx"
|
||||
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm_trx"
|
||||
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp_trx"
|
||||
|
||||
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm_trx"
|
||||
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp_trx"
|
||||
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm_trx"
|
||||
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp_trx"
|
||||
|
||||
static const char *rd_devnames[] = {
|
||||
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
|
||||
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
|
||||
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
|
||||
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
|
||||
};
|
||||
|
||||
static const char *wr_devnames[] = {
|
||||
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
|
||||
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
|
||||
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
|
||||
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
|
||||
};
|
||||
|
||||
/* callback when there's something to read from the l1 msg_queue */
|
||||
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
//struct msgb *msg = l1p_msgb_alloc();
|
||||
struct msgb *msg = msgb_alloc_headroom(sizeof(Litecell15_Prim_t) + 128,
|
||||
128, "1l_fd");
|
||||
struct lc15l1_hdl *fl1h = ofd->data;
|
||||
int rc;
|
||||
|
||||
msg->l1h = msg->data;
|
||||
rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
|
||||
if (rc < 0) {
|
||||
if (rc != -1)
|
||||
LOGP(DL1IF, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
|
||||
switch (ofd->priv_nr) {
|
||||
case MQ_SYS_WRITE:
|
||||
if (rc != sizeof(Litecell15_Prim_t))
|
||||
LOGP(DL1IF, LOGL_NOTICE, "%u != "
|
||||
"sizeof(Litecell15_Prim_t)\n", rc);
|
||||
return l1if_handle_sysprim(fl1h, msg);
|
||||
case MQ_L1_WRITE:
|
||||
case MQ_TCH_WRITE:
|
||||
case MQ_PDTCH_WRITE:
|
||||
if (rc != sizeof(GsmL1_Prim_t))
|
||||
LOGP(DL1IF, LOGL_NOTICE, "%u != "
|
||||
"sizeof(GsmL1_Prim_t)\n", rc);
|
||||
return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
|
||||
default:
|
||||
/* The compiler can't know that priv_nr is an enum. Assist. */
|
||||
LOGP(DL1IF, LOGL_FATAL, "writing on a wrong queue: %d\n",
|
||||
ofd->priv_nr);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/* callback when we can write to one of the l1 msg_queue devices */
|
||||
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = write(ofd->fd, msg->l1h, msgb_l1len(msg));
|
||||
if (rc < 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
return rc;
|
||||
} else if (rc < msg->len) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "short write to L1 msg_queue: "
|
||||
"%u < %u\n", rc, msg->len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
|
||||
{
|
||||
int rc;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
/* Step 1: Open all msg_queue file descriptors */
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_wqueue *wq = &hdl->write_q[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%d", rd_devnames[q], hdl->hw_info.trx_nr);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
rc = open(buf, O_RDONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||
buf, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
read_ofd->fd = rc;
|
||||
read_ofd->priv_nr = q;
|
||||
read_ofd->data = hdl;
|
||||
read_ofd->cb = l1if_fd_cb;
|
||||
read_ofd->when = BSC_FD_READ;
|
||||
rc = osmo_fd_register(read_ofd);
|
||||
if (rc < 0) {
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%d", wr_devnames[q], hdl->hw_info.trx_nr);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
rc = open(buf, O_WRONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||
buf, strerror(errno));
|
||||
goto out_read;
|
||||
}
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = l1fd_write_cb;
|
||||
write_ofd->fd = rc;
|
||||
write_ofd->priv_nr = q;
|
||||
write_ofd->data = hdl;
|
||||
write_ofd->when = BSC_FD_WRITE;
|
||||
rc = osmo_fd_register(write_ofd);
|
||||
if (rc < 0) {
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
goto out_read;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_read:
|
||||
close(hdl->read_ofd[q].fd);
|
||||
osmo_fd_unregister(&hdl->read_ofd[q]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int l1if_transport_close(int q, struct lc15l1_hdl *hdl)
|
||||
{
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
osmo_fd_unregister(read_ofd);
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
|
||||
osmo_fd_unregister(write_ofd);
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,393 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* femto_l1_if.c
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <nrw/litecell15/litecell15.h>
|
||||
#include <nrw/litecell15/gsml1prim.h>
|
||||
#include <nrw/litecell15/gsml1const.h>
|
||||
#include <nrw/litecell15/gsml1types.h>
|
||||
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <lc15_l1_if.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <pcu_l1_if.h>
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
uint32_t l1if_ts_to_hLayer2(uint8_t trx, uint8_t ts)
|
||||
{
|
||||
return (ts << 16) | (trx << 24);
|
||||
}
|
||||
|
||||
/* allocate a msgb containing a GsmL1_Prim_t */
|
||||
struct msgb *l1p_msgb_alloc(void)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(sizeof(GsmL1_Prim_t), "l1_prim");
|
||||
|
||||
if (msg)
|
||||
msg->l1h = msgb_put(msg, sizeof(GsmL1_Prim_t));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int l1if_req_pdch(struct lc15l1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
struct osmo_wqueue *wqueue = &fl1h->write_q[MQ_PDTCH_WRITE];
|
||||
|
||||
if (osmo_wqueue_enqueue(wqueue, msg) != 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct lc15l1_hdl *gl1)
|
||||
{
|
||||
prim->id = id;
|
||||
|
||||
switch (id) {
|
||||
case GsmL1_PrimId_MphInitReq:
|
||||
//prim->u.mphInitReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphCloseReq:
|
||||
prim->u.mphCloseReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphConnectReq:
|
||||
prim->u.mphConnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphDisconnectReq:
|
||||
prim->u.mphDisconnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphActivateReq:
|
||||
prim->u.mphActivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphDeactivateReq:
|
||||
prim->u.mphDeactivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphConfigReq:
|
||||
prim->u.mphConfigReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphMeasureReq:
|
||||
prim->u.mphMeasureReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphInitCnf:
|
||||
case GsmL1_PrimId_MphCloseCnf:
|
||||
case GsmL1_PrimId_MphConnectCnf:
|
||||
case GsmL1_PrimId_MphDisconnectCnf:
|
||||
case GsmL1_PrimId_MphActivateCnf:
|
||||
case GsmL1_PrimId_MphDeactivateCnf:
|
||||
case GsmL1_PrimId_MphConfigCnf:
|
||||
case GsmL1_PrimId_MphMeasureCnf:
|
||||
break;
|
||||
case GsmL1_PrimId_MphTimeInd:
|
||||
break;
|
||||
case GsmL1_PrimId_MphSyncInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhEmptyFrameReq:
|
||||
prim->u.phEmptyFrameReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_PhDataReq:
|
||||
prim->u.phDataReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_PhConnectInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhReadyToSendInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhDataInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhRaInd:
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_ERROR, "unknown L1 primitive %u\n", id);
|
||||
break;
|
||||
}
|
||||
return &prim->u;
|
||||
}
|
||||
|
||||
/* connect PDTCH */
|
||||
int l1if_connect_pdch(void *obj, uint8_t ts)
|
||||
{
|
||||
struct lc15l1_hdl *fl1h = obj;
|
||||
struct msgb *msg = l1p_msgb_alloc();
|
||||
GsmL1_MphConnectReq_t *cr;
|
||||
|
||||
cr = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConnectReq, fl1h);
|
||||
cr->u8Tn = ts;
|
||||
cr->logChComb = GsmL1_LogChComb_XIII;
|
||||
|
||||
return l1if_req_pdch(fl1h, msg);
|
||||
}
|
||||
|
||||
static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1h,
|
||||
GsmL1_PhReadyToSendInd_t *rts_ind)
|
||||
{
|
||||
struct gsm_time g_time;
|
||||
int rc = 0;
|
||||
|
||||
gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-RTS.ind %02u/%02u/%02u SAPI=%s\n",
|
||||
g_time.t1, g_time.t2, g_time.t3,
|
||||
get_value_string(lc15bts_l1sapi_names, rts_ind->sapi));
|
||||
|
||||
switch (rts_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
|
||||
rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void get_meas(struct pcu_l1_meas *meas, const GsmL1_MeasParam_t *l1_meas)
|
||||
{
|
||||
meas->rssi = (int8_t) (l1_meas->fRssi);
|
||||
meas->have_rssi = 1;
|
||||
meas->ber = (uint8_t) (l1_meas->fBer * 100);
|
||||
meas->have_ber = 1;
|
||||
meas->bto = (int16_t) (l1_meas->i16BurstTiming);
|
||||
meas->have_bto = 1;
|
||||
meas->link_qual = (int16_t) (l1_meas->fLinkQuality);
|
||||
meas->have_link_qual = 1;
|
||||
}
|
||||
|
||||
static int handle_ph_data_ind(struct lc15l1_hdl *fl1h,
|
||||
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct pcu_l1_meas meas = {0};
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s\n",
|
||||
get_value_string(lc15bts_l1sapi_names, data_ind->sapi),
|
||||
data_ind->hLayer2,
|
||||
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
|
||||
data_ind->msgUnitParam.u8Size));
|
||||
|
||||
/*
|
||||
* TODO: Add proper bad frame handling here. This could be used
|
||||
* to switch the used CS. Avoid a crash with the PCU right now
|
||||
* feed "0 - 1" amount of data.
|
||||
*/
|
||||
if (data_ind->msgUnitParam.u8Size == 0)
|
||||
return -1;
|
||||
|
||||
gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
|
||||
data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
|
||||
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
|
||||
data_ind->msgUnitParam.u8Size-1);
|
||||
|
||||
get_meas(&meas, &data_ind->measParam);
|
||||
|
||||
switch (data_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
/* drop incomplete UL block */
|
||||
if (data_ind->msgUnitParam.u8Buffer[0]
|
||||
!= GsmL1_PdtchPlType_Full)
|
||||
break;
|
||||
/* PDTCH / PACCH frame handling */
|
||||
pcu_rx_data_ind_pdtch(fl1h->trx_no, data_ind->u8Tn,
|
||||
data_ind->msgUnitParam.u8Buffer + 1,
|
||||
data_ind->msgUnitParam.u8Size - 1,
|
||||
data_ind->u32Fn,
|
||||
&meas);
|
||||
break;
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
|
||||
get_value_string(lc15bts_l1sapi_names, data_ind->sapi));
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define MIN_QUAL_RACH 5.0f
|
||||
|
||||
static int handle_ph_ra_ind(struct lc15l1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
|
||||
{
|
||||
uint8_t acc_delay;
|
||||
|
||||
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
|
||||
return 0;
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-RA.ind");
|
||||
|
||||
/* check for under/overflow / sign */
|
||||
if (ra_ind->measParam.i16BurstTiming < 0)
|
||||
acc_delay = 0;
|
||||
else
|
||||
acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
|
||||
|
||||
LOGP(DL1IF, LOGL_NOTICE, "got (P)RACH request, TA = %u (ignored)\n",
|
||||
acc_delay);
|
||||
|
||||
#warning "The (P)RACH request is just dropped here"
|
||||
|
||||
#if 0
|
||||
if (acc_delay > bts->max_ta) {
|
||||
LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
|
||||
acc_delay, btsb->max_ta);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* handle any random indication from the L1 */
|
||||
int l1if_handle_l1prim(int wq, struct lc15l1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Rx L1 prim %s on queue %d\n",
|
||||
get_value_string(lc15bts_l1prim_names, l1p->id), wq);
|
||||
|
||||
switch (l1p->id) {
|
||||
#if 0
|
||||
case GsmL1_PrimId_MphTimeInd:
|
||||
rc = handle_mph_time_ind(fl1h, &l1p->u.mphTimeInd);
|
||||
break;
|
||||
case GsmL1_PrimId_MphSyncInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhConnectInd:
|
||||
break;
|
||||
#endif
|
||||
case GsmL1_PrimId_PhReadyToSendInd:
|
||||
rc = handle_ph_readytosend_ind(fl1h, &l1p->u.phReadyToSendInd);
|
||||
break;
|
||||
case GsmL1_PrimId_PhDataInd:
|
||||
rc = handle_ph_data_ind(fl1h, &l1p->u.phDataInd, msg);
|
||||
break;
|
||||
case GsmL1_PrimId_PhRaInd:
|
||||
rc = handle_ph_ra_ind(fl1h, &l1p->u.phRaInd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* send packet data request to L1 */
|
||||
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
|
||||
{
|
||||
struct lc15l1_hdl *fl1h = obj;
|
||||
struct msgb *msg;
|
||||
GsmL1_Prim_t *l1p;
|
||||
GsmL1_PhDataReq_t *data_req;
|
||||
GsmL1_MsgUnitParam_t *msu_param;
|
||||
struct gsm_time g_time;
|
||||
|
||||
gsm_fn2gsmtime(&g_time, fn);
|
||||
|
||||
DEBUGP(DL1IF, "TX packet data %02u/%02u/%02u is_ptcch=%d ts=%d "
|
||||
"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
|
||||
g_time.t3, is_ptcch, ts, block_nr, arfcn, len);
|
||||
|
||||
msg = l1p_msgb_alloc();
|
||||
l1p = msgb_l1prim(msg);
|
||||
l1p->id = GsmL1_PrimId_PhDataReq;
|
||||
data_req = &l1p->u.phDataReq;
|
||||
data_req->hLayer1 = (HANDLE)fl1h->hLayer1;
|
||||
data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
|
||||
data_req->subCh = GsmL1_SubCh_NA;
|
||||
data_req->u8BlockNbr = block_nr;
|
||||
data_req->u8Tn = ts;
|
||||
data_req->u32Fn = fn;
|
||||
msu_param = &data_req->msgUnitParam;
|
||||
msu_param->u8Size = len;
|
||||
memcpy(msu_param->u8Buffer, data, len);
|
||||
|
||||
gsmtap_send(fl1h->gsmtap, arfcn, data_req->u8Tn, GSMTAP_CHANNEL_PACCH,
|
||||
0, data_req->u32Fn, 0, 0,
|
||||
data_req->msgUnitParam.u8Buffer,
|
||||
data_req->msgUnitParam.u8Size);
|
||||
|
||||
|
||||
/* transmit */
|
||||
if (osmo_wqueue_enqueue(&fl1h->write_q[MQ_PDTCH_WRITE], msg) != 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1)
|
||||
{
|
||||
struct lc15l1_hdl *fl1h;
|
||||
int rc;
|
||||
|
||||
fl1h = talloc_zero(tall_pcu_ctx, struct lc15l1_hdl);
|
||||
if (!fl1h)
|
||||
return NULL;
|
||||
|
||||
fl1h->hLayer1 = hlayer1;
|
||||
fl1h->trx_no = trx_no;
|
||||
/* hardware queues are numbered starting from 0 */
|
||||
fl1h->hw_info.trx_nr = trx_no;
|
||||
|
||||
DEBUGP(DL1IF, "PCU: Using TRX HW#%u\n", fl1h->hw_info.trx_nr);
|
||||
|
||||
rc = l1if_transport_open(MQ_PDTCH_WRITE, fl1h);
|
||||
if (rc < 0) {
|
||||
talloc_free(fl1h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
|
||||
if (fl1h->gsmtap)
|
||||
gsmtap_source_add_sink(fl1h->gsmtap);
|
||||
|
||||
return fl1h;
|
||||
}
|
||||
|
||||
int l1if_close_pdch(void *obj)
|
||||
{
|
||||
struct lc15l1_hdl *fl1h = obj;
|
||||
if (fl1h)
|
||||
l1if_transport_close(MQ_PDTCH_WRITE, fl1h);
|
||||
talloc_free(fl1h);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* femto_l1_if.h
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LC15_L1_IF_H
|
||||
#define _LC15_L1_IF_H
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include "lc15bts.h"
|
||||
|
||||
enum {
|
||||
MQ_SYS_READ,
|
||||
MQ_L1_READ,
|
||||
MQ_TCH_READ,
|
||||
MQ_PDTCH_READ,
|
||||
_NUM_MQ_READ
|
||||
};
|
||||
|
||||
enum {
|
||||
MQ_SYS_WRITE,
|
||||
MQ_L1_WRITE,
|
||||
MQ_TCH_WRITE,
|
||||
MQ_PDTCH_WRITE,
|
||||
_NUM_MQ_WRITE
|
||||
};
|
||||
|
||||
struct lc15l1_hdl {
|
||||
struct gsm_time gsm_time;
|
||||
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
|
||||
uint32_t dsp_trace_f;
|
||||
struct llist_head wlc_list;
|
||||
|
||||
struct gsmtap_inst *gsmtap;
|
||||
uint32_t gsmtap_sapi_mask;
|
||||
|
||||
uint8_t trx_no;
|
||||
|
||||
struct osmo_timer_list alive_timer;
|
||||
unsigned int alive_prim_cnt;
|
||||
|
||||
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
|
||||
struct osmo_wqueue write_q[_NUM_MQ_WRITE];
|
||||
|
||||
struct {
|
||||
int trx_nr; /* <1-2> */
|
||||
} hw_info;
|
||||
};
|
||||
|
||||
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
|
||||
#define msgb_sysprim(msg) ((Litecell15_Prim_t *)(msg)->l1h)
|
||||
|
||||
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data);
|
||||
|
||||
/* send a request primitive to the L1 and schedule completion call-back */
|
||||
int l1if_req_compl(struct lc15l1_hdl *fl1h, struct msgb *msg,
|
||||
int is_system_prim, l1if_compl_cb *cb, void *data);
|
||||
|
||||
int l1if_reset(struct lc15l1_hdl *hdl);
|
||||
int l1if_activate_rf(struct lc15l1_hdl *hdl, int on);
|
||||
int l1if_set_trace_flags(struct lc15l1_hdl *hdl, uint32_t flags);
|
||||
int l1if_set_txpower(struct lc15l1_hdl *fl1h, float tx_power);
|
||||
|
||||
struct msgb *l1p_msgb_alloc(void);
|
||||
struct msgb *sysp_msgb_alloc(void);
|
||||
|
||||
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan);
|
||||
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2);
|
||||
|
||||
int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg);
|
||||
int l1if_handle_l1prim(int wq, struct lc15l1_hdl *fl1h, struct msgb *msg);
|
||||
|
||||
/* tch.c */
|
||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
|
||||
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
|
||||
|
||||
/*
|
||||
* The implementation of these functions is selected by either compiling and
|
||||
* linking sysmo_l1_hw.c or sysmo_l1_fwd.c
|
||||
*/
|
||||
int l1if_transport_open(int q, struct lc15l1_hdl *hdl);
|
||||
int l1if_transport_close(int q, struct lc15l1_hdl *hdl);
|
||||
|
||||
#endif /* _SYSMO_L1_IF_H */
|
|
@ -0,0 +1,332 @@
|
|||
/* NuRAN Wireless Litecell 1.5 L1 API related definitions */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* sysmobts.c
|
||||
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <nrw/litecell15/litecell15.h>
|
||||
#include <nrw/litecell15/gsml1const.h>
|
||||
#include <nrw/litecell15/gsml1dbg.h>
|
||||
|
||||
#include "lc15bts.h"
|
||||
|
||||
enum l1prim_type lc15bts_get_l1prim_type(GsmL1_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case GsmL1_PrimId_MphInitReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphCloseReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphConnectReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphDisconnectReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphActivateReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphDeactivateReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphConfigReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphMeasureReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphInitCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphCloseCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphConnectCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphDisconnectCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphActivateCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphDeactivateCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphConfigCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphMeasureCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_PhEmptyFrameReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_PhDataReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphTimeInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_MphSyncInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhConnectInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhReadyToSendInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhDataInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhRaInd: return L1P_T_IND;
|
||||
default: return L1P_T_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1] = {
|
||||
{ GsmL1_PrimId_MphInitReq, "MPH-INIT.req" },
|
||||
{ GsmL1_PrimId_MphCloseReq, "MPH-CLOSE.req" },
|
||||
{ GsmL1_PrimId_MphConnectReq, "MPH-CONNECT.req" },
|
||||
{ GsmL1_PrimId_MphDisconnectReq,"MPH-DISCONNECT.req" },
|
||||
{ GsmL1_PrimId_MphActivateReq, "MPH-ACTIVATE.req" },
|
||||
{ GsmL1_PrimId_MphDeactivateReq,"MPH-DEACTIVATE.req" },
|
||||
{ GsmL1_PrimId_MphConfigReq, "MPH-CONFIG.req" },
|
||||
{ GsmL1_PrimId_MphMeasureReq, "MPH-MEASURE.req" },
|
||||
{ GsmL1_PrimId_MphInitCnf, "MPH-INIT.conf" },
|
||||
{ GsmL1_PrimId_MphCloseCnf, "MPH-CLOSE.conf" },
|
||||
{ GsmL1_PrimId_MphConnectCnf, "MPH-CONNECT.conf" },
|
||||
{ GsmL1_PrimId_MphDisconnectCnf,"MPH-DISCONNECT.conf" },
|
||||
{ GsmL1_PrimId_MphActivateCnf, "MPH-ACTIVATE.conf" },
|
||||
{ GsmL1_PrimId_MphDeactivateCnf,"MPH-DEACTIVATE.conf" },
|
||||
{ GsmL1_PrimId_MphConfigCnf, "MPH-CONFIG.conf" },
|
||||
{ GsmL1_PrimId_MphMeasureCnf, "MPH-MEASURE.conf" },
|
||||
{ GsmL1_PrimId_MphTimeInd, "MPH-TIME.ind" },
|
||||
{ GsmL1_PrimId_MphSyncInd, "MPH-SYNC.ind" },
|
||||
{ GsmL1_PrimId_PhEmptyFrameReq, "PH-EMPTY_FRAME.req" },
|
||||
{ GsmL1_PrimId_PhDataReq, "PH-DATA.req" },
|
||||
{ GsmL1_PrimId_PhConnectInd, "PH-CONNECT.ind" },
|
||||
{ GsmL1_PrimId_PhReadyToSendInd,"PH-READY_TO_SEND.ind" },
|
||||
{ GsmL1_PrimId_PhDataInd, "PH-DATA.ind" },
|
||||
{ GsmL1_PrimId_PhRaInd, "PH-RA.ind" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
GsmL1_PrimId_t lc15bts_get_l1prim_conf(GsmL1_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case GsmL1_PrimId_MphInitReq: return GsmL1_PrimId_MphInitCnf;
|
||||
case GsmL1_PrimId_MphCloseReq: return GsmL1_PrimId_MphCloseCnf;
|
||||
case GsmL1_PrimId_MphConnectReq: return GsmL1_PrimId_MphConnectCnf;
|
||||
case GsmL1_PrimId_MphDisconnectReq: return GsmL1_PrimId_MphDisconnectCnf;
|
||||
case GsmL1_PrimId_MphActivateReq: return GsmL1_PrimId_MphActivateCnf;
|
||||
case GsmL1_PrimId_MphDeactivateReq: return GsmL1_PrimId_MphDeactivateCnf;
|
||||
case GsmL1_PrimId_MphConfigReq: return GsmL1_PrimId_MphConfigCnf;
|
||||
case GsmL1_PrimId_MphMeasureReq: return GsmL1_PrimId_MphMeasureCnf;
|
||||
default: return -1; // Weak
|
||||
}
|
||||
}
|
||||
|
||||
enum l1prim_type lc15bts_get_sysprim_type(Litecell15_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case Litecell15_PrimId_SystemInfoReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_SystemInfoCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_SystemFailureInd: return L1P_T_IND;
|
||||
case Litecell15_PrimId_ActivateRfReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_ActivateRfCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_DeactivateRfReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_DeactivateRfCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_SetTraceFlagsReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_Layer1ResetReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_Layer1ResetCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_SetCalibTblReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_SetCalibTblCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_MuteRfReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_MuteRfCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_SetRxAttenReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_SetRxAttenCnf: return L1P_T_CONF;
|
||||
default: return L1P_T_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1] = {
|
||||
{ Litecell15_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
|
||||
{ Litecell15_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
|
||||
{ Litecell15_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
|
||||
{ Litecell15_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
|
||||
{ Litecell15_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
|
||||
{ Litecell15_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
|
||||
{ Litecell15_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
|
||||
{ Litecell15_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
|
||||
{ Litecell15_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
|
||||
{ Litecell15_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
|
||||
{ Litecell15_PrimId_SetCalibTblReq, "SET-CALIB.req" },
|
||||
{ Litecell15_PrimId_SetCalibTblCnf, "SET-CALIB.cnf" },
|
||||
{ Litecell15_PrimId_MuteRfReq, "MUTE-RF.req" },
|
||||
{ Litecell15_PrimId_MuteRfCnf, "MUTE-RF.cnf" },
|
||||
{ Litecell15_PrimId_SetRxAttenReq, "SET-RX-ATTEN.req" },
|
||||
{ Litecell15_PrimId_SetRxAttenCnf, "SET-RX-ATTEN-CNF.cnf" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
Litecell15_PrimId_t lc15bts_get_sysprim_conf(Litecell15_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case Litecell15_PrimId_SystemInfoReq: return Litecell15_PrimId_SystemInfoCnf;
|
||||
case Litecell15_PrimId_ActivateRfReq: return Litecell15_PrimId_ActivateRfCnf;
|
||||
case Litecell15_PrimId_DeactivateRfReq: return Litecell15_PrimId_DeactivateRfCnf;
|
||||
case Litecell15_PrimId_Layer1ResetReq: return Litecell15_PrimId_Layer1ResetCnf;
|
||||
case Litecell15_PrimId_SetCalibTblReq: return Litecell15_PrimId_SetCalibTblCnf;
|
||||
case Litecell15_PrimId_MuteRfReq: return Litecell15_PrimId_MuteRfCnf;
|
||||
case Litecell15_PrimId_SetRxAttenReq: return Litecell15_PrimId_SetRxAttenCnf;
|
||||
default: return -1; // Weak
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
|
||||
{ GsmL1_Sapi_Idle, "IDLE" },
|
||||
{ GsmL1_Sapi_Fcch, "FCCH" },
|
||||
{ GsmL1_Sapi_Sch, "SCH" },
|
||||
{ GsmL1_Sapi_Sacch, "SACCH" },
|
||||
{ GsmL1_Sapi_Sdcch, "SDCCH" },
|
||||
{ GsmL1_Sapi_Bcch, "BCCH" },
|
||||
{ GsmL1_Sapi_Pch, "PCH" },
|
||||
{ GsmL1_Sapi_Agch, "AGCH" },
|
||||
{ GsmL1_Sapi_Cbch, "CBCH" },
|
||||
{ GsmL1_Sapi_Rach, "RACH" },
|
||||
{ GsmL1_Sapi_TchF, "TCH/F" },
|
||||
{ GsmL1_Sapi_FacchF, "FACCH/F" },
|
||||
{ GsmL1_Sapi_TchH, "TCH/H" },
|
||||
{ GsmL1_Sapi_FacchH, "FACCH/H" },
|
||||
{ GsmL1_Sapi_Nch, "NCH" },
|
||||
{ GsmL1_Sapi_Pdtch, "PDTCH" },
|
||||
{ GsmL1_Sapi_Pacch, "PACCH" },
|
||||
{ GsmL1_Sapi_Pbcch, "PBCCH" },
|
||||
{ GsmL1_Sapi_Pagch, "PAGCH" },
|
||||
{ GsmL1_Sapi_Ppch, "PPCH" },
|
||||
{ GsmL1_Sapi_Pnch, "PNCH" },
|
||||
{ GsmL1_Sapi_Ptcch, "PTCCH" },
|
||||
{ GsmL1_Sapi_Prach, "PRACH" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1] = {
|
||||
{ GsmL1_Status_Success, "Success" },
|
||||
{ GsmL1_Status_Generic, "Generic error" },
|
||||
{ GsmL1_Status_NoMemory, "Not enough memory" },
|
||||
{ GsmL1_Status_Timeout, "Timeout" },
|
||||
{ GsmL1_Status_InvalidParam, "Invalid parameter" },
|
||||
{ GsmL1_Status_Busy, "Resource busy" },
|
||||
{ GsmL1_Status_NoRessource, "No more resources" },
|
||||
{ GsmL1_Status_Uninitialized, "Trying to use uninitialized resource" },
|
||||
{ GsmL1_Status_NullInterface, "Trying to call a NULL interface" },
|
||||
{ GsmL1_Status_NullFctnPtr, "Trying to call a NULL function ptr" },
|
||||
{ GsmL1_Status_BadCrc, "Bad CRC" },
|
||||
{ GsmL1_Status_BadUsf, "Bad USF" },
|
||||
{ GsmL1_Status_InvalidCPS, "Invalid CPS field" },
|
||||
{ GsmL1_Status_UnexpectedBurst, "Unexpected burst" },
|
||||
{ GsmL1_Status_UnavailCodec, "AMR codec is unavailable" },
|
||||
{ GsmL1_Status_CriticalError, "Critical error" },
|
||||
{ GsmL1_Status_OverheatError, "Overheat error" },
|
||||
{ GsmL1_Status_DeviceError, "Device error" },
|
||||
{ GsmL1_Status_FacchError, "FACCH / TCH order error" },
|
||||
{ GsmL1_Status_AlreadyDeactivated, "Lchan already deactivated" },
|
||||
{ GsmL1_Status_TxBurstFifoOvrn, "FIFO overrun" },
|
||||
{ GsmL1_Status_TxBurstFifoUndr, "FIFO underrun" },
|
||||
{ GsmL1_Status_NotSynchronized, "Not synchronized" },
|
||||
{ GsmL1_Status_Unsupported, "Unsupported feature" },
|
||||
{ GsmL1_Status_ClockError, "System clock error" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_tracef_names[29] = {
|
||||
{ DBG_DEBUG, "DEBUG" },
|
||||
{ DBG_L1WARNING, "L1_WARNING" },
|
||||
{ DBG_ERROR, "ERROR" },
|
||||
{ DBG_L1RXMSG, "L1_RX_MSG" },
|
||||
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE" },
|
||||
{ DBG_L1TXMSG, "L1_TX_MSG" },
|
||||
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE" },
|
||||
{ DBG_MPHCNF, "MPH_CNF" },
|
||||
{ DBG_MPHIND, "MPH_IND" },
|
||||
{ DBG_MPHREQ, "MPH_REQ" },
|
||||
{ DBG_PHIND, "PH_IND" },
|
||||
{ DBG_PHREQ, "PH_REQ" },
|
||||
{ DBG_PHYRF, "PHY_RF" },
|
||||
{ DBG_PHYRFMSGBYTE, "PHY_MSG_BYTE" },
|
||||
{ DBG_MODE, "MODE" },
|
||||
{ DBG_TDMAINFO, "TDMA_INFO" },
|
||||
{ DBG_BADCRC, "BAD_CRC" },
|
||||
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
|
||||
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
|
||||
{ DBG_DEVICEMSG, "DEVICE_MSG" },
|
||||
{ DBG_RACHINFO, "RACH_INFO" },
|
||||
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
|
||||
{ DBG_MEMORY, "MEMORY" },
|
||||
{ DBG_PROFILING, "PROFILING" },
|
||||
{ DBG_TESTCOMMENT, "TEST_COMMENT" },
|
||||
{ DBG_TEST, "TEST" },
|
||||
{ DBG_STATUS, "STATUS" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_tracef_docs[29] = {
|
||||
{ DBG_DEBUG, "Debug Region" },
|
||||
{ DBG_L1WARNING, "L1 Warning Region" },
|
||||
{ DBG_ERROR, "Error Region" },
|
||||
{ DBG_L1RXMSG, "L1_RX_MSG Region" },
|
||||
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE Region" },
|
||||
{ DBG_L1TXMSG, "L1_TX_MSG Region" },
|
||||
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE Region" },
|
||||
{ DBG_MPHCNF, "MphConfirmation Region" },
|
||||
{ DBG_MPHIND, "MphIndication Region" },
|
||||
{ DBG_MPHREQ, "MphRequest Region" },
|
||||
{ DBG_PHIND, "PhIndication Region" },
|
||||
{ DBG_PHREQ, "PhRequest Region" },
|
||||
{ DBG_PHYRF, "PhyRF Region" },
|
||||
{ DBG_PHYRFMSGBYTE, "PhyRF Message Region" },
|
||||
{ DBG_MODE, "Mode Region" },
|
||||
{ DBG_TDMAINFO, "TDMA Info Region" },
|
||||
{ DBG_BADCRC, "Bad CRC Region" },
|
||||
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
|
||||
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
|
||||
{ DBG_DEVICEMSG, "Device Message Region" },
|
||||
{ DBG_RACHINFO, "RACH Info" },
|
||||
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
|
||||
{ DBG_MEMORY, "Memory Region" },
|
||||
{ DBG_PROFILING, "Profiling Region" },
|
||||
{ DBG_TESTCOMMENT, "Test Comments" },
|
||||
{ DBG_TEST, "Test Region" },
|
||||
{ DBG_STATUS, "Status Region" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_tch_pl_names[] = {
|
||||
{ GsmL1_TchPlType_NA, "N/A" },
|
||||
{ GsmL1_TchPlType_Fr, "FR" },
|
||||
{ GsmL1_TchPlType_Hr, "HR" },
|
||||
{ GsmL1_TchPlType_Efr, "EFR" },
|
||||
{ GsmL1_TchPlType_Amr, "AMR(IF2)" },
|
||||
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
|
||||
{ GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" },
|
||||
{ GsmL1_TchPlType_Amr_Ratscch, "AMR(RATSCCH)" },
|
||||
{ GsmL1_TchPlType_Amr_SidUpdateInH, "AMR(SID_UPDATE INH)" },
|
||||
{ GsmL1_TchPlType_Amr_SidFirstP1, "AMR(SID_FIRST P1)" },
|
||||
{ GsmL1_TchPlType_Amr_SidFirstP2, "AMR(SID_FIRST P2)" },
|
||||
{ GsmL1_TchPlType_Amr_SidFirstInH, "AMR(SID_FIRST INH)" },
|
||||
{ GsmL1_TchPlType_Amr_RatscchMarker, "AMR(RATSCCH MARK)" },
|
||||
{ GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_dir_names[] = {
|
||||
{ GsmL1_Dir_TxDownlink, "TxDL" },
|
||||
{ GsmL1_Dir_TxUplink, "TxUL" },
|
||||
{ GsmL1_Dir_RxUplink, "RxUL" },
|
||||
{ GsmL1_Dir_RxDownlink, "RxDL" },
|
||||
{ GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink, "BOTH" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_chcomb_names[] = {
|
||||
{ GsmL1_LogChComb_0, "dummy" },
|
||||
{ GsmL1_LogChComb_I, "tch_f" },
|
||||
{ GsmL1_LogChComb_II, "tch_h" },
|
||||
{ GsmL1_LogChComb_IV, "ccch" },
|
||||
{ GsmL1_LogChComb_V, "ccch_sdcch4" },
|
||||
{ GsmL1_LogChComb_VII, "sdcch8" },
|
||||
{ GsmL1_LogChComb_XIII, "pdtch" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const uint8_t pdch_msu_size[_NUM_PDCH_CS] = {
|
||||
[PDCH_CS_1] = 23,
|
||||
[PDCH_CS_2] = 34,
|
||||
[PDCH_CS_3] = 40,
|
||||
[PDCH_CS_4] = 54,
|
||||
[PDCH_MCS_1] = 27,
|
||||
[PDCH_MCS_2] = 33,
|
||||
[PDCH_MCS_3] = 42,
|
||||
[PDCH_MCS_4] = 49,
|
||||
[PDCH_MCS_5] = 60,
|
||||
[PDCH_MCS_6] = 78,
|
||||
[PDCH_MCS_7] = 118,
|
||||
[PDCH_MCS_8] = 142,
|
||||
[PDCH_MCS_9] = 154
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef LC15BTS_H
|
||||
#define LC15BTS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <nrw/litecell15/litecell15.h>
|
||||
#include <nrw/litecell15/gsml1const.h>
|
||||
|
||||
/*
|
||||
* Depending on the firmware version either GsmL1_Prim_t or Litecell15_Prim_t
|
||||
* is the bigger struct. For earlier firmware versions the GsmL1_Prim_t was the
|
||||
* bigger struct.
|
||||
*/
|
||||
#define LC15BTS_PRIM_SIZE \
|
||||
(OSMO_MAX(sizeof(Litecell15_Prim_t), sizeof(GsmL1_Prim_t)) + 128)
|
||||
|
||||
enum l1prim_type {
|
||||
L1P_T_INVALID, /* this must be 0 to detect uninitialized elements */
|
||||
L1P_T_REQ,
|
||||
L1P_T_CONF,
|
||||
L1P_T_IND,
|
||||
};
|
||||
|
||||
enum l1prim_type lc15bts_get_l1prim_type(GsmL1_PrimId_t id);
|
||||
const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1];
|
||||
GsmL1_PrimId_t lc15bts_get_l1prim_conf(GsmL1_PrimId_t id);
|
||||
|
||||
enum l1prim_type lc15bts_get_sysprim_type(Litecell15_PrimId_t id);
|
||||
const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1];
|
||||
Litecell15_PrimId_t lc15bts_get_sysprim_conf(Litecell15_PrimId_t id);
|
||||
|
||||
const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1];
|
||||
const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1];
|
||||
|
||||
const struct value_string lc15bts_tracef_names[29];
|
||||
const struct value_string lc15bts_tracef_docs[29];
|
||||
|
||||
const struct value_string lc15bts_tch_pl_names[15];
|
||||
|
||||
const struct value_string lc15bts_clksrc_names[10];
|
||||
|
||||
const struct value_string lc15bts_dir_names[6];
|
||||
|
||||
enum pdch_cs {
|
||||
PDCH_CS_1,
|
||||
PDCH_CS_2,
|
||||
PDCH_CS_3,
|
||||
PDCH_CS_4,
|
||||
PDCH_MCS_1,
|
||||
PDCH_MCS_2,
|
||||
PDCH_MCS_3,
|
||||
PDCH_MCS_4,
|
||||
PDCH_MCS_5,
|
||||
PDCH_MCS_6,
|
||||
PDCH_MCS_7,
|
||||
PDCH_MCS_8,
|
||||
PDCH_MCS_9,
|
||||
_NUM_PDCH_CS
|
||||
};
|
||||
|
||||
const uint8_t pdch_msu_size[_NUM_PDCH_CS];
|
||||
|
||||
#endif /* LC15BTS_H */
|
|
@ -109,23 +109,6 @@ static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct femtol1_hdl
|
|||
return &prim->u;
|
||||
}
|
||||
|
||||
struct sapi_dir {
|
||||
GsmL1_Sapi_t sapi;
|
||||
GsmL1_Dir_t dir;
|
||||
};
|
||||
|
||||
static const struct sapi_dir pdtch_sapis[] = {
|
||||
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink },
|
||||
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink },
|
||||
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_TxDownlink },
|
||||
{ GsmL1_Sapi_Prach, GsmL1_Dir_RxUplink },
|
||||
#if 0
|
||||
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_RxUplink },
|
||||
{ GsmL1_Sapi_Pacch, GsmL1_Dir_TxDownlink },
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/* connect PDTCH */
|
||||
int l1if_connect_pdch(void *obj, uint8_t ts)
|
||||
{
|
||||
|
@ -155,8 +138,8 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
|
|||
switch (rts_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
rc = pcu_rx_rts_req_pdtch((long)fl1h->priv, rts_ind->u8Tn,
|
||||
rts_ind->u16Arfcn, rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
|
||||
rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
default:
|
||||
|
@ -215,7 +198,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
|
|||
!= GsmL1_PdtchPlType_Full)
|
||||
break;
|
||||
/* PDTCH / PACCH frame handling */
|
||||
pcu_rx_data_ind_pdtch((long)fl1h->priv, data_ind->u8Tn,
|
||||
pcu_rx_data_ind_pdtch(fl1h->trx_no, data_ind->u8Tn,
|
||||
data_ind->msgUnitParam.u8Buffer + 1,
|
||||
data_ind->msgUnitParam.u8Size - 1,
|
||||
data_ind->u32Fn,
|
||||
|
@ -357,7 +340,7 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
|
||||
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
|
||||
{
|
||||
struct femtol1_hdl *fl1h;
|
||||
int rc;
|
||||
|
@ -367,7 +350,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
|
|||
return NULL;
|
||||
|
||||
fl1h->hLayer1 = hlayer1;
|
||||
fl1h->priv = priv;
|
||||
fl1h->trx_no = trx_no;
|
||||
fl1h->clk_cal = 0;
|
||||
/* default clock source: OCXO */
|
||||
fl1h->clk_src = SuperFemto_ClkSrcId_Ocxo;
|
|
@ -38,7 +38,7 @@ struct femtol1_hdl {
|
|||
struct gsmtap_inst *gsmtap;
|
||||
uint32_t gsmtap_sapi_mask;
|
||||
|
||||
void *priv; /* user reference */
|
||||
uint8_t trx_no;
|
||||
|
||||
struct osmo_timer_list alive_timer;
|
||||
unsigned int alive_prim_cnt;
|
|
@ -100,7 +100,7 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost)
|
|||
|
||||
/* disable all slots, kick all TBFs */
|
||||
for (trx = 0; trx < 8; trx++) {
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
#ifdef ENABLE_DIRECT_PHY
|
||||
if (bts->trx[trx].fl1h) {
|
||||
l1if_close_pdch(bts->trx[trx].fl1h);
|
||||
bts->trx[trx].fl1h = NULL;
|
||||
|
|
|
@ -44,7 +44,8 @@ extern "C" {
|
|||
|
||||
// FIXME: move this, when changed from c++ to c.
|
||||
extern "C" {
|
||||
void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap);
|
||||
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1,
|
||||
struct gsmtap_inst *gsmtap);
|
||||
int l1if_connect_pdch(void *obj, uint8_t ts);
|
||||
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
|
||||
|
@ -128,7 +129,7 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
|
|||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
#ifdef ENABLE_DIRECT_PHY
|
||||
if (bts->trx[trx].fl1h) {
|
||||
l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
|
||||
msg->data, msg->len);
|
||||
|
@ -147,7 +148,7 @@ void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
|
|||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
#ifdef ENABLE_DIRECT_PHY
|
||||
if (bts->trx[trx].fl1h) {
|
||||
l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr,
|
||||
msg->data, msg->len);
|
||||
|
@ -213,7 +214,15 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind, struct gsmtap_inst
|
|||
int rc;
|
||||
pcu_l1_meas meas;
|
||||
meas.set_rssi(data_ind->rssi);
|
||||
|
||||
#ifndef ENABLE_DIRECT_PHY
|
||||
/* convert BER to % value */
|
||||
meas.set_ber(data_ind->ber10k / 100);
|
||||
meas.set_bto(data_ind->ta_offs_qbits);
|
||||
meas.set_link_qual(data_ind->lqual_cb / 10);
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Data indication with raw measurements "
|
||||
"received: BER10k = %d, BTO = %d, Q = %d\n", data_ind->ber10k,
|
||||
data_ind->ta_offs_qbits, data_ind->lqual_cb);
|
||||
#endif
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
|
||||
"block=%d data=%s\n", data_ind->sapi,
|
||||
data_ind->arfcn, data_ind->block_nr,
|
||||
|
@ -263,11 +272,11 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
|
|||
}
|
||||
|
||||
// FIXME: remove this, when changed from c++ to c.
|
||||
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
|
||||
uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
return gprs_rlcmac_rcv_rts_block(bts_main_data(),
|
||||
trx, ts, arfcn, fn, block_nr);
|
||||
trx, ts, fn, block_nr);
|
||||
}
|
||||
|
||||
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
|
||||
|
@ -281,7 +290,7 @@ static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
|
|||
switch (rts_req->sapi) {
|
||||
case PCU_IF_SAPI_PDTCH:
|
||||
pcu_rx_rts_req_pdtch(rts_req->trx_nr, rts_req->ts_nr,
|
||||
rts_req->arfcn, rts_req->fn, rts_req->block_nr);
|
||||
rts_req->fn, rts_req->block_nr);
|
||||
break;
|
||||
case PCU_IF_SAPI_PTCCH:
|
||||
/* FIXME */
|
||||
|
@ -313,7 +322,8 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
|
|||
case PCU_IF_SAPI_RACH:
|
||||
rc = BTS::main_bts()->rcv_rach(
|
||||
rach_ind->ra, rach_ind->fn,
|
||||
rach_ind->qta);
|
||||
rach_ind->qta, rach_ind->is_11bit,
|
||||
(ph_burst_type)rach_ind->burst_type);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
|
||||
|
@ -330,8 +340,8 @@ static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
|
|||
struct gprs_bssgp_pcu *pcu;
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct in_addr ia;
|
||||
int rc = 0;
|
||||
int trx, ts;
|
||||
int rc = 0, ts;
|
||||
uint8_t trx;
|
||||
int i;
|
||||
|
||||
if (info_ind->version != PCU_IF_VERSION) {
|
||||
|
@ -445,12 +455,12 @@ bssgp_failed:
|
|||
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
|
||||
if ((info_ind->flags & PCU_IF_FLAG_SYSMO)
|
||||
&& info_ind->trx[trx].hlayer1) {
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
#ifdef ENABLE_DIRECT_PHY
|
||||
LOGP(DL1IF, LOGL_DEBUG, " TRX %d hlayer1=%x\n", trx,
|
||||
info_ind->trx[trx].hlayer1);
|
||||
if (!bts->trx[trx].fl1h)
|
||||
bts->trx[trx].fl1h = l1if_open_pdch(
|
||||
(void *)trx,
|
||||
trx,
|
||||
info_ind->trx[trx].hlayer1,
|
||||
bts->gsmtap);
|
||||
if (!bts->trx[trx].fl1h) {
|
||||
|
@ -471,7 +481,7 @@ bssgp_failed:
|
|||
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
|
||||
/* FIXME: activate dynamically at RLCMAC */
|
||||
if (!pdch->is_enabled()) {
|
||||
#ifdef ENABLE_SYSMODSP
|
||||
#ifdef ENABLE_DIRECT_PHY
|
||||
if ((info_ind->flags &
|
||||
PCU_IF_FLAG_SYSMO))
|
||||
l1if_connect_pdch(
|
||||
|
|
|
@ -124,7 +124,7 @@ int pcu_sock_send(struct msgb *msg);
|
|||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
|
||||
uint32_t fn, uint8_t block_nr);
|
||||
|
||||
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
|
||||
|
|
|
@ -32,6 +32,7 @@ extern "C" {
|
|||
#include "pcu_vty.h"
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
|
@ -46,7 +47,7 @@ void *tall_pcu_ctx;
|
|||
extern void *bv_tall_ctx;
|
||||
static int quit = 0;
|
||||
static int rt_prio = -1;
|
||||
static char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
|
||||
static const char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
|
@ -189,8 +190,8 @@ int main(int argc, char *argv[])
|
|||
bts->cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
|
||||
bts->max_cs_ul = 4;
|
||||
bts->max_cs_dl = 4;
|
||||
bts->max_mcs_ul = 4;
|
||||
bts->max_mcs_dl = 4;
|
||||
bts->max_mcs_ul = MAX_GPRS_CS;
|
||||
bts->max_mcs_dl = MAX_GPRS_CS;
|
||||
/* CS-1 to CS-4 */
|
||||
bts->cs_lqual_ranges[0].low = -256;
|
||||
bts->cs_lqual_ranges[0].high = 6;
|
||||
|
@ -210,6 +211,14 @@ int main(int argc, char *argv[])
|
|||
bts->dl_tbf_idle_msec = 2000;
|
||||
bts->llc_idle_ack_csec = 10;
|
||||
|
||||
/*
|
||||
* By default resegmentation is supported in DL
|
||||
* can also be configured through VTY
|
||||
*/
|
||||
bts->dl_arq_type = EGPRS_ARQ1;
|
||||
|
||||
bts->maximise_dir = NO_MAXIMISE;
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
|
@ -244,7 +253,8 @@ int main(int argc, char *argv[])
|
|||
fprintf(stderr, "No config file: '%s' Using default config.\n",
|
||||
config_file);
|
||||
|
||||
rc = telnet_init(tall_pcu_ctx, NULL, 4240);
|
||||
rc = telnet_init_dynif(tall_pcu_ctx, NULL, vty_get_bind_addr(),
|
||||
OSMO_VTY_PORT_PCU);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing telnet\n");
|
||||
exit(1);
|
||||
|
|
|
@ -129,6 +129,10 @@ static int config_write_pcu(struct vty *vty)
|
|||
vty_out(vty, " window-size %d %d%s", bts->ws_base, bts->ws_pdch,
|
||||
VTY_NEWLINE);
|
||||
|
||||
if (bts->dl_arq_type)
|
||||
vty_out(vty, " egprs dl arq-type arq2%s",
|
||||
VTY_NEWLINE);
|
||||
|
||||
if (bts->force_llc_lifetime == 0xffff)
|
||||
vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
|
||||
else if (bts->force_llc_lifetime)
|
||||
|
@ -187,12 +191,6 @@ DEFUN(cfg_pcu_egprs,
|
|||
|
||||
bts->egprs_enabled = 1;
|
||||
|
||||
vty_out(vty, "%%Note that EGPRS support is in an experimental state "
|
||||
"and the PCU will currently fail to use a TBF if the MS is capable "
|
||||
"to do EGPRS. You may want to disable this feature by entering "
|
||||
"the \"no egprs\" command. "
|
||||
"Do not use this in production!%s", VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -480,6 +478,51 @@ DEFUN(cfg_pcu_no_mcs_max,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DL_STR "downlink specific configuration\n"
|
||||
|
||||
DEFUN(cfg_pcu_dl_arq_type,
|
||||
cfg_pcu_dl_arq_cmd,
|
||||
"egprs dl arq-type (spb|arq2)",
|
||||
EGPRS_STR DL_STR "ARQ options\n"
|
||||
"enable SPB(ARQ1) support\n"
|
||||
"enable ARQ2 support")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
if (!strcmp(argv[0], "arq2"))
|
||||
bts->dl_arq_type = 1;
|
||||
else
|
||||
bts->dl_arq_type = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define MAXIMISE_STR "Maximise TS allocation based on configuration\n"
|
||||
|
||||
DEFUN(cfg_pcu_ts_alloc_maximise_type,
|
||||
cfg_pcu_ts_alloc_maximise_cmd,
|
||||
"maximise-direction dl",
|
||||
MAXIMISE_STR "Maximise DL capacity\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->maximise_dir = DL_ONLY;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_maximise_direction,
|
||||
cfg_pcu_no_maximise_direction_cmd,
|
||||
"no maximise-direction",
|
||||
NO_STR "Maximise direction configuration\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->maximise_dir = NO_MAXIMISE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_window_size,
|
||||
cfg_pcu_window_size_cmd,
|
||||
"window-size <0-1024> [<0-256>]",
|
||||
|
@ -954,6 +997,9 @@ int pcu_vty_init(const struct log_info *cat)
|
|||
install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_mcs_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_dl_arq_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_ts_alloc_maximise_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_maximise_direction_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_mcs_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_mcs_max_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_mcs_max_cmd);
|
||||
|
|
|
@ -102,7 +102,7 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
|
|||
llist_for_each(ms_iter, &bts->ms_store().ms_list()) {
|
||||
GprsMs *ms = ms_iter->entry();
|
||||
|
||||
vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%s, CS-DL=%s, LLC=%d, "
|
||||
vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%s, CS-DL=%s, LLC=%zd, "
|
||||
"IMSI=%s%s",
|
||||
ms->tlli(),
|
||||
ms->ta(), ms->current_cs_ul().name(),
|
||||
|
@ -136,9 +136,9 @@ static int show_ms(struct vty *vty, GprsMs *ms)
|
|||
if (slots & (1 << i))
|
||||
vty_out(vty, "%d ", i);
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
vty_out(vty, " LLC queue length: %d%s", ms->llc_queue()->size(),
|
||||
vty_out(vty, " LLC queue length: %zd%s", ms->llc_queue()->size(),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " LLC queue octets: %d%s", ms->llc_queue()->octets(),
|
||||
vty_out(vty, " LLC queue octets: %zd%s", ms->llc_queue()->octets(),
|
||||
VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_rssi)
|
||||
vty_out(vty, " RSSI: %d dBm%s",
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#ifndef _PCUIF_PROTO_H
|
||||
#define _PCUIF_PROTO_H
|
||||
|
||||
#define PCU_IF_VERSION 0x05
|
||||
#include <osmocom/gsm/l1sap.h>
|
||||
|
||||
#define PCU_IF_VERSION 0x07
|
||||
|
||||
/* msg_type */
|
||||
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
|
||||
|
@ -50,6 +52,9 @@ struct gsm_pcu_if_data {
|
|||
uint8_t ts_nr;
|
||||
uint8_t block_nr;
|
||||
int8_t rssi;
|
||||
uint16_t ber10k; /*!< \brief BER in units of 0.01% */
|
||||
int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */
|
||||
int16_t lqual_cb; /* !< \brief Link quality in centiBel */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_rts_req {
|
||||
|
@ -64,10 +69,12 @@ struct gsm_pcu_if_rts_req {
|
|||
|
||||
struct gsm_pcu_if_rach_ind {
|
||||
uint8_t sapi;
|
||||
uint8_t ra;
|
||||
uint16_t ra;
|
||||
int16_t qta;
|
||||
uint32_t fn;
|
||||
uint16_t arfcn;
|
||||
uint8_t is_11bit;
|
||||
uint8_t burst_type;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_info_trx {
|
||||
|
|
172
src/rlc.cpp
172
src/rlc.cpp
|
@ -33,6 +33,9 @@ uint8_t *gprs_rlc_data::prepare(size_t block_data_len)
|
|||
memset(block, 0x0, sizeof(block));
|
||||
memset(block, 0x2b, block_data_len);
|
||||
|
||||
/* Initial value of puncturing scheme */
|
||||
next_ps = EGPRS_PS_1;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
|
@ -80,6 +83,32 @@ int gprs_rlc_dl_window::mark_for_resend()
|
|||
return resend;
|
||||
}
|
||||
|
||||
/* Update the receive block bitmap */
|
||||
uint16_t gprs_rlc_ul_window::update_egprs_rbb(uint8_t *rbb)
|
||||
{
|
||||
int i;
|
||||
uint16_t bsn;
|
||||
uint16_t bitmask = 0x80;
|
||||
int8_t pos = 0;
|
||||
int8_t bit_pos = 0;
|
||||
for (i = 0, bsn = (v_q()+1); ((bsn < (v_r())) && (i < ws())); i++,
|
||||
bsn = this->mod_sns(bsn + 1)) {
|
||||
if (m_v_n.is_received(bsn)) {
|
||||
rbb[pos] = rbb[pos] | bitmask;
|
||||
} else {
|
||||
rbb[pos] = rbb[pos] & (~bitmask);
|
||||
}
|
||||
bitmask = bitmask >> 1;
|
||||
bit_pos++;
|
||||
bit_pos = bit_pos % 8;
|
||||
if(bit_pos == 0) {
|
||||
pos++;
|
||||
bitmask = 0x80;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int gprs_rlc_dl_window::count_unacked()
|
||||
{
|
||||
uint16_t unacked = 0;
|
||||
|
@ -102,7 +131,8 @@ void gprs_rlc_dl_window::update(BTS *bts, const struct bitvec *rbb,
|
|||
uint16_t first_bsn, uint16_t *lost,
|
||||
uint16_t *received)
|
||||
{
|
||||
unsigned num_blocks = rbb->cur_bit;
|
||||
unsigned num_blocks = rbb->cur_bit > (unsigned)distance()
|
||||
? distance() : rbb->cur_bit;
|
||||
unsigned bsn;
|
||||
|
||||
/* first_bsn is in range V(A)..V(S) */
|
||||
|
@ -214,6 +244,8 @@ void gprs_rlc_window::set_sns(uint16_t sns)
|
|||
|
||||
void gprs_rlc_window::set_ws(uint16_t ws)
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "ws(%d)\n",
|
||||
ws);
|
||||
OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
|
||||
OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
|
||||
m_ws = ws;
|
||||
|
@ -224,7 +256,7 @@ void gprs_rlc_ul_window::update_rbb(char *rbb)
|
|||
{
|
||||
int i;
|
||||
for (i=0; i < ws(); i++) {
|
||||
if (m_v_n.is_received(ssn()-1-i))
|
||||
if (m_v_n.is_received((ssn()-1-i) & mod_sns()))
|
||||
rbb[ws()-1-i] = 'R';
|
||||
else
|
||||
rbb[ws()-1-i] = 'I';
|
||||
|
@ -282,7 +314,8 @@ bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
|
|||
}
|
||||
|
||||
static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding, unsigned int header_bits)
|
||||
GprsCodingScheme cs, bool with_padding, unsigned int header_bits,
|
||||
const unsigned int spb)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int padding_bits = with_padding ? cs.optionalPaddingBits() : 0;
|
||||
|
@ -297,7 +330,7 @@ static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
|
|||
|
||||
for (i = 0; i < rlc->num_data_blocks; i++) {
|
||||
gprs_rlc_data_block_info_init(&rlc->block_info[i], cs,
|
||||
with_padding);
|
||||
with_padding, spb);
|
||||
|
||||
rlc->data_offs_bits[i] =
|
||||
header_bits + padding_bits +
|
||||
|
@ -307,21 +340,25 @@ static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
|
|||
}
|
||||
|
||||
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding)
|
||||
GprsCodingScheme cs, bool with_padding, const unsigned int spb)
|
||||
{
|
||||
return gprs_rlc_data_header_init(rlc, cs, with_padding,
|
||||
cs.numDataHeaderBitsDL());
|
||||
cs.numDataHeaderBitsDL(), spb);
|
||||
}
|
||||
|
||||
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding)
|
||||
{
|
||||
/*
|
||||
* last parameter is sent as 0 since common function used
|
||||
* for both DL and UL
|
||||
*/
|
||||
return gprs_rlc_data_header_init(rlc, cs, with_padding,
|
||||
cs.numDataHeaderBitsUL());
|
||||
cs.numDataHeaderBitsUL(), 0);
|
||||
}
|
||||
|
||||
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
|
||||
GprsCodingScheme cs, bool with_padding)
|
||||
GprsCodingScheme cs, bool with_padding, const unsigned int spb)
|
||||
{
|
||||
unsigned int data_len = cs.maxDataBlockBytes();
|
||||
if (with_padding)
|
||||
|
@ -333,24 +370,35 @@ void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
|
|||
rdbi->e = 1;
|
||||
rdbi->cv = 15;
|
||||
rdbi->pi = 0;
|
||||
rdbi->spb = 0;
|
||||
rdbi->spb = spb;
|
||||
}
|
||||
|
||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
|
||||
int with_padding)
|
||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs,
|
||||
enum egprs_puncturing_values punct,
|
||||
enum egprs_puncturing_values punct2, int with_padding)
|
||||
{
|
||||
switch (GprsCodingScheme::Scheme(cs)) {
|
||||
case GprsCodingScheme::MCS1: return 0b1011 + punct % 2;
|
||||
case GprsCodingScheme::MCS2: return 0b1001 + punct % 2;
|
||||
case GprsCodingScheme::MCS1: return 0b1011 +
|
||||
punct % EGPRS_MAX_PS_NUM_2;
|
||||
case GprsCodingScheme::MCS2: return 0b1001 +
|
||||
punct % EGPRS_MAX_PS_NUM_2;
|
||||
case GprsCodingScheme::MCS3: return (with_padding ? 0b0110 : 0b0011) +
|
||||
punct % 3;
|
||||
case GprsCodingScheme::MCS4: return 0b0000 + punct % 3;
|
||||
case GprsCodingScheme::MCS5: return 0b100 + punct % 2;
|
||||
punct % EGPRS_MAX_PS_NUM_3;
|
||||
case GprsCodingScheme::MCS4: return 0b0000 +
|
||||
punct % EGPRS_MAX_PS_NUM_3;
|
||||
case GprsCodingScheme::MCS5: return 0b100 +
|
||||
punct % EGPRS_MAX_PS_NUM_2;
|
||||
case GprsCodingScheme::MCS6: return (with_padding ? 0b010 : 0b000) +
|
||||
punct % 2;
|
||||
case GprsCodingScheme::MCS7: return 0b10100 + 3 * (punct % 3) + punct2 % 3;
|
||||
case GprsCodingScheme::MCS8: return 0b01011 + 3 * (punct % 3) + punct2 % 3;
|
||||
case GprsCodingScheme::MCS9: return 0b00000 + 4 * (punct % 3) + punct2 % 3;
|
||||
punct % EGPRS_MAX_PS_NUM_2;
|
||||
case GprsCodingScheme::MCS7: return 0b10100 +
|
||||
3 * (punct % EGPRS_MAX_PS_NUM_3) +
|
||||
punct2 % EGPRS_MAX_PS_NUM_3;
|
||||
case GprsCodingScheme::MCS8: return 0b01011 +
|
||||
3 * (punct % EGPRS_MAX_PS_NUM_3) +
|
||||
punct2 % EGPRS_MAX_PS_NUM_3;
|
||||
case GprsCodingScheme::MCS9: return 0b00000 +
|
||||
4 * (punct % EGPRS_MAX_PS_NUM_3) +
|
||||
punct2 % EGPRS_MAX_PS_NUM_3;
|
||||
default: ;
|
||||
}
|
||||
|
||||
|
@ -385,3 +433,87 @@ void gprs_rlc_mcs_cps_decode(unsigned int cps,
|
|||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the PS value for retransmission with MCS change,
|
||||
* retransmission with no MCS change, fresh transmission cases.
|
||||
* The return value shall be used for current transmission only
|
||||
* 44.060 9.3.2.1 defines the PS selection for MCS change case
|
||||
* cs_current is the output of MCS selection algorithm for retx
|
||||
* cs is coding scheme of previous transmission of RLC data block
|
||||
*/
|
||||
enum egprs_puncturing_values gprs_get_punct_scheme(
|
||||
enum egprs_puncturing_values punct,
|
||||
const GprsCodingScheme &cs,
|
||||
const GprsCodingScheme &cs_current,
|
||||
const enum egprs_rlcmac_dl_spb spb)
|
||||
{
|
||||
|
||||
/*
|
||||
* 10.4.8b of TS 44.060
|
||||
* If it is second segment of the block
|
||||
* dont change the puncturing scheme
|
||||
*/
|
||||
if (spb == EGPRS_RLCMAC_DL_SEC_SEG)
|
||||
return punct;
|
||||
|
||||
/* TS 44.060 9.3.2.1.1 */
|
||||
if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS9) &&
|
||||
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS6)) {
|
||||
if ((punct == EGPRS_PS_1) || (punct == EGPRS_PS_3))
|
||||
return EGPRS_PS_1;
|
||||
else if (punct == EGPRS_PS_2)
|
||||
return EGPRS_PS_2;
|
||||
} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6) &&
|
||||
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS9)) {
|
||||
if (punct == EGPRS_PS_1)
|
||||
return EGPRS_PS_3;
|
||||
else if (punct == EGPRS_PS_2)
|
||||
return EGPRS_PS_2;
|
||||
} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS7) &&
|
||||
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS5))
|
||||
return EGPRS_PS_1;
|
||||
else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS5) &&
|
||||
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS7))
|
||||
return EGPRS_PS_2;
|
||||
else if (cs != cs_current)
|
||||
return EGPRS_PS_1;
|
||||
/* TS 44.060 9.3.2.1.1 ends here */
|
||||
/*
|
||||
* Below else will handle fresh transmission, retransmission with no
|
||||
* MCS change case
|
||||
*/
|
||||
else
|
||||
return punct;
|
||||
return EGPRS_PS_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function calculates puncturing scheme for retransmission of a RLC
|
||||
* block with same MCS. The computed value shall be used for next transmission
|
||||
* of the same RLC block
|
||||
* TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
|
||||
*/
|
||||
void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
|
||||
const GprsCodingScheme &cs)
|
||||
{
|
||||
switch (GprsCodingScheme::Scheme(cs)) {
|
||||
case GprsCodingScheme::MCS1 :
|
||||
case GprsCodingScheme::MCS2 :
|
||||
case GprsCodingScheme::MCS5 :
|
||||
case GprsCodingScheme::MCS6 :
|
||||
*punct = ((enum egprs_puncturing_values)((*punct + 1) %
|
||||
EGPRS_MAX_PS_NUM_2));
|
||||
break;
|
||||
case GprsCodingScheme::MCS3 :
|
||||
case GprsCodingScheme::MCS4 :
|
||||
case GprsCodingScheme::MCS7 :
|
||||
case GprsCodingScheme::MCS8 :
|
||||
case GprsCodingScheme::MCS9 :
|
||||
*punct = ((enum egprs_puncturing_values)((*punct + 1) %
|
||||
EGPRS_MAX_PS_NUM_3));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
208
src/rlc.h
208
src/rlc.h
|
@ -27,7 +27,6 @@
|
|||
|
||||
#define RLC_GPRS_SNS 128 /* GPRS, must be power of 2 */
|
||||
#define RLC_GPRS_WS 64 /* max window size */
|
||||
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
|
||||
#define RLC_EGPRS_MIN_WS 64 /* min window size */
|
||||
#define RLC_EGPRS_MAX_WS 1024 /* min window size */
|
||||
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
|
||||
|
@ -56,6 +55,88 @@ enum gprs_rlc_dl_bsn_state {
|
|||
GPRS_RLC_DL_BSN_MAX,
|
||||
};
|
||||
|
||||
/*
|
||||
* EGPRS resegment status information for UL
|
||||
* When only first split block is received bsn state
|
||||
* will be set to EGPRS_RESEG_FIRST_SEG_RXD and when
|
||||
* only second segment is received the state will be
|
||||
* set to EGPRS_RESEG_SECOND_SEG_RXD. When both Split
|
||||
* blocks are received the state will be set to
|
||||
* EGPRS_RESEG_DEFAULT
|
||||
* The EGPRS resegmentation feature allows MS to retransmit
|
||||
* RLC blocks of HeaderType1, HeaderType2 by segmenting
|
||||
* them to 2 HeaderType3 blocks(Example MCS5 will be
|
||||
* retransmitted as 2 MCS2 blocks). Table 10.4.8b.1 of 44.060
|
||||
* explains the possible values of SPB in HeadrType3 for UL
|
||||
* direction. When the MCS is changed at the PCU, PCU directs the
|
||||
* changed MCS to MS by PUAN or UPLINK ASSIGNMENT message along
|
||||
* with RESEGMENT flag, Then MS may decide to retransmit the
|
||||
* blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
|
||||
* The retransmission MCS is calculated based on current MCS of
|
||||
* the Block and demanded MCS by PCU. Section 10.3a.4.3 of 44.060
|
||||
* shows the HeadrType3 with SPB field present in it
|
||||
*/
|
||||
enum egprs_rlc_ul_reseg_bsn_state {
|
||||
EGPRS_RESEG_DEFAULT = 0,
|
||||
EGPRS_RESEG_FIRST_SEG_RXD = 0x01,
|
||||
EGPRS_RESEG_SECOND_SEG_RXD = 0x02,
|
||||
EGPRS_RESEG_INVALID = 0x04
|
||||
};
|
||||
|
||||
/*
|
||||
* EGPRS resegment status information for DL
|
||||
* When only first segment is sent, bsn state
|
||||
* will be set to EGPRS_RESEG_FIRST_SEG_SENT and when
|
||||
* second segment is sent the state will be
|
||||
* set to EGPRS_RESEG_SECOND_SEG_SENT.
|
||||
* EGPRS_RESEG_DL_INVALID is set to 8 considering there is a scope for
|
||||
* 3rd segment according to Table 10.4.8b.2 of 44.060
|
||||
* The EGPRS resegmentation feature allows PCU to retransmit
|
||||
* RLC blocks of HeaderType1, HeaderType2 by segmenting
|
||||
* them to 2 HeaderType3 blocks(Example MCS5 will be
|
||||
* retransmitted as 2 MCS2 blocks). Table 10.4.8b.2 of 44.060
|
||||
* explains the possible values of SPB in HeadrType3 for DL
|
||||
* direction.The PCU decides to retransmit the
|
||||
* blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
|
||||
* The retransmission MCS is calculated based on current MCS of
|
||||
* the Block and demanded MCS by PCU. Section 10.3a.3.3 of 44.060
|
||||
* shows the HeadrType3 with SPB field present in it
|
||||
*/
|
||||
enum egprs_rlc_dl_reseg_bsn_state {
|
||||
EGPRS_RESEG_DL_DEFAULT = 0,
|
||||
EGPRS_RESEG_FIRST_SEG_SENT = 0x01,
|
||||
EGPRS_RESEG_SECOND_SEG_SENT = 0x02,
|
||||
EGPRS_RESEG_DL_INVALID = 0x08
|
||||
};
|
||||
|
||||
/* Table 10.4.8b.2 of 44.060 */
|
||||
enum egprs_rlcmac_dl_spb {
|
||||
EGPRS_RLCMAC_DL_NO_RETX = 0,
|
||||
EGPRS_RLCMAC_DL_FIRST_SEG = 2,
|
||||
EGPRS_RLCMAC_DL_SEC_SEG = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* Valid puncturing scheme values
|
||||
* TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
|
||||
*/
|
||||
enum egprs_puncturing_values {
|
||||
EGPRS_PS_1,
|
||||
EGPRS_PS_2,
|
||||
EGPRS_PS_3,
|
||||
EGPRS_PS_INVALID,
|
||||
};
|
||||
|
||||
/*
|
||||
* EGPRS_MAX_PS_NUM_2 is valid for MCS 1,2,5,6.
|
||||
* And EGPRS_MAX_PS_NUM_3 is valid for MCS 3,4,7,8,9
|
||||
* TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
|
||||
*/
|
||||
enum egprs_puncturing_types {
|
||||
EGPRS_MAX_PS_NUM_2 = 2,
|
||||
EGPRS_MAX_PS_NUM_3,
|
||||
EGPRS_MAX_PS_NUM_INVALID,
|
||||
};
|
||||
|
||||
static inline uint16_t mod_sns_half()
|
||||
{
|
||||
|
@ -89,30 +170,64 @@ struct gprs_rlc_data_info {
|
|||
struct gprs_rlc_data_block_info block_info[2];
|
||||
};
|
||||
|
||||
/* holds the current status of the block w.r.t UL/DL split blocks */
|
||||
union split_block_status {
|
||||
egprs_rlc_ul_reseg_bsn_state block_status_ul;
|
||||
egprs_rlc_dl_reseg_bsn_state block_status_dl;
|
||||
};
|
||||
|
||||
struct gprs_rlc_data {
|
||||
uint8_t *prepare(size_t block_data_length);
|
||||
void put_data(const uint8_t *data, size_t len);
|
||||
|
||||
/* block history */
|
||||
/* block data including LI headers */
|
||||
uint8_t block[RLC_MAX_LEN];
|
||||
/* block len of history */
|
||||
/* block data len including LI headers*/
|
||||
uint8_t len;
|
||||
|
||||
struct gprs_rlc_data_block_info block_info;
|
||||
GprsCodingScheme cs;
|
||||
/*
|
||||
* cs_current_trans is variable to hold the cs_last value for
|
||||
* current transmission. cs_current_trans is same as cs_last during
|
||||
* transmission case. during retransmission cs_current_trans is
|
||||
* fetched from egprs_mcs_retx_tbl table based on
|
||||
* cs and demanded cs.reference is 44.060 Table
|
||||
* 8.1.1.1 and Table 8.1.1.2
|
||||
* For UL. cs_last shall be used everywhere.
|
||||
*/
|
||||
GprsCodingScheme cs_current_trans;
|
||||
GprsCodingScheme cs_last;
|
||||
|
||||
/*
|
||||
* The MCS of initial transmission of a BSN
|
||||
* This variable is used for split block
|
||||
* processing in DL
|
||||
*/
|
||||
GprsCodingScheme cs_init;
|
||||
|
||||
/* puncturing scheme value to be used for next transmission*/
|
||||
enum egprs_puncturing_values next_ps;
|
||||
|
||||
/* holds the status of the block w.r.t UL/DL split blocks*/
|
||||
union split_block_status spb_status;
|
||||
};
|
||||
|
||||
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding);
|
||||
GprsCodingScheme cs, bool with_padding, const unsigned int spb);
|
||||
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding);
|
||||
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
|
||||
GprsCodingScheme cs, bool with_padding);
|
||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
|
||||
int with_padding);
|
||||
GprsCodingScheme cs, bool with_padding, const unsigned int spb);
|
||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, enum egprs_puncturing_values
|
||||
punct, enum egprs_puncturing_values punct2, int with_padding);
|
||||
void gprs_rlc_mcs_cps_decode(unsigned int cps, GprsCodingScheme cs,
|
||||
int *punct, int *punct2, int *with_padding);
|
||||
|
||||
enum egprs_puncturing_values gprs_get_punct_scheme(enum egprs_puncturing_values
|
||||
punct, const GprsCodingScheme &cs,
|
||||
const GprsCodingScheme &cs_current_trans,
|
||||
const enum egprs_rlcmac_dl_spb spb);
|
||||
void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
|
||||
const GprsCodingScheme &cs);
|
||||
/*
|
||||
* I hold the currently transferred blocks and will provide
|
||||
* the routines to manipulate these arrays.
|
||||
|
@ -187,6 +302,9 @@ struct gprs_rlc_dl_window: public gprs_rlc_window {
|
|||
const uint16_t v_a() const;
|
||||
const int16_t distance() const;
|
||||
|
||||
void set_v_s(int);
|
||||
void set_v_a(int);
|
||||
|
||||
/* Methods to manage reception */
|
||||
int resend_needed();
|
||||
int mark_for_resend();
|
||||
|
@ -232,6 +350,7 @@ struct gprs_rlc_ul_window: public gprs_rlc_window {
|
|||
bool is_received(uint16_t bsn) const;
|
||||
|
||||
void update_rbb(char *rbb);
|
||||
uint16_t update_egprs_rbb(uint8_t *rbb);
|
||||
void raise_v_r_to(int moves);
|
||||
void raise_v_r(const uint16_t bsn);
|
||||
uint16_t raise_v_q();
|
||||
|
@ -287,67 +406,6 @@ struct rlc_li_field_egprs {
|
|||
uint8_t e:1,
|
||||
li:7;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_ul_header_egprs_3 {
|
||||
uint8_t r:1,
|
||||
si:1,
|
||||
cv:4,
|
||||
tfi_a:2;
|
||||
uint8_t tfi_b:3,
|
||||
bsn1_a:5;
|
||||
uint8_t bsn1_b:6,
|
||||
cps_a:2;
|
||||
uint8_t cps_b:2,
|
||||
spb:2,
|
||||
rsb:1,
|
||||
pi:1,
|
||||
spare:1,
|
||||
dummy:1;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_dl_header_egprs_1 {
|
||||
uint8_t usf:3,
|
||||
es_p:2,
|
||||
rrbp:2,
|
||||
tfi_a:1;
|
||||
uint8_t tfi_b:4,
|
||||
pr:2,
|
||||
bsn1_a:2;
|
||||
uint8_t bsn1_b:8;
|
||||
uint8_t bsn1_c:1,
|
||||
bsn2_a:7;
|
||||
uint8_t bsn2_b:3,
|
||||
cps:5;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_dl_header_egprs_2 {
|
||||
uint8_t usf:3,
|
||||
es_p:2,
|
||||
rrbp:2,
|
||||
tfi_a:1;
|
||||
uint8_t tfi_b:4,
|
||||
pr:2,
|
||||
bsn1_a:2;
|
||||
uint8_t bsn1_b:8;
|
||||
uint8_t bsn1_c:1,
|
||||
cps:3,
|
||||
dummy:4;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_dl_header_egprs_3 {
|
||||
uint8_t usf:3,
|
||||
es_p:2,
|
||||
rrbp:2,
|
||||
tfi_a:1;
|
||||
uint8_t tfi_b:4,
|
||||
pr:2,
|
||||
bsn1_a:2;
|
||||
uint8_t bsn1_b:8;
|
||||
uint8_t bsn1_c:1,
|
||||
cps:4,
|
||||
spb:2,
|
||||
dummy:1;
|
||||
} __attribute__ ((packed));
|
||||
#else
|
||||
# error "Only little endian headers are supported yet. TODO: add missing structs"
|
||||
#endif
|
||||
|
@ -455,6 +513,16 @@ inline const uint16_t gprs_rlc_dl_window::v_s() const
|
|||
return m_v_s;
|
||||
}
|
||||
|
||||
inline void gprs_rlc_dl_window::set_v_s(int v_s)
|
||||
{
|
||||
m_v_s = v_s;
|
||||
}
|
||||
|
||||
inline void gprs_rlc_dl_window::set_v_a(int v_a)
|
||||
{
|
||||
m_v_a = v_a;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
|
||||
{
|
||||
return mod_sns(m_v_s + offset);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -55,6 +56,9 @@ int SBAController::alloc(
|
|||
if (!sba)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!gsm48_ta_is_valid(ta))
|
||||
return -EINVAL;
|
||||
|
||||
for (trx = 0; trx < 8; trx++) {
|
||||
for (ts = 7; ts >= 0; ts--) {
|
||||
pdch = &m_bts.bts_data()->trx[trx].pdch[ts];
|
||||
|
|
40
src/tbf.cpp
40
src/tbf.cpp
|
@ -74,7 +74,7 @@ gprs_rlcmac_tbf::gprs_rlcmac_tbf(BTS *bts_, gprs_rlcmac_tbf_direction dir) :
|
|||
m_tfi(0),
|
||||
m_created_ts(0),
|
||||
m_ms(NULL),
|
||||
m_ta(0),
|
||||
m_ta(GSM48_TA_INVALID),
|
||||
m_ms_class(0),
|
||||
m_list(this),
|
||||
m_ms_list(this),
|
||||
|
@ -151,7 +151,8 @@ void gprs_rlcmac_tbf::set_ta(uint8_t ta)
|
|||
if (ms())
|
||||
ms()->set_ta(ta);
|
||||
|
||||
m_ta = ta;
|
||||
if (gsm48_ta_is_valid(ta))
|
||||
m_ta = ta;
|
||||
}
|
||||
|
||||
uint8_t gprs_rlcmac_tbf::ms_class() const
|
||||
|
@ -375,6 +376,12 @@ int gprs_rlcmac_tbf::update()
|
|||
return -rc;
|
||||
}
|
||||
|
||||
if (is_egprs_enabled()) {
|
||||
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(this);
|
||||
if (dl_tbf)
|
||||
dl_tbf->egprs_calc_window_size();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -687,14 +694,16 @@ struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
|
|||
if (egprs_ms_class > 0 && bts->egprs_enabled) {
|
||||
tbf->enable_egprs();
|
||||
tbf->m_window.set_sns(RLC_EGPRS_SNS);
|
||||
/* TODO: Allow bigger UL windows when CRBB encoding is supported */
|
||||
tbf->m_window.set_ws(RLC_EGPRS_MIN_WS);
|
||||
setup_egprs_mode(bts, ms);
|
||||
setup_egprs_mode(bts, ms);
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Enabled EGPRS for %s, mode %s\n",
|
||||
tbf->name(), GprsCodingScheme::modeName(ms->mode()));
|
||||
}
|
||||
|
||||
rc = setup_tbf(tbf, ms, use_trx, ms_class, egprs_ms_class, single_slot);
|
||||
|
||||
if (tbf->is_egprs_enabled())
|
||||
tbf->egprs_calc_ulwindow_size();
|
||||
|
||||
/* if no resource */
|
||||
if (rc < 0) {
|
||||
talloc_free(tbf);
|
||||
|
@ -780,20 +789,8 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (tbf->is_egprs_enabled()) {
|
||||
unsigned int num_pdch = pcu_bitcount(tbf->dl_slots());
|
||||
unsigned int ws = bts->ws_base + num_pdch * bts->ws_pdch;
|
||||
ws = (ws / 32) * 32;
|
||||
ws = OSMO_MAX(64, ws);
|
||||
if (num_pdch == 1)
|
||||
ws = OSMO_MIN(192, ws);
|
||||
else
|
||||
ws = OSMO_MIN(128 * num_pdch, ws);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "%s: Setting EGPRS window size to %d\n",
|
||||
tbf->name(), ws);
|
||||
tbf->m_window.set_ws(ws);
|
||||
}
|
||||
if (tbf->is_egprs_enabled())
|
||||
tbf->egprs_calc_window_size();
|
||||
|
||||
llist_add(&tbf->list(), &bts->bts->dl_tbfs());
|
||||
tbf->bts->tbf_dl_created();
|
||||
|
@ -1176,14 +1173,11 @@ int gprs_rlcmac_tbf::set_tlli_from_ul(uint32_t new_tlli)
|
|||
|
||||
const char *tbf_name(gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
return tbf->name();
|
||||
return tbf ? tbf->name() : "(no TBF)";
|
||||
}
|
||||
|
||||
const char *gprs_rlcmac_tbf::name() const
|
||||
{
|
||||
if (this == NULL)
|
||||
return "(no TBF)";
|
||||
|
||||
snprintf(m_name_buf, sizeof(m_name_buf) - 1,
|
||||
"TBF(TFI=%d TLLI=0x%08x DIR=%s STATE=%s%s)",
|
||||
m_tfi, tlli(),
|
||||
|
|
24
src/tbf.h
24
src/tbf.h
|
@ -372,6 +372,8 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
|
|||
int release();
|
||||
int abort();
|
||||
|
||||
void egprs_calc_window_size();
|
||||
|
||||
/* TODO: add the gettimeofday as parameter */
|
||||
struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
|
||||
|
||||
|
@ -421,6 +423,11 @@ protected:
|
|||
int analyse_errors(char *show_rbb, uint8_t ssn, ana_result *res);
|
||||
void schedule_next_frame();
|
||||
|
||||
enum egprs_rlc_dl_reseg_bsn_state egprs_dl_get_data
|
||||
(int bsn, uint8_t **block_data);
|
||||
unsigned int get_egprs_dl_spb_status(int bsn);
|
||||
enum egprs_rlcmac_dl_spb get_egprs_dl_spb(int bsn);
|
||||
|
||||
struct osmo_timer_list m_llc_timer;
|
||||
};
|
||||
|
||||
|
@ -439,6 +446,23 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
|
|||
int assemble_forward_llc(const gprs_rlc_data *data);
|
||||
int snd_ul_ud();
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_spb(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx);
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_first_seg(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx);
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_second_seg(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx);
|
||||
|
||||
void egprs_calc_ulwindow_size();
|
||||
|
||||
/* Please note that all variables here will be reset when changing
|
||||
* from WAIT RELEASE back to FLOW state (re-use of TBF).
|
||||
* All states that need reset must be in this struct, so this is why
|
||||
|
|
304
src/tbf_dl.cpp
304
src/tbf_dl.cpp
|
@ -34,6 +34,7 @@
|
|||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gprs/gprs_bssgp_bss.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -43,11 +44,6 @@ extern "C" {
|
|||
/* After sending these frames, we poll for ack/nack. */
|
||||
#define POLL_ACK_AFTER_FRAMES 20
|
||||
|
||||
extern "C" {
|
||||
int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
|
||||
uint8_t num_frames, uint32_t num_octets);
|
||||
}
|
||||
|
||||
static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf,
|
||||
const uint8_t ms_class)
|
||||
{
|
||||
|
@ -124,7 +120,7 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
|
|||
{
|
||||
uint8_t ss;
|
||||
int8_t use_trx;
|
||||
uint16_t ta = 0;
|
||||
uint16_t ta = GSM48_TA_INVALID;
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf = NULL, *old_ul_tbf;
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
|
||||
GprsMs *ms;
|
||||
|
@ -218,7 +214,7 @@ int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts,
|
|||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"%s IMSI %s: "
|
||||
"moving DL TBF to new MS object\n",
|
||||
dl_tbf->name(), imsi);
|
||||
ms_old->dl_tbf()->name(), imsi);
|
||||
dl_tbf = ms_old->dl_tbf();
|
||||
/* Move the DL TBF to the new MS */
|
||||
dl_tbf->set_ms(ms);
|
||||
|
@ -359,13 +355,12 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
|||
{
|
||||
int bsn;
|
||||
int data_len2, force_data_len = -1;
|
||||
GprsCodingScheme cs2;
|
||||
GprsCodingScheme force_cs;
|
||||
|
||||
bsn = m_window.resend_needed();
|
||||
|
||||
if (previous_bsn >= 0) {
|
||||
force_cs = m_rlc.block(previous_bsn)->cs;
|
||||
force_cs = m_rlc.block(previous_bsn)->cs_current_trans;
|
||||
if (!force_cs.isEgprs())
|
||||
return -1;
|
||||
force_data_len = m_rlc.block(previous_bsn)->len;
|
||||
|
@ -379,7 +374,35 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
|||
m_window.mod_sns(bsn - previous_bsn) > RLC_EGPRS_MAX_BSN_DELTA)
|
||||
return -1;
|
||||
|
||||
cs2 = m_rlc.block(bsn)->cs;
|
||||
if (is_egprs_enabled()) {
|
||||
/* Table 8.1.1.2 and Table 8.1.1.1 of 44.060 */
|
||||
m_rlc.block(bsn)->cs_current_trans =
|
||||
GprsCodingScheme::get_retx_mcs(
|
||||
m_rlc.block(bsn)->cs_init,
|
||||
ms()->current_cs_dl(),
|
||||
bts->bts_data()->dl_arq_type);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"- initial_cs_dl(%d) last_mcs(%d)"
|
||||
" demanded_mcs(%d) cs_trans(%d)"
|
||||
" arq_type(%d) bsn(%d)\n",
|
||||
m_rlc.block(bsn)->cs_init.to_num(),
|
||||
m_rlc.block(bsn)->cs_last.to_num(),
|
||||
ms()->current_cs_dl().to_num(),
|
||||
m_rlc.block(bsn)->cs_current_trans.to_num(),
|
||||
bts->bts_data()->dl_arq_type, bsn);
|
||||
|
||||
/* TODO: Need to remove this check when MCS-8 -> MCS-6
|
||||
* transistion is handled.
|
||||
* Refer commit be881c028fc4da00c4046ecd9296727975c206a3
|
||||
*/
|
||||
if (m_rlc.block(bsn)->cs_init == GprsCodingScheme::MCS8)
|
||||
m_rlc.block(bsn)->cs_current_trans =
|
||||
GprsCodingScheme::MCS8;
|
||||
} else
|
||||
m_rlc.block(bsn)->cs_current_trans =
|
||||
m_rlc.block(bsn)->cs_last;
|
||||
|
||||
data_len2 = m_rlc.block(bsn)->len;
|
||||
if (force_data_len > 0 && force_data_len != data_len2)
|
||||
return -1;
|
||||
|
@ -402,13 +425,14 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
|||
if (restart_bsn_cycle())
|
||||
return take_next_bsn(fn, previous_bsn, may_combine);
|
||||
} else if (have_data()) {
|
||||
GprsCodingScheme new_cs;
|
||||
/* New blocks may be send */
|
||||
cs2 = force_cs ? force_cs : current_cs();
|
||||
new_cs = force_cs ? force_cs : current_cs();
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"- Sending new block at BSN %d, CS=%s\n",
|
||||
m_window.v_s(), cs2.name());
|
||||
m_window.v_s(), new_cs.name());
|
||||
|
||||
bsn = create_new_bsn(fn, cs2);
|
||||
bsn = create_new_bsn(fn, new_cs);
|
||||
} else if (!m_window.window_empty()) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
|
||||
"because all blocks have been transmitted (FLOW).\n",
|
||||
|
@ -422,7 +446,7 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
|||
"- Sending new dummy block at BSN %d, CS=%s\n",
|
||||
m_window.v_s(), current_cs().name());
|
||||
bsn = create_new_bsn(fn, current_cs());
|
||||
/* Don't send a second block, so don't set cs2 */
|
||||
/* Don't send a second block, so don't set cs_current_trans */
|
||||
}
|
||||
|
||||
if (bsn < 0) {
|
||||
|
@ -433,7 +457,7 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
|||
bts->rlc_resent();
|
||||
}
|
||||
|
||||
*may_combine = cs2.numDataBlocks() > 1;
|
||||
*may_combine = m_rlc.block(bsn)->cs_current_trans.numDataBlocks() > 1;
|
||||
|
||||
return bsn;
|
||||
}
|
||||
|
@ -504,7 +528,13 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
/* now we still have untransmitted LLC data, so we fill mac block */
|
||||
rlc_data = m_rlc.block(bsn);
|
||||
data = rlc_data->prepare(block_data_len);
|
||||
rlc_data->cs = cs;
|
||||
rlc_data->cs_last = cs;
|
||||
rlc_data->cs_current_trans = cs;
|
||||
|
||||
/* Initialise the variable related to DL SPB */
|
||||
rlc_data->spb_status.block_status_dl = EGPRS_RESEG_DL_DEFAULT;
|
||||
rlc_data->cs_init = cs;
|
||||
|
||||
rlc_data->len = block_data_len;
|
||||
|
||||
rdbi = &(rlc_data->block_info);
|
||||
|
@ -517,8 +547,11 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
|
||||
do {
|
||||
bool is_final;
|
||||
int payload_written = 0;
|
||||
|
||||
if (m_llc.frame_length() == 0) {
|
||||
/* nothing to sent - delay the release of the TBF */
|
||||
|
||||
int space = block_data_len - write_offset;
|
||||
/* A header will need to by added, so we just need
|
||||
* space-1 octets */
|
||||
|
@ -542,7 +575,10 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
is_final = llc_queue()->size() == 0 && !keep_open(fn);
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(rdbi, cs,
|
||||
&m_llc, &write_offset, &num_chunks, data, is_final);
|
||||
&m_llc, &write_offset, &num_chunks, data, is_final, &payload_written);
|
||||
|
||||
if (payload_written > 0)
|
||||
bts->rlc_dl_payload_bytes(payload_written);
|
||||
|
||||
if (ar == Encoding::AR_NEED_MORE_BLOCKS)
|
||||
break;
|
||||
|
@ -550,6 +586,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s"
|
||||
"len=%d\n", tbf_name(this), m_llc.frame_length());
|
||||
gprs_rlcmac_dl_bw(this, m_llc.frame_length());
|
||||
bts->llc_dl_bytes(m_llc.frame_length());
|
||||
m_llc.reset();
|
||||
|
||||
if (is_final) {
|
||||
|
@ -562,7 +599,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
} while (ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "data block (BSN %d, %s): %s\n",
|
||||
bsn, rlc_data->cs.name(),
|
||||
bsn, rlc_data->cs_last.name(),
|
||||
osmo_hexdump(rlc_data->block, block_data_len));
|
||||
/* raise send state and set ack state array */
|
||||
m_window.m_v_b.mark_unacked(bsn);
|
||||
|
@ -589,9 +626,10 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
GprsCodingScheme cs;
|
||||
int bsns[ARRAY_SIZE(rlc.block_info)];
|
||||
unsigned num_bsns;
|
||||
int punct[ARRAY_SIZE(rlc.block_info)];
|
||||
enum egprs_puncturing_values punct[ARRAY_SIZE(rlc.block_info)];
|
||||
bool need_padding = false;
|
||||
|
||||
enum egprs_rlcmac_dl_spb spb = EGPRS_RLCMAC_DL_NO_RETX;
|
||||
unsigned int spb_status = get_egprs_dl_spb_status(index);
|
||||
/*
|
||||
* TODO: This is an experimental work-around to put 2 BSN into
|
||||
* MSC-7 to MCS-9 encoded messages. It just sends the same BSN
|
||||
|
@ -600,7 +638,8 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
* be put into the data area, even if the resulting CS is higher than
|
||||
* the current limit.
|
||||
*/
|
||||
cs = m_rlc.block(index)->cs;
|
||||
cs = m_rlc.block(index)->cs_current_trans;
|
||||
GprsCodingScheme &cs_init = m_rlc.block(index)->cs_init;
|
||||
bsns[0] = index;
|
||||
num_bsns = 1;
|
||||
|
||||
|
@ -609,14 +648,36 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
num_bsns += 1;
|
||||
}
|
||||
|
||||
if (num_bsns == 1) {
|
||||
/*
|
||||
* if the intial mcs is 8 and retransmission mcs is either 6 or 3
|
||||
* we have to include the padding of 6 octets in first segment
|
||||
*/
|
||||
if ((GprsCodingScheme::Scheme(cs_init) == GprsCodingScheme::MCS8) &&
|
||||
(GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6 ||
|
||||
GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS3)) {
|
||||
if (spb_status == EGPRS_RESEG_DL_DEFAULT ||
|
||||
spb_status == EGPRS_RESEG_SECOND_SEG_SENT)
|
||||
need_padding = true;
|
||||
} else if (num_bsns == 1) {
|
||||
/* TODO: remove the conditional when MCS-6 padding isn't
|
||||
* failing to be decoded by MEs anymore */
|
||||
/* TODO: support of MCS-8 -> MCS-6 transition should be
|
||||
* handled
|
||||
* Refer commit be881c028fc4da00c4046ecd9296727975c206a3
|
||||
* dated 2016-02-07 23:45:40 (UTC)
|
||||
*/
|
||||
if (cs != GprsCodingScheme(GprsCodingScheme::MCS8))
|
||||
cs.decToSingleBlock(&need_padding);
|
||||
}
|
||||
|
||||
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding);
|
||||
spb = get_egprs_dl_spb(index);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- need_padding %d spb_status %d spb %d"
|
||||
"(BSN1 %d BSN2 %d)\n",
|
||||
need_padding,
|
||||
spb_status, spb, index, index2);
|
||||
|
||||
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding, spb);
|
||||
|
||||
rlc.usf = 7; /* will be set at scheduler */
|
||||
rlc.pr = 0; /* FIXME: power reduction */
|
||||
|
@ -630,14 +691,17 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
|
||||
msg_data = msgb_put(dl_msg, msg_len);
|
||||
|
||||
OSMO_ASSERT(rlc.num_data_blocks <= ARRAY_SIZE(rlc.block_info));
|
||||
OSMO_ASSERT(rlc.num_data_blocks > 0);
|
||||
|
||||
/* Copy block(s) to RLC message */
|
||||
for (data_block_idx = 0; data_block_idx < rlc.num_data_blocks;
|
||||
data_block_idx++)
|
||||
{
|
||||
int bsn;
|
||||
GprsCodingScheme cs_enc;
|
||||
uint8_t *block_data;
|
||||
gprs_rlc_data_block_info *rdbi, *block_info;
|
||||
enum egprs_rlc_dl_reseg_bsn_state reseg_status;
|
||||
|
||||
/* Check if there are more blocks than BSNs */
|
||||
if (data_block_idx < num_bsns)
|
||||
|
@ -645,25 +709,45 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
else
|
||||
bsn = bsns[0];
|
||||
|
||||
cs_enc = m_rlc.block(bsn)->cs;
|
||||
/* Get current puncturing scheme from block */
|
||||
|
||||
/* get data and header from current block */
|
||||
block_data = m_rlc.block(bsn)->block;
|
||||
m_rlc.block(bsn)->next_ps = gprs_get_punct_scheme(
|
||||
m_rlc.block(bsn)->next_ps,
|
||||
m_rlc.block(bsn)->cs_last, cs, spb);
|
||||
|
||||
/* TODO: Use real puncturing values */
|
||||
punct[data_block_idx] = data_block_idx;
|
||||
if (cs.isEgprs()) {
|
||||
OSMO_ASSERT(m_rlc.block(bsn)->next_ps >= EGPRS_PS_1);
|
||||
OSMO_ASSERT(m_rlc.block(bsn)->next_ps <= EGPRS_PS_3);
|
||||
}
|
||||
punct[data_block_idx] = m_rlc.block(bsn)->next_ps;
|
||||
|
||||
rdbi = &rlc.block_info[data_block_idx];
|
||||
block_info = &m_rlc.block(bsn)->block_info;
|
||||
|
||||
if(rdbi->data_len != m_rlc.block(bsn)->len) {
|
||||
LOGP(DRLCMACDL, LOGL_ERROR,
|
||||
"ERROR: Expected len = %d for %s instead of "
|
||||
"%d in data unit %d (BSN %d, %s)\n",
|
||||
rdbi->data_len, cs.name(), m_rlc.block(bsn)->len,
|
||||
data_block_idx, bsn, cs_enc.name());
|
||||
OSMO_ASSERT(rdbi->data_len == m_rlc.block(bsn)->len);
|
||||
/*
|
||||
* get data and header from current block
|
||||
* function returns the reseg status
|
||||
*/
|
||||
reseg_status = egprs_dl_get_data(bsn, &block_data);
|
||||
m_rlc.block(bsn)->spb_status.block_status_dl = reseg_status;
|
||||
|
||||
/*
|
||||
* If it is first segment of the split block set the state of
|
||||
* bsn to nacked. If it is the first segment dont update the
|
||||
* next ps value of bsn. since next segment also needs same cps
|
||||
*/
|
||||
if (spb == EGPRS_RLCMAC_DL_FIRST_SEG)
|
||||
m_window.m_v_b.mark_nacked(bsn);
|
||||
else {
|
||||
/*
|
||||
* TODO: Need to handle 2 same bsns
|
||||
* in header type 1
|
||||
*/
|
||||
gprs_update_punct_scheme(&m_rlc.block(bsn)->next_ps,
|
||||
cs);
|
||||
}
|
||||
|
||||
m_rlc.block(bsn)->cs_last = cs;
|
||||
rdbi->e = block_info->e;
|
||||
rdbi->cv = block_info->cv;
|
||||
rdbi->bsn = bsn;
|
||||
|
@ -742,8 +826,6 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
/* Increment TX-counter */
|
||||
m_tx_counter++;
|
||||
|
||||
bts->rlc_sent();
|
||||
|
||||
return dl_msg;
|
||||
}
|
||||
|
||||
|
@ -763,7 +845,8 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
|
|||
uint16_t bsn = 0;
|
||||
unsigned received_bytes = 0, lost_bytes = 0;
|
||||
unsigned received_packets = 0, lost_packets = 0;
|
||||
unsigned num_blocks = strlen(show_rbb);
|
||||
unsigned num_blocks = strlen(show_rbb) > (unsigned)m_window.distance()
|
||||
? m_window.distance() : strlen(show_rbb);
|
||||
|
||||
/* SSN - 1 is in range V(A)..V(S)-1 */
|
||||
for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
|
||||
|
@ -796,7 +879,7 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
|
|||
|
||||
/* Get statistics for current CS */
|
||||
|
||||
if (rlc_data->cs != current_cs()) {
|
||||
if (rlc_data->cs_last != current_cs()) {
|
||||
/* This block has already been encoded with a different
|
||||
* CS, so it doesn't help us to decide, whether the
|
||||
* current CS is ok. Ignore it. */
|
||||
|
@ -843,7 +926,8 @@ int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
|
|||
char show_rbb[RLC_MAX_SNS + 1];
|
||||
int error_rate;
|
||||
struct ana_result ana_res;
|
||||
unsigned num_blocks = rbb->cur_bit;
|
||||
unsigned num_blocks = rbb->cur_bit > (unsigned)m_window.distance()
|
||||
? m_window.distance() : rbb->cur_bit;
|
||||
unsigned behind_last_bsn = m_window.mod_sns(first_bsn + num_blocks);
|
||||
|
||||
Decoding::extract_rbb(rbb, show_rbb);
|
||||
|
@ -866,8 +950,7 @@ int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
|
|||
* TODO: check whether this FIXME still makes sense
|
||||
*/
|
||||
LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of "
|
||||
"V(A)..V(S) range %s Free TBF!\n", tbf_name(this));
|
||||
return 1; /* indicate to free TBF */
|
||||
"V(A)..V(S) range %s\n", tbf_name(this));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1125,3 +1208,138 @@ bool gprs_rlcmac_dl_tbf::keep_open(unsigned fn) const
|
|||
keep_time_frames = msecs_to_frames(bts_data()->dl_tbf_idle_msec);
|
||||
return frames_since_last_drain(fn) <= keep_time_frames;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the pointer to data which needs
|
||||
* to be copied. Also updates the status of the block related to
|
||||
* Split block handling in the RLC/MAC block.
|
||||
*/
|
||||
enum egprs_rlc_dl_reseg_bsn_state
|
||||
gprs_rlcmac_dl_tbf::egprs_dl_get_data(int bsn, uint8_t **block_data)
|
||||
{
|
||||
gprs_rlc_data *rlc_data = m_rlc.block(bsn);
|
||||
egprs_rlc_dl_reseg_bsn_state *block_status_dl =
|
||||
&rlc_data->spb_status.block_status_dl;
|
||||
|
||||
GprsCodingScheme &cs_current_trans = m_rlc.block(bsn)->cs_current_trans;
|
||||
GprsCodingScheme &cs_init = m_rlc.block(bsn)->cs_init;
|
||||
*block_data = &rlc_data->block[0];
|
||||
|
||||
/*
|
||||
* Table 10.3a.0.1 of 44.060
|
||||
* MCS6,9: second segment starts at 74/2 = 37
|
||||
* MCS5,7: second segment starts at 56/2 = 28
|
||||
* MCS8: second segment starts at 31
|
||||
* MCS4: second segment starts at 44/2 = 22
|
||||
*/
|
||||
if (cs_current_trans.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3) {
|
||||
if (*block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
|
||||
switch (GprsCodingScheme::Scheme(cs_init)) {
|
||||
case GprsCodingScheme::MCS6 :
|
||||
case GprsCodingScheme::MCS9 :
|
||||
*block_data = &rlc_data->block[37];
|
||||
break;
|
||||
case GprsCodingScheme::MCS7 :
|
||||
case GprsCodingScheme::MCS5 :
|
||||
*block_data = &rlc_data->block[28];
|
||||
break;
|
||||
case GprsCodingScheme::MCS8 :
|
||||
*block_data = &rlc_data->block[31];
|
||||
break;
|
||||
case GprsCodingScheme::MCS4 :
|
||||
*block_data = &rlc_data->block[22];
|
||||
break;
|
||||
default:
|
||||
LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
|
||||
"--%s hit invalid condition. headerType(%d) "
|
||||
" blockstatus(%d) cs(%s) PLEASE FIX!\n", name(),
|
||||
cs_current_trans.headerTypeData(),
|
||||
*block_status_dl, cs_init.name());
|
||||
break;
|
||||
|
||||
}
|
||||
return EGPRS_RESEG_SECOND_SEG_SENT;
|
||||
} else if ((cs_init.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
|
||||
(cs_init.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
|
||||
return EGPRS_RESEG_FIRST_SEG_SENT;
|
||||
} else if ((GprsCodingScheme::Scheme(cs_init) ==
|
||||
GprsCodingScheme::MCS4) &&
|
||||
(GprsCodingScheme::Scheme(cs_current_trans) ==
|
||||
GprsCodingScheme::MCS1)) {
|
||||
return EGPRS_RESEG_FIRST_SEG_SENT;
|
||||
}
|
||||
}
|
||||
return EGPRS_RESEG_DL_DEFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the status of split block
|
||||
* for RLC/MAC block.
|
||||
*/
|
||||
unsigned int gprs_rlcmac_dl_tbf::get_egprs_dl_spb_status(const int bsn)
|
||||
{
|
||||
const gprs_rlc_data *rlc_data = m_rlc.block(bsn);
|
||||
|
||||
return rlc_data->spb_status.block_status_dl;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the spb value to be sent OTA
|
||||
* for RLC/MAC block.
|
||||
*/
|
||||
enum egprs_rlcmac_dl_spb gprs_rlcmac_dl_tbf::get_egprs_dl_spb(const int bsn)
|
||||
{
|
||||
struct gprs_rlc_data *rlc_data = m_rlc.block(bsn);
|
||||
egprs_rlc_dl_reseg_bsn_state block_status_dl =
|
||||
rlc_data->spb_status.block_status_dl;
|
||||
|
||||
GprsCodingScheme &cs_current_trans = m_rlc.block(bsn)->cs_current_trans;
|
||||
GprsCodingScheme &cs_init = m_rlc.block(bsn)->cs_init;
|
||||
|
||||
/* Table 10.4.8b.1 of 44.060 */
|
||||
if (cs_current_trans.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3) {
|
||||
/*
|
||||
* if we are sending the second segment the spb should be 3
|
||||
* other wise it should be 2
|
||||
*/
|
||||
if (block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
|
||||
return EGPRS_RLCMAC_DL_SEC_SEG;
|
||||
} else if ((cs_init.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
|
||||
(cs_init.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
|
||||
return EGPRS_RLCMAC_DL_FIRST_SEG;
|
||||
} else if ((GprsCodingScheme::Scheme(cs_init) ==
|
||||
GprsCodingScheme::MCS4) &&
|
||||
(GprsCodingScheme::Scheme(cs_current_trans) ==
|
||||
GprsCodingScheme::MCS1)) {
|
||||
return EGPRS_RLCMAC_DL_FIRST_SEG;
|
||||
}
|
||||
}
|
||||
/* Non SPB cases 0 is reurned */
|
||||
return EGPRS_RLCMAC_DL_NO_RETX;
|
||||
}
|
||||
|
||||
void gprs_rlcmac_dl_tbf::egprs_calc_window_size()
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data = bts->bts_data();
|
||||
unsigned int num_pdch = pcu_bitcount(dl_slots());
|
||||
unsigned int ws = bts_data->ws_base + num_pdch * bts_data->ws_pdch;
|
||||
|
||||
ws = (ws / 32) * 32;
|
||||
ws = OSMO_MAX(64, ws);
|
||||
|
||||
if (num_pdch == 1)
|
||||
ws = OSMO_MIN(192, ws);
|
||||
else
|
||||
ws = OSMO_MIN(128 * num_pdch, ws);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "%s: Setting EGPRS window size to %d\n",
|
||||
name(), ws);
|
||||
|
||||
m_window.set_ws(ws);
|
||||
}
|
||||
|
|
211
src/tbf_ul.cpp
211
src/tbf_ul.cpp
|
@ -29,6 +29,8 @@
|
|||
#include <decoding.h>
|
||||
#include <pcu_l1_if.h>
|
||||
|
||||
#include "pcu_utils.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
@ -51,7 +53,7 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
|
|||
const uint8_t *data = _data->block;
|
||||
uint8_t len = _data->len;
|
||||
const struct gprs_rlc_data_block_info *rdbi = &_data->block_info;
|
||||
GprsCodingScheme cs = _data->cs;
|
||||
GprsCodingScheme cs = _data->cs_last;
|
||||
|
||||
Decoding::RlcData frames[16], *frame;
|
||||
int i, num_frames = 0;
|
||||
|
@ -60,25 +62,32 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
|
|||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
|
||||
|
||||
num_frames = Decoding::rlc_data_from_ul_data(
|
||||
rdbi, cs, data, &(frames[0]), sizeof(frames),
|
||||
rdbi, cs, data, &(frames[0]), ARRAY_SIZE(frames),
|
||||
&dummy_tlli);
|
||||
|
||||
/* create LLC frames */
|
||||
for (i = 0; i < num_frames; i++) {
|
||||
frame = frames + i;
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset %d, "
|
||||
"length=%d, is_complete=%d\n",
|
||||
i + 1, frame->offset, frame->length, frame->is_complete);
|
||||
if (frame->length) {
|
||||
bts->rlc_ul_payload_bytes(frame->length);
|
||||
|
||||
m_llc.append_frame(data + frame->offset, frame->length);
|
||||
m_llc.consume(frame->length);
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d "
|
||||
"starts at offset %d, "
|
||||
"length=%d, is_complete=%d\n",
|
||||
i + 1, frame->offset, frame->length,
|
||||
frame->is_complete);
|
||||
|
||||
m_llc.append_frame(data + frame->offset, frame->length);
|
||||
m_llc.consume(frame->length);
|
||||
}
|
||||
|
||||
if (frame->is_complete) {
|
||||
/* send frame to SGSN */
|
||||
LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n",
|
||||
tbf_name(this) , m_llc.frame_length());
|
||||
snd_ul_ud();
|
||||
bts->llc_ul_bytes(m_llc.frame_length());
|
||||
m_llc.reset();
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +147,7 @@ struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn, uint8_t ts)
|
|||
return msg;
|
||||
}
|
||||
|
||||
/*! \brief receive data from PDCH/L1 */
|
||||
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
uint8_t *data, struct pcu_l1_meas *meas)
|
||||
|
@ -219,36 +229,28 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
|
|||
rdbi->bsn, m_window.v_q(),
|
||||
m_window.mod_sns(m_window.v_q() + ws - 1));
|
||||
block = m_rlc.block(rdbi->bsn);
|
||||
block->block_info = *rdbi;
|
||||
block->cs = rlc->cs;
|
||||
OSMO_ASSERT(rdbi->data_len < sizeof(block->block));
|
||||
OSMO_ASSERT(rdbi->data_len <= sizeof(block->block));
|
||||
rlc_data = &(block->block[0]);
|
||||
/* TODO: Handle SPB != 0 -> Set length to 2*len, add offset if
|
||||
* 2nd part. Note that resegmentation is currently disabled
|
||||
* within the UL assignment.
|
||||
*/
|
||||
if (rdbi->spb) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"Got SPB != 0 but resegmentation has been "
|
||||
"disabled, skipping %s data block with BSN %d, "
|
||||
"TFI=%d.\n", rlc->cs.name(), rdbi->bsn,
|
||||
rlc->tfi);
|
||||
continue;
|
||||
}
|
||||
|
||||
block->len =
|
||||
Decoding::rlc_copy_to_aligned_buffer(rlc, block_idx, data,
|
||||
rlc_data);
|
||||
if (rdbi->spb) {
|
||||
egprs_rlc_ul_reseg_bsn_state assemble_status;
|
||||
|
||||
assemble_status = handle_egprs_ul_spb(rlc,
|
||||
block, data, block_idx);
|
||||
|
||||
if (assemble_status != EGPRS_RESEG_DEFAULT)
|
||||
return 0;
|
||||
} else {
|
||||
block->block_info = *rdbi;
|
||||
block->cs_last = rlc->cs;
|
||||
block->len =
|
||||
Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data, rlc_data);
|
||||
}
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"%s: data_length=%d, data=%s\n",
|
||||
name(), block->len, osmo_hexdump(rlc_data, block->len));
|
||||
|
||||
/* TODO: Handle SPB != 0 -> set state to partly received
|
||||
* (upper/lower) and continue with the loop, unless the other
|
||||
* part is already present.
|
||||
*/
|
||||
|
||||
/* Get/Handle TLLI */
|
||||
if (rdbi->ti) {
|
||||
num_chunks = Decoding::rlc_data_from_ul_data(
|
||||
|
@ -389,3 +391,150 @@ int gprs_rlcmac_ul_tbf::snd_ul_ud()
|
|||
return 0;
|
||||
}
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_second_seg(
|
||||
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx)
|
||||
{
|
||||
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
|
||||
union split_block_status *spb_status = &block->spb_status;
|
||||
uint8_t *rlc_data = &block->block[0];
|
||||
|
||||
if (spb_status->block_status_ul &
|
||||
EGPRS_RESEG_FIRST_SEG_RXD) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"---%s: Second seg is received "
|
||||
"first seg is already present "
|
||||
"set the status to complete\n", name());
|
||||
spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
|
||||
|
||||
block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data, rlc_data + block->len);
|
||||
block->block_info.data_len += rdbi->data_len;
|
||||
} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"---%s: Second seg is received "
|
||||
"first seg is not received "
|
||||
"set the status to second seg received\n",
|
||||
name());
|
||||
|
||||
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data,
|
||||
rlc_data + rlc->block_info[block_idx].data_len);
|
||||
|
||||
spb_status->block_status_ul = EGPRS_RESEG_SECOND_SEG_RXD;
|
||||
block->block_info = *rdbi;
|
||||
}
|
||||
return spb_status->block_status_ul;
|
||||
}
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_first_seg(
|
||||
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx)
|
||||
{
|
||||
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
|
||||
uint8_t *rlc_data = &block->block[0];
|
||||
union split_block_status *spb_status = &block->spb_status;
|
||||
|
||||
if (spb_status->block_status_ul & EGPRS_RESEG_SECOND_SEG_RXD) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"---%s: First seg is received "
|
||||
"second seg is already present "
|
||||
"set the status to complete\n", name());
|
||||
|
||||
block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data, rlc_data);
|
||||
|
||||
block->block_info.data_len = block->len;
|
||||
spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
|
||||
} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"---%s: First seg is received "
|
||||
"second seg is not received "
|
||||
"set the status to first seg "
|
||||
"received\n", name());
|
||||
|
||||
spb_status->block_status_ul = EGPRS_RESEG_FIRST_SEG_RXD;
|
||||
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data, rlc_data);
|
||||
block->block_info = *rdbi;
|
||||
}
|
||||
return spb_status->block_status_ul;
|
||||
}
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_spb(
|
||||
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx)
|
||||
{
|
||||
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"--%s: Got SPB(%d) "
|
||||
"cs(%s) data block with BSN (%d), "
|
||||
"TFI(%d).\n", name(), rdbi->spb, rlc->cs.name(), rdbi->bsn,
|
||||
rlc->tfi);
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state assemble_status = EGPRS_RESEG_INVALID;
|
||||
|
||||
/* Section 10.4.8b of 44.060*/
|
||||
if (rdbi->spb == 2)
|
||||
assemble_status = handle_egprs_ul_first_seg(rlc,
|
||||
block, data, block_idx);
|
||||
else if (rdbi->spb == 3)
|
||||
assemble_status = handle_egprs_ul_second_seg(rlc,
|
||||
block, data, block_idx);
|
||||
else {
|
||||
LOGP(DRLCMACUL, LOGL_ERROR,
|
||||
"--%s: spb(%d) Not supported SPB for this EGPRS "
|
||||
"configuration\n",
|
||||
name(), rdbi->spb);
|
||||
}
|
||||
|
||||
/*
|
||||
* When the block is successfully constructed out of segmented blocks
|
||||
* upgrade the MCS to the type 2
|
||||
*/
|
||||
if (assemble_status == EGPRS_RESEG_DEFAULT) {
|
||||
switch (GprsCodingScheme::Scheme(rlc->cs)) {
|
||||
case GprsCodingScheme::MCS3 :
|
||||
block->cs_last = GprsCodingScheme::MCS6;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"--%s: Upgrading to MCS6\n", name());
|
||||
break;
|
||||
case GprsCodingScheme::MCS2 :
|
||||
block->cs_last = GprsCodingScheme::MCS5;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"--%s: Upgrading to MCS5\n", name());
|
||||
break;
|
||||
case GprsCodingScheme::MCS1 :
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"--%s: Upgrading to MCS4\n", name());
|
||||
block->cs_last = GprsCodingScheme::MCS4;
|
||||
break;
|
||||
default:
|
||||
LOGP(DRLCMACUL, LOGL_ERROR,
|
||||
"--%s: cs(%s) Error in Upgrading to higher MCS\n",
|
||||
name(), rlc->cs.name());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return assemble_status;
|
||||
}
|
||||
|
||||
void gprs_rlcmac_ul_tbf::egprs_calc_ulwindow_size()
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data = bts->bts_data();
|
||||
unsigned int num_pdch = pcu_bitcount(ul_slots());
|
||||
unsigned int ws = bts_data->ws_base + num_pdch * bts_data->ws_pdch;
|
||||
ws = (ws / 32) * 32;
|
||||
ws = OSMO_MAX(64, ws);
|
||||
|
||||
if (num_pdch == 1)
|
||||
ws = OSMO_MIN(192, ws);
|
||||
else
|
||||
ws = OSMO_MIN(128 * num_pdch, ws);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "%s: Setting EGPRS window size to %d, base(%d) slots(%d) ws_pdch(%d)\n",
|
||||
name(), ws, bts_data->ws_base, num_pdch, bts_data->ws_pdch);
|
||||
|
||||
m_window.set_ws(ws);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/
|
||||
AM_LDFLAGS = -lrt
|
||||
|
||||
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test edge/EdgeTest
|
||||
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test edge/EdgeTest bitcomp/BitcompTest
|
||||
noinst_PROGRAMS = emu/pcu_emu
|
||||
|
||||
rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp
|
||||
|
@ -26,6 +26,11 @@ tbf_TbfTest_LDADD = \
|
|||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
bitcomp_BitcompTest_SOURCES = bitcomp/BitcompTest.cpp ../src/egprs_rlc_compression.cpp
|
||||
bitcomp_BitcompTest_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
edge_EdgeTest_SOURCES = edge/EdgeTest.cpp
|
||||
edge_EdgeTest_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
|
@ -108,6 +113,7 @@ EXTRA_DIST = \
|
|||
rlcmac/RLCMACTest.ok rlcmac/RLCMACTest.err \
|
||||
alloc/AllocTest.ok alloc/AllocTest.err \
|
||||
tbf/TbfTest.ok tbf/TbfTest.err \
|
||||
bitcomp/BitcompTest.ok bitcomp/BitcompTest.err \
|
||||
types/TypesTest.ok types/TypesTest.err \
|
||||
ms/MsTest.ok ms/MsTest.err \
|
||||
llc/LlcTest.ok llc/LlcTest.err \
|
||||
|
|
|
@ -118,6 +118,7 @@ static void test_alloc_a(gprs_rlcmac_tbf_direction dir,
|
|||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_a;
|
||||
bts->maximise_dir = NO_MAXIMISE;
|
||||
|
||||
struct gprs_rlcmac_trx *trx = &bts->trx[0];
|
||||
for (i = 0; i < 8; i += 1)
|
||||
|
@ -196,6 +197,7 @@ static void test_alloc_b(int ms_class)
|
|||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
bts->maximise_dir = NO_MAXIMISE;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[5].enable();
|
||||
|
@ -238,6 +240,7 @@ static void test_alloc_b(int ms_class)
|
|||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
bts->maximise_dir = NO_MAXIMISE;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[5].enable();
|
||||
|
@ -285,6 +288,7 @@ static void test_alloc_b(int ms_class)
|
|||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
bts->maximise_dir = NO_MAXIMISE;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[1].enable();
|
||||
|
@ -660,6 +664,7 @@ static void test_successive_allocation(algo_t algo, unsigned min_class,
|
|||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = algo;
|
||||
bts->maximise_dir = NO_MAXIMISE;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[3].enable();
|
||||
|
@ -698,6 +703,7 @@ static void test_many_connections(algo_t algo, unsigned expect_num,
|
|||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = algo;
|
||||
bts->maximise_dir = NO_MAXIMISE;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[3].enable();
|
||||
|
@ -792,6 +798,52 @@ static void test_many_connections()
|
|||
test_many_connections(alloc_algorithm_dynamic, 160, "algorithm dynamic");
|
||||
}
|
||||
|
||||
static void test_2_consecutive_dl_tbfs()
|
||||
{
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
uint8_t ms_class = 11;
|
||||
uint8_t egprs_ms_class = 11;
|
||||
gprs_rlcmac_tbf *dl_tbf1, *dl_tbf2;
|
||||
uint8_t numTs1 = 0, numTs2 = 0;
|
||||
|
||||
printf("Testing DL TS allocation for Multi UEs\n");
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
bts->maximise_dir = DL_ONLY;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[4].enable();
|
||||
trx->pdch[5].enable();
|
||||
trx->pdch[6].enable();
|
||||
trx->pdch[7].enable();
|
||||
|
||||
dl_tbf1 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf1);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (dl_tbf1->pdch[i])
|
||||
numTs1++;
|
||||
}
|
||||
OSMO_ASSERT(numTs1 == 4);
|
||||
printf("TBF1: numTs(%d)\n", numTs1);
|
||||
|
||||
dl_tbf2 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf2);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (dl_tbf2->pdch[i])
|
||||
numTs2++;
|
||||
}
|
||||
printf("TBF2: numTs(%d)\n", numTs2);
|
||||
OSMO_ASSERT(numTs2 == 4);
|
||||
|
||||
tbf_free(dl_tbf1);
|
||||
tbf_free(dl_tbf2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile AllocTest context");
|
||||
|
@ -809,6 +861,7 @@ int main(int argc, char **argv)
|
|||
test_alloc_b();
|
||||
test_successive_allocation();
|
||||
test_many_connections();
|
||||
test_2_consecutive_dl_tbfs();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -10793,3 +10793,6 @@ Going to test assignment with many connections, algorithm dynamic
|
|||
TBF[31] class 14 reserves ......C.
|
||||
TBF[31] class 15 reserves .......C
|
||||
Successfully allocated 160 TBFs
|
||||
Testing DL TS allocation for Multi UEs
|
||||
TBF1: numTs(4)
|
||||
TBF2: numTs(4)
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rlc.h"
|
||||
#include "gprs_debug.h"
|
||||
#include <gprs_rlcmac.h>
|
||||
#include "egprs_rlc_compression.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/bitvec.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
}
|
||||
|
||||
#define NEW 1
|
||||
#define MASK(n) (0xFF << (8-n))
|
||||
#define MAX_CRBB_LEN 23
|
||||
#define MAX_URBB_LEN 40
|
||||
|
||||
void *tall_pcu_ctx;
|
||||
|
||||
struct test_data {
|
||||
int8_t crbb_len;
|
||||
uint8_t cc;
|
||||
uint8_t crbb_data[MAX_CRBB_LEN]; /* compressed data */
|
||||
uint8_t ucmp_data[MAX_URBB_LEN]; /* uncompressed data */
|
||||
unsigned int ucmp_len;
|
||||
int verify;
|
||||
} test[] = {
|
||||
{ .crbb_len = 67, .cc = 1,
|
||||
.crbb_data = {
|
||||
0x02, 0x0c, 0xa0, 0x30, 0xcb, 0x1a, 0x0c, 0xe3, 0x6c
|
||||
},
|
||||
.ucmp_data = {
|
||||
0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xff, 0xff,
|
||||
0xff, 0xf8, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe,
|
||||
0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xdb
|
||||
},
|
||||
.ucmp_len = 194, .verify = 1
|
||||
},
|
||||
{ .crbb_len = 40, .cc = 1,
|
||||
.crbb_data = {
|
||||
0x53, 0x06, 0xc5, 0x40, 0x6d
|
||||
},
|
||||
.ucmp_data = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03
|
||||
},
|
||||
.ucmp_len = 182, .verify = 1
|
||||
},
|
||||
{ .crbb_len = 8, .cc = 1,
|
||||
.crbb_data = {0x02},
|
||||
.ucmp_data = {0xff, 0xff, 0xff, 0xf8},
|
||||
.ucmp_len = 29, .verify = 1
|
||||
},
|
||||
{ .crbb_len = 103, .cc = 1,
|
||||
.crbb_data = {
|
||||
0x02, 0x0c, 0xe0, 0x41, 0xa0, 0x0c, 0x36, 0x0d, 0x03,
|
||||
0x71, 0xb0, 0x6e, 0x24
|
||||
},
|
||||
.ucmp_data = {
|
||||
0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xff,
|
||||
0xf8, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00,
|
||||
0x0f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x80, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff
|
||||
},
|
||||
.ucmp_len = 288, .verify = 1
|
||||
},
|
||||
/* Test vector from libosmocore test */
|
||||
{ .crbb_len = 18, .cc = 1,
|
||||
.crbb_data = {0xdd, 0x41, 0x00},
|
||||
.ucmp_data = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x00, 0x00
|
||||
},
|
||||
.ucmp_len = 90, .verify = 1
|
||||
},
|
||||
{ .crbb_len = 35, .cc = 0,
|
||||
.crbb_data = {0xde, 0x88, 0x75, 0x65, 0x80},
|
||||
.ucmp_data = {0x37, 0x47, 0x81, 0xf0},
|
||||
.ucmp_len = 28, .verify = 1
|
||||
},
|
||||
/*Invalid inputs*/
|
||||
{ .crbb_len = 18, .cc = 1,
|
||||
.crbb_data = {0x1E, 0x70, 0xc0},
|
||||
.ucmp_data = {0x0},
|
||||
.ucmp_len = 0, .verify = 0
|
||||
},
|
||||
{ .crbb_len = 14, .cc = 1,
|
||||
.crbb_data = {0x00, 0x1E, 0x7c},
|
||||
.ucmp_data = {0x0},
|
||||
.ucmp_len = 0, .verify = 0
|
||||
},
|
||||
{ .crbb_len = 24, .cc = 0,
|
||||
.crbb_data = {0x00, 0x00, 0x00},
|
||||
.ucmp_data = {0x0},
|
||||
.ucmp_len = 0, .verify = 0
|
||||
}
|
||||
};
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
|
||||
{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_DEBUG, 1},
|
||||
{"DRLCMAC", "\033[0;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Downlink (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACMEAS", "\033[1;31m", "GPRS RLC/MAC layer Measurements (RLCMAC)", LOGL_INFO, 1},
|
||||
{"DNS", "\033[1;34m", "GPRS Network Service Protocol (NS)", LOGL_INFO, 1},
|
||||
{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO, 1},
|
||||
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
|
||||
};
|
||||
|
||||
static int filter_fn(const struct log_context *ctx,
|
||||
struct log_target *tar)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* To verify the result with expected result */
|
||||
int check_result(bitvec bits, uint8_t *exp_data, int exp_len)
|
||||
{
|
||||
if (bits.cur_bit != exp_len)
|
||||
return 0;
|
||||
size_t n = (exp_len / 8);
|
||||
int rem = (exp_len % 8);
|
||||
|
||||
if (memcmp(exp_data, bits.data, n) == 0) {
|
||||
if (rem == 0)
|
||||
return 1;
|
||||
if ((bits.data[n] & MASK(rem)) == ((*(exp_data + n)) & MASK(rem)))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* To test decoding of compressed bitmap by Tree based method
|
||||
* and to verify the result with expected result
|
||||
* for invalid input verfication is suppressed
|
||||
*/
|
||||
static void test_EPDAN_decode_tree(void)
|
||||
{
|
||||
bitvec dest;
|
||||
int init_flag = 1;
|
||||
int itr;
|
||||
int rc;
|
||||
uint8_t bits_data[RLC_EGPRS_MAX_WS/8];
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
for (itr = 0 ; itr < (sizeof(test) / sizeof(test_data)) ; itr++) {
|
||||
dest.data = bits_data;
|
||||
dest.data_len = sizeof(bits_data);
|
||||
dest.cur_bit = 0;
|
||||
memset(dest.data, 0, sizeof(bits_data));
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTest:%d\nTree based decoding:"
|
||||
"\nuncompressed data = %s\nlen = %d\n", itr + 1,
|
||||
osmo_hexdump(test[itr].crbb_data,
|
||||
(test[itr].crbb_len + 7)/8), test[itr].crbb_len
|
||||
);
|
||||
rc = egprs_compress::decompress_crbb(test[itr].crbb_len,
|
||||
test[itr].cc, test[itr].crbb_data, &dest);
|
||||
LOGP (DRLCMACDL, LOGL_DEBUG, "PRAVIN %s\n", osmo_hexdump (dest.data, (dest.cur_bit + 7)/8));
|
||||
if (rc < 0) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"\nFailed to decode CRBB: length %d, data %s",
|
||||
test[itr].crbb_len, osmo_hexdump(
|
||||
test[itr].crbb_data, (test[itr].crbb_len + 7)/8));
|
||||
}
|
||||
if (init_flag)
|
||||
init_flag = 0;
|
||||
if (test[itr].verify) {
|
||||
if (check_result(dest, test[itr].ucmp_data,
|
||||
test[itr].ucmp_len) == 0) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTree based decoding"
|
||||
":Error\nexpected data = %s\nexpected"
|
||||
" len = %d\ndecoded data = %s\n"
|
||||
"decoded len = %d\n",
|
||||
osmo_hexdump(test[itr].ucmp_data,
|
||||
(test[itr].ucmp_len + 7)/8),
|
||||
test[itr].ucmp_len, osmo_hexdump(dest.data,
|
||||
(dest.cur_bit + 7)/8), dest.cur_bit
|
||||
);
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "\nexpected data = %s\nexpected len = %d"
|
||||
"\ndecoded data = %s\ndecoded len = %d\n",
|
||||
osmo_hexdump(test[itr].ucmp_data,
|
||||
(test[itr].ucmp_len + 7)/8),
|
||||
test[itr].ucmp_len, osmo_hexdump(dest.data,
|
||||
(dest.cur_bit + 7)/8), dest.cur_bit
|
||||
);
|
||||
if (test[itr].verify) {
|
||||
if (check_result(dest, test[itr].ucmp_data,
|
||||
test[itr].ucmp_len) == 0) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTree based decoding"
|
||||
":Error\nexpected data = %s\nexpected"
|
||||
" len = %d\ndecoded data = %s\n"
|
||||
"decoded len = %d\n",
|
||||
osmo_hexdump(test[itr].ucmp_data,
|
||||
(test[itr].ucmp_len + 7)/8),
|
||||
test[itr].ucmp_len, osmo_hexdump(dest.data,
|
||||
(dest.cur_bit + 7)/8), dest.cur_bit
|
||||
);
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
void puan_test()
|
||||
{
|
||||
printf("=== start %s ===\n", __func__);
|
||||
int is_compressed, itr;
|
||||
bitvec ucmp_vec;
|
||||
unsigned int ucmp_bmplen;
|
||||
uint8_t crbb_bitmap[23] = {'\0'};
|
||||
uint8_t uclen_crbb = 0;
|
||||
bitvec crbb_vec;
|
||||
unsigned int rest_bits = 94;
|
||||
|
||||
for (itr = 0 ; itr < (sizeof(test) / sizeof(test_data)) - 4 ; itr++) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTest:%d\n"
|
||||
"\nuncompressed data = %s\nlen = %d\n", itr + 1,
|
||||
osmo_hexdump(test[itr].ucmp_data,
|
||||
(test[itr].ucmp_len + 7)/8), test[itr].ucmp_len
|
||||
|
||||
);
|
||||
if (rest_bits < test[itr].crbb_len)
|
||||
rest_bits = test[itr].crbb_len + 16;
|
||||
ucmp_bmplen = test[itr].ucmp_len;
|
||||
ucmp_vec.data = test[itr].ucmp_data;
|
||||
ucmp_bmplen = test[itr].ucmp_len;
|
||||
ucmp_vec.cur_bit = ucmp_bmplen;
|
||||
ucmp_vec.data_len = test[itr].ucmp_len;
|
||||
crbb_vec.data = crbb_bitmap;
|
||||
crbb_vec.cur_bit = 0;
|
||||
crbb_vec.data_len = test[itr].crbb_len;
|
||||
is_compressed = compress_rbb(&ucmp_vec, /* Uncompressed bitmap*/
|
||||
&crbb_vec, /*Compressed bitmap vector */
|
||||
&uclen_crbb,
|
||||
(rest_bits - 8 - 8));/* CRBBlength:7 colourcode:1 dissector length:8*/
|
||||
if (test[itr].verify) {
|
||||
if (check_result(crbb_vec, test[itr].crbb_data,
|
||||
test[itr].crbb_len) == 0) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "\nencoding"
|
||||
":Error\nexpected data = %s\nexpected"
|
||||
" len = %d\ndecoded data = %s\n"
|
||||
"encoded len = %d\n",
|
||||
osmo_hexdump(test[itr].crbb_data,
|
||||
(test[itr].crbb_len + 7)/8),
|
||||
test[itr].crbb_len, osmo_hexdump(crbb_vec.data,
|
||||
(crbb_vec.cur_bit + 7)/8), crbb_vec.cur_bit
|
||||
);
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
const struct log_info debug_log_info = {
|
||||
filter_fn,
|
||||
(struct log_info_cat *)default_categories,
|
||||
ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging(&debug_log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
|
||||
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile bitcompTest context");
|
||||
if (!tall_pcu_ctx)
|
||||
abort();
|
||||
|
||||
test_EPDAN_decode_tree();
|
||||
puan_test();
|
||||
|
||||
if (getenv("TALLOC_REPORT_FULL"))
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
talloc_free(tall_pcu_ctx);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* stubs that should not be reached
|
||||
*/
|
||||
extern "C" {
|
||||
void l1if_pdch_req() { abort(); }
|
||||
void l1if_connect_pdch() { abort(); }
|
||||
void l1if_close_pdch() { abort(); }
|
||||
void l1if_open_pdch() { abort(); }
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
|
||||
Test:1
|
||||
Tree based decoding:
|
||||
uncompressed data = 02 0c a0 30 cb 1a 0c e3 6c
|
||||
len = 67
|
||||
Run_length = 29
|
||||
Run_length = 26
|
||||
Run_length = 30
|
||||
Run_length = 27
|
||||
Run_length = 31
|
||||
Run_length = 19
|
||||
Run_length = 32
|
||||
PRAVIN ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff c0
|
||||
|
||||
expected data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff db
|
||||
expected len = 194
|
||||
decoded data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff db
|
||||
decoded len = 194
|
||||
|
||||
Test:2
|
||||
Tree based decoding:
|
||||
uncompressed data = 53 06 c5 40 6d
|
||||
len = 40
|
||||
Run_length = 50
|
||||
Run_length = 40
|
||||
Run_length = 51
|
||||
Run_length = 41
|
||||
PRAVIN ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 00
|
||||
|
||||
expected data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 03
|
||||
expected len = 182
|
||||
decoded data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 03
|
||||
decoded len = 182
|
||||
|
||||
Test:3
|
||||
Tree based decoding:
|
||||
uncompressed data = 02
|
||||
len = 8
|
||||
Run_length = 29
|
||||
PRAVIN ff ff ff f8
|
||||
|
||||
expected data = ff ff ff f8
|
||||
expected len = 29
|
||||
decoded data = ff ff ff f8
|
||||
decoded len = 29
|
||||
|
||||
Test:4
|
||||
Tree based decoding:
|
||||
uncompressed data = 02 0c e0 41 a0 0c 36 0d 03 71 b0 6e 24
|
||||
len = 103
|
||||
Run_length = 29
|
||||
Run_length = 19
|
||||
Run_length = 29
|
||||
Run_length = 20
|
||||
Run_length = 30
|
||||
Run_length = 21
|
||||
Run_length = 31
|
||||
Run_length = 22
|
||||
Run_length = 32
|
||||
Run_length = 22
|
||||
Run_length = 33
|
||||
PRAVIN ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
|
||||
|
||||
expected data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
|
||||
expected len = 288
|
||||
decoded data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
|
||||
decoded len = 288
|
||||
|
||||
Test:5
|
||||
Tree based decoding:
|
||||
uncompressed data = dd 41 00
|
||||
len = 18
|
||||
Run_length = 64
|
||||
Run_length = 16
|
||||
Run_length = 10
|
||||
PRAVIN ff ff ff ff ff ff ff ff ff ff 00 00
|
||||
|
||||
expected data = ff ff ff ff ff ff ff ff ff ff 00 00
|
||||
expected len = 90
|
||||
decoded data = ff ff ff ff ff ff ff ff ff ff 00 00
|
||||
decoded len = 90
|
||||
|
||||
Test:6
|
||||
Tree based decoding:
|
||||
uncompressed data = de 88 75 65 80
|
||||
len = 35
|
||||
Run_length = 2
|
||||
Run_length = 2
|
||||
Run_length = 1
|
||||
Run_length = 3
|
||||
Run_length = 1
|
||||
Run_length = 1
|
||||
Run_length = 3
|
||||
Run_length = 4
|
||||
Run_length = 6
|
||||
Run_length = 5
|
||||
PRAVIN 37 47 81 f0
|
||||
|
||||
expected data = 37 47 81 f0
|
||||
expected len = 28
|
||||
decoded data = 37 47 81 f0
|
||||
decoded len = 28
|
||||
|
||||
Test:7
|
||||
Tree based decoding:
|
||||
uncompressed data = 1e 70 c0
|
||||
len = 18
|
||||
Run_length = 1
|
||||
Run_length = 1
|
||||
Run_length = 2
|
||||
Run_length = 15
|
||||
PRAVIN b0 00 00
|
||||
|
||||
expected data =
|
||||
expected len = 0
|
||||
decoded data =
|
||||
decoded len = 19
|
||||
|
||||
Test:8
|
||||
Tree based decoding:
|
||||
uncompressed data = 00 1e
|
||||
len = 14
|
||||
PRAVIN
|
||||
|
||||
Failed to decode CRBB: length 14, data 00 1e
|
||||
expected data =
|
||||
expected len = 0
|
||||
decoded data =
|
||||
decoded len = 0
|
||||
|
||||
Test:9
|
||||
Tree based decoding:
|
||||
uncompressed data = 00 00 00
|
||||
len = 24
|
||||
PRAVIN
|
||||
|
||||
Failed to decode CRBB: length 24, data 00 00 00
|
||||
expected data =
|
||||
expected len = 0
|
||||
decoded data =
|
||||
decoded len = 0
|
||||
|
||||
Test:1
|
||||
|
||||
uncompressed data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff db
|
||||
len = 194
|
||||
CRBB bitmap = 02 0c a0 30 cb 1a 0c e3 60
|
||||
|
||||
Test:2
|
||||
|
||||
uncompressed data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 03
|
||||
len = 182
|
||||
CRBB bitmap = 53 06 c5 40 6d
|
||||
|
||||
Test:3
|
||||
|
||||
uncompressed data = ff ff ff f8
|
||||
len = 29
|
||||
CRBB bitmap = 02
|
||||
|
||||
Test:4
|
||||
|
||||
uncompressed data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
|
||||
len = 288
|
||||
CRBB bitmap = 02 0c e0 41 a0 0c 36 0d 03 71 b0 6e 24
|
||||
|
||||
Test:5
|
||||
|
||||
uncompressed data = ff ff ff ff ff ff ff ff ff ff 00 00
|
||||
len = 90
|
||||
CRBB bitmap = dd 41 20
|
|
@ -0,0 +1,4 @@
|
|||
=== start test_EPDAN_decode_tree ===
|
||||
=== end test_EPDAN_decode_tree ===
|
||||
=== start puan_test ===
|
||||
=== end puan_test ===
|
|
@ -26,7 +26,7 @@
|
|||
#include "encoding.h"
|
||||
#include "rlc.h"
|
||||
#include "llc.h"
|
||||
|
||||
#include "bts.h"
|
||||
extern "C" {
|
||||
#include "pcu_vty.h"
|
||||
|
||||
|
@ -35,6 +35,7 @@ extern "C" {
|
|||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/gprs/protocol/gsm_04_60.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -495,6 +496,24 @@ static void test_rlc_unit_decoder()
|
|||
OSMO_ASSERT(chunks[2].length == 1);
|
||||
OSMO_ASSERT(!chunks[2].is_complete);
|
||||
|
||||
rdbi.e = 0;
|
||||
rdbi.ti = 0;
|
||||
rdbi.cv = 1;
|
||||
tlli = 0;
|
||||
offs = 0;
|
||||
data[offs++] = 1;
|
||||
num_chunks = Decoding::rlc_data_from_ul_data(&rdbi, cs, data,
|
||||
chunks, ARRAY_SIZE(chunks), &tlli);
|
||||
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
OSMO_ASSERT(chunks[0].offset == 1);
|
||||
OSMO_ASSERT(chunks[0].length == 0);
|
||||
OSMO_ASSERT(chunks[0].is_complete);
|
||||
|
||||
OSMO_ASSERT(chunks[1].offset == 1);
|
||||
OSMO_ASSERT(chunks[1].length == 43);
|
||||
OSMO_ASSERT(!chunks[1].is_complete);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
|
@ -506,6 +525,7 @@ static void test_rlc_unit_encoder()
|
|||
uint8_t llc_data[1500] = {0,};
|
||||
int num_chunks = 0;
|
||||
int write_offset;
|
||||
int count_payload;
|
||||
struct gprs_llc llc;
|
||||
Encoding::AppendResult ar;
|
||||
|
||||
|
@ -515,43 +535,49 @@ static void test_rlc_unit_encoder()
|
|||
|
||||
/* TS 44.060, B.1 */
|
||||
cs = GprsCodingScheme::CS4;
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 11);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 11);
|
||||
OSMO_ASSERT(count_payload == 11);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 26);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 2 + 11 + 26);
|
||||
OSMO_ASSERT(count_payload == 26);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 11);
|
||||
OSMO_ASSERT(num_chunks == 3);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((11 << 2) | (1 << 1) | (0 << 0)));
|
||||
|
@ -562,50 +588,57 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 20);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 19);
|
||||
OSMO_ASSERT(count_payload == 19);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((0 << 2) | (0 << 1) | (1 << 0)));
|
||||
OSMO_ASSERT(data[1] == 0);
|
||||
|
||||
/* Block 2 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
OSMO_ASSERT(llc.chunk_size() == 1);
|
||||
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 1);
|
||||
OSMO_ASSERT(count_payload == 1);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 1 + 18);
|
||||
OSMO_ASSERT(count_payload == 18);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((1 << 2) | (1 << 1) | (1 << 0)));
|
||||
|
@ -615,31 +648,35 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 7);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 7);
|
||||
OSMO_ASSERT(count_payload == 7);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 11);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 2 + 7 + 11);
|
||||
OSMO_ASSERT(count_payload == 11);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((7 << 2) | (1 << 1) | (0 << 0)));
|
||||
|
@ -650,20 +687,22 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 1);
|
||||
OSMO_ASSERT(write_offset == 20);
|
||||
OSMO_ASSERT(count_payload == 20);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
|
||||
|
@ -673,20 +712,22 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 20);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 1);
|
||||
OSMO_ASSERT(write_offset == 20);
|
||||
OSMO_ASSERT(count_payload == 20);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
|
||||
|
@ -696,49 +737,55 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 30);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 1);
|
||||
OSMO_ASSERT(write_offset == 20);
|
||||
OSMO_ASSERT(count_payload == 20);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
OSMO_ASSERT(data[0] == 0);
|
||||
|
||||
/* Block 2 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
OSMO_ASSERT(llc.chunk_size() == 10);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 10);
|
||||
OSMO_ASSERT(count_payload == 10);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 10 + 9);
|
||||
OSMO_ASSERT(count_payload == 9);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((10 << 2) | (1 << 1) | (1 << 0)));
|
||||
|
@ -748,43 +795,49 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS4;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 11);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 11);
|
||||
OSMO_ASSERT(count_payload == 11);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 26);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 2 + 11 + 26);
|
||||
OSMO_ASSERT(count_payload == 26);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 5);
|
||||
OSMO_ASSERT(num_chunks == 3);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((11 << 1) | (0 << 0)));
|
||||
|
@ -800,75 +853,86 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS2;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 15);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 15);
|
||||
OSMO_ASSERT(count_payload == 15);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 12);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
/* no LI here, becaues there are exact 12 bytes left. Put LI into next frame */
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 12);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((15 << 1) | (1 << 0)));
|
||||
OSMO_ASSERT(data[1] == 0);
|
||||
|
||||
/* Block 2 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
OSMO_ASSERT(llc.chunk_size() == 0);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 0);
|
||||
OSMO_ASSERT(count_payload == 0);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 7);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == 2 + 0 + 7);
|
||||
OSMO_ASSERT(count_payload == 7);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 18);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 18);
|
||||
OSMO_ASSERT(num_chunks == 3);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((0 << 1) | (0 << 0)));
|
||||
|
@ -877,32 +941,36 @@ static void test_rlc_unit_encoder()
|
|||
OSMO_ASSERT(data[3] == 0);
|
||||
|
||||
/* Block 3 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 6);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 6);
|
||||
OSMO_ASSERT(count_payload == 6);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 12);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 12);
|
||||
OSMO_ASSERT(num_chunks == 3);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((6 << 1) | (0 << 0)));
|
||||
|
@ -917,21 +985,23 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS2;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, rdbi.data_len);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 1);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == rdbi.data_len);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
OSMO_ASSERT(data[0] == 0);
|
||||
|
@ -941,21 +1011,23 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS2;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, rdbi.data_len - 1);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == rdbi.data_len - 1);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
OSMO_ASSERT(data[0] == (((rdbi.data_len-1) << 1) | (1 << 0)));
|
||||
|
@ -966,21 +1038,23 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS2;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, rdbi.data_len - 2);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == rdbi.data_len - 2);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == (((rdbi.data_len-2) << 1) | (0 << 0)));
|
||||
|
@ -1018,7 +1092,7 @@ static void test_rlc_unaligned_copy()
|
|||
block_idx++)
|
||||
{
|
||||
struct gprs_rlc_data_info rlc;
|
||||
gprs_rlc_data_info_init_dl(&rlc, cs, false);
|
||||
gprs_rlc_data_info_init_dl(&rlc, cs, false, 0);
|
||||
|
||||
memset(bits, pattern, sizeof(bits));
|
||||
Decoding::rlc_copy_to_aligned_buffer(
|
||||
|
@ -1062,12 +1136,14 @@ static void test_rlc_info_init()
|
|||
struct gprs_rlc_data_info rlc;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
gprs_rlc_data_info_init_dl(&rlc, GprsCodingScheme(GprsCodingScheme::CS1), false);
|
||||
gprs_rlc_data_info_init_dl(&rlc,
|
||||
GprsCodingScheme(GprsCodingScheme::CS1), false, 0);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.data_offs_bits[0] == 24);
|
||||
OSMO_ASSERT(rlc.block_info[0].data_len == 20);
|
||||
|
||||
gprs_rlc_data_info_init_dl(&rlc, GprsCodingScheme(GprsCodingScheme::MCS1), false);
|
||||
gprs_rlc_data_info_init_dl(&rlc,
|
||||
GprsCodingScheme(GprsCodingScheme::MCS1), false, 0);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.data_offs_bits[0] == 33);
|
||||
OSMO_ASSERT(rlc.block_info[0].data_len == 22);
|
||||
|
@ -1101,6 +1177,250 @@ const struct log_info debug_log_info = {
|
|||
ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
static void setup_bts(BTS *the_bts, uint8_t ts_no, uint8_t cs = 1)
|
||||
{
|
||||
gprs_rlcmac_bts *bts;
|
||||
gprs_rlcmac_trx *trx;
|
||||
|
||||
bts = the_bts->bts_data();
|
||||
bts->egprs_enabled = true;
|
||||
bts->alloc_algorithm = alloc_algorithm_a;
|
||||
bts->initial_cs_dl = cs;
|
||||
bts->initial_cs_ul = cs;
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[ts_no].enable();
|
||||
}
|
||||
static void uplink_header_type_2_parsing_test(BTS *the_bts,
|
||||
uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
|
||||
uint8_t ms_class)
|
||||
{
|
||||
GprsMs *ms;
|
||||
struct pcu_l1_meas meas;
|
||||
int tfi = 0;
|
||||
gprs_rlcmac_bts *bts;
|
||||
RlcMacUplink_t ulreq = {0};
|
||||
uint8_t data[79] = {0};
|
||||
struct gprs_rlc_ul_header_egprs_2 *egprs2 = NULL;
|
||||
|
||||
egprs2 = (struct gprs_rlc_ul_header_egprs_2 *) data;
|
||||
bts = the_bts->bts_data();
|
||||
|
||||
tfi = 1;
|
||||
|
||||
struct gprs_rlc_data_info rlc;
|
||||
GprsCodingScheme cs;
|
||||
int rc, offs;
|
||||
|
||||
/*without padding*/
|
||||
cs = GprsCodingScheme::MCS5;
|
||||
egprs2 = (struct gprs_rlc_ul_header_egprs_2 *) data;
|
||||
egprs2->r = 1;
|
||||
egprs2->si = 1;
|
||||
egprs2->cv = 7;
|
||||
egprs2->tfi_hi = tfi & 0x03;
|
||||
egprs2->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs2->bsn1_hi = 0;
|
||||
egprs2->bsn1_lo = 0;
|
||||
egprs2->cps_hi = 3;
|
||||
egprs2->cps_lo = 0;
|
||||
egprs2->rsb = 0;
|
||||
egprs2->pi = 0;
|
||||
data[4] = 0x20; /* Setting E field */
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
offs = rlc.data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(offs == 4);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
|
||||
/* with padding case */
|
||||
cs = GprsCodingScheme::MCS6;
|
||||
egprs2 = (struct gprs_rlc_ul_header_egprs_2 *) data;
|
||||
egprs2->r = 1;
|
||||
egprs2->si = 1;
|
||||
egprs2->cv = 7;
|
||||
egprs2->tfi_hi = tfi & 0x03;
|
||||
egprs2->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs2->bsn1_hi = 0;
|
||||
egprs2->bsn1_lo = 0;
|
||||
egprs2->cps_hi = 3;
|
||||
egprs2->cps_lo = 0;
|
||||
egprs2->rsb = 0;
|
||||
egprs2->pi = 0;
|
||||
data[10] = 0x20; /* Setting E field */
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
offs = rlc.data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(offs == 10);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
|
||||
egprs2->r = 1;
|
||||
egprs2->si = 1;
|
||||
egprs2->cv = 7;
|
||||
egprs2->tfi_hi = tfi & 0x03;
|
||||
egprs2->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs2->bsn1_hi = 1;
|
||||
egprs2->bsn1_lo = 0;
|
||||
egprs2->cps_hi = 2;
|
||||
egprs2->cps_lo = 0;
|
||||
egprs2->rsb = 0;
|
||||
egprs2->pi = 0;
|
||||
data[10] = 0x20; /* Setting E field */
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
offs = rlc.data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(offs == 10);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 1);
|
||||
}
|
||||
|
||||
static void uplink_header_type2_test(void)
|
||||
{
|
||||
BTS the_bts;
|
||||
int ts_no = 7;
|
||||
uint32_t fn = 2654218;
|
||||
uint16_t qta = 31;
|
||||
uint32_t tlli = 0xf1223344;
|
||||
const char *imsi = "0011223344";
|
||||
uint8_t ms_class = 1;
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
GprsMs *ms;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
setup_bts(&the_bts, ts_no, 10);
|
||||
|
||||
uplink_header_type_2_parsing_test(&the_bts, ts_no,
|
||||
tlli, &fn, qta, ms_class);
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void uplink_header_type_1_parsing_test(BTS *the_bts,
|
||||
uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
|
||||
uint8_t ms_class)
|
||||
{
|
||||
uint8_t trx_no = 0;
|
||||
int tfi = 0;
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
gprs_rlcmac_bts *bts;
|
||||
uint8_t data[155] = {0};
|
||||
struct gprs_rlc_ul_header_egprs_1 *egprs1 = NULL;
|
||||
struct gprs_rlc_data_info rlc;
|
||||
GprsCodingScheme cs;
|
||||
int rc, offs;
|
||||
|
||||
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
|
||||
bts = the_bts->bts_data();
|
||||
|
||||
tfi = 1;
|
||||
|
||||
/* MCS 7 */
|
||||
cs = GprsCodingScheme::MCS7;
|
||||
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
|
||||
egprs1->si = 1;
|
||||
egprs1->r = 1;
|
||||
egprs1->cv = 7;
|
||||
egprs1->tfi_hi = tfi & 0x03;
|
||||
egprs1->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs1->bsn1_hi = 0;
|
||||
egprs1->bsn1_lo = 0;
|
||||
egprs1->bsn2_hi = 1;
|
||||
egprs1->bsn2_lo = 0;
|
||||
egprs1->cps = 15;
|
||||
egprs1->rsb = 0;
|
||||
egprs1->pi = 0;
|
||||
data[5] = 0xc0;
|
||||
data[5 + 57] = 1;
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 2);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
OSMO_ASSERT(rlc.block_info[1].bsn == 1);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
|
||||
/* MCS 8 */
|
||||
cs = GprsCodingScheme::MCS8;
|
||||
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
|
||||
egprs1->si = 1;
|
||||
egprs1->r = 1;
|
||||
egprs1->cv = 7;
|
||||
egprs1->tfi_hi = tfi & 0x03;
|
||||
egprs1->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs1->bsn1_hi = 0;
|
||||
egprs1->bsn1_lo = 0;
|
||||
egprs1->bsn2_hi = 1;
|
||||
egprs1->bsn2_lo = 0;
|
||||
egprs1->cps = 15;
|
||||
egprs1->rsb = 0;
|
||||
egprs1->pi = 0;
|
||||
data[5] = 0xc0;
|
||||
data[5 + 69] = 1;
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 2);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
OSMO_ASSERT(rlc.block_info[1].bsn == 1);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
|
||||
/* MCS 9 */
|
||||
cs = GprsCodingScheme::MCS9;
|
||||
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
|
||||
egprs1->si = 1;
|
||||
egprs1->r = 1;
|
||||
egprs1->cv = 7;
|
||||
egprs1->tfi_hi = tfi & 0x03;
|
||||
egprs1->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs1->bsn1_hi = 0;
|
||||
egprs1->bsn1_lo = 0;
|
||||
egprs1->bsn2_hi = 1;
|
||||
egprs1->bsn2_lo = 0;
|
||||
egprs1->cps = 15;
|
||||
egprs1->rsb = 0;
|
||||
egprs1->pi = 0;
|
||||
data[5] = 0xc0;
|
||||
data[5 + 75] = 1;
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 2);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
OSMO_ASSERT(rlc.block_info[1].bsn == 1);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
}
|
||||
|
||||
void uplink_header_type1_test(void)
|
||||
{
|
||||
BTS the_bts;
|
||||
int ts_no = 7;
|
||||
uint32_t fn = 2654218;
|
||||
uint16_t qta = 31;
|
||||
uint32_t tlli = 0xf1223344;
|
||||
const char *imsi = "0011223344";
|
||||
uint8_t ms_class = 1;
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
GprsMs *ms;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
setup_bts(&the_bts, ts_no, 12);
|
||||
uplink_header_type_1_parsing_test(&the_bts, ts_no, tlli, &fn,
|
||||
qta, ms_class);
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct vty_app_info pcu_vty_info = {0};
|
||||
|
@ -1123,6 +1443,9 @@ int main(int argc, char **argv)
|
|||
test_rlc_unaligned_copy();
|
||||
test_rlc_unit_encoder();
|
||||
|
||||
uplink_header_type2_test();
|
||||
uplink_header_type1_test();
|
||||
|
||||
if (getenv("TALLOC_REPORT_FULL"))
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
return EXIT_SUCCESS;
|
||||
|
|
|
@ -6,3 +6,7 @@
|
|||
=== end test_rlc_unit_decoder ===
|
||||
=== start test_rlc_unit_encoder ===
|
||||
=== end test_rlc_unit_encoder ===
|
||||
=== start uplink_header_type2_test ===
|
||||
=== end uplink_header_type2_test ===
|
||||
=== start uplink_header_type1_test ===
|
||||
=== end uplink_header_type1_test ===
|
||||
|
|
|
@ -30,6 +30,7 @@ extern const struct log_info gprs_log_info;
|
|||
#include "pcu_vty.h"
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
}
|
||||
using namespace std;
|
||||
|
@ -89,9 +90,12 @@ void testRlcMacDownlink()
|
|||
|
||||
std::string testData[] = {
|
||||
"4e082500e3f1a81d080820800b2b2b2b2b2b2b2b2b2b2b", // Packet Downlink Assignment
|
||||
"48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
|
||||
"48282407a6a07422720100032b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
|
||||
"47240c00400000000000000079eb2ac9402b2b2b2b2b2b", // Packet Uplink Ack Nack
|
||||
"47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b" // Packet Uplink Assignment
|
||||
"47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
|
||||
"400820001a3904df0680efb3300b2b2b2b2b2b2b2b2b2b", // Packet Downlink Assignment (EGPRS)
|
||||
"40284f0000001009810c826f4406809dcecb2b2b2b2b2b", // Packet Uplink Assignment (EGPRS)
|
||||
"4024030f2f0000000087b0042b2b2b2b2b2b2b2b2b2b2b" // Packet Uplink Ack Nack (EGPRS)
|
||||
"4913e00850884013a8048b2b2b2b2b2b2b2b2b2b2b2b2b"
|
||||
"412430007fffffffffffffffefd19c7ba12b2b2b2b2b2b"
|
||||
"41942b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
|
||||
|
@ -152,9 +156,10 @@ void testRlcMacUplink()
|
|||
bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
||||
|
||||
std::string testData[] = {
|
||||
"400e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Dummy Control Block
|
||||
"400b8020000000000000002480e00b2b2b2b2b2b2b2b2b", // Packet Downlink Ack/Nack
|
||||
"4016713dc094270ca2ae57ef909006aa0fc0001f80222b" // Packet Resource Request
|
||||
"400e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Dummy Control Block
|
||||
"400b8020000000000000002480e0032b2b2b2b2b2b2b2b", // Packet Downlink Ack/Nack
|
||||
"4016713dc094270ca2ae57ef909006aa0fc0001f80222b", // Packet Resource Request
|
||||
"40200ffc0021ec010b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // EPDAN
|
||||
"400a9020000000000000003010012a0800132b2b2b2b2b"
|
||||
};
|
||||
|
||||
|
@ -207,6 +212,21 @@ void testRlcMacUplink()
|
|||
bitvec_free(resultVector);
|
||||
}
|
||||
|
||||
void testCsnLeftAlignedVarBmpBounds()
|
||||
{
|
||||
bitvec *vector = bitvec_alloc(23);
|
||||
|
||||
bitvec_unhex(vector, "40200bffd161003e0e519ffffffb800000000000000000");
|
||||
RlcMacUplink_t data;
|
||||
|
||||
EGPRS_AckNack_Desc_t *urbb =
|
||||
&data.u.Egprs_Packet_Downlink_Ack_Nack.EGPRS_AckNack.Desc;
|
||||
decode_gsm_rlcmac_uplink(vector, &data);
|
||||
|
||||
OSMO_ASSERT(!strcmp(osmo_hexdump(urbb->URBB, 13),
|
||||
"7f ff ff ee 00 00 00 00 00 00 00 00 00 "));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
|
@ -214,5 +234,5 @@ int main(int argc, char *argv[])
|
|||
//printSizeofRLCMAC();
|
||||
testRlcMacDownlink();
|
||||
testRlcMacUplink();
|
||||
|
||||
testCsnLeftAlignedVarBmpBounds();
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
|
|||
vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 = 4828247a6a07422721032b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 = 4828247a6a07422721032b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4828247a6a07422721032b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4724c040000000079eb2ac9402b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
|
@ -31,22 +31,46 @@ vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
|
|||
vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
UPLINK
|
||||
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 = 4082001a394df680efb330b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 = 4082001a394df680efb330b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4082001a394df680efb330b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
|
||||
vector1 = 40284f00010981c826f446809dcecb2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
|
||||
vector1 = 40284f00010981c826f446809dcecb2b2b2b2b2b
|
||||
vector2 = 40284f00010981c826f446809dcecb2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 40243f2f000087b042b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40243f2f000087b042b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40243f2f000087b042b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
UPLINK
|
||||
vector1 = 40e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 40b802000000002480e032b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40b802000000002480e032b2b2b2b2b2b2b2b
|
||||
vector2 = 40b802000000002480e032b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
||||
=========Start DECODE===========
|
||||
|
@ -56,3 +80,19 @@ vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
|||
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
||||
vector2 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4020ffc021ec1b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 4020ffc021ec1b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4020ffc021ec1b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 40a90200000000301012a80132b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40a90200000000301012a80132b2b2b2b2b
|
||||
vector2 = 40a90200000000301012a80132b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -32,6 +32,8 @@
|
|||
=== end test_tbf_ws ===
|
||||
=== start test_tbf_egprs_two_phase ===
|
||||
=== end test_tbf_egprs_two_phase ===
|
||||
=== start test_tbf_egprs_two_phase_spb ===
|
||||
=== end test_tbf_egprs_two_phase_spb ===
|
||||
=== start test_tbf_egprs_dl ===
|
||||
Testing MCS-1
|
||||
Testing MCS-2
|
||||
|
@ -43,3 +45,26 @@ Testing MCS-7
|
|||
Testing MCS-8
|
||||
Testing MCS-9
|
||||
=== end test_tbf_egprs_dl ===
|
||||
=== start test_tbf_egprs_retx_dl ===
|
||||
Testing retx for MCS 6 - 6
|
||||
Testing retx for MCS 1 - 9
|
||||
Testing retx for MCS 2 - 8
|
||||
Testing retx for MCS 5 - 7
|
||||
Testing retx for MCS 6 - 9
|
||||
Testing retx for MCS 7 - 5
|
||||
Testing retx for MCS 9 - 6
|
||||
=== end test_tbf_egprs_retx_dl ===
|
||||
=== start test_tbf_egprs_spb_dl ===
|
||||
Testing retx for MCS 6 to reseg_mcs 3
|
||||
Testing retx for MCS 5 to reseg_mcs 2
|
||||
Testing retx for MCS 4 to reseg_mcs 1
|
||||
Testing retx for MCS 6 to reseg_mcs 3
|
||||
=== end test_tbf_egprs_spb_dl ===
|
||||
=== start test_tbf_puan_urbb_len ===
|
||||
=== end test_tbf_puan_urbb_len ===
|
||||
=== start test_tbf_update_ws ===
|
||||
=== end test_tbf_update_ws ===
|
||||
=== start test_tbf_li_decoding ===
|
||||
=== end test_tbf_li_decoding ===
|
||||
=== start test_tbf_egprs_epdan ===
|
||||
=== end test_tbf_egprs_epdan ===
|
||||
|
|
|
@ -23,6 +23,13 @@ cat $abs_srcdir/tbf/TbfTest.err > experr
|
|||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/tbf/TbfTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([bitcomp])
|
||||
AT_KEYWORDS([bitcomp])
|
||||
cat $abs_srcdir/bitcomp/BitcompTest.ok > expout
|
||||
cat $abs_srcdir/bitcomp/BitcompTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/bitcomp/BitcompTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([edge])
|
||||
AT_KEYWORDS([edge])
|
||||
cat $abs_srcdir/edge/EdgeTest.ok > expout
|
||||
|
|
|
@ -322,6 +322,23 @@ static void test_rlc_dl_ul_basic()
|
|||
ul_win.receive_bsn(4);
|
||||
count = ul_win.raise_v_q();
|
||||
OSMO_ASSERT(count == 0);
|
||||
|
||||
/*
|
||||
* SSN wrap around case
|
||||
* Should not expect any BSN as nacked.
|
||||
*/
|
||||
rbb = "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR";
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
ul_win.receive_bsn(i);
|
||||
ul_win.raise_v_q();
|
||||
}
|
||||
ul_win.receive_bsn(0);
|
||||
ul_win.raise_v_q();
|
||||
ul_win.receive_bsn(1);
|
||||
ul_win.raise_v_q();
|
||||
ul_win.update_rbb(win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
OSMO_ASSERT(ul_win.ssn() == 2);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue