Compare commits

...

56 Commits

Author SHA1 Message Date
pravin bd1e4bcc4c Fix computation of max value in decompression profiling test
Bug in calculating max value is fixed.
Along with this, new test vectors are identified where in existing
algorithm fails whereas tree based algorithm succeeds.
Abnormally high timings are counted and counter is logged.
2016-06-23 15:40:41 +05:30
pravin 0a918983f3 Add test case for decompression profiling
Add test case to collect the decompression profiling data
and to compare tree based algorithm with existing algorithm.
note:tbf test suite fails due to log comparison failure.
For getting the profiling data gettimeofday is used and timing
varies during each iterations.
2016-06-22 11:55:52 +05:30
pravin 624471e672 Add EPDAN decoding by tree based Algorithm
Implemented tree based algorithm to decode compressed bitmap in EPDAN.
This algorithm intends to improve the performance over existing method.
2016-06-22 10:52:19 +05:30
Aravind Sirsikar a36b5d7896 Merge branch 'master' of git://git.osmocom.org/osmo-pcu into Integration 2016-05-31 17:23:12 +05:30
Alexander Couzens cb846ecbbc encoding: add doxygen for rlc_data_to_dl_append*
Change-Id: I6ead0f1d14a91c657448227e17438b49a54e6c4a
Reviewed-on: https://gerrit.osmocom.org/141
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-05-31 11:52:19 +00:00
Alexander Couzens b82bd92e57 decoding: improve and add comments
Change-Id: I45c9fc55243224909ca2fdece8cbfa686b0f444d
Reviewed-on: https://gerrit.osmocom.org/139
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-05-31 11:51:59 +00:00
Aravind Sirsikar 93ef548a43 Merge branch 'master' of git://git.osmocom.org/osmo-pcu into Integration
Conflicts:
	src/tbf_ul.cpp
2016-05-31 16:39:27 +05:30
Aravind Sirsikar 6c0ff27cb5 Add test cases to validate EGPRS DL SPB for Retx
Upgrade the test suite with test cases to validate the EGPRS
Downlink Split block for Retransmission
2016-05-30 15:59:45 +05:30
Aravind Sirsikar 7c70740484 Modify EGPRS DL TBF flow to support Split block
Modify the EGPRS DL TBF flow to support Split block during
Retx.
2016-05-30 15:45:36 +05:30
Aravind Sirsikar b7cc6a4743 Add data structure to handle Split block in EGPR DL
Modify the header files with necessary data structure to handle
Split block for EGPRS DL TBF.
2016-05-30 15:38:29 +05:30
Alexander Couzens 2fcfc29020 add comments to describe functions
Change-Id: Ie351632001abbeb82008a5eecae0d0323a8ef7d7
Reviewed-on: https://gerrit.osmocom.org/106
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Tested-by: Jenkins Builder
2016-05-25 20:07:03 +00:00
Alexander Couzens ce936f3cd4 tbf_ul: use correct size for chunk_size
The size of the hole array in bytes was used instead of the size of elements.

Change-Id: If6bf3e5f1ad773ddaa9fb2ce7c069e6b26659cbf
Reviewed-on: https://gerrit.osmocom.org/105
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-25 19:45:50 +00:00
Alexander Couzens c1c9d6a9d8 rlc.h: remove duplicated define RLC_EGPRS_SNS
The second #define RLC_EGPRS_SNS is 3 lines below of the first one.

Change-Id: Ibb718ba9be21831c56c5949e730fab5acd691d7c
Reviewed-on: https://gerrit.osmocom.org/107
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-25 19:44:29 +00:00
Alexander Couzens c8fd4b7c42 bts/counter: replace '_' with '-' in counter names
Conform to the convention.

Change-Id: I6162694aae8d354aba318cc1acfdac108239fef0
Reviewed-on: https://gerrit.osmocom.org/103
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-24 10:18:07 +00:00
Alexander Couzens f929e62525 introduce new counter rlc_sent_control
Counts control messages (UL/DL assignment, UL ACKs or page requests)

Change-Id: Ib41031d430beddfb48d54470e632436f2c99c360
Reviewed-on: https://gerrit.osmocom.org/99
Reviewed-by: Holger Freyther <holger@freyther.de>
Tested-by: Jenkins Builder
2016-05-22 11:11:53 +00:00
Alexander Couzens 4acb6b7251 gprs_rlcmac_sched: fix mistype of CONTROL ACK
Change-Id: If37b33f69cd659d913ed81eb6060a42734ba524f
Reviewed-on: https://gerrit.osmocom.org/100
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-22 11:00:29 +00:00
Alexander Couzens 95e379241a tbf_dl: replace cross-file declaration with correct header
Change-Id: I9b4eb664d444258c9bcf53f9b44552d8dd3155e9
Reviewed-on: https://gerrit.osmocom.org/95
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-22 10:59:04 +00:00
Alexander Couzens 543756adbe bts/rate_ctr: replace spaces by tabs
Use tabs like other counters for seperation.
Introduced by 2cb1547

Change-Id: I32eebfe5934c919eccc1e28938ca00c49368297e
Reviewed-on: https://gerrit.osmocom.org/96
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-22 10:44:04 +00:00
Alexander Couzens 2cb1547993 introduce new counter rlc_sent_dummy
rlc_sent_dummy count the amount of dummy package which are
sent in case no data packet is in the queue.

Change-Id: Ia60eab853d9145980f30d63e4ce4b520b8c51381
Reviewed-on: https://gerrit.osmocom.org/85
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-21 17:59:37 +00:00
Alexander Couzens 9736d00b12 move statistics counter rlc_sent() to gprs_rlcmac_sched
The counter rlc_sent has nothing to do with the TBF.
The RLC packet got sent in the gprs_rlcmac_sched().

Change-Id: I5d2b910ea7cc250f17530406eda3be9b29b051fd
Reviewed-on: https://gerrit.osmocom.org/84
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-20 16:32:06 +00:00
Yves Godin 660709dc7c Add support for NuRAN Wireless Litecell 1.5 BTS
Layer 1 compatibility with previous generation or NuRan GSM product,
therefore the support for the Litecell 1.5 uses its own sources instead
of using tons of ifdef/endif.

Max's amendments:
* make headers path configurable
* use configured TRX instead of hardcoded value
* split subdir-objects into separate commit
* cosmetic changes

Change-Id: Ib1287375cb10a889625bbac8528fa60deed23a2b
Fixes: SYS#2443
Reviewed-on: https://gerrit.osmocom.org/61
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-05-20 16:26:20 +00:00
Aravind Sirsikar b3c85bac48 Fix PUAN construction for VQ not equal to VR case
This fix will correct the encoding of URBB where VQ != VR. Before
this fix only VQ == VR case has been handled.
2016-05-19 21:26:34 +05:30
Aravind Sirsikar 7106c361d6 Add test cases for EGPRS UL SPB handling
This patch will update the test suite to validate EGPRS UL SPB and
generated log file
2016-05-19 21:25:48 +05:30
Aravind Sirsikar db39e6cb72 Add support for Split block handling for EGPRS UL TBF
This patch will modify the EGPRS UL TBF flow to support Split block
handling.
2016-05-19 21:23:34 +05:30
Aravind Sirsikar 2da0dbc7e5 Add data structure to for split block in EGPRS UL
Modify header files with data structures required to support
split blocks for EGPRS UL TBF
2016-05-19 21:22:28 +05:30
Aravind Sirsikar 14099d7b1b Add test cases for Header type1 in EGPRS UL
Update test suite with test cases for Header type 1 in EGPRS UL
2016-05-19 21:21:42 +05:30
Aravind Sirsikar 459e856b70 Add test cases for Header Type 2 in EGPRS UL
Updates the test suite to add test cases for Header type 2 parsing
in EGPRS UL.
2016-05-19 21:21:02 +05:30
Aravind Sirsikar ebebf5d175 Add header type 1 support for EGPRS uplink
Function is added to parse the EGPRS header type 1 in uplink tbf path.
along with configuration parameter updation to reflect max mcs in UL
2016-05-19 21:19:40 +05:30
Aravind Sirsikar ef12a45d46 Add Header Type2 support in EGPRS UL
This patch will add support for MCS5,6 in EGPRS UL along with incorrect
assert correction to let MCS 6 work.
2016-05-19 21:18:33 +05:30
Aravind Sirsikar be3dc223f1 Remove GMSK only check in EGPRS UL
Since we are supporting MCS 5-9 in this patch series for EGPRS UL, This condition
is not relevant. So removing it.
2016-05-19 21:16:57 +05:30
Aravind Sirsikar d14add5c2e Add test cases to support ARQ-II for EGPRS DL Retx
During MCS upgradation such as MCS6->MCS9, 2 blocks which
were sent separately as MCS6, will be clubbed into one MCS9
block during retransmission. Same holds good for
MCS5->MCS7 transistion. During MCS reduction such as
MCS9->MCS6,2 blocks which were sent together will be
sent separately during the retransmission case.
Same is verified through the generated log file. Currently
MCS8->MCS6 transition is not supported. The retransmission
MCS is being calculated from Table 8.1.1.2 of TS 44.060.
2016-05-19 21:15:43 +05:30
Aravind Sirsikar f666f9585d Modify DL tbf flow for ARQ-II in EGPRS DL Retx
Modify the DL TBF flow to support ARQ-II EGPRS DL retransmission
2016-05-19 21:14:32 +05:30
Aravind Sirsikar 7b01c15b6c Add Accessor functions for ARQ-II in EGPRS DL
Add accessor function in existing classes to support ARQ-II for
retransmission in EGPRS DL
2016-05-19 21:13:42 +05:30
Aravind Sirsikar 5974ec7609 Add data structure for ARQ-II in EGPRS DL
Modify the existing data structure to support ARQ-II for Retx in EGPRS DL.
2016-05-19 21:12:29 +05:30
Max 58b6646750 Change internal API for consistency
Make TRX API (void *) consistent with the way it's used (integer). Use
uint8_t for TRX numbering everywhere (we don't expect hardware with
more than 256 transceivers in the near future). This change helps to
avoid unnecessary casts and make API much clearer.

Change-Id: Ic584611184b0c8b5417ecff0ddae3d526b55a079
Related: SYS#2443
Reviewed-on: https://gerrit.osmocom.org/59
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-19 06:40:26 +00:00
Alexander Couzens ed3ae4a392 add .gitreview
A .gitreview file is required to use git review.
More information about git review
https://www.mediawiki.org/wiki/Gerrit/git-review

Change-Id: I03cbdf3a95bcf36a7388b5fa2652fd774b8f0f5b
Reviewed-on: https://gerrit.osmocom.org/68
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-19 06:39:45 +00:00
Yves Godin f0bb25450c Enable subdir-objects automake option
Change-Id: I01fd264fd1f990f39cdbf309149e0eb857d7732f
Related: SYS#2443
Reviewed-on: https://gerrit.osmocom.org/60
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Tested-by: Jenkins Builder
2016-05-17 16:41:19 +00:00
Max de810f2005 Restructure sources
Move hardware-spicefic files into subdirectory similar to the way it's
done in OsmoBTS to make adding new hardware support easier.

Change-Id: I05004ad9032759a5dbfa57290ed1df83e89d5cb8
Related: SYS#2443
Reviewed-on: https://gerrit.osmocom.org/58
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-05-17 16:19:32 +00:00
Max cad867ec8d Rename define for direct hw access 2016-04-22 14:41:36 +02:00
Max 280448ba7b Cleanup build leftovers 2016-04-22 14:41:16 +02:00
Holger Hans Peter Freyther 1aa7527302 jenkins: Add the build script from jenkins here
This can be used to replicate a build issue more easily.
2016-04-13 19:05:52 -04:00
Holger Hans Peter Freyther ca025c02ef misc: Ignore test files and debian packaging 2016-04-01 19:27:56 +02:00
Holger Hans Peter Freyther 97e48a3252 debian: Initial debian packaging
Add initial debian package for plain osmo-pcu (without the
sysmoBTS supporot).
2016-04-01 19:26:09 +02:00
Harald Welte 63d33ad2d7 fix compiler warnings about format string for size_t
with gcc-5.3 on x86_64 I get the following compliler warnings:
warning: format ‘%d’ expects argument of type ‘int’, but argument 7 has
type ‘size_t {aka long unsigned int}

This patch resolves them
2016-03-30 22:08:18 +02:00
Aravind Sirsikar 7952282b78 Support puncturing scheme selection for EGPRS DL
Adds support to find the puncturing scheme for retransmission
with MCS change, retransmission with no MCS change, transmission
case. Puncturing scheme selection for retransmission case with
MCS change is aligned with TS 44.060 9.3.2.1. Puncturing scheme
selection for retransmission without MCS change, fresh transmission
is aligned with TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
2016-03-30 22:02:48 +02:00
Aravind Sirsikar a859a21800 Update CPS calculation with new data structures
Update existing CPS calculation function to align with new data
structure introduced
2016-03-30 22:02:47 +02:00
Aravind Sirsikar 7a05b039c8 Add data structure for CPS calculation in DL
Define new data structure with respect to TS 44.060
10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1 for puncturing scheme values
and initialize the variable introduced
2016-03-30 22:02:47 +02:00
Bhargava Abhyankar e44383baa4 Refactor the Uplink RLC header parsing function
Parsing the uplink data header for GPRS and EGPRS header type 3
is handled in separate functions.
This patch will enhance modularity of the code.
2016-03-30 22:01:52 +02:00
Aravind Sirsikar 5a5d2b7a27 Introduce EGPRS header type1 and type2 in UL
Defines new structures for UL EGPRS header type1 and type2 for
supporting MCS5-MCS9
2016-03-16 15:02:54 +01:00
Saurabh Sharan 2b09c39c9c Fix issue in encoding CSN_RECURSIVE_ARRAY
The remaining_bits_len is correctly decremented while encoding
CSN_RECURSIVE_ARRAY for fixing the bug.
Details of the bug is in https://projects.osmocom.org/issues/1641

During introduction of basic EGPRS feature new hex dump message
PUASS, from a different working network log was used in Unit test.
It exposed the issue of incorrect handling of recursive array
encoding in osmo-pcu.

Fixes: OS#1641
2016-03-16 15:01:53 +01:00
Saurabh Sharan bacb65b48b Add test vectors for EGPRS messages
This patch is the test suite modification for the fix encoding of
padding bits. New test vectors have been added both in downlink
and uplink.
2016-03-15 10:05:07 +01:00
Saurabh Sharan 656eed5975 Fix encoding of padding bits to start with 0 bit
This patch is for fixing encoding of padding bits according to the
3gpp spec 44.060 section 11, wherein it shall always start with 0
bit followed with spare padding bits.

During introduction of basic EGPRS feature new hex dump messages
from a different working network log were used in Unit test. These
exposed the issue of incorrect handling of padding bits encoding
in osmo-pcu.

Corrections in the existing test vector of rlcmac is also updated.
In testsuite tbf appropriate corrections for the Tbftest.err is
also done.
2016-03-15 10:04:34 +01:00
Holger Hans Peter Freyther 173ef90a53 pcu: Fix compiler warning about using string
Make the gsmtap hostname const to avoid turning a constant into
a mutable character. We never tried to modify the string so the
warning didn't reveal a genuine issue.

pcu_main.cpp:49:28: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
 static char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
2016-03-04 18:26:44 +01:00
Holger Hans Peter Freyther fd263b0dfd tbf: Fix copy and paste in the set_mode routine
This is the second attempt to fix what looks like a copy and paste
issue. The code assigns m_current_cs_ul and then compares the _dl
variant, then assigns m_current_cs_ul with a default value. It seems
to indicate that _ul should be used.

Fixes: Coverity: CID 1351733
2016-03-04 18:26:43 +01:00
Holger Hans Peter Freyther 99db40ad2d Revert "Refactor coding scheme assignment code"
Roll-out the refactoring change. The code did not include the
necessary update to the test result and there are some concerns
about it in itself and the right approach would have been to
fix the copy and paste issue, then do the refactoring.

This reverts commit 22d7e75e1f.
2016-03-04 18:26:43 +01:00
Max 22d7e75e1f Refactor coding scheme assignment code
Previously this code used too much copy-paste of boilerplate code which
is error-prone and hard to read. Factor out actual (M)CS assignment into
separate function and use it for both DL and UL cases in respective
mode.

Fixes: Coverity: CID 1351733
2016-02-25 14:03:49 +01:00
52 changed files with 4905 additions and 450 deletions

17
.gitignore vendored
View File

@ -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/

3
.gitreview Normal file
View File

@ -0,0 +1,3 @@
[gerrit]
host=gerrit.osmocom.org
project=osmo-pcu

View File

@ -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]]),

42
contrib/jenkins.sh Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -ex
if [ $sysmobts = "no" -a $sysmodsp = "yes" ]; then
echo "This config does not make sense."
exit 0
fi
rm -rf deps/install
mkdir deps || true
cd deps
osmo-deps.sh libosmocore
cd libosmocore
autoreconf --install --force
./configure --prefix=$PWD/../install
$MAKE $PARALLEL_MAKE install
# Install the API
cd ../
if ! test -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
mkdir -p $PWD/../install/include/sysmocom/femtobts/
cp include/*.h ../install/include/sysmocom/femtobts/
cd ../../
autoreconf --install --force
BTS_CONFIG="--enable-sysmocom-bts=$sysmobts --enable-sysmocom-dsp=$sysmodsp"
if [ $sysmobts = "no" ]; then
BTS_CONFIG="$BTS_CONFIG --enable-vty-tests"
fi
PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig ./configure $BTS_CONFIG
PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig $MAKE $PARALLEL_MAKE
DISTCHECK_CONFIGURE_FLAGS="$BTS_CONFIG" AM_DISTCHECK_CONFIGURE_FLAGS="$BTS_CONFIG" PKG_CONFIG_PATH=$PWD/deps/install/lib/pkgconfig LD_LIBRARY_PATH=$PWD/deps/install/lib $MAKE distcheck

5
debian/changelog vendored Normal file
View File

@ -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

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
7

24
debian/control vendored Normal file
View File

@ -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

2
debian/osmo-pcu.install vendored Normal file
View File

@ -0,0 +1,2 @@
etc/osmocom/osmo-pcu.cfg
usr/bin/osmo-pcu

15
debian/osmo-pcu.service vendored Normal file
View File

@ -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

19
debian/rules vendored Executable file
View File

@ -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

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)

View File

@ -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

View File

@ -71,6 +71,8 @@ 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 "},
{ "decode.errors", "Decode Errors "},
{ "sba.allocated", "SBA Allocated "},
{ "sba.freed", "SBA Freed "},
@ -1315,6 +1317,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 +1336,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));

View File

@ -183,12 +183,15 @@ struct gprs_rlcmac_bts {
uint8_t force_two_phase;
uint8_t alpha, gamma;
uint8_t egprs_enabled;
/* Value 0 to support resegmentation enabled in DL, 1 for no reseg */
uint8_t arq_type;
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
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[9];
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 +236,8 @@ public:
CTR_RLC_ACK_FAILED,
CTR_RLC_REL_TIMEDOUT,
CTR_RLC_LATE_BLOCK,
CTR_RLC_SENT_DUMMY,
CTR_RLC_SENT_CONTROL,
CTR_DECODE_ERRORS,
CTR_SBA_ALLOCATED,
CTR_SBA_FREED,
@ -309,6 +314,8 @@ public:
void rlc_ack_failed();
void rlc_rel_timedout();
void rlc_late_block();
void rlc_sent_dummy();
void rlc_sent_control();
void decode_error();
void sba_allocated();
void sba_freed();
@ -449,6 +456,8 @@ 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_INLINE(decode_error, CTR_DECODE_ERRORS)
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)

View File

@ -2400,7 +2400,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 +2504,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;

View File

@ -30,9 +30,9 @@ extern "C" {
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#define LENGTH_TO_END 255
/*
/*!
* \returns num extensions fields (num frames == offset) on success,
* -errno otherwise.
*/
@ -197,6 +197,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;
@ -344,78 +345,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 +369,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_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;
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_a << 0) | (egprs2->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 = egprs2->r;
rlc->si = egprs2->si;
rlc->tfi = (egprs2->tfi_a << 0) | (egprs2->tfi_b << 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_a << 0) | (egprs2->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 == 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_a << 0) | (egprs1->tfi_b << 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_a << 0) | (egprs1->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 == 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_a << 0) | (egprs1->bsn2_b << 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.
*
@ -536,7 +654,7 @@ int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc,
bool have_bitmap;
int implicitly_acked_blocks;
int ssn = desc->STARTING_SEQUENCE_NUMBER;
int rc;
struct timeval t0, t1;
if (desc->FINAL_ACK_INDICATION)
return handle_final_ack(bits, bsn_begin, bsn_end, window);
@ -576,26 +694,16 @@ 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);
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));
/* We don't know the SSN offset for the URBB,
* return what we have so far and assume the
* bitmap has stopped here */
goto aborted;
}
LOGP(DRLCMACDL, LOGL_DEBUG, "Compress bitmap exist,"
"CRBB LEN =%d and Starting color code =%d",
desc->CRBB_LENGTH, desc->CRBB_STARTING_COLOR_CODE);
gettimeofday(&t0, 0);
decompress_crbb(desc->CRBB_LENGTH, desc->CRBB_STARTING_COLOR_CODE, desc->CRBB, bits);
gettimeofday(&t1, 0);
long long elapsed = (t1.tv_sec-t0.tv_sec)*1000000LL + t1.tv_usec-t0.tv_usec;
LOGP(DRLCMACDL, LOGL_DEBUG,
"PRAVIN:time elapsed for decompression %d\n", elapsed);
LOGP(DRLCMACDL, LOGL_DEBUG,
"CRBB len: %d, decoded len: %d, cc: %d, crbb: '%s'\n",

View File

@ -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(
@ -60,6 +76,12 @@ public:
struct gprs_rlc_dl_window *window);
static int decode_gprs_acknack_bits(
const Ack_Nack_Description_t *desc,
bitvec *bits, int *bsn_begin, int *bsn_end,
gprs_rlc_dl_window *window);
bitvec * bits, int *bsn_begin, int *bsn_end,
gprs_rlc_dl_window * window);
static void decompress_crbb(
int8_t compress_bmap_len,
uint8_t clr_code_bit,
const uint8_t *orig_buf,
bitvec * dest
);
};

View File

@ -0,0 +1,313 @@
/* egprs_rlc_compression.h
* Routines for EGPRS RLC bitmap compression handling
*/
#include <egprs_rlc_compression.h>
#include <errno.h>
#include <decoding.h>
egprs_compress *egprs_compress::s_instance = 0;
Node *egprs_compress::ones_list = NULL;
Node *egprs_compress::zeros_list = NULL;
void egprs_compress::build_codeword(Node *root, const char *cdwd[])
{
Node *iter; /* iterate the node on the tree */
int len; /* length of the code word */
int i; /* iterater */
uint16_t idx; /* interate index of the code word table */
root->left = NULL;
root->right = NULL;
root->run_length = NULL;
for (idx = 0; idx < MAX_CDWDTBL_LEN; idx++) {
len = strlen((const char *)cdwd[idx]);
iter = root;
for (i = 0; i < len; i++) {
if (cdwd[idx][i] == '0') {
if (iter->left == NULL) {
iter->left = (Node *)malloc(sizeof(Node));
/* create a clean node */
iter->left->left = NULL;
iter->left->right = NULL;
iter->left->run_length = NULL;
}
iter = iter->left;
} else if (cdwd[idx][i] == '1') {
if (iter->right == NULL) {
iter->right = (Node *)malloc(sizeof(Node));
/* create a clean node */
iter->right->left = NULL;
iter->right->right = NULL;
iter->right->run_length = NULL;
}
iter = iter->right;
}
}
if (iter != NULL) {
iter->run_length = (uint16_t *)malloc(sizeof(uint16_t));
*(iter->run_length) = (uint16_t)NULL;
if (idx < 64)
*(iter->run_length) = idx;
else
*(iter->run_length) = (idx - 63) * 64;
}
}
}
const char *one_run_len_code_list[MAX_CDWDTBL_LEN] = {
"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[MAX_CDWDTBL_LEN] = {
"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"
};
int search_runlen(
Node *root, /* root of Ones or Zeros tree */
const uint8_t *bmbuf, /* Received compressed bitmap buf */
uint8_t bit_pos, /* the start bit pos to read codeword */
uint8_t *len_codewd, /* length of code word */
uint16_t *rlen)
{
Node *iter;
uint8_t dir;
iter = root;
*len_codewd = 0;
while (iter->run_length == 0) {
if ((iter->left == NULL) && (iter->right == NULL))
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;
}
(*rlen) = *(iter->run_length);
return 1;
} /* search_runlen */
void Decoding::decompress_crbb(
int8_t compress_bmap_len, /* compressed bitmap length */
uint8_t clr_code_bit, /* run length of Ones or Zeros */
const uint8_t *orig_crbb_buf, /* received block crbb bitmap */
bitvec * dest)
{
uint8_t bit_pos = 0;
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;
egprs_compress *compress = egprs_compress::instance();
while (compress_bmap_len > 0) {
if (clr_code_bit == 1) {
search_runlen(compress->ones_list, orig_crbb_buf,
bit_pos, &nbits, &run_length);
/*If run length > 64, need makeup and terminating code*/
if (run_length < 64)
clr_code_bit = 0;
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, 0xff, 8);
run_length = run_length - 8;
} else {
bitvec_write_field(dest, wp, 0xff,
run_length);
run_length = 0;
}
}
} else {
search_runlen(compress->zeros_list, orig_crbb_buf,
bit_pos, &nbits, &run_length);
/*If run length > 64, need makeup and terminating code*/
if (run_length < 64)
clr_code_bit = 1;
cbmaplen = cbmaplen + run_length;
/* put run length of Zeros in uncompressed bitmap */
while (run_length != 0) {
if (run_length > 8) {
bitvec_write_field(dest, wp, 0x00, 8);
run_length = run_length - 8;
} else {
bitvec_write_field(dest, wp, 0x00,
run_length);
run_length = 0;
}
}
}
bit_pos = bit_pos + nbits;
compress_bmap_len = compress_bmap_len - nbits;
}
} /* Decompress_CRBB */

View File

@ -0,0 +1,60 @@
/* egprs_rlc_compression.h
* Routines for EGPRS RLC bitmap compression handling
*/
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/stats.h>
}
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#define MAX_CDWDTBL_LEN 79 /* total number of codewords */
#define BITS_TO_BYTES(X) ((X ? (X/8):0)+1)
#define MOD8(X) (((X)+8) & (0x07))
typedef struct node {
struct node *left;
struct node *right;
uint16_t *run_length;
} Node;
extern const char *one_run_len_code_list[MAX_CDWDTBL_LEN];
extern const char *zero_run_len_code_list[MAX_CDWDTBL_LEN];
/* Creating singleton class
*/
class egprs_compress
{
static egprs_compress *s_instance;
egprs_compress()
{
ones_list = (Node *)malloc(sizeof(Node));
zeros_list = (Node *)malloc(sizeof(Node));
}
void build_codeword(Node *root, const char *cdwd[]);
public:
static Node *ones_list;
static Node *zeros_list;
void decode_tree_init(void)
{
instance()->build_codeword(
ones_list, one_run_len_code_list);
instance()->build_codeword(
zeros_list, zero_run_len_code_list);
}
static egprs_compress *instance()
{
if (!s_instance)
s_instance = new egprs_compress;
return s_instance;
}
};

View File

@ -276,7 +276,8 @@ 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
/* No RESEGMENT */
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
@ -567,6 +568,9 @@ static void write_packet_ack_nack_desc_egprs(
eow = false;
urbb_len = rest_bits - 9;
/* TODO: use compression (see above) */
} else {
eow = true;
urbb_len = num_blocks;
}
if (urbb_len + crbb_len == rest_bits)
@ -610,9 +614,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 (nyi) */
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
@ -891,6 +897,16 @@ 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
* \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,
@ -1012,6 +1028,16 @@ 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
* \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,
@ -1154,6 +1180,17 @@ 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
* \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,

View File

@ -21,6 +21,41 @@
#include "gprs_coding_scheme.h"
/*
* 44.060 Table 8.1.1.1 and Table 8.1.1.2
* In 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)
* in 0th level indexing only ARQ type 2 is supported i.e index 1 for
* incremental redundancy
*/
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;

View File

@ -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);
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 char 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,13 @@ inline bool operator ==(GprsCodingScheme a, GprsCodingScheme b)
return GprsCodingScheme::Scheme(a) == GprsCodingScheme::Scheme(b);
}
inline bool GprsCodingScheme::operator == (Scheme scheme)
{
if (this->m_scheme == scheme)
return true;
return false;
}
inline bool operator !=(GprsCodingScheme a, GprsCodingScheme b)
{
return !(a == b);
@ -214,3 +234,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 char arq_type)
{
return egprs_mcs_retx_tbl[arq_type][mcs.to_num() - 1]
[demanded_mcs.to_num() - 1];
}

View File

@ -237,7 +237,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()) {
@ -574,6 +574,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 +637,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 > 9)
current_cs_num = 9;
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
} else {

View File

@ -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;

View File

@ -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.
*/
@ -339,14 +339,23 @@ 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;

View File

@ -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;
}

View File

@ -0,0 +1,410 @@
/* 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;
}
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)
{
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->u16Arfcn, 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 1 */
fl1h->hw_info.trx_nr = trx_no + 1;
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;
}

View File

@ -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 */

View File

@ -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
};

View File

@ -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 */

View File

@ -155,7 +155,7 @@ 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,
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
rts_ind->u16Arfcn, rts_ind->u32Fn, rts_ind->u8BlockNbr);
case GsmL1_Sapi_Ptcch:
// FIXME
@ -215,7 +215,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 +357,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 +367,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;

View File

@ -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;

View File

@ -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;

View File

@ -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);
@ -330,8 +331,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 +446,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 +472,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(

View File

@ -28,6 +28,7 @@
#include <signal.h>
#include <sched.h>
#include <bts.h>
#include <egprs_rlc_compression.h>
extern "C" {
#include "pcu_vty.h"
#include <osmocom/vty/telnet_interface.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 = 9;
bts->max_mcs_dl = 9;
/* CS-1 to CS-4 */
bts->cs_lqual_ranges[0].low = -256;
bts->cs_lqual_ranges[0].high = 6;
@ -210,6 +211,12 @@ 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
* TODO: VTY should be updated to make it configurable
*/
bts->arq_type = 0;
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
@ -253,6 +260,8 @@ int main(int argc, char *argv[])
if (!bts->alloc_algorithm)
bts->alloc_algorithm = alloc_algorithm_dynamic;
egprs_compress::instance()->decode_tree_init();
rc = pcu_l1if_open();
if (rc < 0)

View File

@ -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",

View File

@ -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;
}
@ -282,7 +285,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,
unsigned int spb = 0)
{
unsigned int i;
unsigned int padding_bits = with_padding ? cs.optionalPaddingBits() : 0;
@ -297,7 +301,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,10 +311,10 @@ 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, 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,
@ -321,7 +325,7 @@ void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
}
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
GprsCodingScheme cs, bool with_padding)
GprsCodingScheme cs, bool with_padding, unsigned int spb)
{
unsigned int data_len = cs.maxDataBlockBytes();
if (with_padding)
@ -333,24 +337,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 +400,84 @@ 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,
unsigned int spb)
{
/* If it is second segment of the split block
* dont change the puncturing scheme
*/
if (spb == 3)
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;
}
}

116
src/rlc.h
View File

@ -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,43 @@ enum gprs_rlc_dl_bsn_state {
GPRS_RLC_DL_BSN_MAX,
};
/* EDGE resegment status information for UL */
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
};
/* EDGE resegment status information for DL */
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_STATE_INVALID
};
/*
* 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()
{
@ -99,20 +135,50 @@ struct gprs_rlc_data {
uint8_t len;
struct gprs_rlc_data_block_info block_info;
/*
* cs_current_trans is variable to hold the cs value for
* current transmission. cs_current_trans is same as cs 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
*/
GprsCodingScheme cs_current_trans;
GprsCodingScheme cs;
/*
* The MCS of initial transmission
* 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 current status of the block w.r.t UL split blocks*/
egprs_rlc_ul_reseg_bsn_state block_status_ul;
/* Holds the current status of the block w.r.t DL split blocks*/
egprs_rlc_dl_reseg_bsn_state block_status_dl;
};
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding);
GprsCodingScheme cs, bool with_padding, unsigned int spb = 0);
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, unsigned int spb = 0);
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,
unsigned int 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.
@ -288,6 +354,44 @@ struct rlc_li_field_egprs {
li:7;
} __attribute__ ((packed));
/* TS 44.060 10.3a.4.1.1 */
struct gprs_rlc_ul_header_egprs_1 {
uint8_t r:1,
si:1,
cv:4,
tfi_a:2;
uint8_t tfi_b:3,
bsn1_a:5;
uint8_t bsn1_b:6,
bsn2_a:2;
uint8_t bsn2_b;
uint8_t cps:5,
rsb:1,
pi:1,
spare_a:1;
uint8_t spare_b:6,
dummy:2;
} __attribute__ ((packed));
/* TS 44.060 10.3a.4.2.1 */
struct gprs_rlc_ul_header_egprs_2 {
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:1,
rsb:1,
pi:1,
spare_a:5;
uint8_t spare_b:5,
dummy:3;
} __attribute__ ((packed));
/* TS 44.060 10.3a.4.3.1 */
struct gprs_rlc_ul_header_egprs_3 {
uint8_t r:1,
si:1,

View File

@ -420,6 +420,9 @@ protected:
void start_llc_timer();
int analyse_errors(char *show_rbb, uint8_t ssn, ana_result *res);
void schedule_next_frame();
void egprs_dl_spb_status_get_data_buffer(int bsn, uint8_t **block_data);
unsigned int get_egprs_dl_spb_status(int bsn);
unsigned int get_egprs_dl_spb_value(int bsn);
struct osmo_timer_list m_llc_timer;
};
@ -439,6 +442,18 @@ 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);
/* 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

View File

@ -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)
{
@ -365,7 +361,7 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
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 +375,30 @@ 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()) {
m_rlc.block(bsn)->cs_current_trans =
GprsCodingScheme::get_retx_mcs(
m_rlc.block(bsn)->cs, ms()->current_cs_dl(),
bts->bts_data()->arq_type);
/* TODO: Need to remove this check when MCS-8 -> MCS-6
* transistion is handled
*/
if (m_rlc.block(bsn)->cs == GprsCodingScheme::MCS8)
m_rlc.block(bsn)->cs_current_trans =
GprsCodingScheme::MCS8;
LOGP(DRLCMACDL, LOGL_DEBUG,
"- current_cs_dl(%d) demanded_mcs(%d)"
"cs_trans(%d) arq_type(%d) bsn(%d)\n",
m_rlc.block(bsn)->cs.to_num(),
ms()->current_cs_dl().to_num(),
m_rlc.block(bsn)->cs_current_trans.to_num(),
bts->bts_data()->arq_type, bsn);
} else
m_rlc.block(bsn)->cs_current_trans =
m_rlc.block(bsn)->cs;
data_len2 = m_rlc.block(bsn)->len;
if (force_data_len > 0 && force_data_len != data_len2)
return -1;
@ -422,7 +441,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 +452,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;
}
@ -505,6 +524,9 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
rlc_data = m_rlc.block(bsn);
data = rlc_data->prepare(block_data_len);
rlc_data->cs = cs;
rlc_data->cs_current_trans = cs;
rlc_data->block_status_dl = EGPRS_RESEG_DL_DEFAULT;
rlc_data->cs_init = cs;
rlc_data->len = block_data_len;
rdbi = &(rlc_data->block_info);
@ -589,8 +611,9 @@ 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;
unsigned int spb = 0;
/*
* TODO: This is an experimental work-around to put 2 BSN into
@ -600,7 +623,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 +633,32 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
num_bsns += 1;
}
if (num_bsns == 1) {
if ((GprsCodingScheme::Scheme(cs_init) == GprsCodingScheme::MCS8) &&
(GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6 ||
GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS3)) {
if ((get_egprs_dl_spb_status(index) ==
EGPRS_RESEG_DL_DEFAULT) ||
(get_egprs_dl_spb_status(index) ==
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
*/
if (cs != GprsCodingScheme(GprsCodingScheme::MCS8))
cs.decToSingleBlock(&need_padding);
}
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding);
spb = get_egprs_dl_spb_value(index);
LOGP(DRLCMACDL, LOGL_DEBUG, "- need_padding %d spb_status %d spb %d"
"(BSN1 %d BSN2 %d)\n",
need_padding,
get_egprs_dl_spb_status(index), 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 */
@ -635,7 +677,6 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
data_block_idx++)
{
int bsn;
GprsCodingScheme cs_enc;
uint8_t *block_data;
gprs_rlc_data_block_info *rdbi, *block_info;
@ -645,25 +686,38 @@ 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 */
m_rlc.block(bsn)->next_ps = gprs_get_punct_scheme(
m_rlc.block(bsn)->next_ps,
m_rlc.block(bsn)->cs, cs, spb);
/* get data and header from current block */
block_data = m_rlc.block(bsn)->block;
/* 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);
}
egprs_dl_spb_status_get_data_buffer(bsn, &block_data);
/* TODO: Need to handle 2 same bsns
* in header type 1
*/
/*
* 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
*/
if (spb == 2)
m_window.m_v_b.mark_nacked(bsn);
else
gprs_update_punct_scheme(&m_rlc.block(bsn)->next_ps,
cs);
m_rlc.block(bsn)->cs = cs;
rdbi->e = block_info->e;
rdbi->cv = block_info->cv;
rdbi->bsn = bsn;
@ -742,8 +796,6 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
/* Increment TX-counter */
m_tx_counter++;
bts->rlc_sent();
return dl_msg;
}
@ -1125,3 +1177,106 @@ 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.
*/
void gprs_rlcmac_dl_tbf::egprs_dl_spb_status_get_data_buffer(int bsn,
uint8_t **block_data)
{
struct gprs_rlc_data *rlc_data = m_rlc.block(bsn);
GprsCodingScheme &cs_current_trans = m_rlc.block(bsn)->cs_current_trans;
GprsCodingScheme &cs_init = m_rlc.block(bsn)->cs_init;
/*
* 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 (rlc_data->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:
OSMO_ASSERT(false);
break;
}
rlc_data->block_status_dl = EGPRS_RESEG_SECOND_SEG_SENT;
return;
} else if ((cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
(cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
rlc_data->block_status_dl =
EGPRS_RESEG_FIRST_SEG_SENT;
} else if ((GprsCodingScheme::Scheme(cs_init) ==
GprsCodingScheme::MCS4) &&
(GprsCodingScheme::Scheme(cs_current_trans) ==
GprsCodingScheme::MCS1)) {
rlc_data->block_status_dl = EGPRS_RESEG_FIRST_SEG_SENT;
}
}
*block_data = &rlc_data->block[0];
}
/*
* This function returns the status of split block
* for RLC/MAC block.
*/
unsigned int gprs_rlcmac_dl_tbf::get_egprs_dl_spb_status(int bsn)
{
struct gprs_rlc_data *rlc_data = m_rlc.block(bsn);
return rlc_data->block_status_dl;
}
/*
* This function returns the spb value to be sent OTA
* for RLC/MAC block.
*/
unsigned int gprs_rlcmac_dl_tbf::get_egprs_dl_spb_value(int bsn)
{
struct gprs_rlc_data *rlc_data = m_rlc.block(bsn);
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 (rlc_data->block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
return 3;
} else if ((cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
(cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
return 2;
} else if ((GprsCodingScheme::Scheme(cs_init) ==
GprsCodingScheme::MCS4) &&
(GprsCodingScheme::Scheme(cs_current_trans) ==
GprsCodingScheme::MCS1)) {
return 2;
}
}
return 0;
}

View File

@ -60,7 +60,7 @@ 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 */
@ -138,6 +138,135 @@ struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn, uint8_t ts)
return msg;
}
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 struct gprs_rlc_data_block_info *rdbi =
&rlc->block_info[block_idx];
uint8_t *rlc_data = &(block->block[0]);
if (block->block_status_ul & EGPRS_RESEG_FIRST_SEG_RXD) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"Second seg is received "
"first seg is already present "
"set the status to complete\n");
block->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;
return EGPRS_RESEG_DEFAULT;
} else if (block->block_status_ul == EGPRS_RESEG_DEFAULT) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"Second seg is received "
"first seg is not received "
"set the status to second seg received\n");
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data);
block->block_status_ul = EGPRS_RESEG_SECOND_SEG_RXD;
block->block_info = *rdbi;
return EGPRS_RESEG_SECOND_SEG_RXD;
}
return EGPRS_RESEG_INVALID;
}
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 struct gprs_rlc_data_block_info *rdbi =
&rlc->block_info[block_idx];
uint8_t *rlc_data = &(block->block[0]);
if (block->block_status_ul & EGPRS_RESEG_SECOND_SEG_RXD) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"First seg is received "
"second seg is already present "
"set the status to complete");
static uint8_t seg_data[RLC_MAX_LEN];
uint8_t len = block->len;
/* Assembling the resegmented split blocks */
memcpy(seg_data, block->block, len);
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data);
memcpy(block->block + block->len, seg_data, len);
block->len += len;
block->block_info.data_len += rdbi->data_len;
block->block_status_ul = EGPRS_RESEG_DEFAULT;
return EGPRS_RESEG_DEFAULT;
} else if (block->block_status_ul == EGPRS_RESEG_DEFAULT) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"First seg is received "
"second seg is not received "
"set the status to first seg "
"received\n");
block->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 EGPRS_RESEG_FIRST_SEG_RXD;
}
return EGPRS_RESEG_INVALID;
}
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 struct gprs_rlc_data_block_info *rdbi =
&rlc->block_info[block_idx];
LOGP(DRLCMACUL, LOGL_NOTICE,
"Got SPB(%d) "
"cs(%s) data block with BSN (%d), "
"TFI(%d).\n", rdbi->spb, rlc->cs.name(), rdbi->bsn,
rlc->tfi);
egprs_rlc_ul_reseg_bsn_state re_assemble_status = EGPRS_RESEG_INVALID;
/* Section 10.4.8b of 44.060*/
if (rdbi->spb == 2)
re_assemble_status = handle_egprs_ul_first_seg(rlc,
block, data, block_idx);
else if (rdbi->spb == 3)
re_assemble_status = handle_egprs_ul_second_seg(rlc,
block, data, block_idx);
else {
LOGP(DRLCMACUL, LOGL_ERROR,
"Not supported SPB for this EGPRS configuration\n");
OSMO_ASSERT(false);
}
/*
* When the block is successfully constructed out of segmented blocks
* upgrade the MCS to the type 2
*/
if (re_assemble_status == EGPRS_RESEG_DEFAULT) {
switch (GprsCodingScheme::Scheme(rlc->cs)) {
case GprsCodingScheme::MCS3 :
block->cs = GprsCodingScheme::MCS6;
break;
case GprsCodingScheme::MCS2 :
block->cs = GprsCodingScheme::MCS5;
break;
case GprsCodingScheme::MCS1 :
block->cs = GprsCodingScheme::MCS4;
break;
default:
OSMO_ASSERT(false);
break;
}
}
return re_assemble_status;
}
/*! \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 +348,26 @@ 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 re_assemble_status =
handle_egprs_ul_spb(rlc, block,
data, block_idx);
if (re_assemble_status != EGPRS_RESEG_DEFAULT)
return 0;
} else {
block->block_info = *rdbi;
block->cs = 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(

View File

@ -26,7 +26,7 @@
#include "encoding.h"
#include "rlc.h"
#include "llc.h"
#include "bts.h"
extern "C" {
#include "pcu_vty.h"
@ -1101,6 +1101,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_a = tfi & 0x03;
egprs2->tfi_b = (tfi & 0x1c) >> 2;
egprs2->bsn1_a = 0;
egprs2->bsn1_b = 0;
egprs2->cps_a = 3;
egprs2->cps_b = 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_a = tfi & 0x03;
egprs2->tfi_b = (tfi & 0x1c) >> 2;
egprs2->bsn1_a = 0;
egprs2->bsn1_b = 0;
egprs2->cps_a = 3;
egprs2->cps_b = 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_a = tfi & 0x03;
egprs2->tfi_b = (tfi & 0x1c) >> 2;
egprs2->bsn1_a = 1;
egprs2->bsn1_b = 0;
egprs2->cps_a = 2;
egprs2->cps_b = 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_a = tfi & 0x03;
egprs1->tfi_b = (tfi & 0x1c) >> 2;
egprs1->bsn1_a = 0;
egprs1->bsn1_b = 0;
egprs1->bsn2_a = 1;
egprs1->bsn2_b = 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_a = tfi & 0x03;
egprs1->tfi_b = (tfi & 0x1c) >> 2;
egprs1->bsn1_a = 0;
egprs1->bsn1_b = 0;
egprs1->bsn2_a = 1;
egprs1->bsn2_b = 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_a = tfi & 0x03;
egprs1->tfi_b = (tfi & 0x1c) >> 2;
egprs1->bsn1_a = 0;
egprs1->bsn1_b = 0;
egprs1->bsn2_a = 1;
egprs1->bsn2_b = 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 +1367,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;

View File

@ -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 ===

View File

@ -89,9 +89,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 +155,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"
};

View File

@ -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

View File

@ -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,17 @@ 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
=== end test_tbf_egprs_spb_dl ===