forked from cellular-infrastructure/osmo-pcu
Compare commits
132 Commits
master
...
radisys/eg
Author | SHA1 | Date |
---|---|---|
aravind sirsikar | 9ddc3c7845 | |
aravind sirsikar | d3ae14c18f | |
aravind sirsikar | fcf2af400a | |
Pravin Kumarvel | f595a4eadb | |
Pravin Kumarvel | ac82ca15e6 | |
Pravin Kumarvel | 1b465dbadd | |
Pravin Kumarvel | 77b5da51ac | |
Pravin Kumarvel | 22529354e1 | |
Pravin Kumarvel | 17ee617032 | |
Pravin Kumarvel | 366931baa6 | |
sivasankari | ee78bf0882 | |
sivasankari | da7250ad2c | |
Harald Welte | 963cdaffd5 | |
Harald Welte | 1f2bb6e93e | |
sivasankari | 5395073fff | |
aravind sirsikar | cc4214a429 | |
Mrinal Mishra | 0e63644d14 | |
Neels Hofmeyr | 34bfbdaf9e | |
sivasankari | 168911b438 | |
Harald Welte | 68fc12775f | |
Harald Welte | 5d93f0f4ec | |
Harald Welte | bb47d957a8 | |
Mrinal Mishra | f86307e1e4 | |
aravind sirsikar | ed3413e397 | |
aravind sirsikar | c0c3afd079 | |
Max | ae4838101a | |
aravind sirsikar | fb41afaaf6 | |
aravind sirsikar | 9434e52af9 | |
aravind sirsikar | f276138202 | |
Mrinal Mishra | d453eaa788 | |
Neels Hofmeyr | f868bdbe76 | |
Max | d71e8b32e3 | |
Neels Hofmeyr | 4ea452689d | |
Alexander Couzens | e4727a3591 | |
Alexander Couzens | d38b92e972 | |
Pravin Kumarvel | 0a4a6c1200 | |
Neels Hofmeyr | 6348aea6a2 | |
Neels Hofmeyr | da66f71ffe | |
Neels Hofmeyr | da933e0ff8 | |
Neels Hofmeyr | fd9e16ce97 | |
bhargava | 465f5bbb6f | |
bhargava | 628dcfbc97 | |
Aravind Sirsikar | 0ee31cfa38 | |
Aravind Sirsikar | 8e70bb5bb4 | |
Aravind Sirsikar | 22a901905c | |
Aravind Sirsikar | 3463bd4adc | |
Aravind Sirsikar | e26ee01d56 | |
Neels Hofmeyr | 0241526836 | |
Aravind Sirsikar | 9f5f008aed | |
Aravind Sirsikar | 8d2d9e8985 | |
Max | 9bbe1600cc | |
Minh-Quang Nguyen | 16ddc90eab | |
Aravind Sirsikar | 7c7a86c080 | |
Aravind Sirsikar | a35c911a91 | |
Aravind Sirsikar | 3c2eaebd21 | |
Aravind Sirsikar | fd71384104 | |
Aravind Sirsikar | b119198992 | |
Neels Hofmeyr | 01826c13b1 | |
bhargava | 959d1dee67 | |
Aravind Sirsikar | eebcb1e3e8 | |
Aravind Sirsikar | 02352b487a | |
Aravind Sirsikar | 50b097003b | |
Aravind Sirsikar | e6cadb4e3c | |
Aravind Sirsikar | 1ec4d80176 | |
Neels Hofmeyr | 9876f4bb21 | |
Neels Hofmeyr | 7fd177b91c | |
Neels Hofmeyr | 2d91260ea4 | |
Neels Hofmeyr | 6bae2d11f1 | |
Neels Hofmeyr | 0b4da058ad | |
Max | 79cb245157 | |
Max | cbf9a721d6 | |
Aravind Sirsikar | 505a86d396 | |
Aravind Sirsikar | 36bdc5f7a4 | |
Max | d572054ca7 | |
Max | 878bd1f296 | |
Max | 1d7644b23a | |
Max | 2ec6b8e758 | |
Tom Tsou | df69809b82 | |
Holger Hans Peter Freyther | 5d94b5455f | |
Aravind Sirsikar | 1a679127af | |
Aravind Sirsikar | cf2152b24c | |
Aravind Sirsikar | e8ccafc63d | |
Aravind Sirsikar | 914955209e | |
Harald Welte | 899d36d813 | |
Neels Hofmeyr | d32aa03520 | |
Aravind Sirsikar | 2c9f980163 | |
Aravind Sirsikar | 99ab0a8fa0 | |
Aravind Sirsikar | 550a54184b | |
Aravind Sirsikar | 23617c001d | |
Aravind Sirsikar | 189742b66c | |
Alexander Couzens | e04fd0cf0f | |
Alexander Couzens | 6922bcd929 | |
Alexander Couzens | 7fdbf89ef3 | |
Alexander Couzens | 6f0dc96929 | |
Alexander Couzens | 1a5066112f | |
Alexander Couzens | d302e4fb28 | |
Alexander Couzens | 68e2c6375e | |
Alexander Couzens | cb846ecbbc | |
Alexander Couzens | b82bd92e57 | |
Alexander Couzens | 2fcfc29020 | |
Alexander Couzens | ce936f3cd4 | |
Alexander Couzens | c1c9d6a9d8 | |
Alexander Couzens | c8fd4b7c42 | |
Alexander Couzens | f929e62525 | |
Alexander Couzens | 4acb6b7251 | |
Alexander Couzens | 95e379241a | |
Alexander Couzens | 543756adbe | |
Alexander Couzens | 2cb1547993 | |
Alexander Couzens | 9736d00b12 | |
Yves Godin | 660709dc7c | |
Max | 58b6646750 | |
Alexander Couzens | ed3ae4a392 | |
Yves Godin | f0bb25450c | |
Max | de810f2005 | |
Max | cad867ec8d | |
Max | 280448ba7b | |
Holger Hans Peter Freyther | 1aa7527302 | |
Holger Hans Peter Freyther | ca025c02ef | |
Holger Hans Peter Freyther | 97e48a3252 | |
Harald Welte | 63d33ad2d7 | |
Aravind Sirsikar | 7952282b78 | |
Aravind Sirsikar | a859a21800 | |
Aravind Sirsikar | 7a05b039c8 | |
Bhargava Abhyankar | e44383baa4 | |
Aravind Sirsikar | 5a5d2b7a27 | |
Saurabh Sharan | 2b09c39c9c | |
Saurabh Sharan | bacb65b48b | |
Saurabh Sharan | 656eed5975 | |
Holger Hans Peter Freyther | 173ef90a53 | |
Holger Hans Peter Freyther | fd263b0dfd | |
Holger Hans Peter Freyther | 99db40ad2d | |
Max | 22d7e75e1f |
|
@ -14,10 +14,11 @@ config.guess
|
|||
config.sub
|
||||
config.status
|
||||
configure
|
||||
compile
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
libtool
|
||||
*libtool
|
||||
ltmain.sh
|
||||
|
||||
core
|
||||
|
@ -43,3 +44,17 @@ tests/codel/codel_test
|
|||
tests/emu/pcu_emu
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
tests/edge/EdgeTest
|
||||
tests/llc/LlcTest
|
||||
|
||||
# ignore debian files
|
||||
.tarball-version
|
||||
debian/autoreconf.after
|
||||
debian/autoreconf.before
|
||||
debian/files
|
||||
debian/*.debhelper*
|
||||
debian/*.substvars
|
||||
debian/osmo-pcu-dbg/
|
||||
debian/osmo-pcu.substvars
|
||||
debian/osmo-pcu/
|
||||
debian/tmp/
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=osmo-pcu
|
|
@ -1,5 +1,5 @@
|
|||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||
|
||||
SUBDIRS = src examples tests
|
||||
SUBDIRS = include src examples tests
|
||||
EXTRA_DIST = osmoappdesc.py
|
||||
|
||||
|
|
13
README
13
README
|
@ -1,7 +1,17 @@
|
|||
This is an implementation of Packet Control Unit (PCU) according to TS 04.60
|
||||
his is an implementation of Packet Control Unit (PCU) according to TS 04.60
|
||||
|
||||
The PCU is part of BSS, so it connects directly to SGSN.
|
||||
|
||||
For this PCU the dependent libosmocore patch is in contrib which has to be
|
||||
applied on top of libosmocore 2ae5f186929a720b5604e2bd19ef54606b37fb87.
|
||||
|
||||
This PCU enables
|
||||
* Support for EPDAN
|
||||
* Support for PUAN with CRBB
|
||||
|
||||
Feature support for Deactivate PDP context Request from SGSN VTY.
|
||||
which has two patches are present in contrib has to be
|
||||
applied on top of openbsc 80abe522e2ddc979d994530f21b103808fc465d7.
|
||||
|
||||
== Current limitations ==
|
||||
|
||||
|
@ -9,7 +19,6 @@ The PCU is part of BSS, so it connects directly to SGSN.
|
|||
* No fixed allocation support
|
||||
* No extended dynamic allocation support
|
||||
* No unacknowledged mode operation
|
||||
* No PCCCH/PBCCH support
|
||||
* Only single slot assignment on uplink direction
|
||||
* No half-duplex class support (only semi-duplex)
|
||||
* No handover support
|
||||
|
|
33
configure.ac
33
configure.ac
|
@ -3,6 +3,9 @@ AC_INIT([osmo-pcu],
|
|||
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||
[osmocom-net-gprs@lists.osmocom.org])
|
||||
|
||||
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
|
||||
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
|
@ -16,6 +19,13 @@ AC_PROG_CXX
|
|||
AC_PROG_INSTALL
|
||||
LT_INIT
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
AC_MSG_WARN([You need to install pkg-config])
|
||||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
|
||||
|
@ -35,6 +45,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]]),
|
||||
|
@ -50,7 +79,11 @@ AC_MSG_CHECKING([whether to enable VTY tests])
|
|||
AC_MSG_RESULT([$enable_vty_tests])
|
||||
AM_CONDITIONAL(ENABLE_VTY_TESTS, test "x$enable_vty_tests" = "xyes")
|
||||
|
||||
STD_DEFINES_AND_INCLUDES="-Wall"
|
||||
AC_SUBST(STD_DEFINES_AND_INCLUDES)
|
||||
|
||||
AC_OUTPUT(
|
||||
include/Makefile
|
||||
src/Makefile
|
||||
examples/Makefile
|
||||
tests/Makefile
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
From df53ee248964652e64ced971ad5ef7ce954f5bbd Mon Sep 17 00:00:00 2001
|
||||
From: Pravin Kumarvel <pmanohar@radisys.com>
|
||||
Date: Thu, 22 Dec 2016 12:06:07 +0530
|
||||
Subject: [PATCH] Add function to get uninterrupted bit run
|
||||
|
||||
Function bitvec_rl_curbit added to get number of uninterrupted
|
||||
bits run in vector starting from the current bit till max number
|
||||
of bits.
|
||||
Test case is added to check bitvec_rl_curbit.
|
||||
---
|
||||
include/osmocom/core/bitvec.h | 1 +
|
||||
src/bitvec.c | 45 +++++++++++++++++++++++++++++++++++++++++++
|
||||
tests/bitvec/bitvec_test.c | 41 +++++++++++++++++++++++++++++++++++++++
|
||||
tests/bitvec/bitvec_test.ok | 2 ++
|
||||
4 files changed, 89 insertions(+)
|
||||
|
||||
diff --git a/include/osmocom/core/bitvec.h b/include/osmocom/core/bitvec.h
|
||||
index 19e2af8..0e17ba7 100644
|
||||
--- a/include/osmocom/core/bitvec.h
|
||||
+++ b/include/osmocom/core/bitvec.h
|
||||
@@ -89,6 +89,7 @@ char bit_value_to_char(enum bit_value v);
|
||||
void bitvec_to_string_r(const struct bitvec *bv, char *str);
|
||||
void bitvec_zero(struct bitvec *bv);
|
||||
unsigned bitvec_rl(const struct bitvec *bv, bool b);
|
||||
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits);
|
||||
void bitvec_shiftl(struct bitvec *bv, unsigned int n);
|
||||
int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits);
|
||||
unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array,
|
||||
diff --git a/src/bitvec.c b/src/bitvec.c
|
||||
index 38148ac..c895cff 100644
|
||||
--- a/src/bitvec.c
|
||||
+++ b/src/bitvec.c
|
||||
@@ -575,6 +575,51 @@ unsigned bitvec_rl(const struct bitvec *bv, bool b)
|
||||
return bv->cur_bit;
|
||||
}
|
||||
|
||||
+/*! \brief Return number (bits) of uninterrupted bit run in vector
|
||||
+ * starting from the current bit
|
||||
+ * \param[in] bv The boolean vector to work on
|
||||
+ * \param[in] b The boolean, sequence of 1's or 0's to be checked
|
||||
+ * \param[in] max_bits Total Number of Uncmopresed bits
|
||||
+ * \returns Number of consecutive bits of \p b in \p bv and cur_bit will
|
||||
+ * \go to cur_bit + number of consecutive bit
|
||||
+ */
|
||||
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits)
|
||||
+{
|
||||
+ unsigned i = 0;
|
||||
+ unsigned j = 8;
|
||||
+ int temp_res = 0;
|
||||
+ int count = 0;
|
||||
+ unsigned readIndex = bv->cur_bit;
|
||||
+ unsigned remaining_bits = max_bits % 8;
|
||||
+ unsigned remaining_bytes = max_bits / 8;
|
||||
+ unsigned byte_mask = 0xFF;
|
||||
+
|
||||
+ if (readIndex % 8) {
|
||||
+ for (j -= (readIndex % 8) ; j > 0 ; j--) {
|
||||
+ if (readIndex < max_bits && bitvec_read_field(bv, &readIndex, 1) == b)
|
||||
+ temp_res++;
|
||||
+ else {
|
||||
+ bv->cur_bit--;
|
||||
+ return temp_res;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ for (i = (readIndex / 8);
|
||||
+ i < (remaining_bits ? remaining_bytes + 1 : remaining_bytes);
|
||||
+ i++, count++) {
|
||||
+ if ((b ? byte_mask : 0) != bv->data[i]) {
|
||||
+ bv->cur_bit = (count * 8 +
|
||||
+ leading_bits(bv->data[i], b) + readIndex);
|
||||
+ return count * 8 +
|
||||
+ leading_bits(bv->data[i], b) + temp_res;
|
||||
+ }
|
||||
+ }
|
||||
+ bv->cur_bit = (temp_res + (count * 8)) + readIndex;
|
||||
+ if (bv->cur_bit > max_bits)
|
||||
+ bv->cur_bit = max_bits;
|
||||
+ return (bv->cur_bit - readIndex + temp_res);
|
||||
+}
|
||||
+
|
||||
/*! \brief Shifts bitvec to the left, n MSB bits lost */
|
||||
void bitvec_shiftl(struct bitvec *bv, unsigned n)
|
||||
{
|
||||
diff --git a/tests/bitvec/bitvec_test.c b/tests/bitvec/bitvec_test.c
|
||||
index a98a91c..afcc942 100644
|
||||
--- a/tests/bitvec/bitvec_test.c
|
||||
+++ b/tests/bitvec/bitvec_test.c
|
||||
@@ -150,6 +150,18 @@ static inline void test_array_item(unsigned t, struct bitvec *b, unsigned int n,
|
||||
}
|
||||
}
|
||||
|
||||
+static inline void test_bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits,
|
||||
+ int result )
|
||||
+{
|
||||
+ int num = 0;
|
||||
+ int readIndex = bv->cur_bit;
|
||||
+ OSMO_ASSERT(bv->cur_bit < max_bits);
|
||||
+ num = bitvec_rl_curbit(bv, b, max_bits);
|
||||
+ readIndex += num;
|
||||
+ OSMO_ASSERT(bv->cur_bit == readIndex);
|
||||
+ OSMO_ASSERT(num == result);
|
||||
+}
|
||||
+
|
||||
static void test_array()
|
||||
{
|
||||
struct bitvec b;
|
||||
@@ -245,6 +257,35 @@ int main(int argc, char **argv)
|
||||
|
||||
test_array();
|
||||
|
||||
+ printf("\nbitvec_runlength....\n");
|
||||
+
|
||||
+ bitvec_zero(&bv);
|
||||
+ bitvec_set_uint(&bv, 0xff, 8);
|
||||
+ bv.cur_bit -= 8;
|
||||
+ test_bitvec_rl_curbit(&bv, 1, 64, 8);
|
||||
+
|
||||
+ bitvec_zero(&bv);
|
||||
+ bitvec_set_uint(&bv, 0xfc, 8);
|
||||
+ bv.cur_bit -= 8;
|
||||
+ test_bitvec_rl_curbit(&bv, 1, 64, 6);
|
||||
+
|
||||
+ bitvec_zero(&bv);
|
||||
+ test_bitvec_rl_curbit(&bv, 0, 52, 52);
|
||||
+
|
||||
+ bitvec_zero(&bv);
|
||||
+ bitvec_set_uint(&bv, 0xfc, 8);
|
||||
+ bv.cur_bit -= 2;
|
||||
+ test_bitvec_rl_curbit(&bv, 0, 64, 58);
|
||||
+
|
||||
+ bitvec_zero(&bv);
|
||||
+ bitvec_set_uint(&bv, 0x07, 8);
|
||||
+ bitvec_set_uint(&bv, 0xf8, 8);
|
||||
+ bv.cur_bit -= 11;
|
||||
+ test_bitvec_rl_curbit(&bv, 1, 64, 8);
|
||||
+
|
||||
+ bitvec_zero(&bv);
|
||||
+ test_bitvec_rl_curbit(&bv, 1, 64, 0);
|
||||
+
|
||||
printf("\nbitvec ok.\n");
|
||||
|
||||
return 0;
|
||||
diff --git a/tests/bitvec/bitvec_test.ok b/tests/bitvec/bitvec_test.ok
|
||||
index e256108..6281973 100644
|
||||
--- a/tests/bitvec/bitvec_test.ok
|
||||
+++ b/tests/bitvec/bitvec_test.ok
|
||||
@@ -166,4 +166,6 @@ bits: 17, est: 1153, real: 1153, x: 0, y: 0
|
||||
........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
|
||||
........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
|
||||
........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........
|
||||
+bitvec_runlength....
|
||||
+
|
||||
bitvec ok.
|
||||
--
|
||||
1.9.1
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
From 4ce4f62ae647a8ff852f29815011186f15ee1d16 Mon Sep 17 00:00:00 2001
|
||||
From: Pravin Kumarvel <pmanohar@radisys.com>
|
||||
Date: Thu, 29 Dec 2016 20:17:14 +0530
|
||||
Subject: [PATCH 1/2] Support Deactivate PDP Context Request from network
|
||||
|
||||
Enable Deactivate PDP context based on the IMSI of the subscriber.
|
||||
When there are PDP contexts present for a MM context,
|
||||
PDP context will be deactivated along with GMM Detach(MM context deletion).
|
||||
If there are no PDP present, MM context will be deleted to avoid
|
||||
further PDP context request from the MS.
|
||||
Test cases is added to check this functionality.
|
||||
---
|
||||
openbsc/include/openbsc/gprs_sgsn.h | 2 +
|
||||
openbsc/src/gprs/gprs_sgsn.c | 36 ++++++++++
|
||||
openbsc/tests/sgsn/sgsn_test.c | 132 ++++++++++++++++++++++++++++++++++++
|
||||
openbsc/tests/sgsn/sgsn_test.ok | 4 ++
|
||||
4 files changed, 174 insertions(+)
|
||||
|
||||
diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h
|
||||
index 24e286c..b3f250d 100644
|
||||
--- a/openbsc/include/openbsc/gprs_sgsn.h
|
||||
+++ b/openbsc/include/openbsc/gprs_sgsn.h
|
||||
@@ -369,6 +369,8 @@ void sgsn_inst_init(void);
|
||||
* ottherwise lost state (recovery procedure) */
|
||||
int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn);
|
||||
|
||||
+void drop_gmm_ctx_for_ms(const char *imsi);
|
||||
+
|
||||
char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len);
|
||||
|
||||
/*
|
||||
diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c
|
||||
index e85e1a9..94fcd5f 100644
|
||||
--- a/openbsc/src/gprs/gprs_sgsn.c
|
||||
+++ b/openbsc/src/gprs/gprs_sgsn.c
|
||||
@@ -669,6 +669,42 @@ static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
|
||||
}
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * High-level function to be called for PDP deactivation initiated from SGSN VTY.
|
||||
+ * When there are PDP contexts present for a MM context, PDP context will be
|
||||
+ * deactivated along with GMM Detach(MM context deletion).
|
||||
+ * If there are no PDP present, MM context will be deleted to avoid further
|
||||
+ * PDP context activation for that MS.
|
||||
+ */
|
||||
+void drop_gmm_ctx_for_ms(const char *imsi)
|
||||
+{
|
||||
+ OSMO_ASSERT(imsi != NULL);
|
||||
+ struct sgsn_mm_ctx *mm;
|
||||
+ struct sgsn_pdp_ctx *pdp;
|
||||
+
|
||||
+ /* Search the MM context subscriber */
|
||||
+ mm = sgsn_mm_ctx_by_imsi(imsi);
|
||||
+ LOGMMCTXP(LOGL_INFO, mm, "SGSN intiated Deactivate PDP request\n");
|
||||
+ if (mm) {
|
||||
+ /* Search the PDP for this subscriber */
|
||||
+ if (llist_empty(&mm->pdp_list)) {
|
||||
+ /*
|
||||
+ * Deleting mm context for the subscriber when no PDP
|
||||
+ * context is present.
|
||||
+ */
|
||||
+ LOGMMCTXP(LOGL_NOTICE, mm, "No PDP context to deactivate\n");
|
||||
+ gsm0408_gprs_access_cancelled(mm, GMM_CAUSE_GPRS_NOTALLOWED);
|
||||
+ } else {
|
||||
+ llist_for_each_entry(pdp, &mm->pdp_list, list) {
|
||||
+ gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_DEACT_REGULAR);
|
||||
+ LOGPDPCTXP(LOGL_INFO, pdp, "PDP Deactivation "
|
||||
+ "Successful\n");
|
||||
+ }
|
||||
+ }
|
||||
+ } else
|
||||
+ LOGMMCTXP(LOGL_NOTICE, mm, "No MM context to deactivate\n");
|
||||
+}
|
||||
+
|
||||
/* High-level function to be called in case a GGSN has disappeared or
|
||||
* otherwise lost state (recovery procedure) */
|
||||
int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
|
||||
diff --git a/openbsc/tests/sgsn/sgsn_test.c b/openbsc/tests/sgsn/sgsn_test.c
|
||||
index b4bcaf6..f8bf186 100644
|
||||
--- a/openbsc/tests/sgsn/sgsn_test.c
|
||||
+++ b/openbsc/tests/sgsn/sgsn_test.c
|
||||
@@ -38,14 +38,17 @@
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#include <stdio.h>
|
||||
+#include <gtp.h>
|
||||
|
||||
void *tall_bsc_ctx;
|
||||
+struct gsn_t gsn_ctx;
|
||||
static struct sgsn_instance sgsn_inst = {
|
||||
.config_file = "osmo_sgsn.cfg",
|
||||
.cfg = {
|
||||
.gtp_statedir = "./",
|
||||
.auth_policy = SGSN_AUTH_POLICY_CLOSED,
|
||||
},
|
||||
+ .gsn = &gsn_ctx,
|
||||
};
|
||||
struct sgsn_instance *sgsn = &sgsn_inst;
|
||||
unsigned sgsn_tx_counter = 0;
|
||||
@@ -2363,6 +2366,134 @@ static void test_ggsn_selection(void)
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
+static void test_pdp_deactivation_with_pdp_ctx(void)
|
||||
+{
|
||||
+ struct apn_ctx *actxs[4];
|
||||
+ struct sgsn_ggsn_ctx *ggc, *ggcs[3];
|
||||
+ struct gsm_subscriber *s1;
|
||||
+ const char *imsi1 = "12345678901";
|
||||
+ struct sgsn_mm_ctx *ctx;
|
||||
+ struct gprs_ra_id raid = { 0, };
|
||||
+ uint32_t local_tlli = 0xffeeddcc;
|
||||
+ enum gsm48_gsm_cause gsm_cause;
|
||||
+ struct tlv_parsed tp;
|
||||
+ uint8_t apn_enc[GSM_APN_LENGTH + 10];
|
||||
+ struct sgsn_subscriber_pdp_data *pdp_data;
|
||||
+ char apn_str[GSM_APN_LENGTH];
|
||||
+
|
||||
+ printf("Testing Pdp deactivation for MS with pdp ctx\n");
|
||||
+
|
||||
+ /* Check for emptiness */
|
||||
+ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
|
||||
+
|
||||
+ /* Create a context */
|
||||
+ OSMO_ASSERT(count(gprs_llme_list()) == 0);
|
||||
+ ctx = alloc_mm_ctx(local_tlli, &raid);
|
||||
+ strncpy(ctx->imsi, imsi1, sizeof(ctx->imsi) - 1);
|
||||
+
|
||||
+ /* Allocate and attach a subscriber */
|
||||
+ s1 = gprs_subscr_get_or_create_by_mmctx(ctx);
|
||||
+ assert_subscr(s1, imsi1);
|
||||
+
|
||||
+ struct sgsn_pdp_ctx *pdp;
|
||||
+
|
||||
+ tp.lv[GSM48_IE_GSM_APN].len = 0;
|
||||
+ tp.lv[GSM48_IE_GSM_APN].val = apn_enc;
|
||||
+
|
||||
+ tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].len = 2;
|
||||
+ tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].val = apn_enc;
|
||||
+
|
||||
+ tp.lv[OSMO_IE_GSM_REQ_QOS].len = 14;
|
||||
+ tp.lv[OSMO_IE_GSM_REQ_QOS].val = apn_enc;
|
||||
+
|
||||
+ ggcs[0] = sgsn_ggsn_ctx_find_alloc(0);
|
||||
+
|
||||
+ actxs[0] = sgsn_apn_ctx_find_alloc("test.apn", "123456");
|
||||
+ actxs[0]->ggsn = ggcs[0];
|
||||
+
|
||||
+ pdp_data = sgsn_subscriber_pdp_data_alloc(s1->sgsn_data);
|
||||
+ pdp_data->context_id = 1;
|
||||
+ pdp_data->pdp_type = 0x0121;
|
||||
+ strncpy(pdp_data->apn_str, "*", sizeof(pdp_data->apn_str)-1);
|
||||
+
|
||||
+ /* Resolve GGSNs */
|
||||
+ tp.lv[GSM48_IE_GSM_APN].len =
|
||||
+ gprs_str_to_apn(apn_enc, sizeof(apn_enc), "Test.Apn");
|
||||
+
|
||||
+ ggc = sgsn_mm_ctx_find_ggsn_ctx(ctx, &tp, &gsm_cause, apn_str);
|
||||
+
|
||||
+ OSMO_ASSERT(ggc != NULL);
|
||||
+ OSMO_ASSERT(ggc->id == 0);
|
||||
+
|
||||
+ ggc = sgsn_ggsn_ctx_alloc(ggc->id);
|
||||
+ /* Create a pdp context */
|
||||
+ pdp = sgsn_create_pdp_ctx(ggc, ctx, 5, &tp);
|
||||
+
|
||||
+ /* Intiate PDP deactivation for imsi1 */
|
||||
+ drop_gmm_ctx_for_ms(imsi1);
|
||||
+ gsm48_tx_gsm_deact_pdp_acc(pdp);
|
||||
+ gsm0408_gprs_access_cancelled(ctx, GMM_CAUSE_GPRS_NOTALLOWED);
|
||||
+
|
||||
+ /* Cleanup */
|
||||
+
|
||||
+ subscr_put(s1);
|
||||
+
|
||||
+ sgsn_apn_ctx_free(actxs[0]);
|
||||
+ sgsn_ggsn_ctx_free(ggcs[0]);
|
||||
+ sgsn_ggsn_ctx_free(ggc);
|
||||
+ talloc_free(pdp);
|
||||
+
|
||||
+ cleanup_test();
|
||||
+}
|
||||
+
|
||||
+static void test_pdp_deactivation_with_only_mm_ctx(void)
|
||||
+{
|
||||
+ struct gsm_subscriber *s1;
|
||||
+ const char *imsi1 = "1234567890";
|
||||
+ struct sgsn_mm_ctx *ctx;
|
||||
+ struct gprs_ra_id raid = { 0, };
|
||||
+ uint32_t local_tlli = 0xffeeddcc;
|
||||
+
|
||||
+ printf("Testing Pdp deactivation for MS with only MM ctx\n");
|
||||
+
|
||||
+ /* Check for emptiness */
|
||||
+ OSMO_ASSERT(gprs_subscr_get_by_imsi(imsi1) == NULL);
|
||||
+
|
||||
+ /* Create a context */
|
||||
+ OSMO_ASSERT(count(gprs_llme_list()) == 0);
|
||||
+ ctx = alloc_mm_ctx(local_tlli, &raid);
|
||||
+ strncpy(ctx->imsi, imsi1, sizeof(ctx->imsi) - 1);
|
||||
+
|
||||
+ /* Allocate and attach a subscriber */
|
||||
+ s1 = gprs_subscr_get_or_create_by_mmctx(ctx);
|
||||
+ assert_subscr(s1, imsi1);
|
||||
+
|
||||
+ /* Intiate PDP deactivation for imsi1 */
|
||||
+ drop_gmm_ctx_for_ms(imsi1);
|
||||
+
|
||||
+ cleanup_test();
|
||||
+}
|
||||
+
|
||||
+static void test_pdp_deactivation_without_mm_ctx(void)
|
||||
+{
|
||||
+ const char *imsi1 = "1234567890";
|
||||
+
|
||||
+ printf("Testing Pdp deactivation for MS without MM ctx\n");
|
||||
+
|
||||
+ /* Intiate PDP deactivation for imsi1 */
|
||||
+ drop_gmm_ctx_for_ms(imsi1);
|
||||
+
|
||||
+ cleanup_test();
|
||||
+}
|
||||
+
|
||||
+static void test_pdp_deactivation(void)
|
||||
+{
|
||||
+ printf("Testing pdp deactivation\n");
|
||||
+
|
||||
+ test_pdp_deactivation_with_only_mm_ctx();
|
||||
+ test_pdp_deactivation_with_pdp_ctx();
|
||||
+ test_pdp_deactivation_without_mm_ctx();
|
||||
+}
|
||||
static struct log_info_cat gprs_categories[] = {
|
||||
[DMM] = {
|
||||
.name = "DMM",
|
||||
@@ -2454,6 +2585,7 @@ int main(int argc, char **argv)
|
||||
test_gmm_routing_areas();
|
||||
test_apn_matching();
|
||||
test_ggsn_selection();
|
||||
+ test_pdp_deactivation();
|
||||
printf("Done\n");
|
||||
|
||||
talloc_report_full(osmo_sgsn_ctx, stderr);
|
||||
diff --git a/openbsc/tests/sgsn/sgsn_test.ok b/openbsc/tests/sgsn/sgsn_test.ok
|
||||
index c7a53b9..6159bc9 100644
|
||||
--- a/openbsc/tests/sgsn/sgsn_test.ok
|
||||
+++ b/openbsc/tests/sgsn/sgsn_test.ok
|
||||
@@ -34,4 +34,8 @@ Testing routing area changes
|
||||
- RA Update Request (RA 2 -> RA 2)
|
||||
Testing APN matching
|
||||
Testing GGSN selection
|
||||
+Testing pdp deactivation
|
||||
+Testing Pdp deactivation for MS with only MM ctx
|
||||
+Testing Pdp deactivation for MS with pdp ctx
|
||||
+Testing Pdp deactivation for MS without MM ctx
|
||||
Done
|
||||
--
|
||||
1.9.1
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
From 29f7bd1023fc698ebdb19b93f8029b2559392f17 Mon Sep 17 00:00:00 2001
|
||||
From: Pravin Kumarvel <pmanohar@radisys.com>
|
||||
Date: Thu, 29 Dec 2016 20:18:49 +0530
|
||||
Subject: [PATCH 2/2] Trigger Deactivate PDP context Request from SGSN VTY
|
||||
|
||||
In SGSN for acl based authorization IMSI values of all registered
|
||||
MS are maintained.
|
||||
Through imsi_acl_del Deactivate PDP context Request from network
|
||||
can be triggered.
|
||||
Hence, It will remove all PDP context related to that MS.
|
||||
---
|
||||
openbsc/src/gprs/gprs_gmm.c | 2 ++
|
||||
openbsc/src/gprs/sgsn_vty.c | 6 ++++--
|
||||
2 files changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c
|
||||
index 363b457..77f94f8 100644
|
||||
--- a/openbsc/src/gprs/gprs_gmm.c
|
||||
+++ b/openbsc/src/gprs/gprs_gmm.c
|
||||
@@ -2535,6 +2535,8 @@ static int gsm0408_rcv_gsm(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
break;
|
||||
case GSM48_MT_GSM_DEACT_PDP_ACK:
|
||||
rc = gsm48_rx_gsm_deact_pdp_ack(mmctx, msg);
|
||||
+ if (sgsn_auth_state(mmctx) != SGSN_AUTH_ACCEPTED)
|
||||
+ gsm0408_gprs_access_cancelled(mmctx, GMM_CAUSE_GPRS_NOTALLOWED);
|
||||
break;
|
||||
case GSM48_MT_GSM_STATUS:
|
||||
rc = gsm48_rx_gsm_status(mmctx, msg);
|
||||
diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c
|
||||
index 21c865b..feeb0ff 100644
|
||||
--- a/openbsc/src/gprs/sgsn_vty.c
|
||||
+++ b/openbsc/src/gprs/sgsn_vty.c
|
||||
@@ -573,9 +573,11 @@ DEFUN(imsi_acl, cfg_imsi_acl_cmd,
|
||||
|
||||
if (!strcmp(op, "add"))
|
||||
rc = sgsn_acl_add(imsi, g_cfg);
|
||||
- else
|
||||
+ else {
|
||||
+ vty_out(vty, "%% Network initiated PDP deactivate%s", VTY_NEWLINE);
|
||||
+ drop_gmm_ctx_for_ms(imsi);
|
||||
rc = sgsn_acl_del(imsi, g_cfg);
|
||||
-
|
||||
+ }
|
||||
if (rc < 0) {
|
||||
vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE);
|
||||
|
||||
--
|
||||
1.9.1
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
if [ -z "$MAKE" ]; then
|
||||
echo 'The $MAKE variable is not defined, cannot build'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
base="$PWD"
|
||||
deps="$base/deps"
|
||||
inst="$deps/install"
|
||||
export deps inst
|
||||
|
||||
mkdir "$deps" || true
|
||||
rm -rf "$inst"
|
||||
|
||||
# Collect configure options for osmo-pcu
|
||||
PCU_CONFIG=""
|
||||
if [ "$with_dsp" = sysmo ]; then
|
||||
PCU_CONFIG="$PCU_CONFIG --enable-sysmocom-dsp"
|
||||
|
||||
# For direct sysmo DSP access, provide the SysmoBTS Layer 1 API
|
||||
cd "$deps"
|
||||
if [ ! -d layer1-api ]; then
|
||||
git clone git://git.sysmocom.de/sysmo-bts/layer1-api.git layer1-api
|
||||
fi
|
||||
cd layer1-api
|
||||
git fetch origin
|
||||
git reset --hard origin/master
|
||||
api_incl="$inst/include/sysmocom/femtobts/"
|
||||
mkdir -p "$api_incl"
|
||||
cp include/*.h "$api_incl"
|
||||
cd "$base"
|
||||
|
||||
elif [ -z "$with_dsp" -o "$with_dsp" = none ]; then
|
||||
echo "Direct DSP access disabled"
|
||||
else
|
||||
echo 'Invalid $with_dsp value:' $with_dsp
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$with_vty" = "yes" ]; then
|
||||
PCU_CONFIG="$PCU_CONFIG --enable-vty-tests"
|
||||
elif [ -z "$with_vty" -o "$with_vty" = "no" ]; then
|
||||
echo "VTY tests disabled"
|
||||
else
|
||||
echo 'Invalid $with_vty value:' $with_vty
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build deps
|
||||
osmo-build-dep.sh libosmocore
|
||||
|
||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
|
||||
set +x
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo " =============================== osmo-pcu ==============================="
|
||||
echo
|
||||
set -x
|
||||
|
||||
autoreconf --install --force
|
||||
./configure $PCU_CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" AM_DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" \
|
||||
$MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
|
@ -3,7 +3,7 @@ Description=sysmocom sysmoPCU
|
|||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg -e
|
||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
RestartPreventExitStatus=1
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
osmo-pcu (0.3) UNRELEASED; urgency=medium
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Holger Hans Peter Freyther <holger@moiji-mobile.com> Fri, 01 Apr 2016 18:59:00 +0200
|
|
@ -0,0 +1 @@
|
|||
7
|
|
@ -0,0 +1,24 @@
|
|||
Source: osmo-pcu
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
|
||||
Build-Depends: debhelper (>= 7.0.0~), dh-autoreconf, dh-systemd (>= 1.5), autotools-dev, pkg-config, libosmocore-dev
|
||||
Standards-Version: 3.8.4
|
||||
Homepage: http://osmocom.org/projects/osmopcu
|
||||
Vcs-Git: git://git.osmocom.org/osmo-pcu
|
||||
Vcs-Browser: http://git.osmocom.org/osmo-pcu/
|
||||
|
||||
Package: osmo-pcu
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: osmo-pcu GSM PCU for GPRS and EDGE
|
||||
osmo-pcu for GPRS and EDGE support in the network
|
||||
|
||||
Package: osmo-pcu-dbg
|
||||
Architecture: any
|
||||
Section: debug
|
||||
Priority: extra
|
||||
Depends: osmo-pcu (= ${binary:Version}), ${misc:Depends}
|
||||
Description: Debug symbols for the osmo-pcu
|
||||
Make debugging possible
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
etc/osmocom/osmo-pcu.cfg
|
||||
usr/bin/osmo-pcu
|
||||
usr/include/osmocom/pcu/pcuif_proto.h
|
|
@ -0,0 +1,15 @@
|
|||
[Unit]
|
||||
Description=Osmocom osmo-pcu
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
# Read quickly enough
|
||||
CPUSchedulingPolicy=rr
|
||||
CPUSchedulingPriority=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2)
|
||||
DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
|
||||
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
|
||||
|
||||
#export DH_VERBOSE=1
|
||||
export DEB_BUILD_HARDENING=1
|
||||
|
||||
|
||||
%:
|
||||
dh $@ --with=systemd --with autoreconf --fail-missing
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=osmo-pcu-dbg
|
||||
|
||||
override_dh_autoreconf:
|
||||
echo $(VERSION) > .tarball-version
|
||||
dh_autoreconf
|
|
@ -0,0 +1 @@
|
|||
3.0 (native)
|
|
@ -0,0 +1,2 @@
|
|||
nobase_include_HEADERS = \
|
||||
osmocom/pcu/pcuif_proto.h
|
|
@ -1,7 +1,9 @@
|
|||
#ifndef _PCUIF_PROTO_H
|
||||
#define _PCUIF_PROTO_H
|
||||
|
||||
#define PCU_IF_VERSION 0x05
|
||||
#include <osmocom/gsm/l1sap.h>
|
||||
|
||||
#define PCU_IF_VERSION 0x07
|
||||
|
||||
/* msg_type */
|
||||
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
|
||||
|
@ -50,6 +52,9 @@ struct gsm_pcu_if_data {
|
|||
uint8_t ts_nr;
|
||||
uint8_t block_nr;
|
||||
int8_t rssi;
|
||||
uint16_t ber10k; /*!< \brief BER in units of 0.01% */
|
||||
int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */
|
||||
int16_t lqual_cb; /* !< \brief Link quality in centiBel */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_rts_req {
|
||||
|
@ -64,10 +69,12 @@ struct gsm_pcu_if_rts_req {
|
|||
|
||||
struct gsm_pcu_if_rach_ind {
|
||||
uint8_t sapi;
|
||||
uint8_t ra;
|
||||
uint16_t ra;
|
||||
int16_t qta;
|
||||
uint32_t fn;
|
||||
uint16_t arfcn;
|
||||
uint8_t is_11bit;
|
||||
uint8_t burst_type;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm_pcu_if_info_trx {
|
|
@ -18,10 +18,15 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include $(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 \
|
||||
|
@ -77,14 +78,11 @@ noinst_HEADERS = \
|
|||
gprs_rlcmac.h \
|
||||
gprs_ms.h \
|
||||
gprs_ms_storage.h \
|
||||
pcuif_proto.h \
|
||||
pcu_l1_if.h \
|
||||
gsm_timer.h \
|
||||
bitvector.h \
|
||||
pcu_vty.h \
|
||||
pcu_vty_functions.h \
|
||||
sysmo_l1_if.h \
|
||||
femtobts.h \
|
||||
tbf.h \
|
||||
bts.h \
|
||||
poll_controller.h \
|
||||
|
@ -96,19 +94,68 @@ noinst_HEADERS = \
|
|||
pcu_utils.h \
|
||||
cxx_linuxlist.h \
|
||||
gprs_codel.h \
|
||||
gprs_coding_scheme.h
|
||||
gprs_coding_scheme.h \
|
||||
egprs_rlc_compression.h
|
||||
|
||||
nobase_include_HEADERS =
|
||||
osmocom/pcu/pcuif_proto.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 +165,4 @@ osmo_pcu_LDADD = \
|
|||
$(LIBOSMOGSM_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
if ENABLE_SYSMODSP
|
||||
osmo_pcu_remote_LDADD = \
|
||||
libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(COMMON_LA)
|
||||
endif
|
||||
|
||||
#MOSTLYCLEANFILES += testSource testDestination
|
||||
|
|
|
@ -101,23 +101,6 @@ uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len
|
|||
}
|
||||
|
||||
|
||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc;
|
||||
bv->cur_bit = write_index;
|
||||
for (i = 0; i < len; i++) {
|
||||
int bit = 0;
|
||||
if (val & ((uint64_t)1 << (len - i - 1)))
|
||||
bit = 1;
|
||||
rc = bitvec_set_bit(bv, (bit_value)bit);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
write_index += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bitvec_write_field_lh(struct bitvec *bv, unsigned& write_index,
|
||||
uint64_t val, unsigned len)
|
||||
{
|
||||
|
|
|
@ -38,9 +38,15 @@ int bitvec_unhex(struct bitvec *bv, const char* src);
|
|||
unsigned int bitvec_pack(struct bitvec *bv, uint8_t *buffer);
|
||||
unsigned int bitvec_unpack(struct bitvec *bv, uint8_t *buffer);
|
||||
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len);
|
||||
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
|
||||
int bitvec_write_field_lh(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
|
||||
|
||||
|
||||
static inline int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len)
|
||||
{
|
||||
/* Call the libosmocore variant */
|
||||
return ::bitvec_write_field(bv, &write_index, val, len);
|
||||
}
|
||||
|
||||
/*! }@ */
|
||||
|
||||
#endif // BITVECTOR_H
|
||||
|
|
515
src/bts.cpp
515
src/bts.cpp
|
@ -33,6 +33,7 @@ extern "C" {
|
|||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
}
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
@ -65,12 +66,19 @@ static const struct rate_ctr_desc bts_ctr_description[] = {
|
|||
{ "rlc.restarted", "RLC Restarted "},
|
||||
{ "rlc.stalled", "RLC Stalled "},
|
||||
{ "rlc.nacked", "RLC Nacked "},
|
||||
{ "rlc.final_block_resent", "RLC Final Blk resent "},
|
||||
{ "rlc.ass.timedout", "RLC Assign Timeout "},
|
||||
{ "rlc.ass.failed", "RLC Assign Failed "},
|
||||
{ "rlc.ack.timedout", "RLC Ack Timeout "},
|
||||
{ "rlc.ack.failed", "RLC Ack Failed "},
|
||||
{ "rlc.rel.timedout", "RLC Release Timeout "},
|
||||
{ "rlc.late-block", "RLC Late Block "},
|
||||
{ "rlc.sent-dummy", "RLC Sent Dummy "},
|
||||
{ "rlc.sent-control", "RLC Sent Control "},
|
||||
{ "rlc.dl_bytes", "RLC DL Bytes "},
|
||||
{ "rlc.dl_payload_bytes", "RLC DL Payload Bytes "},
|
||||
{ "rlc.ul_bytes", "RLC UL Bytes "},
|
||||
{ "rlc.ul_payload_bytes", "RLC UL Payload Bytes "},
|
||||
{ "decode.errors", "Decode Errors "},
|
||||
{ "sba.allocated", "SBA Allocated "},
|
||||
{ "sba.freed", "SBA Freed "},
|
||||
|
@ -78,7 +86,56 @@ static const struct rate_ctr_desc bts_ctr_description[] = {
|
|||
{ "llc.timeout", "Timedout Frames "},
|
||||
{ "llc.dropped", "Dropped Frames "},
|
||||
{ "llc.scheduled", "Scheduled Frames "},
|
||||
{ "llc.dl_bytes", "RLC encapsulated PDUs"},
|
||||
{ "llc.ul_bytes", "full PDUs received "},
|
||||
{ "rach.requests", "RACH requests "},
|
||||
{ "11bit_rach.requests", "11BIT_RACH requests "},
|
||||
{ "spb.uplink_first_segment", "First seg of UL SPB "},
|
||||
{ "spb.uplink_second_segment", "Second seg of UL SPB "},
|
||||
{ "spb.downlink_first_segment", "First seg of DL SPB "},
|
||||
{ "spb.downlink_second_segment","Second seg of DL SPB "},
|
||||
{ "immediate.assignment_UL", "Immediate Assign UL "},
|
||||
{ "immediate.assignment_rej", "Immediate Assign Rej "},
|
||||
{ "immediate.assignment_DL", "Immediate Assign DL "},
|
||||
{ "channel.request_description","Channel Request Desc "},
|
||||
{ "pkt.ul_assignment", "Packet UL Assignment "},
|
||||
{ "pkt.access_reject", "Packet Access Reject "},
|
||||
{ "pkt.dl_assignment", "Packet DL Assignment "},
|
||||
{ "ul.control", "UL control Block "},
|
||||
{ "ul.assignment_poll_timeout", "UL Assign Timeout "},
|
||||
{ "ul.assignment_failed", "UL Assign Failed "},
|
||||
{ "dl.assignment_timeout", "DL Assign Timeout "},
|
||||
{ "dl.assignment_failed", "DL Assign Failed "},
|
||||
{ "pkt.ul_ack_nack_timeout", "PUAN Poll Timeout "},
|
||||
{ "pkt.ul_ack_nack_failed", "PUAN poll Failed "},
|
||||
{ "pkt.dl_ack_nack_timeout", "PDAN poll Timeout "},
|
||||
{ "pkt.dl_ack_nack_failed", "PDAN poll Failed "},
|
||||
{ "gprs.downlink_cs1", "CS1 downlink "},
|
||||
{ "gprs.downlink_cs2", "CS2 downlink "},
|
||||
{ "gprs.downlink_cs3", "CS3 downlink "},
|
||||
{ "gprs.downlink_cs4", "CS4 downlink "},
|
||||
{ "egprs.downlink_mcs1", "MCS1 downlink "},
|
||||
{ "egprs.downlink_mcs2", "MCS2 downlink "},
|
||||
{ "egprs.downlink_mcs3", "MCS3 downlink "},
|
||||
{ "egprs.downlink_mcs4", "MCS4 downlink "},
|
||||
{ "egprs.downlink_mcs5", "MCS5 downlink "},
|
||||
{ "egprs.downlink_mcs6", "MCS6 downlink "},
|
||||
{ "egprs.downlink_mcs7", "MCS7 downlink "},
|
||||
{ "egprs.downlink_mcs8", "MCS8 downlink "},
|
||||
{ "egprs.downlink_mcs9", "MCS9 downlink "},
|
||||
{ "gprs.uplink_cs1", "CS1 Uplink "},
|
||||
{ "gprs.uplink_cs2", "CS2 Uplink "},
|
||||
{ "gprs.uplink_cs3", "CS3 Uplink "},
|
||||
{ "gprs.uplink_cs4", "CS4 Uplink "},
|
||||
{ "egprs.uplink_mcs1", "MCS1 Uplink "},
|
||||
{ "egprs.uplink_mcs2", "MCS2 Uplink "},
|
||||
{ "egprs.uplink_mcs3", "MCS3 Uplink "},
|
||||
{ "egprs.uplink_mcs4", "MCS4 Uplink "},
|
||||
{ "egprs.uplink_mcs5", "MCS5 Uplink "},
|
||||
{ "egprs.uplink_mcs6", "MCS6 Uplink "},
|
||||
{ "egprs.uplink_mcs7", "MCS7 Uplink "},
|
||||
{ "egprs.uplink_mcs8", "MCS8 Uplink "},
|
||||
{ "egprs.uplink_mcs9", "MCS9 Uplink "},
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc bts_ctrg_desc = {
|
||||
|
@ -131,11 +188,13 @@ BTS::BTS()
|
|||
{
|
||||
memset(&m_bts, 0, sizeof(m_bts));
|
||||
m_bts.bts = this;
|
||||
|
||||
m_total_pdch = 0;
|
||||
/* initialize back pointers */
|
||||
for (size_t trx_no = 0; trx_no < ARRAY_SIZE(m_bts.trx); ++trx_no) {
|
||||
struct gprs_rlcmac_trx *trx = &m_bts.trx[trx_no];
|
||||
trx->trx_no = trx_no;
|
||||
trx->num_pdch = 0;
|
||||
trx->current_load = 0;
|
||||
trx->bts = this;
|
||||
|
||||
for (size_t ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ++ts_no) {
|
||||
|
@ -360,9 +419,82 @@ int BTS::tfi_find_free(enum gprs_rlcmac_tbf_direction dir,
|
|||
uint8_t *_trx, int8_t use_trx)
|
||||
{
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
uint32_t free_tfis;
|
||||
uint32_t free_tfis = 0xffffffff;
|
||||
uint8_t ts, tfi;
|
||||
bool possible_trx[8];
|
||||
int ret;
|
||||
|
||||
/* This function will get list of possible TRXs */
|
||||
ret = get_possible_trxs(dir, possible_trx, use_trx);
|
||||
if (ret < 0)
|
||||
return -EBUSY;
|
||||
|
||||
|
||||
if (use_trx >= 0)
|
||||
*_trx = use_trx;
|
||||
else
|
||||
*_trx = get_suitable_trx(possible_trx);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||
"Searching for first unallocated TFI: TRX=%d\n", *_trx);
|
||||
|
||||
for (ts = 0; ts < 8; ts++) {
|
||||
pdch = &m_bts.trx[*_trx].pdch[ts];
|
||||
free_tfis &= ~pdch->assigned_tfi(dir);
|
||||
}
|
||||
/* find the first */
|
||||
for (tfi = 0; tfi < 32; tfi++) {
|
||||
if (free_tfis & 1 << tfi)
|
||||
break;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(tfi < 32);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, " Found TFI=%d.\n", tfi);
|
||||
|
||||
return tfi;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for free TFI and return TFI, TRX.
|
||||
* This method returns the first TFI that is currently not used in any PDCH of
|
||||
* a TRX. The first TRX that contains such an TFI is returned. Negative values
|
||||
* indicate errors.
|
||||
*/
|
||||
int BTS::get_possible_trxs_sba(
|
||||
bool *_trx)
|
||||
{
|
||||
uint8_t trx;
|
||||
bool has_pdch = false;
|
||||
uint8_t trx_from, trx_to, trx, ts, tfi;
|
||||
|
||||
for (trx = 0; trx < 8; trx++) {
|
||||
if (bts_data()->trx[trx].num_pdch) {
|
||||
_trx[trx] = true;
|
||||
has_pdch = true;
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, " Valid TRX=%d.\n", trx);
|
||||
} else {
|
||||
_trx[trx] = false;
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, " Not valid TRX=%d.\n", trx);
|
||||
}
|
||||
}
|
||||
if (has_pdch)
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method loops through all possible TRX and
|
||||
* returns subset of possible TRXs based on availability.
|
||||
*/
|
||||
int BTS::get_possible_trxs(enum gprs_rlcmac_tbf_direction dir,
|
||||
bool *_trx, int8_t use_trx)
|
||||
{
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
uint32_t free_tfis;
|
||||
uint32_t is_tfis_available = false;
|
||||
bool has_pdch = false;
|
||||
uint8_t trx_from, trx_to, trx, ts;
|
||||
|
||||
if (use_trx >= 0 && use_trx < 8)
|
||||
trx_from = trx_to = use_trx;
|
||||
|
@ -385,8 +517,12 @@ int BTS::tfi_find_free(enum gprs_rlcmac_tbf_direction dir,
|
|||
trx_has_pdch = true;
|
||||
has_pdch = true;
|
||||
}
|
||||
if (trx_has_pdch && free_tfis)
|
||||
break;
|
||||
if (trx_has_pdch && free_tfis) {
|
||||
_trx[trx] = true;
|
||||
is_tfis_available = true;
|
||||
} else {
|
||||
_trx[trx] = false;
|
||||
}
|
||||
|
||||
free_tfis = 0;
|
||||
}
|
||||
|
@ -395,26 +531,69 @@ int BTS::tfi_find_free(enum gprs_rlcmac_tbf_direction dir,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!free_tfis) {
|
||||
if (!is_tfis_available) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No TFI available.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method loops through all subset of TRXs provided by
|
||||
* get_possible_trxs and does the load balancing algorithm. and
|
||||
* selects best TRX possible based on load and capacity
|
||||
*/
|
||||
int BTS::get_suitable_trx(bool *avail_trx)
|
||||
{
|
||||
uint8_t trx_from, trx_to, trx;
|
||||
uint32_t select_probability = 0;
|
||||
uint32_t temp_probability = 0;
|
||||
int selected_trx = -1;
|
||||
bool is_better_trx = false;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||
"Searching for first unallocated TFI: TRX=%d\n", trx);
|
||||
trx_from = 0;
|
||||
trx_to = 7;
|
||||
|
||||
/* find the first */
|
||||
for (tfi = 0; tfi < 32; tfi++) {
|
||||
if (free_tfis & 1 << tfi)
|
||||
break;
|
||||
for (trx = trx_from; trx <= trx_to; trx++) {
|
||||
/* Check if this TRX is in possible list */
|
||||
if (!avail_trx[trx])
|
||||
continue;
|
||||
|
||||
is_better_trx = false;
|
||||
|
||||
temp_probability = MAX_LOAD_PROBABILITY -
|
||||
(get_num_pdch() * 100 *
|
||||
m_bts.trx[trx].current_load)
|
||||
/ m_bts.trx[trx].num_pdch;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "trx(%d) cur load(%d)"
|
||||
" numpdch(%d) prob1(%u) seleprob(%u)"
|
||||
" btsnumpdch(%d)\n", trx,
|
||||
m_bts.trx[trx].current_load,
|
||||
m_bts.trx[trx].num_pdch,
|
||||
temp_probability, select_probability,
|
||||
get_num_pdch());
|
||||
|
||||
if (temp_probability >= select_probability) {
|
||||
if (temp_probability > select_probability)
|
||||
is_better_trx = true;
|
||||
else if (temp_probability == select_probability)
|
||||
if (selected_trx >= 0 || selected_trx < 8)
|
||||
if (m_bts.trx[selected_trx].num_pdch
|
||||
< m_bts.trx[trx].num_pdch)
|
||||
is_better_trx = true;
|
||||
}
|
||||
if (is_better_trx) {
|
||||
selected_trx = trx;
|
||||
select_probability =
|
||||
temp_probability;
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "selected pro(%u)"
|
||||
"selected_trx(%d)\n",
|
||||
select_probability, selected_trx);
|
||||
}
|
||||
}
|
||||
|
||||
OSMO_ASSERT(tfi < 32);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, " Found TFI=%d.\n", tfi);
|
||||
*_trx = trx;
|
||||
return tfi;
|
||||
return selected_trx;
|
||||
}
|
||||
|
||||
int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn)
|
||||
|
@ -459,100 +638,191 @@ int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
|
||||
int BTS::rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit,
|
||||
enum ph_burst_type burst_type)
|
||||
{
|
||||
struct gprs_rlcmac_ul_tbf *tbf = NULL;
|
||||
uint8_t trx_no, ts_no = 0;
|
||||
uint8_t sb = 0;
|
||||
uint32_t sb_fn = 0;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
int plen;
|
||||
uint8_t usf = 7;
|
||||
uint8_t tsc;
|
||||
uint16_t ta;
|
||||
uint8_t tsc = 0, ta = qta2ta(qta);
|
||||
uint16_t ms_class = 0;
|
||||
uint16_t priority = 0;
|
||||
bool failure = false;
|
||||
|
||||
rach_frame();
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide "
|
||||
"one:\n");
|
||||
if ((ra & 0xf8) == 0x70) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
|
||||
"allocation\n");
|
||||
sb = 1;
|
||||
} else if (m_bts.force_two_phase) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single phase access, "
|
||||
"but we force two phase access\n");
|
||||
sb = 1;
|
||||
}
|
||||
if (qta < 0)
|
||||
qta = 0;
|
||||
if (qta > 252)
|
||||
qta = 252;
|
||||
if (is_11bit)
|
||||
rach_frame_11bit();
|
||||
|
||||
ta = qta >> 2;
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, "
|
||||
"so we provide one \n"
|
||||
"ra=0x%02x Fn=%u qta=%d is_11bit=%d:\n", ra, Fn, qta, is_11bit);
|
||||
|
||||
sb = is_single_block(ra, burst_type, is_11bit, &ms_class, &priority);
|
||||
|
||||
if (sb) {
|
||||
rc = sba()->alloc(&trx_no, &ts_no, &sb_fn, ta);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH qbit-ta=%d "
|
||||
"ra=0x%02x, Fn=%d (%d,%d,%d), SBFn=%d\n",
|
||||
qta, ra,
|
||||
Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26,
|
||||
sb_fn);
|
||||
LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment Uplink "
|
||||
"(AGCH)\n");
|
||||
tsc = m_bts.trx[trx_no].pdch[ts_no].tsc;
|
||||
if (rc < 0) {
|
||||
failure = true;
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource for "
|
||||
"single block allocation."
|
||||
"sending Immediate "
|
||||
"Assignment Uplink (AGCH) reject\n");
|
||||
} else {
|
||||
tsc = m_bts.trx[trx_no].pdch[ts_no].tsc;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH "
|
||||
" qbit-ta=%d ra=0x%02x, Fn=%d (%d,%d,%d),"
|
||||
" SBFn=%d\n",
|
||||
qta, ra,
|
||||
Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26,
|
||||
sb_fn);
|
||||
LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment "
|
||||
"Uplink (AGCH)\n");
|
||||
}
|
||||
} else {
|
||||
// Create new TBF
|
||||
#warning "Copy and pate with other routines.."
|
||||
/* set class to 0, since we don't know the multislot class yet */
|
||||
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0, 0, 1);
|
||||
if (!tbf) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
|
||||
/* FIXME: send reject */
|
||||
return -EBUSY;
|
||||
#warning "Copy and paste with other routines.."
|
||||
|
||||
if (is_11bit) {
|
||||
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0,
|
||||
ms_class, 1);
|
||||
} else {
|
||||
/* set class to 0, since we don't know the multislot
|
||||
* class yet */
|
||||
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0, 0, 1);
|
||||
}
|
||||
|
||||
if (!tbf) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource sending "
|
||||
"Immediate Assignment Uplink (AGCH) "
|
||||
"reject\n");
|
||||
rc = -EBUSY;
|
||||
failure = true;
|
||||
} else {
|
||||
tbf->set_ta(ta);
|
||||
tbf->set_state(GPRS_RLCMAC_FLOW);
|
||||
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
|
||||
tbf_timer_start(tbf, 3169, m_bts.t3169, 0);
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "%s [UPLINK] START\n",
|
||||
tbf_name(tbf));
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "%s RX: [PCU <- BTS] RACH "
|
||||
"qbit-ta=%d ra=0x%02x, Fn=%d "
|
||||
" (%d,%d,%d)\n",
|
||||
tbf_name(tbf),
|
||||
qta, ra, Fn, (Fn / (26 * 51)) % 32,
|
||||
Fn % 51, Fn % 26);
|
||||
LOGP(DRLCMAC, LOGL_INFO, "%s TX: START Immediate "
|
||||
"Assignment Uplink (AGCH)\n",
|
||||
tbf_name(tbf));
|
||||
trx_no = tbf->trx->trx_no;
|
||||
ts_no = tbf->first_ts;
|
||||
usf = tbf->m_usf[ts_no];
|
||||
tsc = tbf->tsc();
|
||||
}
|
||||
tbf->set_ta(ta);
|
||||
tbf->set_state(GPRS_RLCMAC_FLOW);
|
||||
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
|
||||
tbf_timer_start(tbf, 3169, m_bts.t3169, 0);
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "%s [UPLINK] START\n",
|
||||
tbf_name(tbf));
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "%s RX: [PCU <- BTS] RACH "
|
||||
"qbit-ta=%d ra=0x%02x, Fn=%d (%d,%d,%d)\n",
|
||||
tbf_name(tbf),
|
||||
qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
|
||||
LOGP(DRLCMAC, LOGL_INFO, "%s TX: START Immediate "
|
||||
"Assignment Uplink (AGCH)\n", tbf_name(tbf));
|
||||
trx_no = tbf->trx->trx_no;
|
||||
ts_no = tbf->first_ts;
|
||||
usf = tbf->m_usf[ts_no];
|
||||
tsc = tbf->tsc();
|
||||
}
|
||||
bitvec *immediate_assignment = bitvec_alloc(22) /* without plen */;
|
||||
bitvec_unhex(immediate_assignment,
|
||||
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||
" - TRX=%d (%d) TS=%d TA=%d TSC=%d TFI=%d USF=%d\n",
|
||||
trx_no, m_bts.trx[trx_no].arfcn, ts_no, ta, tsc,
|
||||
tbf ? tbf->tfi() : -1, usf);
|
||||
|
||||
plen = Encoding::write_immediate_assignment(
|
||||
tbf, immediate_assignment, 0, ra, Fn, ta,
|
||||
m_bts.trx[trx_no].arfcn, ts_no, tsc, usf, 0, sb_fn,
|
||||
m_bts.alpha, m_bts.gamma, -1);
|
||||
if (failure) {
|
||||
plen = Encoding::write_immediate_assignment_reject(
|
||||
immediate_assignment, ra, Fn,
|
||||
burst_type);
|
||||
immediate_assignment_reject();
|
||||
}
|
||||
else {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG,
|
||||
" - TRX=%d (%d) TS=%d TA=%d TSC=%d TFI=%d USF=%d\n",
|
||||
trx_no, m_bts.trx[trx_no].arfcn, ts_no, ta, tsc,
|
||||
tbf ? tbf->tfi() : -1, usf);
|
||||
|
||||
plen = Encoding::write_immediate_assignment(
|
||||
tbf, immediate_assignment, 0, ra, Fn, ta,
|
||||
m_bts.trx[trx_no].arfcn, ts_no, tsc, usf, 0, sb_fn,
|
||||
m_bts.alpha, m_bts.gamma, -1, burst_type, sb);
|
||||
}
|
||||
|
||||
if (plen >= 0) {
|
||||
immediate_assignment_ul_tbf();
|
||||
pcu_l1if_tx_agch(immediate_assignment, plen);
|
||||
if (tbf)
|
||||
tbf->set_state(GPRS_RLCMAC_WAIT_ASSIGN);
|
||||
}
|
||||
|
||||
bitvec_free(immediate_assignment);
|
||||
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint8_t BTS::is_single_block(uint16_t ra, enum ph_burst_type burst_type,
|
||||
uint8_t is_11bit, uint16_t *ms_class, uint16_t *priority)
|
||||
{
|
||||
uint8_t sb = 0, val = 0;
|
||||
|
||||
if (!is_11bit && (burst_type == GSM_L1_BURST_TYPE_ACCESS_0)) {
|
||||
|
||||
if ((ra & 0xf8) == 0x70) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
|
||||
"allocation\n");
|
||||
sb = 1;
|
||||
} else if (m_bts.force_two_phase) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single "
|
||||
"phase access, but we force two phase "
|
||||
"access\n");
|
||||
sb = 1;
|
||||
}
|
||||
|
||||
} else if (is_11bit &&
|
||||
((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
||||
|
||||
val = !!(ra & (1 << 10));
|
||||
|
||||
if (!val) {
|
||||
if (m_bts.force_two_phase) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS 11 bit RACH "
|
||||
"received. MS requests single phase "
|
||||
"access but we force two phase "
|
||||
"access\n");
|
||||
sb = 1;
|
||||
} else {
|
||||
sb = 0;
|
||||
*ms_class = (ra & 0x3e0) >> 5;
|
||||
*priority = (ra & 0x18) >> 3;
|
||||
}
|
||||
|
||||
} else {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS 11 bit RACH received."
|
||||
"MS requests single block allocation\n");
|
||||
sb = 1;
|
||||
}
|
||||
|
||||
} else if (is_11bit &&
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_0)) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR,
|
||||
"Error: GPRS 11 bit RACH not supported\n");
|
||||
|
||||
} else if (burst_type == GSM_L1_BURST_TYPE_NONE) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "pcu has not received burst type "
|
||||
"from bts \n");
|
||||
|
||||
if ((ra & 0xf8) == 0x70) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
|
||||
"allocation\n");
|
||||
sb = 1;
|
||||
} else if (m_bts.force_two_phase) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single "
|
||||
"phase access, but we force two phase "
|
||||
"access\n");
|
||||
sb = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
/* depending on the current TBF, we assign on PACCH or AGCH */
|
||||
|
@ -607,9 +877,10 @@ void BTS::snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi)
|
|||
tbf->trx->arfcn, ts, tbf->tsc(), 7, poll,
|
||||
tbf->poll_fn, m_bts.alpha, m_bts.gamma, -1);
|
||||
if (plen >= 0) {
|
||||
immediate_assignment_dl_tbf();
|
||||
pcu_l1if_tx_pch(immediate_assignment, plen, imsi);
|
||||
tbf->set_state(GPRS_RLCMAC_WAIT_ASSIGN);
|
||||
}
|
||||
|
||||
bitvec_free(immediate_assignment);
|
||||
}
|
||||
|
||||
|
@ -634,13 +905,17 @@ void gprs_rlcmac_pdch::enable()
|
|||
{
|
||||
/* TODO: Check if there are still allocated resources.. */
|
||||
INIT_LLIST_HEAD(&paging_list);
|
||||
trx->num_pdch++;
|
||||
m_is_enabled = 1;
|
||||
bts()->increment_num_pdch();
|
||||
}
|
||||
|
||||
void gprs_rlcmac_pdch::disable()
|
||||
{
|
||||
/* TODO.. kick free_resources once we know the TRX/TS we are on */
|
||||
m_is_enabled = 0;
|
||||
trx->num_pdch--;
|
||||
bts()->decrement_num_pdch();
|
||||
}
|
||||
|
||||
void gprs_rlcmac_pdch::free_resources()
|
||||
|
@ -986,16 +1261,25 @@ void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_n
|
|||
}
|
||||
/* check for channel request */
|
||||
if (ack_nack->Exist_Channel_Request_Description) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
|
||||
"message, so we provide one:\n");
|
||||
|
||||
bts()->channel_request_description();
|
||||
|
||||
/* This call will register the new TBF with the MS on success */
|
||||
tbf_alloc_ul(bts_data(), tbf->trx->trx_no,
|
||||
gprs_rlcmac_ul_tbf *ul_tbf = tbf_alloc_ul(bts_data(),
|
||||
tbf->trx->trx_no,
|
||||
tbf->ms_class(), tbf->ms()->egprs_ms_class(),
|
||||
tbf->tlli(), tbf->ta(), tbf->ms());
|
||||
|
||||
/* schedule uplink assignment */
|
||||
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
|
||||
/* schedule uplink assignment or reject*/
|
||||
if (ul_tbf) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
|
||||
"message, so we provide one:\n");
|
||||
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
|
||||
} else {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
|
||||
"message, so we pacekt access reject:\n");
|
||||
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ;
|
||||
}
|
||||
}
|
||||
/* get measurements */
|
||||
if (tbf->ms()) {
|
||||
|
@ -1088,16 +1372,25 @@ void gprs_rlcmac_pdch::rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *ack_nac
|
|||
|
||||
/* check for channel request */
|
||||
if (ack_nack->Exist_ChannelRequestDescription) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
|
||||
"message, so we provide one:\n");
|
||||
|
||||
bts()->channel_request_description();
|
||||
|
||||
/* This call will register the new TBF with the MS on success */
|
||||
tbf_alloc_ul(bts_data(), tbf->trx->trx_no,
|
||||
gprs_rlcmac_ul_tbf *ul_tbf = tbf_alloc_ul(bts_data(),
|
||||
tbf->trx->trx_no,
|
||||
tbf->ms_class(), tbf->ms()->egprs_ms_class(),
|
||||
tbf->tlli(), tbf->ta(), tbf->ms());
|
||||
|
||||
/* schedule uplink assignment */
|
||||
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
|
||||
/* schedule uplink assignment or reject*/
|
||||
if (ul_tbf) {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
|
||||
"message, so we provide one:\n");
|
||||
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
|
||||
} else {
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
|
||||
"message, so we send packet access reject:\n");
|
||||
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ;
|
||||
}
|
||||
}
|
||||
|
||||
/* get measurements */
|
||||
|
@ -1120,7 +1413,7 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
|
|||
uint32_t tlli = request->ID.u.TLLI;
|
||||
uint8_t ms_class = 0;
|
||||
uint8_t egprs_ms_class = 0;
|
||||
uint8_t ta = 0;
|
||||
uint8_t ta = GSM48_TA_INVALID;
|
||||
struct pcu_l1_meas meas;
|
||||
|
||||
GprsMs *ms = bts()->ms_by_tlli(tlli);
|
||||
|
@ -1180,8 +1473,12 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
|
|||
egprs_ms_class);
|
||||
ul_tbf = tbf_alloc_ul(bts_data(), trx_no(), ms_class,
|
||||
egprs_ms_class, tlli, ta, ms);
|
||||
if (!ul_tbf)
|
||||
|
||||
if (!ul_tbf) {
|
||||
handle_tbf_reject(bts_data(), ms, tlli,
|
||||
trx_no(), ts_no);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set control ts to current MS's TS, until assignment complete */
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "Change control TS to %d until assinment is complete.\n", ts_no);
|
||||
|
@ -1258,6 +1555,7 @@ int gprs_rlcmac_pdch::rcv_control_block(
|
|||
decode_gsm_rlcmac_uplink(rlc_block, ul_control_block);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "\n");
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- RX : Uplink Control Block -------------------------\n");
|
||||
bts()->rlc_rcvd_control();
|
||||
switch (ul_control_block->u.MESSAGE_TYPE) {
|
||||
case MT_PACKET_CONTROL_ACK:
|
||||
rcv_control_ack(&ul_control_block->u.Packet_Control_Acknowledgement, fn);
|
||||
|
@ -1300,6 +1598,8 @@ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
bts()->rlc_ul_bytes(len);
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "Got RLC block, coding scheme: %s, "
|
||||
"length: %d (%d))\n", cs.name(), len, cs.usedSizeUL());
|
||||
|
||||
|
@ -1315,6 +1615,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 +1634,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));
|
||||
|
@ -1405,6 +1697,23 @@ int gprs_rlcmac_pdch::rcv_block_gprs(uint8_t *data, uint32_t fn,
|
|||
return rc;
|
||||
}
|
||||
|
||||
void bts_update_tbf_ta(const char *p, uint32_t fn, uint8_t trx_no, uint8_t ts,
|
||||
uint8_t ta)
|
||||
{
|
||||
struct gprs_rlcmac_ul_tbf *tbf =
|
||||
bts_main_data()->bts->ul_tbf_by_poll_fn(fn, trx_no, ts);
|
||||
if (!tbf)
|
||||
LOGP(DL1IF, LOGL_DEBUG, "[%s] update TA = %u ignored due to "
|
||||
"unknown UL TBF on TRX = %d, TS = %d, FN = %d\n",
|
||||
p, ta, trx_no, ts, fn);
|
||||
else if (tbf->ta() != ta) {
|
||||
LOGP(DL1IF, LOGL_INFO, "[%s] Updating TA %u -> %u on "
|
||||
"TRX = %d, TS = %d, FN = %d\n",
|
||||
p, tbf->ta(), ta, trx_no, ts, fn);
|
||||
tbf->set_ta(ta);
|
||||
}
|
||||
}
|
||||
|
||||
gprs_rlcmac_tbf *gprs_rlcmac_pdch::tbf_from_list_by_tfi(
|
||||
LListHead<gprs_rlcmac_tbf> *tbf_list, uint8_t tfi,
|
||||
enum gprs_rlcmac_tbf_direction dir)
|
||||
|
|
224
src/bts.h
224
src/bts.h
|
@ -28,6 +28,7 @@ extern "C" {
|
|||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/gsm/l1sap.h>
|
||||
}
|
||||
|
||||
#include "poll_controller.h"
|
||||
|
@ -41,6 +42,9 @@ extern "C" {
|
|||
|
||||
#define LLC_CODEL_DISABLE 0
|
||||
#define LLC_CODEL_USE_DEFAULT (-1)
|
||||
#define MAX_GPRS_CS 9
|
||||
|
||||
#define MAX_LOAD_PROBABILITY 0xffffffff
|
||||
|
||||
struct BTS;
|
||||
struct GprsMs;
|
||||
|
@ -133,13 +137,23 @@ struct gprs_rlcmac_trx {
|
|||
/* back pointers */
|
||||
struct BTS *bts;
|
||||
uint8_t trx_no;
|
||||
|
||||
uint8_t current_load;
|
||||
uint8_t num_pdch;
|
||||
#ifdef __cplusplus
|
||||
void reserve_slots(enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
|
||||
void unreserve_slots(enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void bts_update_tbf_ta(const char *p, uint32_t fn, uint8_t trx_no, uint8_t ts,
|
||||
uint8_t ta);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This is the data from C. As soon as our minimal compiler is gcc 4.7
|
||||
* we can start to compile pcu_vty.c with c++ and remove the split.
|
||||
|
@ -184,11 +198,15 @@ struct gprs_rlcmac_bts {
|
|||
uint8_t alpha, gamma;
|
||||
uint8_t egprs_enabled;
|
||||
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
|
||||
|
||||
/* 0 to support resegmentation in DL, 1 for no reseg */
|
||||
uint8_t dl_arq_type;
|
||||
|
||||
uint32_t ms_idle_sec;
|
||||
uint8_t cs_adj_enabled;
|
||||
uint8_t cs_adj_upper_limit;
|
||||
uint8_t cs_adj_lower_limit;
|
||||
struct {int16_t low; int16_t high;} cs_lqual_ranges[4];
|
||||
struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
|
||||
uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
|
||||
uint16_t ws_base;
|
||||
uint16_t ws_pdch; /* increase WS by this value per PDCH */
|
||||
|
@ -227,12 +245,19 @@ public:
|
|||
CTR_RLC_RESTARTED,
|
||||
CTR_RLC_STALLED,
|
||||
CTR_RLC_NACKED,
|
||||
CTR_RLC_FINAL_BLOCK_RESENT,
|
||||
CTR_RLC_ASS_TIMEDOUT,
|
||||
CTR_RLC_ASS_FAILED,
|
||||
CTR_RLC_ACK_TIMEDOUT,
|
||||
CTR_RLC_ACK_FAILED,
|
||||
CTR_RLC_REL_TIMEDOUT,
|
||||
CTR_RLC_LATE_BLOCK,
|
||||
CTR_RLC_SENT_DUMMY,
|
||||
CTR_RLC_SENT_CONTROL,
|
||||
CTR_RLC_DL_BYTES,
|
||||
CTR_RLC_DL_PAYLOAD_BYTES,
|
||||
CTR_RLC_UL_BYTES,
|
||||
CTR_RLC_UL_PAYLOAD_BYTES,
|
||||
CTR_DECODE_ERRORS,
|
||||
CTR_SBA_ALLOCATED,
|
||||
CTR_SBA_FREED,
|
||||
|
@ -240,7 +265,56 @@ public:
|
|||
CTR_LLC_FRAME_TIMEDOUT,
|
||||
CTR_LLC_FRAME_DROPPED,
|
||||
CTR_LLC_FRAME_SCHED,
|
||||
CTR_LLC_DL_BYTES,
|
||||
CTR_LLC_UL_BYTES,
|
||||
CTR_RACH_REQUESTS,
|
||||
CTR_11BIT_RACH_REQUESTS,
|
||||
CTR_SPB_UL_FIRST_SEGMENT,
|
||||
CTR_SPB_UL_SECOND_SEGMENT,
|
||||
CTR_SPB_DL_FIRST_SEGMENT,
|
||||
CTR_SPB_DL_SECOND_SEGMENT,
|
||||
CTR_IMMEDIATE_ASSIGN_UL_TBF,
|
||||
CTR_IMMEDIATE_ASSIGN_REJ,
|
||||
CTR_IMMEDIATE_ASSIGN_DL_TBF,
|
||||
CTR_CHANNEL_REQUEST_DESCRIPTION,
|
||||
CTR_PKT_UL_ASSIGNMENT,
|
||||
CTR_PKT_ACCESS_REJ,
|
||||
CTR_PKT_DL_ASSIGNMENT,
|
||||
CTR_RLC_RECV_CONTROL,
|
||||
CTR_PUA_POLL_TIMEDOUT,
|
||||
CTR_PUA_POLL_FAILED,
|
||||
CTR_PDA_POLL_TIMEDOUT,
|
||||
CTR_PDA_POLL_FAILED,
|
||||
CTR_PUAN_POLL_TIMEDOUT,
|
||||
CTR_PUAN_POLL_FAILED,
|
||||
CTR_PDAN_POLL_TIMEDOUT,
|
||||
CTR_PDAN_POLL_FAILED,
|
||||
CTR_GPRS_DL_CS1,
|
||||
CTR_GPRS_DL_CS2,
|
||||
CTR_GPRS_DL_CS3,
|
||||
CTR_GPRS_DL_CS4,
|
||||
CTR_EGPRS_DL_MCS1,
|
||||
CTR_EGPRS_DL_MCS2,
|
||||
CTR_EGPRS_DL_MCS3,
|
||||
CTR_EGPRS_DL_MCS4,
|
||||
CTR_EGPRS_DL_MCS5,
|
||||
CTR_EGPRS_DL_MCS6,
|
||||
CTR_EGPRS_DL_MCS7,
|
||||
CTR_EGPRS_DL_MCS8,
|
||||
CTR_EGPRS_DL_MCS9,
|
||||
CTR_GPRS_UL_CS1,
|
||||
CTR_GPRS_UL_CS2,
|
||||
CTR_GPRS_UL_CS3,
|
||||
CTR_GPRS_UL_CS4,
|
||||
CTR_EGPRS_UL_MCS1,
|
||||
CTR_EGPRS_UL_MCS2,
|
||||
CTR_EGPRS_UL_MCS3,
|
||||
CTR_EGPRS_UL_MCS4,
|
||||
CTR_EGPRS_UL_MCS5,
|
||||
CTR_EGPRS_UL_MCS6,
|
||||
CTR_EGPRS_UL_MCS7,
|
||||
CTR_EGPRS_UL_MCS8,
|
||||
CTR_EGPRS_UL_MCS9,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -273,9 +347,15 @@ public:
|
|||
gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
|
||||
|
||||
int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx);
|
||||
int get_possible_trxs(enum gprs_rlcmac_tbf_direction dir,
|
||||
bool *_trx, int8_t use_trx);
|
||||
int get_suitable_trx(bool *suitable_trx);
|
||||
|
||||
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
|
||||
int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
|
||||
uint8_t is_single_block(uint16_t ra, enum ph_burst_type burst_type,
|
||||
uint8_t is_11bit, uint16_t *ms_class, uint16_t *priority);
|
||||
int rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit,
|
||||
enum ph_burst_type burst_type);
|
||||
|
||||
void trigger_dl_ass(gprs_rlcmac_dl_tbf *tbf, gprs_rlcmac_tbf *old_tbf);
|
||||
void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
|
||||
|
@ -303,12 +383,19 @@ public:
|
|||
void rlc_restarted();
|
||||
void rlc_stalled();
|
||||
void rlc_nacked();
|
||||
void rlc_final_block_resent();
|
||||
void rlc_ass_timedout();
|
||||
void rlc_ass_failed();
|
||||
void rlc_ack_timedout();
|
||||
void rlc_ack_failed();
|
||||
void rlc_rel_timedout();
|
||||
void rlc_late_block();
|
||||
void rlc_sent_dummy();
|
||||
void rlc_sent_control();
|
||||
void rlc_dl_bytes(int bytes);
|
||||
void rlc_dl_payload_bytes(int bytes);
|
||||
void rlc_ul_bytes(int bytes);
|
||||
void rlc_ul_payload_bytes(int bytes);
|
||||
void decode_error();
|
||||
void sba_allocated();
|
||||
void sba_freed();
|
||||
|
@ -316,10 +403,63 @@ public:
|
|||
void llc_timedout_frame();
|
||||
void llc_dropped_frame();
|
||||
void llc_frame_sched();
|
||||
void llc_dl_bytes(int bytes);
|
||||
void llc_ul_bytes(int bytes);
|
||||
void rach_frame();
|
||||
void rach_frame_11bit();
|
||||
void spb_uplink_first_segment();
|
||||
void spb_uplink_second_segment();
|
||||
void spb_downlink_first_segment();
|
||||
void spb_downlink_second_segment();
|
||||
void immediate_assignment_ul_tbf();
|
||||
void immediate_assignment_reject();
|
||||
void immediate_assignment_dl_tbf();
|
||||
void channel_request_description();
|
||||
void pkt_ul_assignment();
|
||||
void pkt_access_reject();
|
||||
void pkt_dl_assignemnt();
|
||||
void rlc_rcvd_control();
|
||||
void pua_poll_timedout();
|
||||
void pua_poll_failed();
|
||||
void pda_poll_timedout();
|
||||
void pda_poll_failed();
|
||||
void pkt_ul_ack_nack_poll_timedout();
|
||||
void pkt_ul_ack_nack_poll_failed();
|
||||
void pkt_dl_ack_nack_poll_timedout();
|
||||
void pkt_dl_ack_nack_poll_failed();
|
||||
void gprs_dl_cs1();
|
||||
void gprs_dl_cs2();
|
||||
void gprs_dl_cs3();
|
||||
void gprs_dl_cs4();
|
||||
void egprs_dl_mcs1();
|
||||
void egprs_dl_mcs2();
|
||||
void egprs_dl_mcs3();
|
||||
void egprs_dl_mcs4();
|
||||
void egprs_dl_mcs5();
|
||||
void egprs_dl_mcs6();
|
||||
void egprs_dl_mcs7();
|
||||
void egprs_dl_mcs8();
|
||||
void egprs_dl_mcs9();
|
||||
void gprs_ul_cs1();
|
||||
void gprs_ul_cs2();
|
||||
void gprs_ul_cs3();
|
||||
void gprs_ul_cs4();
|
||||
void egprs_ul_mcs1();
|
||||
void egprs_ul_mcs2();
|
||||
void egprs_ul_mcs3();
|
||||
void egprs_ul_mcs4();
|
||||
void egprs_ul_mcs5();
|
||||
void egprs_ul_mcs6();
|
||||
void egprs_ul_mcs7();
|
||||
void egprs_ul_mcs8();
|
||||
void egprs_ul_mcs9();
|
||||
|
||||
void ms_present(int32_t n);
|
||||
int32_t ms_present_get();
|
||||
void increment_num_pdch();
|
||||
void decrement_num_pdch();
|
||||
uint8_t get_num_pdch() const;
|
||||
int get_possible_trxs_sba(bool *_trx);
|
||||
|
||||
/*
|
||||
* Below for C interface for the VTY
|
||||
|
@ -345,6 +485,8 @@ private:
|
|||
/* list of downlink TBFs */
|
||||
LListHead<gprs_rlcmac_tbf> m_dl_tbfs;
|
||||
|
||||
/* The summation of all the PDCH across all TRX for this BTS*/
|
||||
uint8_t m_total_pdch;
|
||||
private:
|
||||
/* disable copying to avoid slicing */
|
||||
BTS(const BTS&);
|
||||
|
@ -356,6 +498,21 @@ inline int BTS::current_frame_number() const
|
|||
return m_cur_fn;
|
||||
}
|
||||
|
||||
inline void BTS::increment_num_pdch()
|
||||
{
|
||||
m_total_pdch++;
|
||||
}
|
||||
|
||||
inline void BTS::decrement_num_pdch()
|
||||
{
|
||||
m_total_pdch--;
|
||||
}
|
||||
|
||||
inline uint8_t BTS::get_num_pdch() const
|
||||
{
|
||||
return m_total_pdch;
|
||||
}
|
||||
|
||||
inline SBAController *BTS::sba()
|
||||
{
|
||||
return &m_sba;
|
||||
|
@ -423,6 +580,11 @@ inline struct osmo_stat_item_group *BTS::stat_items() const
|
|||
return m_statg;
|
||||
}
|
||||
|
||||
#define CREATE_COUNT_ADD_INLINE(func_name, ctr_name) \
|
||||
inline void BTS::func_name(int inc) {\
|
||||
rate_ctr_add(&m_ratectrs->ctr[ctr_name], inc); \
|
||||
}
|
||||
|
||||
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
|
||||
inline void BTS::func_name() {\
|
||||
rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
|
||||
|
@ -443,12 +605,19 @@ CREATE_COUNT_INLINE(rlc_resent, CTR_RLC_RESENT)
|
|||
CREATE_COUNT_INLINE(rlc_restarted, CTR_RLC_RESTARTED)
|
||||
CREATE_COUNT_INLINE(rlc_stalled, CTR_RLC_STALLED)
|
||||
CREATE_COUNT_INLINE(rlc_nacked, CTR_RLC_NACKED)
|
||||
CREATE_COUNT_INLINE(rlc_final_block_resent, CTR_RLC_FINAL_BLOCK_RESENT);
|
||||
CREATE_COUNT_INLINE(rlc_ass_timedout, CTR_RLC_ASS_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_ass_failed, CTR_RLC_ASS_FAILED);
|
||||
CREATE_COUNT_INLINE(rlc_ack_timedout, CTR_RLC_ACK_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_ack_failed, CTR_RLC_ACK_FAILED);
|
||||
CREATE_COUNT_INLINE(rlc_rel_timedout, CTR_RLC_REL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(rlc_late_block, CTR_RLC_LATE_BLOCK);
|
||||
CREATE_COUNT_INLINE(rlc_sent_dummy, CTR_RLC_SENT_DUMMY);
|
||||
CREATE_COUNT_INLINE(rlc_sent_control, CTR_RLC_SENT_CONTROL);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_dl_bytes, CTR_RLC_DL_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_dl_payload_bytes, CTR_RLC_DL_PAYLOAD_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_ul_bytes, CTR_RLC_UL_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(rlc_ul_payload_bytes, CTR_RLC_UL_PAYLOAD_BYTES);
|
||||
CREATE_COUNT_INLINE(decode_error, CTR_DECODE_ERRORS)
|
||||
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
|
||||
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)
|
||||
|
@ -456,7 +625,56 @@ CREATE_COUNT_INLINE(sba_timedout, CTR_SBA_TIMEDOUT)
|
|||
CREATE_COUNT_INLINE(llc_timedout_frame, CTR_LLC_FRAME_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(llc_dropped_frame, CTR_LLC_FRAME_DROPPED);
|
||||
CREATE_COUNT_INLINE(llc_frame_sched, CTR_LLC_FRAME_SCHED);
|
||||
CREATE_COUNT_ADD_INLINE(llc_dl_bytes, CTR_LLC_DL_BYTES);
|
||||
CREATE_COUNT_ADD_INLINE(llc_ul_bytes, CTR_LLC_UL_BYTES);
|
||||
CREATE_COUNT_INLINE(rach_frame, CTR_RACH_REQUESTS);
|
||||
CREATE_COUNT_INLINE(rach_frame_11bit, CTR_11BIT_RACH_REQUESTS);
|
||||
CREATE_COUNT_INLINE(spb_uplink_first_segment, CTR_SPB_UL_FIRST_SEGMENT);
|
||||
CREATE_COUNT_INLINE(spb_uplink_second_segment, CTR_SPB_UL_SECOND_SEGMENT);
|
||||
CREATE_COUNT_INLINE(spb_downlink_first_segment, CTR_SPB_DL_FIRST_SEGMENT);
|
||||
CREATE_COUNT_INLINE(spb_downlink_second_segment, CTR_SPB_DL_SECOND_SEGMENT);
|
||||
CREATE_COUNT_INLINE(immediate_assignment_ul_tbf, CTR_IMMEDIATE_ASSIGN_UL_TBF);
|
||||
CREATE_COUNT_INLINE(immediate_assignment_reject, CTR_IMMEDIATE_ASSIGN_REJ);
|
||||
CREATE_COUNT_INLINE(immediate_assignment_dl_tbf, CTR_IMMEDIATE_ASSIGN_DL_TBF);
|
||||
CREATE_COUNT_INLINE(channel_request_description, CTR_CHANNEL_REQUEST_DESCRIPTION);
|
||||
CREATE_COUNT_INLINE(pkt_ul_assignment, CTR_PKT_UL_ASSIGNMENT);
|
||||
CREATE_COUNT_INLINE(pkt_access_reject, CTR_PKT_ACCESS_REJ);
|
||||
CREATE_COUNT_INLINE(pkt_dl_assignemnt, CTR_PKT_DL_ASSIGNMENT);
|
||||
CREATE_COUNT_INLINE(rlc_rcvd_control, CTR_RLC_RECV_CONTROL);
|
||||
CREATE_COUNT_INLINE(pua_poll_timedout, CTR_PUA_POLL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(pua_poll_failed, CTR_PUA_POLL_FAILED);
|
||||
CREATE_COUNT_INLINE(pda_poll_timedout, CTR_PDA_POLL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(pda_poll_failed, CTR_PDA_POLL_FAILED);
|
||||
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_timedout, CTR_PUAN_POLL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(pkt_ul_ack_nack_poll_failed, CTR_PUAN_POLL_FAILED);
|
||||
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_timedout, CTR_PDAN_POLL_TIMEDOUT);
|
||||
CREATE_COUNT_INLINE(pkt_dl_ack_nack_poll_failed, CTR_PDAN_POLL_FAILED);
|
||||
CREATE_COUNT_INLINE(gprs_dl_cs1, CTR_GPRS_DL_CS1);
|
||||
CREATE_COUNT_INLINE(gprs_dl_cs2, CTR_GPRS_DL_CS2);
|
||||
CREATE_COUNT_INLINE(gprs_dl_cs3, CTR_GPRS_DL_CS3);
|
||||
CREATE_COUNT_INLINE(gprs_dl_cs4, CTR_GPRS_DL_CS4);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs1, CTR_EGPRS_DL_MCS1);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs2, CTR_EGPRS_DL_MCS2);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs3, CTR_EGPRS_DL_MCS3);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs4, CTR_EGPRS_DL_MCS4);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs5, CTR_EGPRS_DL_MCS5);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs6, CTR_EGPRS_DL_MCS6);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs7, CTR_EGPRS_DL_MCS7);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs8, CTR_EGPRS_DL_MCS8);
|
||||
CREATE_COUNT_INLINE(egprs_dl_mcs9, CTR_EGPRS_DL_MCS9);
|
||||
CREATE_COUNT_INLINE(gprs_ul_cs1, CTR_GPRS_UL_CS1);
|
||||
CREATE_COUNT_INLINE(gprs_ul_cs2, CTR_GPRS_UL_CS2);
|
||||
CREATE_COUNT_INLINE(gprs_ul_cs3, CTR_GPRS_UL_CS3);
|
||||
CREATE_COUNT_INLINE(gprs_ul_cs4, CTR_GPRS_UL_CS4);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs1, CTR_EGPRS_UL_MCS1);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs2, CTR_EGPRS_UL_MCS2);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs3, CTR_EGPRS_UL_MCS3);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs4, CTR_EGPRS_UL_MCS4);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs5, CTR_EGPRS_UL_MCS5);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs6, CTR_EGPRS_UL_MCS6);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs7, CTR_EGPRS_UL_MCS7);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs8, CTR_EGPRS_UL_MCS8);
|
||||
CREATE_COUNT_INLINE(egprs_ul_mcs9, CTR_EGPRS_UL_MCS9);
|
||||
|
||||
#undef CREATE_COUNT_INLINE
|
||||
|
||||
|
|
19
src/csn1.cpp
19
src/csn1.cpp
|
@ -1110,22 +1110,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
|
|||
|
||||
{ /* extract bits */
|
||||
guint8* pui8 = pui8DATA(data, pDescr->offset);
|
||||
gint16 nB1 = no_of_bits & 0x07;/* no_of_bits Mod 8 */
|
||||
|
||||
while (no_of_bits > 0)
|
||||
while (no_of_bits >= 8)
|
||||
{
|
||||
*pui8 = bitvec_read_field(vector, readIndex, 8);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
|
||||
pui8++;
|
||||
no_of_bits -= 8;
|
||||
}
|
||||
if (nB1 > 0)
|
||||
if (no_of_bits > 0)
|
||||
{
|
||||
*pui8 = bitvec_read_field(vector, readIndex, nB1);
|
||||
*pui8 = bitvec_read_field(vector, readIndex, no_of_bits);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
|
||||
pui8++;
|
||||
no_of_bits -= nB1;
|
||||
bit_offset += nB1; /* (nB1 is no_of_bits Mod 8) */
|
||||
bit_offset += no_of_bits;
|
||||
no_of_bits = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2400,7 +2399,12 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||
guint8 bits_to_handle = remaining_bits_len%8;
|
||||
if (bits_to_handle > 0)
|
||||
{
|
||||
guint8 fl = filler&(0xff>>(8-bits_to_handle));
|
||||
/* section 11 of 44.060
|
||||
* The padding bits may be the 'null' string. Otherwise, the
|
||||
* padding bits starts with bit '0', followed by 'spare padding'
|
||||
* < padding bits > ::= { null | 0 < spare padding > ! < Ignore : 1 bit** = < no string > > } ;
|
||||
*/
|
||||
guint8 fl = filler&(0xff>>(8-bits_to_handle + 1));
|
||||
bitvec_write_field(vector, writeIndex, fl, bits_to_handle);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%u|", fl);
|
||||
remaining_bits_len -= bits_to_handle;
|
||||
|
@ -2499,6 +2503,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
|
|||
bitvec_write_field(vector, writeIndex, !Tag, 1);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(!Tag));
|
||||
bit_offset++;
|
||||
remaining_bits_len--;
|
||||
|
||||
pDescr++;
|
||||
break;
|
||||
|
|
296
src/decoding.cpp
296
src/decoding.cpp
|
@ -20,10 +20,12 @@
|
|||
#include <decoding.h>
|
||||
#include <rlc.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <egprs_rlc_compression.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/bitcomp.h>
|
||||
#include <osmocom/gprs/protocol/gsm_04_60.h>
|
||||
}
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
@ -32,7 +34,7 @@ extern "C" {
|
|||
#include <string.h>
|
||||
|
||||
#define LENGTH_TO_END 255
|
||||
/*
|
||||
/*!
|
||||
* \returns num extensions fields (num frames == offset) on success,
|
||||
* -errno otherwise.
|
||||
*/
|
||||
|
@ -66,17 +68,17 @@ static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
|
|||
"but no more chunks possible\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
if (li->li == 0 && num_chunks == 0 && li->e == 0) {
|
||||
if (li->li == 0 && num_chunks == 0) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 2a */
|
||||
/* TS 44.060, table 10.4.14a.1, row 4 */
|
||||
chunks[num_chunks].length = 0;
|
||||
chunks[num_chunks].is_complete = true;
|
||||
} else if (li->li == 0 && num_chunks == 0 && li->e == 1) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 4 */
|
||||
chunks[num_chunks].length = LENGTH_TO_END;
|
||||
chunks[num_chunks].is_complete = is_last_block;
|
||||
} else if (li->li == 127 && li->e == 1) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 3 & 5 */
|
||||
/* only filling bytes left */
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
|
||||
"only filling bytes with extention octet: LI=%d, E=%d, count=%d\n",
|
||||
li->li, li->e, num_chunks);
|
||||
break;
|
||||
} else if (li->li > 0) {
|
||||
/* TS 44.060, table 10.4.14a.1, row 1 & 2b */
|
||||
|
@ -89,6 +91,9 @@ static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
|
||||
"extention octet: LI=%d, E=%d, count=%d\n",
|
||||
li->li, li->e, num_chunks);
|
||||
num_chunks += 1;
|
||||
|
||||
if (e == 1) {
|
||||
|
@ -162,6 +167,9 @@ static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
|
|||
|
||||
chunks[num_chunks].is_complete = li->li || is_last_block;
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA LI contains "
|
||||
"extention octet: LI=%d, M=%d, E=%d, count=%d\n",
|
||||
li->li, li->m, li->e, num_chunks);
|
||||
num_chunks += 1;
|
||||
|
||||
if (e == 1 && m == 1) {
|
||||
|
@ -197,6 +205,7 @@ int Decoding::rlc_data_from_ul_data(
|
|||
e = rdbi->e;
|
||||
if (e) {
|
||||
if (chunks_size > 0) {
|
||||
/* Block without LI means it only contains data of one LLC PDU */
|
||||
chunks[num_chunks].offset = offs;
|
||||
chunks[num_chunks].length = LENGTH_TO_END;
|
||||
chunks[num_chunks].is_complete = is_last_block;
|
||||
|
@ -266,7 +275,7 @@ int Decoding::rlc_data_from_ul_data(
|
|||
* so drop it (this may happen with EGPRS since
|
||||
* there is no M flag. */
|
||||
num_chunks -= 1;
|
||||
break;;
|
||||
break;
|
||||
}
|
||||
chunks[i].length = data_len - offs;
|
||||
}
|
||||
|
@ -344,78 +353,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 +377,181 @@ int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
|||
return cur_bit;
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header_egprs_type_3(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs)
|
||||
{
|
||||
int punct, punct2, with_padding, cps;
|
||||
unsigned int e_ti_header, offs, cur_bit = 0;
|
||||
const struct gprs_rlc_ul_header_egprs_3 *egprs3;
|
||||
|
||||
egprs3 = static_cast < struct gprs_rlc_ul_header_egprs_3 * >
|
||||
((void *)data);
|
||||
|
||||
cps = (egprs3->cps_hi << 0) | (egprs3->cps_lo << 2);
|
||||
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||
|
||||
rlc->r = egprs3->r;
|
||||
rlc->si = egprs3->si;
|
||||
rlc->tfi = (egprs3->tfi_hi << 0) | (egprs3->tfi_lo << 2);
|
||||
rlc->cps = cps;
|
||||
rlc->rsb = egprs3->rsb;
|
||||
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = egprs3->cv;
|
||||
rlc->block_info[0].pi = egprs3->pi;
|
||||
rlc->block_info[0].spb = egprs3->spb;
|
||||
rlc->block_info[0].bsn =
|
||||
(egprs3->bsn1_hi << 0) | (egprs3->bsn1_lo << 5);
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||
offs = rlc->data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1);
|
||||
e_ti_header = (data[offs-1] + (data[offs] << 8)) >> 7;
|
||||
rlc->block_info[0].e = !!(e_ti_header & 0x01);
|
||||
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header_egprs_type_2(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs)
|
||||
{
|
||||
const struct gprs_rlc_ul_header_egprs_2 *egprs2;
|
||||
unsigned int e_ti_header, offs, cur_bit = 0;
|
||||
int punct, punct2, with_padding, cps;
|
||||
|
||||
egprs2 = static_cast < struct gprs_rlc_ul_header_egprs_2 * >
|
||||
((void *)data);
|
||||
|
||||
cps = (egprs2->cps_hi << 0) | (egprs2->cps_lo << 2);
|
||||
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||
|
||||
rlc->r = egprs2->r;
|
||||
rlc->si = egprs2->si;
|
||||
rlc->tfi = (egprs2->tfi_hi << 0) | (egprs2->tfi_lo << 2);
|
||||
rlc->cps = cps;
|
||||
rlc->rsb = egprs2->rsb;
|
||||
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = egprs2->cv;
|
||||
rlc->block_info[0].pi = egprs2->pi;
|
||||
rlc->block_info[0].bsn =
|
||||
(egprs2->bsn1_hi << 0) | (egprs2->bsn1_lo << 5);
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||
|
||||
offs = rlc->data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 7);
|
||||
|
||||
e_ti_header = (data[offs] & 0x60) >> 5;
|
||||
rlc->block_info[0].e = !!(e_ti_header & 0x01);
|
||||
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header_egprs_type_1(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, const GprsCodingScheme &cs)
|
||||
{
|
||||
struct gprs_rlc_ul_header_egprs_1 *egprs1;
|
||||
unsigned int e_ti_header, cur_bit = 0, offs;
|
||||
int punct, punct2, with_padding;
|
||||
|
||||
egprs1 = static_cast < struct gprs_rlc_ul_header_egprs_1 * >
|
||||
((void *)data);
|
||||
gprs_rlc_mcs_cps_decode(egprs1->cps, cs, &punct, &punct2,
|
||||
&with_padding);
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
|
||||
|
||||
rlc->r = egprs1->r;
|
||||
rlc->si = egprs1->si;
|
||||
rlc->tfi = (egprs1->tfi_hi << 0) | (egprs1->tfi_lo << 2);
|
||||
rlc->cps = egprs1->cps;
|
||||
rlc->rsb = egprs1->rsb;
|
||||
rlc->num_data_blocks = 2;
|
||||
rlc->block_info[0].cv = egprs1->cv;
|
||||
rlc->block_info[0].pi = egprs1->pi;
|
||||
rlc->block_info[0].bsn =
|
||||
(egprs1->bsn1_hi << 0) | (egprs1->bsn1_lo << 5);
|
||||
|
||||
cur_bit += rlc->data_offs_bits[0] - 2;
|
||||
offs = rlc->data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 0);
|
||||
|
||||
e_ti_header = data[offs - 1] >> 6;
|
||||
rlc->block_info[0].e = (e_ti_header & 0x01);
|
||||
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
|
||||
rlc->block_info[1].cv = egprs1->cv;
|
||||
rlc->block_info[1].pi = egprs1->pi;
|
||||
rlc->block_info[1].bsn = rlc->block_info[0].bsn +
|
||||
((egprs1->bsn2_hi << 0) | (egprs1->bsn2_lo << 2));
|
||||
rlc->block_info[1].bsn = rlc->block_info[1].bsn & (RLC_EGPRS_SNS - 1);
|
||||
|
||||
if ((rlc->block_info[1].bsn != rlc->block_info[0].bsn) &&
|
||||
(rlc->block_info[0].cv == 0))
|
||||
rlc->block_info[0].cv = 1;
|
||||
|
||||
cur_bit = rlc->data_offs_bits[1] - 2;
|
||||
|
||||
offs = rlc->data_offs_bits[1] / 8;
|
||||
OSMO_ASSERT(rlc->data_offs_bits[1] % 8 == 2);
|
||||
|
||||
e_ti_header = (data[offs] & (0x03));
|
||||
rlc->block_info[1].e = (e_ti_header & 0x01);
|
||||
rlc->block_info[1].ti = !!(e_ti_header & 0x02);
|
||||
cur_bit += 2;
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, const GprsCodingScheme &cs)
|
||||
{
|
||||
const struct rlc_ul_header *gprs;
|
||||
unsigned int cur_bit = 0;
|
||||
|
||||
gprs = static_cast < struct rlc_ul_header * >
|
||||
((void *)data);
|
||||
|
||||
gprs_rlc_data_info_init_ul(rlc, cs, false);
|
||||
|
||||
rlc->r = gprs->r;
|
||||
rlc->si = gprs->si;
|
||||
rlc->tfi = gprs->tfi;
|
||||
rlc->cps = 0;
|
||||
rlc->rsb = 0;
|
||||
rlc->num_data_blocks = 1;
|
||||
rlc->block_info[0].cv = gprs->cv;
|
||||
rlc->block_info[0].pi = gprs->pi;
|
||||
rlc->block_info[0].bsn = gprs->bsn;
|
||||
rlc->block_info[0].e = gprs->e;
|
||||
rlc->block_info[0].ti = gprs->ti;
|
||||
rlc->block_info[0].spb = 0;
|
||||
cur_bit += rlc->data_offs_bits[0];
|
||||
/* skip data area */
|
||||
cur_bit += cs.maxDataBlockBytes() * 8;
|
||||
|
||||
return cur_bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Copy LSB bitstream RLC data block to byte aligned buffer.
|
||||
*
|
||||
|
@ -576,21 +702,17 @@ int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc,
|
|||
|
||||
if (crbb_len > 0) {
|
||||
int old_len = bits->cur_bit;
|
||||
struct bitvec crbb;
|
||||
|
||||
crbb.data = (uint8_t *)desc->CRBB;
|
||||
crbb.data_len = sizeof(desc->CRBB);
|
||||
crbb.cur_bit = desc->CRBB_LENGTH;
|
||||
|
||||
rc = osmo_t4_decode(&crbb, desc->CRBB_STARTING_COLOR_CODE,
|
||||
bits);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "Compress bitmap exists, "
|
||||
"CRBB LEN = %d and Starting color code = %d",
|
||||
desc->CRBB_LENGTH, desc->CRBB_STARTING_COLOR_CODE);
|
||||
rc = egprs_compress::decompress_crbb(desc->CRBB_LENGTH,
|
||||
desc->CRBB_STARTING_COLOR_CODE, desc->CRBB, bits);
|
||||
if (rc < 0) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"Failed to decode CRBB: "
|
||||
"length %d, data '%s'\n",
|
||||
desc->CRBB_LENGTH,
|
||||
osmo_hexdump(crbb.data, crbb.data_len));
|
||||
"Failed to decode CRBB: length %d, data '%s'\n",
|
||||
desc->CRBB_LENGTH, osmo_hexdump(
|
||||
desc->CRBB, (desc->CRBB_LENGTH + 7)/8));
|
||||
/* We don't know the SSN offset for the URBB,
|
||||
* return what we have so far and assume the
|
||||
* bitmap has stopped here */
|
||||
|
|
|
@ -28,10 +28,11 @@ struct bitvec;
|
|||
|
||||
class Decoding {
|
||||
public:
|
||||
/* represents (parts) LLC PDUs within one RLC Data block */
|
||||
struct RlcData {
|
||||
uint8_t offset;
|
||||
uint8_t length;
|
||||
bool is_complete;
|
||||
bool is_complete; /* if this PDU ends in this block */
|
||||
};
|
||||
|
||||
static int rlc_data_from_ul_data(
|
||||
|
@ -43,7 +44,22 @@ public:
|
|||
|
||||
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
|
||||
static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
|
||||
|
||||
static int rlc_parse_ul_data_header_egprs_type_3(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header_egprs_type_2(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header_egprs_type_1(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header_gprs(
|
||||
struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data,
|
||||
const GprsCodingScheme &cs);
|
||||
static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
|
||||
const uint8_t *data, GprsCodingScheme cs);
|
||||
static unsigned int rlc_copy_to_aligned_buffer(
|
||||
|
|
|
@ -0,0 +1,691 @@
|
|||
/* egprs_rlc_compression.h
|
||||
* Routines for EGPRS RLC bitmap compression handling
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <decoding.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_rlcmac.h>
|
||||
#include <egprs_rlc_compression.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
}
|
||||
|
||||
#define EGPRS_CODEWORDS 79 /* total number of codewords */
|
||||
|
||||
struct egprs_compress_node{
|
||||
struct egprs_compress_node *left;
|
||||
struct egprs_compress_node *right;
|
||||
int run_length;
|
||||
};
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
egprs_compress *egprs_compress::s_instance = 0;
|
||||
|
||||
egprs_compress_node *egprs_compress::create_tree_node(void *parent)
|
||||
{
|
||||
egprs_compress_node *new_node;
|
||||
|
||||
new_node = talloc_zero(parent, egprs_compress_node);
|
||||
new_node->left = NULL;
|
||||
new_node->right = NULL;
|
||||
new_node->run_length = -1;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
egprs_compress *egprs_compress::instance()
|
||||
{
|
||||
if (!egprs_compress::s_instance)
|
||||
egprs_compress::s_instance = new egprs_compress;
|
||||
return egprs_compress::s_instance;
|
||||
}
|
||||
|
||||
/* Expands the given tree by incorporating
|
||||
* the given codewords.
|
||||
* \param root[in] Root of ones or zeros tree
|
||||
* \param cdwd[in] Array of code words
|
||||
* number of codewords is EGPRS_CODEWORDS
|
||||
*/
|
||||
void egprs_compress::build_codewords(egprs_compress_node *root, const char *cdwd[])
|
||||
{
|
||||
egprs_compress_node *iter;
|
||||
int len;
|
||||
int i;
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < EGPRS_CODEWORDS; idx++) {
|
||||
len = strlen((const char *)cdwd[idx]);
|
||||
iter = root;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (cdwd[idx][i] == '0') {
|
||||
if (!iter->left)
|
||||
iter->left = create_tree_node(root);
|
||||
iter = iter->left;
|
||||
} else {
|
||||
if (!iter->right)
|
||||
iter->right = create_tree_node(root);
|
||||
iter = iter->right;
|
||||
}
|
||||
}
|
||||
if (iter) {
|
||||
/* The first 64 run lengths are 0, 1, 2, ..., 63
|
||||
* and the following ones are 64, 128, 192 described in
|
||||
* section 9.1.10 of 3gpp 44.060 */
|
||||
if (idx < 64)
|
||||
iter->run_length = idx;
|
||||
else
|
||||
iter->run_length = (idx - 63) * 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminating codes for uninterrupted sequences of 0 and 1 up to 64 bit length
|
||||
* according to TS 44.060 9.1.10
|
||||
*/
|
||||
static const unsigned t4_term[2][64] = {
|
||||
{
|
||||
0b0000110111,
|
||||
0b10,
|
||||
0b11,
|
||||
0b010,
|
||||
0b011,
|
||||
0b0011,
|
||||
0b0010,
|
||||
0b00011,
|
||||
0b000101,
|
||||
0b000100,
|
||||
0b0000100,
|
||||
0b0000101,
|
||||
0b0000111,
|
||||
0b00000100,
|
||||
0b00000111,
|
||||
0b000011000,
|
||||
0b0000010111,
|
||||
0b0000011000,
|
||||
0b0000001000,
|
||||
0b00001100111,
|
||||
0b00001101000,
|
||||
0b00001101100,
|
||||
0b00000110111,
|
||||
0b00000101000,
|
||||
0b00000010111,
|
||||
0b00000011000,
|
||||
0b000011001010,
|
||||
0b000011001011,
|
||||
0b000011001100,
|
||||
0b000011001101,
|
||||
0b000001101000,
|
||||
0b000001101001,
|
||||
0b000001101010,
|
||||
0b000001101011,
|
||||
0b000011010010,
|
||||
0b000011010011,
|
||||
0b000011010100,
|
||||
0b000011010101,
|
||||
0b000011010110,
|
||||
0b000011010111,
|
||||
0b000001101100,
|
||||
0b000001101101,
|
||||
0b000011011010,
|
||||
0b000011011011,
|
||||
0b000001010100,
|
||||
0b000001010101,
|
||||
0b000001010110,
|
||||
0b000001010111,
|
||||
0b000001100100,
|
||||
0b000001100101,
|
||||
0b000001010010,
|
||||
0b000001010011,
|
||||
0b000000100100,
|
||||
0b000000110111,
|
||||
0b000000111000,
|
||||
0b000000100111,
|
||||
0b000000101000,
|
||||
0b000001011000,
|
||||
0b000001011001,
|
||||
0b000000101011,
|
||||
0b000000101100,
|
||||
0b000001011010,
|
||||
0b000001100110,
|
||||
0b000001100111
|
||||
|
||||
},
|
||||
{
|
||||
0b00110101,
|
||||
0b000111,
|
||||
0b0111,
|
||||
0b1000,
|
||||
0b1011,
|
||||
0b1100,
|
||||
0b1110,
|
||||
0b1111,
|
||||
0b10011,
|
||||
0b10100,
|
||||
0b00111,
|
||||
0b01000,
|
||||
0b001000,
|
||||
0b000011,
|
||||
0b110100,
|
||||
0b110101,
|
||||
0b101010,
|
||||
0b101011,
|
||||
0b0100111,
|
||||
0b0001100,
|
||||
0b0001000,
|
||||
0b0010111,
|
||||
0b0000011,
|
||||
0b0000100,
|
||||
0b0101000,
|
||||
0b0101011,
|
||||
0b0010011,
|
||||
0b0100100,
|
||||
0b0011000,
|
||||
0b00000010,
|
||||
0b00000011,
|
||||
0b00011010,
|
||||
0b00011011,
|
||||
0b00010010,
|
||||
0b00010011,
|
||||
0b00010100,
|
||||
0b00010101,
|
||||
0b00010110,
|
||||
0b00010111,
|
||||
0b00101000,
|
||||
0b00101001,
|
||||
0b00101010,
|
||||
0b00101011,
|
||||
0b00101100,
|
||||
0b00101101,
|
||||
0b00000100,
|
||||
0b00000101,
|
||||
0b00001010,
|
||||
0b00001011,
|
||||
0b01010010,
|
||||
0b01010011,
|
||||
0b01010100,
|
||||
0b01010101,
|
||||
0b00100100,
|
||||
0b00100101,
|
||||
0b01011000,
|
||||
0b01011001,
|
||||
0b01011010,
|
||||
0b01011011,
|
||||
0b01001010,
|
||||
0b01001011,
|
||||
0b00110010,
|
||||
0b00110011,
|
||||
0b00110100
|
||||
}
|
||||
};
|
||||
static const unsigned t4_term_length[2][64] = {
|
||||
{10, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
|
||||
{8, 6, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
|
||||
};
|
||||
|
||||
static const unsigned t4_min_term_length[] = {2, 4};
|
||||
static const unsigned t4_min_make_up_length[] = {10, 5};
|
||||
|
||||
static const unsigned t4_max_term_length[] = {12, 8};
|
||||
static const unsigned t4_max_make_up_length[] = {13, 9};
|
||||
|
||||
static const unsigned t4_make_up_length[2][15] = {
|
||||
{10, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13},
|
||||
{5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9}
|
||||
};
|
||||
|
||||
static const unsigned t4_make_up_ind[15] = {64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960};
|
||||
|
||||
static const unsigned t4_make_up[2][15] = {
|
||||
{
|
||||
0b0000001111,
|
||||
0b000011001000,
|
||||
0b000011001001,
|
||||
0b000001011011,
|
||||
0b000000110011,
|
||||
0b000000110100,
|
||||
0b000000110101,
|
||||
0b0000001101100,
|
||||
0b0000001101101,
|
||||
0b0000001001010,
|
||||
0b0000001001011,
|
||||
0b0000001001100,
|
||||
0b0000001001101,
|
||||
0b0000001110010,
|
||||
0b0000001110011
|
||||
},
|
||||
{
|
||||
0b11011,
|
||||
0b10010,
|
||||
0b010111,
|
||||
0b0110111,
|
||||
0b00110110,
|
||||
0b00110111,
|
||||
0b01100100,
|
||||
0b01100101,
|
||||
0b01101000,
|
||||
0b01100111,
|
||||
0b011001100,
|
||||
0b011001101,
|
||||
0b011010010,
|
||||
0b011010011,
|
||||
0b011010100
|
||||
}
|
||||
};
|
||||
|
||||
/* The code words for one run length and zero
|
||||
* run length are described in table 9.1.10.1
|
||||
* of 3gpp 44.060
|
||||
*/
|
||||
const char *one_run_len_code_list[EGPRS_CODEWORDS] = {
|
||||
"00110101",
|
||||
"000111",
|
||||
"0111",
|
||||
"1000",
|
||||
"1011",
|
||||
"1100",
|
||||
"1110",
|
||||
"1111",
|
||||
"10011",
|
||||
"10100",
|
||||
"00111",
|
||||
"01000",
|
||||
"001000",
|
||||
"000011",
|
||||
"110100",
|
||||
"110101",
|
||||
"101010",
|
||||
"101011",
|
||||
"0100111",
|
||||
"0001100",
|
||||
"0001000",
|
||||
"0010111",
|
||||
"0000011",
|
||||
"0000100",
|
||||
"0101000",
|
||||
"0101011",
|
||||
"0010011",
|
||||
"0100100",
|
||||
"0011000",
|
||||
"00000010",
|
||||
"00000011",
|
||||
"00011010",
|
||||
"00011011",
|
||||
"00010010",
|
||||
"00010011",
|
||||
"00010100",
|
||||
"00010101",
|
||||
"00010110",
|
||||
"00010111",
|
||||
"00101000",
|
||||
"00101001",
|
||||
"00101010",
|
||||
"00101011",
|
||||
"00101100",
|
||||
"00101101",
|
||||
"00000100",
|
||||
"00000101",
|
||||
"00001010",
|
||||
"00001011",
|
||||
"01010010",
|
||||
"01010011",
|
||||
"01010100",
|
||||
"01010101",
|
||||
"00100100",
|
||||
"00100101",
|
||||
"01011000",
|
||||
"01011001",
|
||||
"01011010",
|
||||
"01011011",
|
||||
"01001010",
|
||||
"01001011",
|
||||
"00110010",
|
||||
"00110011",
|
||||
"00110100",
|
||||
"11011",
|
||||
"10010",
|
||||
"010111",
|
||||
"0110111",
|
||||
"00110110",
|
||||
"00110111",
|
||||
"01100100",
|
||||
"01100101",
|
||||
"01101000",
|
||||
"01100111",
|
||||
"011001100",
|
||||
"011001101",
|
||||
"011010010",
|
||||
"011010011",
|
||||
"011010100"
|
||||
};
|
||||
|
||||
const char *zero_run_len_code_list[EGPRS_CODEWORDS] = {
|
||||
"0000110111",
|
||||
"10",
|
||||
"11",
|
||||
"010",
|
||||
"011",
|
||||
"0011",
|
||||
"0010",
|
||||
"00011",
|
||||
"000101",
|
||||
"000100",
|
||||
"0000100",
|
||||
"0000101",
|
||||
"0000111",
|
||||
"00000100",
|
||||
"00000111",
|
||||
"000011000",
|
||||
"0000010111",
|
||||
"0000011000",
|
||||
"0000001000",
|
||||
"00001100111",
|
||||
"00001101000",
|
||||
"00001101100",
|
||||
"00000110111",
|
||||
"00000101000",
|
||||
"00000010111",
|
||||
"00000011000",
|
||||
"000011001010",
|
||||
"000011001011",
|
||||
"000011001100",
|
||||
"000011001101",
|
||||
"000001101000",
|
||||
"000001101001",
|
||||
"000001101010",
|
||||
"000001101011",
|
||||
"000011010010",
|
||||
"000011010011",
|
||||
"000011010100",
|
||||
"000011010101",
|
||||
"000011010110",
|
||||
"000011010111",
|
||||
"000001101100",
|
||||
"000001101101",
|
||||
"000011011010",
|
||||
"000011011011",
|
||||
"000001010100",
|
||||
"000001010101",
|
||||
"000001010110",
|
||||
"000001010111",
|
||||
"000001100100",
|
||||
"000001100101",
|
||||
"000001010010",
|
||||
"000001010011",
|
||||
"000000100100",
|
||||
"000000110111",
|
||||
"000000111000",
|
||||
"000000100111",
|
||||
"000000101000",
|
||||
"000001011000",
|
||||
"000001011001",
|
||||
"000000101011",
|
||||
"000000101100",
|
||||
"000001011010",
|
||||
"000001100110",
|
||||
"000001100111",
|
||||
"0000001111",
|
||||
"000011001000",
|
||||
"000011001001",
|
||||
"000001011011",
|
||||
"000000110011",
|
||||
"000000110100",
|
||||
"000000110101",
|
||||
"0000001101100",
|
||||
"0000001101101",
|
||||
"0000001001010",
|
||||
"0000001001011",
|
||||
"0000001001100",
|
||||
"0000001001101",
|
||||
"0000001110010",
|
||||
"0000001110011"
|
||||
};
|
||||
|
||||
/* Calculate runlength of a codeword
|
||||
* \param root[in] Root of Ones or Zeros tree
|
||||
* \param bmbuf[in] Received compressed bitmap buf
|
||||
* \param bit_pos[in] The start bit pos to read codeword
|
||||
* \param len_codewd[in] Length of code word
|
||||
* \param rlen[out] Calculated run length
|
||||
*/
|
||||
static int search_runlen(
|
||||
egprs_compress_node *root,
|
||||
const uint8_t *bmbuf,
|
||||
uint8_t bit_pos,
|
||||
uint8_t *len_codewd,
|
||||
uint16_t *rlen)
|
||||
{
|
||||
egprs_compress_node *iter;
|
||||
uint8_t dir;
|
||||
|
||||
iter = root;
|
||||
*len_codewd = 0;
|
||||
|
||||
while (iter->run_length == -1) {
|
||||
if ((!iter->left) && (!iter->right))
|
||||
return -1;
|
||||
/* get the bit value at the bitpos and put it in right most of dir */
|
||||
dir = (bmbuf[bit_pos/8] >> (7 - (bit_pos & 0x07))) & 0x01;
|
||||
bit_pos++;
|
||||
(*len_codewd)++;
|
||||
if (!dir && (iter->left != NULL))
|
||||
iter = iter->left;
|
||||
else if (dir && (iter->right != NULL))
|
||||
iter = iter->right;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "Run_length = %d\n", iter->run_length);
|
||||
*rlen = iter->run_length;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Decompress received block bitmap
|
||||
* \param compress_bmap_len[in] Compressed bitmap length
|
||||
* \param start[in] Starting Color Code, true if bitmap starts with a run
|
||||
* length of ones, false if zeros; see 9.1.10, 3GPP 44.060.
|
||||
* \param orig_crbb_buf[in] Received block crbb bitmap
|
||||
* \param dest[out] Uncompressed bitvector
|
||||
*/
|
||||
int egprs_compress::decompress_crbb(
|
||||
int8_t compress_bmap_len,
|
||||
bool start,
|
||||
const uint8_t *orig_crbb_buf,
|
||||
bitvec *dest)
|
||||
{
|
||||
|
||||
uint8_t bit_pos = 0;
|
||||
uint8_t data;
|
||||
egprs_compress_node *list = NULL;
|
||||
uint8_t nbits = 0; /* number of bits of codeword */
|
||||
uint16_t run_length = 0;
|
||||
uint16_t cbmaplen = 0; /* compressed bitmap part after decompression */
|
||||
unsigned wp = dest->cur_bit;
|
||||
int rc = 0;
|
||||
egprs_compress *compress = instance();
|
||||
|
||||
while (compress_bmap_len > 0) {
|
||||
if (start) {
|
||||
data = 0xff;
|
||||
list = compress->ones_list;
|
||||
} else {
|
||||
data = 0x00;
|
||||
list = compress->zeros_list;
|
||||
}
|
||||
rc = search_runlen(list, orig_crbb_buf,
|
||||
bit_pos, &nbits, &run_length);
|
||||
if (rc == -1)
|
||||
return -1;
|
||||
/* If run length > 64, need makeup and terminating code */
|
||||
if (run_length < 64)
|
||||
start = !start;
|
||||
cbmaplen = cbmaplen + run_length;
|
||||
/* put run length of Ones in uncompressed bitmap */
|
||||
while (run_length != 0) {
|
||||
if (run_length > 8) {
|
||||
bitvec_write_field(dest, wp, data, 8);
|
||||
run_length = run_length - 8;
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, data, run_length);
|
||||
run_length = 0;
|
||||
}
|
||||
}
|
||||
bit_pos = bit_pos + nbits;
|
||||
compress_bmap_len = compress_bmap_len - nbits;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void egprs_compress::decode_tree_init()
|
||||
{
|
||||
ones_list = create_tree_node(tall_pcu_ctx);
|
||||
zeros_list = create_tree_node(tall_pcu_ctx);
|
||||
build_codewords(ones_list, one_run_len_code_list);
|
||||
build_codewords(zeros_list, zero_run_len_code_list);
|
||||
}
|
||||
|
||||
egprs_compress::egprs_compress()
|
||||
{
|
||||
decode_tree_init();
|
||||
}
|
||||
|
||||
/* Compress received block bitmap
|
||||
* \param run_len_cnt[in] Count of number of 1's and 0's
|
||||
* \param codewrd_bitmap[in] Code word for coresponding run length.
|
||||
* \param crbb_vec[out] compressed bitvector.
|
||||
*/
|
||||
static void compress_bitmap(
|
||||
uint16_t *run_len_cnt, /* cnt: run length count */
|
||||
uint16_t *codewrd_bitmap, /* code word */
|
||||
int16_t *codewrd_len, /* number of bits in the code word */
|
||||
bitvec *crbb_vec, /* bitmap buffer to put code word in */
|
||||
bool start)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned writeIndex = crbb_vec->cur_bit;
|
||||
*codewrd_bitmap = 0;
|
||||
*codewrd_len = 0;
|
||||
if (*run_len_cnt >= 64) {
|
||||
for (i = 0; i < 15; i++) {
|
||||
if (t4_make_up_ind[i] == *run_len_cnt) {
|
||||
*codewrd_bitmap = t4_make_up[start][i];
|
||||
*codewrd_len = t4_make_up_length[start][i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*codewrd_bitmap = t4_term[start][*run_len_cnt];
|
||||
*codewrd_len = t4_term_length[start][*run_len_cnt];
|
||||
}
|
||||
bitvec_write_field(crbb_vec, writeIndex, *codewrd_bitmap, *codewrd_len);
|
||||
}
|
||||
|
||||
/* Compress received block bitmap */
|
||||
int egprs_compress::osmo_t4_compress(struct bitvec *bv)
|
||||
{
|
||||
uint8_t crbb_len = 0;
|
||||
uint8_t uclen_crbb = 0;
|
||||
uint8_t crbb_bitmap[127] = {'\0'};
|
||||
bool start = (bv->data[0] & 0x80)>>7;
|
||||
struct bitvec crbb_vec;
|
||||
|
||||
crbb_vec.data = crbb_bitmap;
|
||||
crbb_vec.cur_bit = 0;
|
||||
crbb_vec.data_len = 127;
|
||||
bv->data_len = bv->cur_bit;
|
||||
bv->cur_bit = 0;
|
||||
if (egprs_compress::compress_rbb(bv, &crbb_vec, &uclen_crbb, 23*8)) {
|
||||
memcpy(bv->data, crbb_bitmap, (crbb_len+7)/8);
|
||||
bv->cur_bit = crbb_len;
|
||||
bv->data_len = (crbb_len+7)/8;
|
||||
return start;
|
||||
}
|
||||
else
|
||||
printf("Encode failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! \brief compression algorithm using T4 encoding
|
||||
* the compressed bitmap's are copied in crbb_bitmap
|
||||
* \param[in] rbb_vec bit vector to be encoded
|
||||
* \return 1 if compression is success or 0 for failure
|
||||
*/
|
||||
int egprs_compress::compress_rbb(
|
||||
struct bitvec *urbb_vec,
|
||||
struct bitvec *crbb_vec,
|
||||
uint8_t *uclen_crbb, /* Uncompressed bitmap len in CRBB */
|
||||
uint8_t max_bits) /* max remaining bits */
|
||||
{
|
||||
bool run_len_bit;
|
||||
int buflen = urbb_vec->cur_bit;
|
||||
int total_bits = urbb_vec->cur_bit;
|
||||
uint16_t rlen;
|
||||
uint16_t temprl = 0;
|
||||
uint16_t cbmap = 0; /* Compressed code word */
|
||||
int16_t nbits; /* Length of code word */
|
||||
uint16_t uclen = 0;
|
||||
int16_t clen = 0;
|
||||
bool start; /* Starting color code see 9.1.10, 3GPP 44.060 */
|
||||
urbb_vec->cur_bit = 0;
|
||||
run_len_bit = (urbb_vec->data[0] & 0x80)>>7;
|
||||
while (buflen > 0) {
|
||||
temprl = 0;
|
||||
/* Find Run length */
|
||||
if (run_len_bit == 1)
|
||||
rlen = bitvec_rl_curbit(urbb_vec, true, total_bits);
|
||||
else
|
||||
rlen = bitvec_rl_curbit(urbb_vec, false, total_bits);
|
||||
buflen = buflen - rlen;
|
||||
/* if rlen > 64 need Makeup code word */
|
||||
/*Compress the bits */
|
||||
if (run_len_bit == 0) {
|
||||
start = 0;
|
||||
if (rlen >= 64) {
|
||||
temprl = (rlen/64)*64;
|
||||
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||
crbb_vec, start);
|
||||
clen = clen + nbits;
|
||||
}
|
||||
temprl = MOD64(rlen);
|
||||
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||
crbb_vec, start);
|
||||
/* next time the run length will be Ones */
|
||||
run_len_bit = 1;
|
||||
} else {
|
||||
start = 1;
|
||||
if (rlen >= 64) {
|
||||
temprl = (rlen/64)*64;
|
||||
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||
crbb_vec, start);
|
||||
clen = clen + nbits;
|
||||
}
|
||||
temprl = MOD64(rlen);
|
||||
compress_bitmap(&temprl, &cbmap, &nbits,
|
||||
crbb_vec, start);
|
||||
|
||||
/* next time the run length will be Zeros */
|
||||
run_len_bit = 0;
|
||||
}
|
||||
uclen = uclen + rlen;
|
||||
clen = clen + nbits;
|
||||
/*compressed bitmap exceeds the buffer space */
|
||||
if (clen > max_bits) {
|
||||
uclen = uclen - rlen;
|
||||
clen = clen - nbits;
|
||||
break;
|
||||
}
|
||||
}
|
||||
crbb_vec->cur_bit = clen;
|
||||
*uclen_crbb = uclen;
|
||||
if (clen >= uclen)
|
||||
/* No Gain is observed, So no need to compress */
|
||||
return 0;
|
||||
else
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "CRBB bitmap = %s\n", osmo_hexdump(crbb_vec->data, (crbb_vec->cur_bit+7)/8));
|
||||
/* Add compressed bitmap to final buffer */
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/* egprs_rlc_compression.h
|
||||
* Routines for EGPRS RLC bitmap compression handling
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct egprs_compress_node;
|
||||
#define MOD64(X) (((X) + 64) & 0x3F)
|
||||
|
||||
/* Singleton to manage the EGPRS compression algorithm. */
|
||||
class egprs_compress
|
||||
{
|
||||
public:
|
||||
static int decompress_crbb(int8_t compress_bmap_len,
|
||||
bool start, const uint8_t *orig_buf,
|
||||
bitvec *dest);
|
||||
egprs_compress();
|
||||
int osmo_t4_compress(struct bitvec *bv);
|
||||
static int compress_rbb(struct bitvec *urbb_vec, struct bitvec *crbb_vec,
|
||||
uint8_t *uclen_crbb, uint8_t max_bits);
|
||||
|
||||
private:
|
||||
egprs_compress_node *ones_list;
|
||||
egprs_compress_node *zeros_list;
|
||||
|
||||
void decode_tree_init(void);
|
||||
static egprs_compress *s_instance;
|
||||
static egprs_compress*instance();
|
||||
egprs_compress_node *create_tree_node(void *);
|
||||
void build_codewords(egprs_compress_node *root, const char *cdwd[]);
|
||||
/* singleton class, so this private destructor is left unimplemented. */
|
||||
~egprs_compress();
|
||||
};
|
539
src/encoding.cpp
539
src/encoding.cpp
|
@ -24,14 +24,59 @@
|
|||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <egprs_rlc_compression.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/gprs/protocol/gsm_04_60.h>
|
||||
}
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
/* { 0 | 1 < TIMING_ADVANCE_INDEX : bit (4) > } */
|
||||
static inline bool write_tai(bitvec *dest, unsigned& wp, int8_t tai)
|
||||
{
|
||||
if (tai < 0) { /* No TIMING_ADVANCE_INDEX: */
|
||||
bitvec_write_field(dest, wp, 0, 1);
|
||||
return false;
|
||||
}
|
||||
/* TIMING_ADVANCE_INDEX: */
|
||||
bitvec_write_field(dest, wp, 1, 1);
|
||||
bitvec_write_field(dest, wp, tai, 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* { 0 | 1 < TIMING_ADVANCE_VALUE : bit (6) > } */
|
||||
static inline void write_ta(bitvec *dest, unsigned& wp, int8_t ta)
|
||||
{
|
||||
if (ta < 0) /* No TIMING_ADVANCE_VALUE: */
|
||||
bitvec_write_field(dest, wp, 0, 1);
|
||||
else { /* TIMING_ADVANCE_VALUE: */
|
||||
bitvec_write_field(dest, wp, 1, 1);
|
||||
bitvec_write_field(dest, wp, ta, 6);
|
||||
}
|
||||
}
|
||||
|
||||
/* 3GPP TS 44.060 § 12.12:
|
||||
{ 0 | 1 < TIMING_ADVANCE_VALUE : bit (6) > }
|
||||
{ 0 | 1 < TIMING_ADVANCE_INDEX : bit (4) >
|
||||
< TIMING_ADVANCE_TIMESLOT_NUMBER : bit (3) > }
|
||||
*/
|
||||
static inline void write_ta_ie(bitvec *dest, unsigned& wp,
|
||||
int8_t ta, int8_t tai, uint8_t ts)
|
||||
{
|
||||
write_ta(dest, wp, ta);
|
||||
if (write_tai(dest, wp, tai)) /* TIMING_ADVANCE_TIMESLOT_NUMBER: */
|
||||
bitvec_write_field(dest, wp, ts, 3);
|
||||
}
|
||||
|
||||
static int write_ia_rest_downlink(
|
||||
gprs_rlcmac_dl_tbf *tbf,
|
||||
bitvec * dest, unsigned& wp,
|
||||
uint8_t polling, uint32_t fn,
|
||||
uint8_t polling, bool ta_valid, uint32_t fn,
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
|
||||
{
|
||||
if (!tbf) {
|
||||
|
@ -54,13 +99,8 @@ static int write_ia_rest_downlink(
|
|||
}
|
||||
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
|
||||
bitvec_write_field(dest, wp,polling,1); // Polling Bit
|
||||
bitvec_write_field(dest, wp,!polling,1); // TA_VALID ???
|
||||
if (ta_idx < 0) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
|
||||
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
|
||||
}
|
||||
bitvec_write_field(dest, wp, ta_valid, 1); // N. B: NOT related to TAI!
|
||||
write_tai(dest, wp, ta_idx);
|
||||
if (polling) {
|
||||
bitvec_write_field(dest, wp,0x1,1); // TBF Starting TIME present
|
||||
bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
|
||||
|
@ -103,12 +143,7 @@ static int write_ia_rest_uplink(
|
|||
} else
|
||||
bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
|
||||
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
|
||||
if (ta_idx < 0) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
|
||||
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
|
||||
}
|
||||
write_tai(dest, wp, ta_idx);
|
||||
bitvec_write_field(dest, wp, 1, 1); // TBF_STARTING_TIME_FLAG
|
||||
bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
|
||||
bitvec_write_field(dest, wp,fn % 51,6); // T3
|
||||
|
@ -140,11 +175,149 @@ static int write_ia_rest_egprs_uplink(
|
|||
gprs_rlcmac_ul_tbf *tbf,
|
||||
bitvec * dest, unsigned& wp,
|
||||
uint8_t usf, uint32_t fn,
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
|
||||
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
|
||||
enum ph_burst_type burst_type, uint16_t ra)
|
||||
{
|
||||
LOGP(DRLCMACUL, LOGL_ERROR,
|
||||
"EGPRS Packet Uplink Assignment is not yet implemented\n");
|
||||
return -EINVAL;
|
||||
unsigned int ws_enc = 0;
|
||||
uint8_t extended_ra = 0;
|
||||
|
||||
extended_ra = (ra & 0x1F);
|
||||
|
||||
bitvec_write_field(dest, wp, 1, 2); /* LH */
|
||||
bitvec_write_field(dest, wp, 0, 2); /* 0 EGPRS Uplink Assignment */
|
||||
bitvec_write_field(dest, wp, extended_ra, 5); /* Extended RA */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* Access technology Request */
|
||||
|
||||
if (tbf == NULL) {
|
||||
|
||||
bitvec_write_field(dest, wp, 0, 1); /* multiblock allocation */
|
||||
|
||||
if (alpha) {
|
||||
bitvec_write_field(dest, wp, 0x1, 1); /* ALPHA =yes */
|
||||
bitvec_write_field(dest, wp, alpha, 4); /* ALPHA */
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 0x0, 1); /* ALPHA = no */
|
||||
}
|
||||
|
||||
bitvec_write_field(dest, wp, gamma, 5); /* GAMMA power contrl */
|
||||
bitvec_write_field(dest, wp, (fn / (26 * 51)) % 32, 5);/* T1' */
|
||||
bitvec_write_field(dest, wp, fn % 51, 6); /* T3 */
|
||||
bitvec_write_field(dest, wp, fn % 26, 5); /* T2 */
|
||||
bitvec_write_field(dest, wp, 0, 2); /* Radio block allocation */
|
||||
|
||||
bitvec_write_field(dest, wp, 0, 1);
|
||||
|
||||
} else {
|
||||
|
||||
ws_enc = (tbf->m_window.ws() - 64) / 32;
|
||||
|
||||
bitvec_write_field(dest, wp, 1, 1); /* single block alloc */
|
||||
bitvec_write_field(dest, wp, tbf->tfi(), 5);/* TFI assignment */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* polling bit */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* constant */
|
||||
bitvec_write_field(dest, wp, usf, 3); /* USF bit */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* USF granularity */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* P0 */
|
||||
/* MCS */
|
||||
bitvec_write_field(dest, wp, tbf->current_cs().to_num()-1, 4);
|
||||
/* tlli channel block */
|
||||
bitvec_write_field(dest, wp, tbf->tlli(), 1);
|
||||
bitvec_write_field(dest, wp, 0, 1); /* BEP period present */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* resegmentation */
|
||||
bitvec_write_field(dest, wp, ws_enc, 5);/* egprs window_size */
|
||||
|
||||
if (alpha) {
|
||||
bitvec_write_field(dest, wp, 0x1, 1); /* ALPHA =yes */
|
||||
bitvec_write_field(dest, wp, alpha, 4); /* ALPHA */
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 0x0, 1); /* ALPHA = no */
|
||||
}
|
||||
|
||||
bitvec_write_field(dest, wp, gamma, 5); /* GAMMA power contrl */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* TIMING_ADVANCE_INDEX */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* TBF_STARTING_TIME_FLAG */
|
||||
bitvec_write_field(dest, wp, 0, 1); /* NULL */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Immediate assignment reject, sent on the CCCH/AGCH
|
||||
* see GSM 44.018, 9.1.20 + 10.5.2.30
|
||||
*/
|
||||
int Encoding::write_immediate_assignment_reject(
|
||||
bitvec *dest, uint16_t ra,
|
||||
uint32_t ref_fn,
|
||||
enum ph_burst_type burst_type)
|
||||
{
|
||||
unsigned wp = 0;
|
||||
int plen;
|
||||
int i;
|
||||
|
||||
bitvec_write_field(dest, wp, 0x0, 4); // Skip Indicator
|
||||
bitvec_write_field(dest, wp, 0x6, 4); // Protocol Discriminator
|
||||
bitvec_write_field(dest, wp, 0x3A, 8); // Immediate Assign Message Type
|
||||
|
||||
// feature indicator
|
||||
bitvec_write_field(dest, wp, 0x0, 1); // spare
|
||||
bitvec_write_field(dest, wp, 0x0, 1); // spare
|
||||
bitvec_write_field(dest, wp, 0x0, 1); // no cs
|
||||
bitvec_write_field(dest, wp, 0x1, 1); // implicit detach for PS
|
||||
|
||||
bitvec_write_field(dest, wp, 0x0, 4); // Page Mode
|
||||
/*
|
||||
* 9.1.20.2 of 44.018 version 11.7.0 Release 11
|
||||
* Filling of the message
|
||||
* If necessary the request reference information element and the
|
||||
* wait indication information element should be duplicated to
|
||||
* fill the message.
|
||||
* TODO: group rejection for multiple MS
|
||||
*/
|
||||
for (i = 0; i < 4; i++) {
|
||||
//10.5.2.30 Request Reference
|
||||
if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
||||
//9.1.20.2a of 44.018 version 11.7.0 Release 11
|
||||
bitvec_write_field(dest, wp, 0x7f, 8); /* RACH value */
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, ra, 8); /* RACH value */
|
||||
}
|
||||
|
||||
bitvec_write_field(dest, wp,
|
||||
(ref_fn / (26 * 51)) % 32, 5); // T1'
|
||||
bitvec_write_field(dest, wp, ref_fn % 51, 6); // T3
|
||||
bitvec_write_field(dest, wp, ref_fn % 26, 5); // T2
|
||||
|
||||
/* TODO: Make it configurable */
|
||||
bitvec_write_field(dest, wp, 20, 8); //Wait Indication 1
|
||||
}
|
||||
|
||||
plen = wp / 8;
|
||||
|
||||
if ((wp % 8)) {
|
||||
LOGP(DRLCMACUL, LOGL_ERROR, "Length of IMM.ASS.Rej without"
|
||||
"rest octets is not multiple of 8 bits, PLEASE FIX!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Extended RA
|
||||
else if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
||||
//9.1.20.2a of 44.018 version 11.7.0 Release 11
|
||||
uint8_t extended_ra = 0;
|
||||
|
||||
extended_ra = (ra & 0x1F);
|
||||
bitvec_write_field(dest, wp, 0x1, 1);
|
||||
bitvec_write_field(dest, wp, extended_ra, 5); /* Extended RA */
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 0x0, 1);
|
||||
}
|
||||
bitvec_write_field(dest, wp, 0x0, 1);
|
||||
bitvec_write_field(dest, wp, 0x0, 1);
|
||||
bitvec_write_field(dest, wp, 0x0, 1);
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -153,10 +326,10 @@ static int write_ia_rest_egprs_uplink(
|
|||
*/
|
||||
int Encoding::write_immediate_assignment(
|
||||
struct gprs_rlcmac_tbf *tbf,
|
||||
bitvec * dest, uint8_t downlink, uint8_t ra,
|
||||
bitvec * dest, uint8_t downlink, uint16_t ra,
|
||||
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
|
||||
uint8_t usf, uint8_t polling, uint32_t fn, uint8_t alpha,
|
||||
uint8_t gamma, int8_t ta_idx)
|
||||
uint8_t gamma, int8_t ta_idx, enum ph_burst_type burst_type, uint8_t sb)
|
||||
{
|
||||
unsigned wp = 0;
|
||||
int plen;
|
||||
|
@ -182,7 +355,13 @@ int Encoding::write_immediate_assignment(
|
|||
bitvec_write_field(dest, wp,arfcn,10); // ARFCN
|
||||
|
||||
//10.5.2.30 Request Reference
|
||||
bitvec_write_field(dest, wp,ra,8); // RA
|
||||
if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
||||
bitvec_write_field(dest, wp, 0x7f, 8); /* RACH value */
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, ra, 8); /* RACH value */
|
||||
}
|
||||
|
||||
bitvec_write_field(dest, wp,(ref_fn / (26 * 51)) % 32,5); // T1'
|
||||
bitvec_write_field(dest, wp,ref_fn % 51,6); // T3
|
||||
bitvec_write_field(dest, wp,ref_fn % 26,5); // T2
|
||||
|
@ -204,12 +383,13 @@ int Encoding::write_immediate_assignment(
|
|||
|
||||
if (downlink)
|
||||
rc = write_ia_rest_downlink(as_dl_tbf(tbf), dest, wp,
|
||||
polling, fn,
|
||||
polling, gsm48_ta_is_valid(ta), fn,
|
||||
alpha, gamma, ta_idx);
|
||||
else if (as_ul_tbf(tbf) && as_ul_tbf(tbf)->is_egprs_enabled())
|
||||
else if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
||||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2)))
|
||||
rc = write_ia_rest_egprs_uplink(as_ul_tbf(tbf), dest, wp,
|
||||
usf, fn,
|
||||
alpha, gamma, ta_idx);
|
||||
alpha, gamma, ta_idx, burst_type, ra);
|
||||
else
|
||||
rc = write_ia_rest_uplink(as_ul_tbf(tbf), dest, wp,
|
||||
usf, fn,
|
||||
|
@ -237,6 +417,8 @@ void Encoding::write_packet_uplink_assignment(
|
|||
// TODO We should use our implementation of encode RLC/MAC Control messages.
|
||||
unsigned wp = 0;
|
||||
uint8_t ts;
|
||||
/* timeslot assigned for the Continuous Timing Advance procedure */
|
||||
uint8_t ta_ts = 0; /* FIXME: supply it as parameter from caller */
|
||||
|
||||
bitvec_write_field(dest, wp,0x1,2); // Payload Type
|
||||
bitvec_write_field(dest, wp,0x0,2); // Uplink block with TDMA framenumber (N+13)
|
||||
|
@ -260,15 +442,7 @@ void Encoding::write_packet_uplink_assignment(
|
|||
bitvec_write_field(dest, wp,0x0,1); // Message escape
|
||||
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 2); // CHANNEL_CODING_COMMAND
|
||||
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
|
||||
bitvec_write_field(dest, wp,tbf->ta(),6); // TIMING_ADVANCE_VALUE
|
||||
if (ta_idx < 0) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
|
||||
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
|
||||
}
|
||||
|
||||
write_ta_ie(dest, wp,tbf->ta(), ta_idx, ta_ts);
|
||||
} else { /* EPGRS */
|
||||
unsigned int ws_enc = (tbf->m_window.ws() - 64) / 32;
|
||||
bitvec_write_field(dest, wp,0x1,1); // Message escape
|
||||
|
@ -276,22 +450,14 @@ void Encoding::write_packet_uplink_assignment(
|
|||
bitvec_write_field(dest, wp,0x0,1); // No CONTENTION_RESOLUTION_TLLI
|
||||
bitvec_write_field(dest, wp,0x0,1); // No COMPACT reduced MA
|
||||
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 4); // EGPRS Modulation and Coding IE
|
||||
bitvec_write_field(dest, wp,0x0,1); // No RESEGMENT
|
||||
/* 0: no RESEGMENT, 1: Segmentation*/
|
||||
bitvec_write_field(dest, wp, 0x1, 1);
|
||||
bitvec_write_field(dest, wp,ws_enc,5); // EGPRS Window Size
|
||||
bitvec_write_field(dest, wp,0x0,1); // No Access Technologies Request
|
||||
bitvec_write_field(dest, wp,0x0,1); // No ARAC RETRANSMISSION REQUEST
|
||||
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
|
||||
bitvec_write_field(dest, wp,0x0,1); // No BEP_PERIOD2
|
||||
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
|
||||
bitvec_write_field(dest, wp,tbf->ta(),6); // TIMING_ADVANCE_VALUE
|
||||
if (ta_idx < 0) {
|
||||
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
|
||||
} else {
|
||||
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
|
||||
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
|
||||
}
|
||||
|
||||
write_ta_ie(dest, wp,tbf->ta(), ta_idx, ta_ts);
|
||||
bitvec_write_field(dest, wp,0x0,1); // No Packet Extended Timing Advance
|
||||
}
|
||||
|
||||
|
@ -534,17 +700,44 @@ static void write_packet_uplink_ack_gprs(
|
|||
|
||||
static void write_packet_ack_nack_desc_egprs(
|
||||
struct gprs_rlcmac_bts *bts, bitvec * dest, unsigned& wp,
|
||||
gprs_rlc_ul_window *window, bool is_final)
|
||||
gprs_rlc_ul_window *window, bool is_final, unsigned& rest_bits)
|
||||
{
|
||||
int urbb_len = 0;
|
||||
int crbb_len = 0;
|
||||
int len;
|
||||
unsigned int urbb_len = 0;
|
||||
uint8_t crbb_len = 0;
|
||||
uint8_t len;
|
||||
bool bow = true;
|
||||
bool eow = true;
|
||||
int ssn = window->mod_sns(window->v_q() + 1);
|
||||
int num_blocks = window->mod_sns(window->v_r() - window->v_q());
|
||||
unsigned int num_blocks = window->mod_sns(window->v_r() - window->v_q());
|
||||
int esn_crbb = window->mod_sns(ssn - 1);
|
||||
int rest_bits = dest->data_len * 8 - wp;
|
||||
static uint8_t rbb[RLC_EGPRS_MAX_WS] = {'\0'};
|
||||
uint8_t iter = 0;
|
||||
int is_compressed = 0;
|
||||
bool try_compression = false;
|
||||
uint8_t ucmp_bmplen;
|
||||
uint8_t crbb_bitmap[23] = {'\0'};
|
||||
bitvec ucmp_vec;
|
||||
bitvec crbb_vec;
|
||||
uint8_t uclen_crbb = 0;
|
||||
bool len_coded = true;
|
||||
uint8_t crbb_start_clr_code;
|
||||
uint8_t i;
|
||||
#if 0
|
||||
/* static size of 16 bits*/
|
||||
..0. .... = ACKNACK: (Union)
|
||||
Desc
|
||||
|
||||
...0 .... = FINAL_ACK_INDICATION: False
|
||||
|
||||
.... 1... = BEGINNING_OF_WINDOW: 1
|
||||
|
||||
.... .1.. = END_OF_WINDOW: 1
|
||||
|
||||
.... ..10 0101 0001 1... .... = STARTING_SEQUENCE_NUMBER: 1187
|
||||
|
||||
.0.. .... = CRBB Exist: 0
|
||||
#endif
|
||||
rest_bits -= 16;
|
||||
|
||||
if (num_blocks > 0)
|
||||
/* V(Q) is NACK and omitted -> SSN = V(Q) + 1 */
|
||||
|
@ -552,32 +745,71 @@ static void write_packet_ack_nack_desc_egprs(
|
|||
|
||||
if (num_blocks > window->ws())
|
||||
num_blocks = window->ws();
|
||||
|
||||
/* Try Compression as number of blocks does not fit */
|
||||
if (num_blocks > rest_bits) {
|
||||
eow = false;
|
||||
urbb_len = rest_bits;
|
||||
/* TODO: use compression, start encoding bits and stop when the
|
||||
* space is exhausted. Use the first combination that encodes
|
||||
* all bits. If there is none, use the combination that encodes
|
||||
* the largest number of bits (e.g. by setting num_blocks to the
|
||||
* max and repeating the construction).
|
||||
*/
|
||||
} else if (num_blocks > rest_bits - 9) {
|
||||
/* union bit and length field take 9 bits */
|
||||
eow = false;
|
||||
urbb_len = rest_bits - 9;
|
||||
/* TODO: use compression (see above) */
|
||||
try_compression = true;
|
||||
}
|
||||
if (try_compression == true) {
|
||||
ucmp_bmplen = window->update_egprs_rbb(rbb);
|
||||
ucmp_vec.data = rbb;
|
||||
ucmp_vec.cur_bit = ucmp_bmplen;
|
||||
ucmp_vec.data_len = 127;
|
||||
crbb_vec.data = crbb_bitmap;
|
||||
crbb_vec.cur_bit = 0;
|
||||
crbb_vec.data_len = 127;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"rest_bits=%d uncompressed len %d and uncompressed bitmap = %s\n",
|
||||
rest_bits, ucmp_bmplen,
|
||||
osmo_hexdump(ucmp_vec.data, (ucmp_bmplen+7)/8));
|
||||
|
||||
is_compressed = egprs_compress::compress_rbb(&ucmp_vec, /* Uncompressed bitmap*/
|
||||
&crbb_vec, /*Compressed bitmap vector */
|
||||
&uclen_crbb,
|
||||
(rest_bits - 16));/* CRBBlength:7 colourcode:1 dissector length:8*/
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"the ucmp len=%d uclen_crbb=%d num_blocks=%d crbb length %d, "
|
||||
"and the CRBB bitmap = %s\n",
|
||||
ucmp_bmplen, uclen_crbb, num_blocks, crbb_vec.cur_bit,
|
||||
osmo_hexdump(crbb_bitmap, (crbb_vec.cur_bit+7)/8));
|
||||
crbb_len = crbb_vec.cur_bit;
|
||||
}
|
||||
|
||||
if (urbb_len + crbb_len == rest_bits)
|
||||
len = -1;
|
||||
else if (crbb_len == 0)
|
||||
if (is_compressed == 0) {
|
||||
/* length field takes 8 bits*/
|
||||
if (num_blocks > rest_bits - 8) {
|
||||
eow = false;
|
||||
urbb_len = rest_bits;
|
||||
len_coded = false;
|
||||
} else if (num_blocks == rest_bits) {
|
||||
urbb_len = rest_bits;
|
||||
len_coded = false;
|
||||
} else
|
||||
urbb_len = num_blocks;
|
||||
|
||||
len = urbb_len + 15;
|
||||
else
|
||||
} else {
|
||||
if (num_blocks > uclen_crbb) {
|
||||
eow = false;
|
||||
urbb_len = num_blocks - uclen_crbb;
|
||||
}
|
||||
/* Union bit takes 1 bit */
|
||||
/* Other fields in descr of compresed bitmap takes 23 bits
|
||||
* -8 = CRBB_STARTING_COLOR_CODE + CRBB_LENGTH */
|
||||
if (urbb_len > (rest_bits - crbb_len - 8)) {
|
||||
eow = false;
|
||||
len_coded = false;
|
||||
urbb_len = rest_bits - crbb_len - 8;
|
||||
/* -16 = ACKNACK Dissector length + CRBB_STARTING_COLOR_CODE + CRBB_LENGTH */
|
||||
} else if (urbb_len > (rest_bits - crbb_len - 16)) {
|
||||
eow = false;
|
||||
len_coded = false;
|
||||
urbb_len = rest_bits - crbb_len - 16;
|
||||
}
|
||||
len = urbb_len + crbb_len + 23;
|
||||
}
|
||||
|
||||
/* EGPRS Ack/Nack Description IE */
|
||||
if (len < 0) {
|
||||
if (len_coded == false) {
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have length
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 1, 1); // 1: have length
|
||||
|
@ -588,17 +820,37 @@ static void write_packet_ack_nack_desc_egprs(
|
|||
bitvec_write_field(dest, wp, bow, 1); // BEGINNING_OF_WINDOW
|
||||
bitvec_write_field(dest, wp, eow, 1); // END_OF_WINDOW
|
||||
bitvec_write_field(dest, wp, ssn, 11); // STARTING_SEQUENCE_NUMBER
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have CRBB
|
||||
|
||||
/* TODO: Add CRBB support */
|
||||
|
||||
if (is_compressed) {
|
||||
bitvec_write_field(dest, wp, 1, 1); // CRBB_Exist
|
||||
bitvec_write_field(dest, wp, crbb_len, 7); // CRBB_LENGTH
|
||||
crbb_start_clr_code = (0x80 & ucmp_vec.data[0])>>7;
|
||||
bitvec_write_field(dest, wp, crbb_start_clr_code, 1); // CRBB_clr_code
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"EGPRS CRBB, crbb_len = %d, crbb_start_clr_code = %d\n",
|
||||
crbb_len, crbb_start_clr_code);
|
||||
while (crbb_len != 0) {
|
||||
if (crbb_len > 8) {
|
||||
bitvec_write_field(dest, wp, crbb_bitmap[iter], 8);
|
||||
crbb_len = crbb_len - 8;
|
||||
iter++;
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, crbb_bitmap[iter], crbb_len);
|
||||
crbb_len = 0;
|
||||
}
|
||||
}
|
||||
esn_crbb = window->mod_sns(esn_crbb + uclen_crbb);
|
||||
} else {
|
||||
bitvec_write_field(dest, wp, 0, 1); // CRBB_Exist
|
||||
}
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
" - EGPRS URBB, len = %d, SSN = %d, ESN_CRBB = %d, "
|
||||
"EGPRS URBB, urbb len = %d, SSN = %d, ESN_CRBB = %d, "
|
||||
"len present = %s,desc len = %d, "
|
||||
"SNS = %d, WS = %d, V(Q) = %d, V(R) = %d%s%s\n",
|
||||
urbb_len, ssn, esn_crbb,
|
||||
urbb_len, ssn, esn_crbb, len_coded ? "yes" : "No" , len,
|
||||
window->sns(), window->ws(), window->v_q(), window->v_r(),
|
||||
bow ? ", BOW" : "", eow ? ", EOW" : "");
|
||||
for (int i = urbb_len; i > 0; i--) {
|
||||
|
||||
for (i = urbb_len; i > 0; i--) {
|
||||
/* Set bit at the appropriate position (see 3GPP TS 04.60 12.3.1) */
|
||||
bool is_ack = window->m_v_n.is_received(esn_crbb + i);
|
||||
bitvec_write_field(dest, wp, is_ack, 1);
|
||||
|
@ -610,9 +862,11 @@ static void write_packet_uplink_ack_egprs(
|
|||
struct gprs_rlcmac_ul_tbf *tbf, bool is_final)
|
||||
{
|
||||
bitvec_write_field(dest, wp, 0, 2); // fixed 00
|
||||
bitvec_write_field(dest, wp, 2, 4); // CHANNEL_CODING_COMMAND: MCS-3
|
||||
// bitvec_write_field(dest, wp, tbf->current_cs() - 1, 4); // CHANNEL_CODING_COMMAND
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: no RESEGMENT (nyi)
|
||||
/* CHANNEL_CODING_COMMAND */
|
||||
bitvec_write_field(dest, wp,
|
||||
tbf->current_cs().to_num() - 1, 4);
|
||||
/* 0: no RESEGMENT, 1: Segmentation*/
|
||||
bitvec_write_field(dest, wp, 1, 1);
|
||||
bitvec_write_field(dest, wp, 1, 1); // PRE_EMPTIVE_TRANSMISSION, TODO: This resembles GPRS, change it?
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: no PRR_RETRANSMISSION_REQUEST, TODO: clarify
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: no ARAC_RETRANSMISSION_REQUEST, TODO: clarify
|
||||
|
@ -624,7 +878,9 @@ static void write_packet_uplink_ack_egprs(
|
|||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have Power Control Parameters
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have Extension Bits
|
||||
|
||||
write_packet_ack_nack_desc_egprs(bts, dest, wp, &tbf->m_window, is_final);
|
||||
/* -2 for last bit 0 mandatory and REL5 not supported */
|
||||
unsigned bits_ack_nack = dest->data_len * 8 - wp - 2;
|
||||
write_packet_ack_nack_desc_egprs(bts, dest, wp, &tbf->m_window, is_final, bits_ack_nack);
|
||||
|
||||
bitvec_write_field(dest, wp, 0, 1); // fixed 0
|
||||
bitvec_write_field(dest, wp, 0, 1); // 0: don't have REL 5
|
||||
|
@ -744,20 +1000,20 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
|
|||
egprs1->usf = rlc->usf;
|
||||
egprs1->es_p = rlc->es_p;
|
||||
egprs1->rrbp = rlc->rrbp;
|
||||
egprs1->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs1->tfi_b = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs1->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs1->tfi_lo = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs1->pr = rlc->pr;
|
||||
egprs1->cps = rlc->cps;
|
||||
|
||||
egprs1->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs1->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs1->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
egprs1->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs1->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs1->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
|
||||
bsn_delta = (rlc->block_info[1].bsn - rlc->block_info[0].bsn) &
|
||||
(RLC_EGPRS_SNS - 1);
|
||||
|
||||
egprs1->bsn2_a = bsn_delta >> 0; /* 7 bits LSB */
|
||||
egprs1->bsn2_b = bsn_delta >> 7; /* 3 bits */
|
||||
egprs1->bsn2_hi = bsn_delta >> 0; /* 7 bits LSB */
|
||||
egprs1->bsn2_lo = bsn_delta >> 7; /* 3 bits */
|
||||
|
||||
/* first FBI/E header */
|
||||
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
|
||||
|
@ -783,14 +1039,14 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
|
|||
egprs2->usf = rlc->usf;
|
||||
egprs2->es_p = rlc->es_p;
|
||||
egprs2->rrbp = rlc->rrbp;
|
||||
egprs2->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs2->tfi_b = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs2->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs2->tfi_lo = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs2->pr = rlc->pr;
|
||||
egprs2->cps = rlc->cps;
|
||||
|
||||
egprs2->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs2->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs2->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
egprs2->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs2->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs2->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
|
||||
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
|
||||
e_fbi_header |= rlc->block_info[0].cv == 0 ? 0x02 : 0; /* FBI */
|
||||
|
@ -807,14 +1063,14 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
|
|||
egprs3->usf = rlc->usf;
|
||||
egprs3->es_p = rlc->es_p;
|
||||
egprs3->rrbp = rlc->rrbp;
|
||||
egprs3->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs3->tfi_b = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs3->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
|
||||
egprs3->tfi_lo = rlc->tfi >> 1; /* 4 bits */
|
||||
egprs3->pr = rlc->pr;
|
||||
egprs3->cps = rlc->cps;
|
||||
|
||||
egprs3->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs3->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs3->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
egprs3->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
||||
egprs3->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
||||
egprs3->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
||||
|
||||
egprs3->spb = rlc->block_info[0].spb;
|
||||
|
||||
|
@ -891,11 +1147,21 @@ unsigned int Encoding::rlc_copy_from_aligned_buffer(
|
|||
return rdbi->data_len;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief (GPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
|
||||
* \param rdbi rlc/mac block info
|
||||
* \param llc llc pdu
|
||||
* \param offset given offset within the rlc/mac block
|
||||
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
|
||||
* \param data_block buffer holds rlc/mac data
|
||||
* \param is_final if this is the last rlc/mac within a TBF
|
||||
* \param count_payload if not NULL save the written size of payload in bytes into it
|
||||
* \return the state of the rlc/mac like if there is more space for another chunk
|
||||
*/
|
||||
static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
||||
struct gprs_rlc_data_block_info *rdbi,
|
||||
gprs_llc *llc, int *offset, int *num_chunks,
|
||||
uint8_t *data_block,
|
||||
bool is_final)
|
||||
uint8_t *data_block, bool is_final, int *count_payload)
|
||||
{
|
||||
int chunk;
|
||||
int space;
|
||||
|
@ -920,6 +1186,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
*e_pointer |= 0x01;
|
||||
/* fill only space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
/* return data block as message */
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
|
@ -937,6 +1205,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
*e_pointer |= 0x01;
|
||||
/* fill space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
rdbi->cv = 0;
|
||||
|
@ -964,6 +1234,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
// no need to set e_pointer nor increase delimiter
|
||||
/* fill only space, which is 1 octet less than chunk */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
/* return data block as message */
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
|
@ -989,6 +1261,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
(*num_chunks)++;
|
||||
/* copy (rest of) LLC frame to space and reset later */
|
||||
llc->consume(data, chunk);
|
||||
if (count_payload)
|
||||
*count_payload = chunk;
|
||||
data += chunk;
|
||||
space -= chunk;
|
||||
(*offset) += chunk;
|
||||
|
@ -1012,11 +1286,22 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
|||
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief (EGPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
|
||||
* \param rdbi rlc/mac block info
|
||||
* \param llc llc pdu
|
||||
* \param offset given offset within the rlc/mac block
|
||||
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
|
||||
* \param data_block buffer holds rlc/mac data
|
||||
* \param is_final if this is the last rlc/mac within a TBF
|
||||
* \param count_payload if not NULL save the written size of payload in bytes into it
|
||||
* \return the state of the rlc/mac like if there is more space for another chunk
|
||||
*/
|
||||
static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
||||
struct gprs_rlc_data_block_info *rdbi,
|
||||
gprs_llc *llc, int *offset, int *num_chunks,
|
||||
uint8_t *data_block,
|
||||
bool is_final)
|
||||
bool is_final, int *count_payload)
|
||||
{
|
||||
int chunk;
|
||||
int space;
|
||||
|
@ -1040,6 +1325,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
chunk, space);
|
||||
/* fill only space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
/* return data block as message */
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
|
@ -1054,6 +1341,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
"header, and we are done\n", chunk, space);
|
||||
/* fill space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
rdbi->cv = 0;
|
||||
|
@ -1068,6 +1357,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
chunk, space);
|
||||
/* fill space */
|
||||
llc->consume(data, space);
|
||||
if (count_payload)
|
||||
*count_payload = space;
|
||||
*offset = rdbi->data_len;
|
||||
(*num_chunks)++;
|
||||
return Encoding::AR_NEED_MORE_BLOCKS;
|
||||
|
@ -1098,6 +1389,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
(*num_chunks)++;
|
||||
/* copy (rest of) LLC frame to space and reset later */
|
||||
llc->consume(data, chunk);
|
||||
if (count_payload)
|
||||
*count_payload = chunk;
|
||||
data += chunk;
|
||||
space -= chunk;
|
||||
(*offset) += chunk;
|
||||
|
@ -1154,22 +1447,60 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|||
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Encoding::rlc_data_to_dl_append
|
||||
* \param rdbi rlc/mac block info
|
||||
* \param cs the coding scheme to use
|
||||
* \param llc llc pdu
|
||||
* \param offset given offset within the rlc/mac block
|
||||
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
|
||||
* \param data_block buffer holds rlc/mac data
|
||||
* \param is_final if this is the last rlc/mac within a TBF
|
||||
* \param count_payload if not NULL save the written size of payload in bytes into it
|
||||
* \return the state of the rlc/mac like if there is more space for another chunk
|
||||
*/
|
||||
Encoding::AppendResult Encoding::rlc_data_to_dl_append(
|
||||
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
|
||||
gprs_llc *llc, int *offset, int *num_chunks,
|
||||
uint8_t *data_block,
|
||||
bool is_final)
|
||||
uint8_t *data_block, bool is_final, int *count_payload)
|
||||
{
|
||||
if (cs.isGprs())
|
||||
return rlc_data_to_dl_append_gprs(rdbi,
|
||||
llc, offset, num_chunks, data_block, is_final);
|
||||
llc, offset, num_chunks, data_block, is_final,
|
||||
count_payload);
|
||||
|
||||
if (cs.isEgprs())
|
||||
return rlc_data_to_dl_append_egprs(rdbi,
|
||||
llc, offset, num_chunks, data_block, is_final);
|
||||
llc, offset, num_chunks, data_block, is_final,
|
||||
count_payload);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_ERROR, "%s data block encoding not implemented\n",
|
||||
cs.name());
|
||||
|
||||
return AR_NEED_MORE_BLOCKS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Refer 44.060 version 7.27.0 Release 7
|
||||
* section 7.1.3.2.1 On receipt of a PACKET RESOURCE REQUEST message
|
||||
* 8.1.2.5 Establishment of uplink TBF
|
||||
*/
|
||||
void Encoding::write_packet_access_reject(
|
||||
bitvec * dest, uint32_t tlli)
|
||||
{
|
||||
unsigned wp = 0;
|
||||
|
||||
bitvec_write_field(dest, wp, 0x1, 2); // Payload Type
|
||||
bitvec_write_field(dest, wp, 0x0, 2); // Uplink block with TDMA FN
|
||||
bitvec_write_field(dest, wp, 0, 1); // No Polling Bit
|
||||
bitvec_write_field(dest, wp, 0x0, 3); // Uplink state flag
|
||||
bitvec_write_field(dest, wp,
|
||||
MT_PACKET_ACCESS_REJECT, 6); // MESSAGE TYPE
|
||||
bitvec_write_field(dest, wp, 0, 2); // fixed 00
|
||||
bitvec_write_field(dest, wp, 0x0, 1); // TLLI / G-RNTI : bit (32)
|
||||
bitvec_write_field(dest, wp, tlli, 32); // CONTENTION_RESOLUTION_TLLI
|
||||
bitvec_write_field(dest, wp, 1, 1); // WAIT_INDICATION size in seconds
|
||||
/* TODO: make it configurable */
|
||||
bitvec_write_field(dest, wp, 5, 8); // WAIT_INDICATION value
|
||||
bitvec_write_field(dest, wp, 0, 1); // WAIT_INDICATION size in seconds
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
#include <stdint.h>
|
||||
#include <gsm_rlcmac.h>
|
||||
#include <gprs_coding_scheme.h>
|
||||
extern "C" {
|
||||
#include <osmocom/gsm/l1sap.h>
|
||||
}
|
||||
|
||||
struct gprs_rlcmac_bts;
|
||||
struct gprs_rlcmac_tbf;
|
||||
|
@ -40,11 +43,20 @@ class Encoding {
|
|||
public:
|
||||
static int write_immediate_assignment(
|
||||
struct gprs_rlcmac_tbf *tbf,
|
||||
bitvec * dest, uint8_t downlink, uint8_t ra,
|
||||
bitvec * dest, uint8_t downlink, uint16_t ra,
|
||||
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts,
|
||||
uint8_t tsc, uint8_t usf, uint8_t polling,
|
||||
uint32_t fn, uint8_t alpha, uint8_t gamma,
|
||||
int8_t ta_idx);
|
||||
int8_t ta_idx,
|
||||
enum ph_burst_type burst_type =
|
||||
GSM_L1_BURST_TYPE_ACCESS_0,
|
||||
uint8_t sb = 1);
|
||||
|
||||
static int write_immediate_assignment_reject(
|
||||
bitvec *dest, uint16_t ra,
|
||||
uint32_t ref_fn,
|
||||
enum ph_burst_type burst_type
|
||||
);
|
||||
|
||||
static void write_packet_uplink_assignment(
|
||||
struct gprs_rlcmac_bts *bts,
|
||||
|
@ -62,6 +74,9 @@ public:
|
|||
|
||||
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
|
||||
|
||||
static void write_packet_access_reject(
|
||||
bitvec * dest, uint32_t tlli);
|
||||
|
||||
static void write_packet_uplink_ack(
|
||||
struct gprs_rlcmac_bts *bts, bitvec * dest,
|
||||
struct gprs_rlcmac_ul_tbf *tbf, bool is_final,
|
||||
|
@ -91,6 +106,5 @@ public:
|
|||
static AppendResult rlc_data_to_dl_append(
|
||||
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
|
||||
gprs_llc *llc, int *offset, int *num_chunks,
|
||||
uint8_t *data,
|
||||
bool is_final);
|
||||
uint8_t *data, bool is_final, int *count_payload);
|
||||
};
|
||||
|
|
|
@ -21,6 +21,38 @@
|
|||
|
||||
#include "gprs_coding_scheme.h"
|
||||
|
||||
/*
|
||||
* 44.060 Table 8.1.1.1 and Table 8.1.1.2
|
||||
* It has 3 level indexing. 0th level is ARQ type
|
||||
* 1st level is Original MCS( index 0 corresponds to MCS1 and so on)
|
||||
* 2nd level is MS MCS (index 0 corresponds to MCS1 and so on)
|
||||
*/
|
||||
enum GprsCodingScheme::Scheme GprsCodingScheme::egprs_mcs_retx_tbl[MAX_NUM_ARQ]
|
||||
[MAX_NUM_MCS][MAX_NUM_MCS] = {
|
||||
{
|
||||
{MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1},
|
||||
{MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3},
|
||||
{MCS1, MCS1, MCS1, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4},
|
||||
{MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9},
|
||||
{MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS8, MCS8},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9}
|
||||
},
|
||||
{
|
||||
{MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1},
|
||||
{MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2},
|
||||
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3},
|
||||
{MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4},
|
||||
{MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7},
|
||||
{MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9},
|
||||
{MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7},
|
||||
{MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS8, MCS8},
|
||||
{MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9}
|
||||
}
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
unsigned int bytes;
|
||||
|
|
|
@ -26,6 +26,12 @@
|
|||
|
||||
class GprsCodingScheme {
|
||||
public:
|
||||
|
||||
#define MAX_NUM_ARQ 2 /* max. number of ARQ */
|
||||
#define MAX_NUM_MCS 9 /* max. number of MCS */
|
||||
#define EGPRS_ARQ1 0x0
|
||||
#define EGPRS_ARQ2 0x1
|
||||
|
||||
enum Scheme {
|
||||
UNKNOWN,
|
||||
CS1, CS2, CS3, CS4,
|
||||
|
@ -64,6 +70,7 @@ public:
|
|||
unsigned int to_num() const;
|
||||
|
||||
GprsCodingScheme& operator =(Scheme s);
|
||||
bool operator == (Scheme s) const;
|
||||
GprsCodingScheme& operator =(GprsCodingScheme o);
|
||||
|
||||
bool isValid() const {return UNKNOWN <= m_scheme && m_scheme <= MCS9;}
|
||||
|
@ -105,6 +112,12 @@ public:
|
|||
static GprsCodingScheme getEgprsByNum(unsigned num);
|
||||
|
||||
static const char *modeName(Mode mode);
|
||||
static Scheme get_retx_mcs(const GprsCodingScheme mcs,
|
||||
const GprsCodingScheme retx_mcs,
|
||||
const unsigned arq_type);
|
||||
|
||||
static enum Scheme egprs_mcs_retx_tbl[MAX_NUM_ARQ]
|
||||
[MAX_NUM_MCS][MAX_NUM_MCS];
|
||||
private:
|
||||
GprsCodingScheme(int s); /* fail on use */
|
||||
GprsCodingScheme& operator =(int s); /* fail on use */
|
||||
|
@ -188,6 +201,11 @@ inline bool operator ==(GprsCodingScheme a, GprsCodingScheme b)
|
|||
return GprsCodingScheme::Scheme(a) == GprsCodingScheme::Scheme(b);
|
||||
}
|
||||
|
||||
inline bool GprsCodingScheme::operator == (Scheme scheme) const
|
||||
{
|
||||
return this->m_scheme == scheme;
|
||||
}
|
||||
|
||||
inline bool operator !=(GprsCodingScheme a, GprsCodingScheme b)
|
||||
{
|
||||
return !(a == b);
|
||||
|
@ -213,4 +231,11 @@ inline bool operator >=(GprsCodingScheme a, GprsCodingScheme b)
|
|||
{
|
||||
return a == b || a > b;
|
||||
}
|
||||
|
||||
inline GprsCodingScheme::Scheme GprsCodingScheme::get_retx_mcs(
|
||||
const GprsCodingScheme mcs,
|
||||
const GprsCodingScheme demanded_mcs,
|
||||
const unsigned arq_type)
|
||||
{
|
||||
return egprs_mcs_retx_tbl[arq_type][mcs.to_num() - 1]
|
||||
[demanded_mcs.to_num() - 1];
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
}
|
||||
|
||||
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
|
||||
|
@ -95,7 +96,7 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
|
|||
m_tlli(tlli),
|
||||
m_new_ul_tlli(0),
|
||||
m_new_dl_tlli(0),
|
||||
m_ta(0),
|
||||
m_ta(GSM48_TA_INVALID),
|
||||
m_ms_class(0),
|
||||
m_egprs_ms_class(0),
|
||||
m_is_idle(true),
|
||||
|
@ -107,7 +108,8 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
|
|||
m_reserved_ul_slots(0),
|
||||
m_current_trx(NULL),
|
||||
m_codel_state(NULL),
|
||||
m_mode(GprsCodingScheme::GPRS)
|
||||
m_mode(GprsCodingScheme::GPRS),
|
||||
m_dl_ctrl_msg(0)
|
||||
{
|
||||
int codel_interval = LLC_CODEL_USE_DEFAULT;
|
||||
|
||||
|
@ -237,7 +239,7 @@ void GprsMs::set_mode(GprsCodingScheme::Mode mode)
|
|||
if (!m_current_cs_ul.isEgprs()) {
|
||||
m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
|
||||
m_bts->bts_data()->initial_mcs_ul);
|
||||
if (!m_current_cs_dl.isValid())
|
||||
if (!m_current_cs_ul.isValid())
|
||||
m_current_cs_ul = GprsCodingScheme::MCS1;
|
||||
}
|
||||
if (!m_current_cs_dl.isEgprs()) {
|
||||
|
@ -464,11 +466,15 @@ void GprsMs::set_ta(uint8_t ta_)
|
|||
if (ta_ == m_ta)
|
||||
return;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
|
||||
tlli(), m_ta, ta_);
|
||||
|
||||
m_ta = ta_;
|
||||
if (gsm48_ta_is_valid(ta_)) {
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
|
||||
tlli(), m_ta, ta_);
|
||||
m_ta = ta_;
|
||||
} else
|
||||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
|
||||
"value %d kept)\n", tlli(), ta_, m_ta);
|
||||
}
|
||||
|
||||
void GprsMs::set_ms_class(uint8_t ms_class_)
|
||||
|
@ -574,6 +580,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 +643,8 @@ void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
|
|||
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
|
||||
} else if (m_current_cs_ul.isEgprs()) {
|
||||
/* TODO, use separate table */
|
||||
if (current_cs_num > 4)
|
||||
current_cs_num = 4;
|
||||
if (current_cs_num > MAX_GPRS_CS)
|
||||
current_cs_num = MAX_GPRS_CS;
|
||||
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
|
||||
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
|
||||
} else {
|
||||
|
|
|
@ -86,6 +86,7 @@ public:
|
|||
uint8_t egprs_ms_class() const;
|
||||
void set_ms_class(uint8_t ms_class);
|
||||
void set_egprs_ms_class(uint8_t ms_class);
|
||||
void set_current_cs_dl(GprsCodingScheme::Scheme scheme);
|
||||
|
||||
GprsCodingScheme current_cs_ul() const;
|
||||
GprsCodingScheme current_cs_dl() const;
|
||||
|
@ -129,6 +130,8 @@ public:
|
|||
void update_l1_meas(const pcu_l1_meas *meas);
|
||||
const pcu_l1_meas* l1_meas() const {return &m_l1_meas;};
|
||||
unsigned nack_rate_dl() const;
|
||||
unsigned dl_ctrl_msg() const;
|
||||
void update_dl_ctrl_msg();
|
||||
|
||||
/* internal use */
|
||||
static void timeout(void *priv_);
|
||||
|
@ -179,6 +182,8 @@ private:
|
|||
|
||||
struct gprs_codel *m_codel_state;
|
||||
GprsCodingScheme::Mode m_mode;
|
||||
|
||||
unsigned m_dl_ctrl_msg;
|
||||
};
|
||||
|
||||
inline bool GprsMs::is_idle() const
|
||||
|
@ -262,6 +267,16 @@ inline unsigned GprsMs::nack_rate_dl() const
|
|||
return m_nack_rate_dl;
|
||||
}
|
||||
|
||||
inline unsigned GprsMs::dl_ctrl_msg() const
|
||||
{
|
||||
return m_dl_ctrl_msg;
|
||||
}
|
||||
|
||||
inline void GprsMs::update_dl_ctrl_msg()
|
||||
{
|
||||
m_dl_ctrl_msg++;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::reserved_dl_slots() const
|
||||
{
|
||||
return m_reserved_dl_slots;
|
||||
|
|
|
@ -90,7 +90,7 @@ int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
|
|||
const char *imsi);
|
||||
|
||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||
uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint8_t trx, uint8_t ts,
|
||||
uint32_t fn, uint8_t block_nr);
|
||||
|
||||
int gprs_alloc_max_dl_slots_per_ms(struct gprs_rlcmac_bts *bts,
|
||||
|
|
|
@ -179,6 +179,8 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
|
|||
if (elapsed < 128)
|
||||
return 0;
|
||||
|
||||
tbf->m_bw.dl_throughput = (tbf->m_bw.dl_bw_octets/elapsed);
|
||||
|
||||
LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
|
||||
"%d KBits/s\n", tbf->imsi(), tbf->tlli(),
|
||||
tbf->m_bw.dl_bw_octets / elapsed);
|
||||
|
|
|
@ -56,7 +56,9 @@ static uint32_t sched_poll(BTS *bts,
|
|||
*ul_ack_tbf = ul_tbf;
|
||||
if (ul_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
|
||||
*dl_ass_tbf = ul_tbf;
|
||||
if (ul_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||
if (ul_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS
|
||||
|| ul_tbf->ul_ass_state ==
|
||||
GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ)
|
||||
*ul_ass_tbf = ul_tbf;
|
||||
#warning "Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all states?"
|
||||
}
|
||||
|
@ -72,7 +74,8 @@ static uint32_t sched_poll(BTS *bts,
|
|||
*poll_tbf = dl_tbf;
|
||||
if (dl_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
|
||||
*dl_ass_tbf = dl_tbf;
|
||||
if (dl_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
|
||||
if (dl_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS
|
||||
|| dl_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ)
|
||||
*ul_ass_tbf = dl_tbf;
|
||||
}
|
||||
|
||||
|
@ -132,12 +135,19 @@ 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.
|
||||
*/
|
||||
|
||||
if (tbf == ul_ass_tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
|
||||
msg = ul_ass_tbf->create_ul_ass(fn, ts);
|
||||
if (tbf == ul_ass_tbf && tbf->ul_ass_state ==
|
||||
GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ)
|
||||
msg = ul_ass_tbf->create_packet_access_reject();
|
||||
else if (tbf == ul_ass_tbf && tbf->direction ==
|
||||
GPRS_RLCMAC_DL_TBF)
|
||||
if (tbf->ul_ass_state ==
|
||||
GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ)
|
||||
msg = ul_ass_tbf->create_packet_access_reject();
|
||||
else
|
||||
msg = ul_ass_tbf->create_ul_ass(fn, ts);
|
||||
else if (tbf == dl_ass_tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
|
||||
msg = dl_ass_tbf->create_dl_ass(fn, ts);
|
||||
else if (tbf == ul_ack_tbf)
|
||||
|
@ -174,6 +184,8 @@ static struct msgb *sched_select_ctrl_msg(
|
|||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
|
||||
"message at RTS for %s (TRX=%d, TS=%d)\n",
|
||||
tbf_name(tbf), trx, ts);
|
||||
/* Updates the dl ctrl msg counter for ms */
|
||||
tbf->ms()->update_dl_ctrl_msg();
|
||||
return msg;
|
||||
}
|
||||
/* schedule PACKET PAGING REQUEST */
|
||||
|
@ -181,6 +193,9 @@ static struct msgb *sched_select_ctrl_msg(
|
|||
if (msg) {
|
||||
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
|
||||
"message at RTS for (TRX=%d, TS=%d)\n", trx, ts);
|
||||
|
||||
/* Updates the dl ctrl msg counter for ms */
|
||||
tbf->ms()->update_dl_ctrl_msg();
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -289,7 +304,7 @@ static struct msgb *sched_dummy(void)
|
|||
}
|
||||
|
||||
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||
uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
uint8_t trx, uint8_t ts,
|
||||
uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
|
@ -300,8 +315,6 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
|||
struct msgb *msg = NULL;
|
||||
uint32_t poll_fn, sba_fn;
|
||||
|
||||
#warning "ARFCN... it is already in the TRX..... is it consistent with it?"
|
||||
|
||||
if (trx >= 8 || ts >= 8)
|
||||
return -EINVAL;
|
||||
pdch = &bts->trx[trx].pdch[ts];
|
||||
|
@ -339,18 +352,28 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
|||
/* Prio 1: select control message */
|
||||
msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
|
||||
dl_ass_tbf, ul_ack_tbf);
|
||||
if (msg)
|
||||
bts->bts->rlc_sent_control();
|
||||
|
||||
/* Prio 2: select data message for downlink */
|
||||
if (!msg)
|
||||
if (!msg) {
|
||||
msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
|
||||
if (msg)
|
||||
bts->bts->rlc_sent();
|
||||
}
|
||||
|
||||
/* Prio 3: send dummy contol message */
|
||||
if (!msg)
|
||||
if (!msg) {
|
||||
/* increase counter */
|
||||
msg = sched_dummy();
|
||||
if (msg)
|
||||
bts->bts->rlc_sent_dummy();
|
||||
}
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
/* msg is now available */
|
||||
bts->bts->rlc_dl_bytes(msg->data_len);
|
||||
|
||||
/* set USF */
|
||||
OSMO_ASSERT(msgb_length(msg) > 0);
|
||||
|
@ -360,7 +383,7 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
|||
gprs_bssgp_update_frames_sent();
|
||||
|
||||
/* send PDTCH/PACCH to L1 */
|
||||
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
|
||||
pcu_l1if_tx_pdtch(msg, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -383,8 +383,11 @@ static int tfi_find_free(BTS *bts, const GprsMs *ms,
|
|||
int tfi;
|
||||
uint8_t trx_no;
|
||||
|
||||
if (use_trx == -1 && ms->current_trx())
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, " USE trx = %d \n",use_trx);
|
||||
if (use_trx == -1 && ms->current_trx()) {
|
||||
use_trx = ms->current_trx()->trx_no;
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, " MS alive = %d \n",use_trx);
|
||||
}
|
||||
|
||||
tfi = bts->tfi_find_free(dir, &trx_no, use_trx);
|
||||
if (tfi < 0)
|
||||
|
@ -393,6 +396,8 @@ static int tfi_find_free(BTS *bts, const GprsMs *ms,
|
|||
if (trx_no_)
|
||||
*trx_no_ = trx_no;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, " TREE trx = %d TFI = %d\n", *trx_no_, tfi);
|
||||
|
||||
return tfi;
|
||||
}
|
||||
|
||||
|
@ -479,6 +484,7 @@ int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,
|
|||
}
|
||||
|
||||
tbf_->trx = trx;
|
||||
trx->current_load++;
|
||||
/* the only one TS is the common TS */
|
||||
tbf_->first_ts = tbf_->first_common_ts = ts;
|
||||
ms_->set_reserved_slots(trx, 1 << ts, 1 << ts);
|
||||
|
@ -1000,6 +1006,7 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
|
|||
if (!(dl_slots & (1 << ts)))
|
||||
continue;
|
||||
|
||||
trx->current_load++;
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS "
|
||||
"%d\n", ts);
|
||||
assign_dlink_tbf(&trx->pdch[ts], dl_tbf, tfi);
|
||||
|
@ -1012,6 +1019,7 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
|
|||
continue;
|
||||
|
||||
OSMO_ASSERT(usf[ts] >= 0);
|
||||
trx->current_load++;
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS "
|
||||
"%d\n", ts);
|
||||
|
|
|
@ -127,10 +127,10 @@ inline bool gprs_llc::fits_in_current_frame(uint8_t chunk_size) const
|
|||
|
||||
inline size_t gprs_llc_queue::size() const
|
||||
{
|
||||
return this ? m_queue_size : 0;
|
||||
return m_queue_size;
|
||||
}
|
||||
|
||||
inline size_t gprs_llc_queue::octets() const
|
||||
{
|
||||
return this ? m_queue_octets : 0;
|
||||
return m_queue_octets;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/* Interface handler for Nuran Wireless Litecell 1.5 L1 (real hardware) */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* femto_l1_hw.c
|
||||
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <nrw/litecell15/litecell15.h>
|
||||
#include <nrw/litecell15/gsml1prim.h>
|
||||
#include <nrw/litecell15/gsml1const.h>
|
||||
#include <nrw/litecell15/gsml1types.h>
|
||||
|
||||
#include "gprs_debug.h"
|
||||
#include "lc15bts.h"
|
||||
#include "lc15_l1_if.h"
|
||||
|
||||
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/litecell15_dsp2arm_trx"
|
||||
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/litecell15_arm2dsp_trx"
|
||||
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm_trx"
|
||||
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp_trx"
|
||||
|
||||
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm_trx"
|
||||
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp_trx"
|
||||
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm_trx"
|
||||
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp_trx"
|
||||
|
||||
static const char *rd_devnames[] = {
|
||||
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
|
||||
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
|
||||
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
|
||||
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
|
||||
};
|
||||
|
||||
static const char *wr_devnames[] = {
|
||||
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
|
||||
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
|
||||
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
|
||||
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
|
||||
};
|
||||
|
||||
/* callback when there's something to read from the l1 msg_queue */
|
||||
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
//struct msgb *msg = l1p_msgb_alloc();
|
||||
struct msgb *msg = msgb_alloc_headroom(sizeof(Litecell15_Prim_t) + 128,
|
||||
128, "1l_fd");
|
||||
struct lc15l1_hdl *fl1h = ofd->data;
|
||||
int rc;
|
||||
|
||||
msg->l1h = msg->data;
|
||||
rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
|
||||
if (rc < 0) {
|
||||
if (rc != -1)
|
||||
LOGP(DL1IF, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
|
||||
switch (ofd->priv_nr) {
|
||||
case MQ_SYS_WRITE:
|
||||
if (rc != sizeof(Litecell15_Prim_t))
|
||||
LOGP(DL1IF, LOGL_NOTICE, "%u != "
|
||||
"sizeof(Litecell15_Prim_t)\n", rc);
|
||||
return l1if_handle_sysprim(fl1h, msg);
|
||||
case MQ_L1_WRITE:
|
||||
case MQ_TCH_WRITE:
|
||||
case MQ_PDTCH_WRITE:
|
||||
if (rc != sizeof(GsmL1_Prim_t))
|
||||
LOGP(DL1IF, LOGL_NOTICE, "%u != "
|
||||
"sizeof(GsmL1_Prim_t)\n", rc);
|
||||
return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
|
||||
default:
|
||||
/* The compiler can't know that priv_nr is an enum. Assist. */
|
||||
LOGP(DL1IF, LOGL_FATAL, "writing on a wrong queue: %d\n",
|
||||
ofd->priv_nr);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/* callback when we can write to one of the l1 msg_queue devices */
|
||||
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = write(ofd->fd, msg->l1h, msgb_l1len(msg));
|
||||
if (rc < 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
|
||||
strerror(errno));
|
||||
return rc;
|
||||
} else if (rc < msg->len) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "short write to L1 msg_queue: "
|
||||
"%u < %u\n", rc, msg->len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
|
||||
{
|
||||
int rc;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
/* Step 1: Open all msg_queue file descriptors */
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_wqueue *wq = &hdl->write_q[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%d", rd_devnames[q], hdl->hw_info.trx_nr);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
rc = open(buf, O_RDONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||
buf, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
read_ofd->fd = rc;
|
||||
read_ofd->priv_nr = q;
|
||||
read_ofd->data = hdl;
|
||||
read_ofd->cb = l1if_fd_cb;
|
||||
read_ofd->when = BSC_FD_READ;
|
||||
rc = osmo_fd_register(read_ofd);
|
||||
if (rc < 0) {
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "%s%d", wr_devnames[q], hdl->hw_info.trx_nr);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
rc = open(buf, O_WRONLY);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
|
||||
buf, strerror(errno));
|
||||
goto out_read;
|
||||
}
|
||||
osmo_wqueue_init(wq, 10);
|
||||
wq->write_cb = l1fd_write_cb;
|
||||
write_ofd->fd = rc;
|
||||
write_ofd->priv_nr = q;
|
||||
write_ofd->data = hdl;
|
||||
write_ofd->when = BSC_FD_WRITE;
|
||||
rc = osmo_fd_register(write_ofd);
|
||||
if (rc < 0) {
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
goto out_read;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_read:
|
||||
close(hdl->read_ofd[q].fd);
|
||||
osmo_fd_unregister(&hdl->read_ofd[q]);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int l1if_transport_close(int q, struct lc15l1_hdl *hdl)
|
||||
{
|
||||
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
|
||||
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
|
||||
|
||||
osmo_fd_unregister(read_ofd);
|
||||
close(read_ofd->fd);
|
||||
read_ofd->fd = -1;
|
||||
|
||||
osmo_fd_unregister(write_ofd);
|
||||
close(write_ofd->fd);
|
||||
write_ofd->fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,377 @@
|
|||
/* 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>
|
||||
#include <bts.h>
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
uint32_t l1if_ts_to_hLayer2(uint8_t trx, uint8_t ts)
|
||||
{
|
||||
return (ts << 16) | (trx << 24);
|
||||
}
|
||||
|
||||
/* allocate a msgb containing a GsmL1_Prim_t */
|
||||
struct msgb *l1p_msgb_alloc(void)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(sizeof(GsmL1_Prim_t), "l1_prim");
|
||||
|
||||
if (msg)
|
||||
msg->l1h = msgb_put(msg, sizeof(GsmL1_Prim_t));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int l1if_req_pdch(struct lc15l1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
struct osmo_wqueue *wqueue = &fl1h->write_q[MQ_PDTCH_WRITE];
|
||||
|
||||
if (osmo_wqueue_enqueue(wqueue, msg) != 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct lc15l1_hdl *gl1)
|
||||
{
|
||||
prim->id = id;
|
||||
|
||||
switch (id) {
|
||||
case GsmL1_PrimId_MphInitReq:
|
||||
//prim->u.mphInitReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphCloseReq:
|
||||
prim->u.mphCloseReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphConnectReq:
|
||||
prim->u.mphConnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphDisconnectReq:
|
||||
prim->u.mphDisconnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphActivateReq:
|
||||
prim->u.mphActivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphDeactivateReq:
|
||||
prim->u.mphDeactivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphConfigReq:
|
||||
prim->u.mphConfigReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphMeasureReq:
|
||||
prim->u.mphMeasureReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_MphInitCnf:
|
||||
case GsmL1_PrimId_MphCloseCnf:
|
||||
case GsmL1_PrimId_MphConnectCnf:
|
||||
case GsmL1_PrimId_MphDisconnectCnf:
|
||||
case GsmL1_PrimId_MphActivateCnf:
|
||||
case GsmL1_PrimId_MphDeactivateCnf:
|
||||
case GsmL1_PrimId_MphConfigCnf:
|
||||
case GsmL1_PrimId_MphMeasureCnf:
|
||||
break;
|
||||
case GsmL1_PrimId_MphTimeInd:
|
||||
break;
|
||||
case GsmL1_PrimId_MphSyncInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhEmptyFrameReq:
|
||||
prim->u.phEmptyFrameReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_PhDataReq:
|
||||
prim->u.phDataReq.hLayer1 = (HANDLE)gl1->hLayer1;
|
||||
break;
|
||||
case GsmL1_PrimId_PhConnectInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhReadyToSendInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhDataInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhRaInd:
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_ERROR, "unknown L1 primitive %u\n", id);
|
||||
break;
|
||||
}
|
||||
return &prim->u;
|
||||
}
|
||||
|
||||
/* connect PDTCH */
|
||||
int l1if_connect_pdch(void *obj, uint8_t ts)
|
||||
{
|
||||
struct lc15l1_hdl *fl1h = obj;
|
||||
struct msgb *msg = l1p_msgb_alloc();
|
||||
GsmL1_MphConnectReq_t *cr;
|
||||
|
||||
cr = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConnectReq, fl1h);
|
||||
cr->u8Tn = ts;
|
||||
cr->logChComb = GsmL1_LogChComb_XIII;
|
||||
|
||||
return l1if_req_pdch(fl1h, msg);
|
||||
}
|
||||
|
||||
static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1h,
|
||||
GsmL1_PhReadyToSendInd_t *rts_ind)
|
||||
{
|
||||
struct gsm_time g_time;
|
||||
int rc = 0;
|
||||
|
||||
gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-RTS.ind %02u/%02u/%02u SAPI=%s\n",
|
||||
g_time.t1, g_time.t2, g_time.t3,
|
||||
get_value_string(lc15bts_l1sapi_names, rts_ind->sapi));
|
||||
|
||||
switch (rts_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
|
||||
rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void get_meas(struct pcu_l1_meas *meas, const GsmL1_MeasParam_t *l1_meas)
|
||||
{
|
||||
meas->rssi = (int8_t) (l1_meas->fRssi);
|
||||
meas->have_rssi = 1;
|
||||
meas->ber = (uint8_t) (l1_meas->fBer * 100);
|
||||
meas->have_ber = 1;
|
||||
meas->bto = (int16_t) (l1_meas->i16BurstTiming);
|
||||
meas->have_bto = 1;
|
||||
meas->link_qual = (int16_t) (l1_meas->fLinkQuality);
|
||||
meas->have_link_qual = 1;
|
||||
}
|
||||
|
||||
static int handle_ph_data_ind(struct lc15l1_hdl *fl1h,
|
||||
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct pcu_l1_meas meas = {0};
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s\n",
|
||||
get_value_string(lc15bts_l1sapi_names, data_ind->sapi),
|
||||
data_ind->hLayer2,
|
||||
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
|
||||
data_ind->msgUnitParam.u8Size));
|
||||
|
||||
/*
|
||||
* TODO: Add proper bad frame handling here. This could be used
|
||||
* to switch the used CS. Avoid a crash with the PCU right now
|
||||
* feed "0 - 1" amount of data.
|
||||
*/
|
||||
if (data_ind->msgUnitParam.u8Size == 0)
|
||||
return -1;
|
||||
|
||||
gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
|
||||
data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
|
||||
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
|
||||
data_ind->msgUnitParam.u8Size-1);
|
||||
|
||||
get_meas(&meas, &data_ind->measParam);
|
||||
bts_update_tbf_ta("PH-DATA", data_ind->u32Fn, fl1h->trx_no,
|
||||
data_ind->u8Tn, qta2ta(meas.bto));
|
||||
|
||||
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)
|
||||
{
|
||||
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
|
||||
return 0;
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-RA.ind");
|
||||
bts_update_tbf_ta("PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
|
||||
qta2ta(ra_ind->measParam.i16BurstTiming));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* handle any random indication from the L1 */
|
||||
int l1if_handle_l1prim(int wq, struct lc15l1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Rx L1 prim %s on queue %d\n",
|
||||
get_value_string(lc15bts_l1prim_names, l1p->id), wq);
|
||||
|
||||
switch (l1p->id) {
|
||||
#if 0
|
||||
case GsmL1_PrimId_MphTimeInd:
|
||||
rc = handle_mph_time_ind(fl1h, &l1p->u.mphTimeInd);
|
||||
break;
|
||||
case GsmL1_PrimId_MphSyncInd:
|
||||
break;
|
||||
case GsmL1_PrimId_PhConnectInd:
|
||||
break;
|
||||
#endif
|
||||
case GsmL1_PrimId_PhReadyToSendInd:
|
||||
rc = handle_ph_readytosend_ind(fl1h, &l1p->u.phReadyToSendInd);
|
||||
break;
|
||||
case GsmL1_PrimId_PhDataInd:
|
||||
rc = handle_ph_data_ind(fl1h, &l1p->u.phDataInd, msg);
|
||||
break;
|
||||
case GsmL1_PrimId_PhRaInd:
|
||||
rc = handle_ph_ra_ind(fl1h, &l1p->u.phRaInd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* send packet data request to L1 */
|
||||
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
|
||||
{
|
||||
struct lc15l1_hdl *fl1h = obj;
|
||||
struct msgb *msg;
|
||||
GsmL1_Prim_t *l1p;
|
||||
GsmL1_PhDataReq_t *data_req;
|
||||
GsmL1_MsgUnitParam_t *msu_param;
|
||||
struct gsm_time g_time;
|
||||
|
||||
gsm_fn2gsmtime(&g_time, fn);
|
||||
|
||||
DEBUGP(DL1IF, "TX packet data %02u/%02u/%02u is_ptcch=%d ts=%d "
|
||||
"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
|
||||
g_time.t3, is_ptcch, ts, block_nr, arfcn, len);
|
||||
|
||||
msg = l1p_msgb_alloc();
|
||||
l1p = msgb_l1prim(msg);
|
||||
l1p->id = GsmL1_PrimId_PhDataReq;
|
||||
data_req = &l1p->u.phDataReq;
|
||||
data_req->hLayer1 = (HANDLE)fl1h->hLayer1;
|
||||
data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
|
||||
data_req->subCh = GsmL1_SubCh_NA;
|
||||
data_req->u8BlockNbr = block_nr;
|
||||
data_req->u8Tn = ts;
|
||||
data_req->u32Fn = fn;
|
||||
msu_param = &data_req->msgUnitParam;
|
||||
msu_param->u8Size = len;
|
||||
memcpy(msu_param->u8Buffer, data, len);
|
||||
|
||||
gsmtap_send(fl1h->gsmtap, arfcn, data_req->u8Tn, GSMTAP_CHANNEL_PACCH,
|
||||
0, data_req->u32Fn, 0, 0,
|
||||
data_req->msgUnitParam.u8Buffer,
|
||||
data_req->msgUnitParam.u8Size);
|
||||
|
||||
|
||||
/* transmit */
|
||||
if (osmo_wqueue_enqueue(&fl1h->write_q[MQ_PDTCH_WRITE], msg) != 0) {
|
||||
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1)
|
||||
{
|
||||
struct lc15l1_hdl *fl1h;
|
||||
int rc;
|
||||
|
||||
fl1h = talloc_zero(tall_pcu_ctx, struct lc15l1_hdl);
|
||||
if (!fl1h)
|
||||
return NULL;
|
||||
|
||||
fl1h->hLayer1 = hlayer1;
|
||||
fl1h->trx_no = trx_no;
|
||||
/* hardware queues are numbered starting from 0 */
|
||||
fl1h->hw_info.trx_nr = trx_no;
|
||||
|
||||
DEBUGP(DL1IF, "PCU: Using TRX HW#%u\n", fl1h->hw_info.trx_nr);
|
||||
|
||||
rc = l1if_transport_open(MQ_PDTCH_WRITE, fl1h);
|
||||
if (rc < 0) {
|
||||
talloc_free(fl1h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
|
||||
if (fl1h->gsmtap)
|
||||
gsmtap_source_add_sink(fl1h->gsmtap);
|
||||
|
||||
return fl1h;
|
||||
}
|
||||
|
||||
int l1if_close_pdch(void *obj)
|
||||
{
|
||||
struct lc15l1_hdl *fl1h = obj;
|
||||
if (fl1h)
|
||||
l1if_transport_close(MQ_PDTCH_WRITE, fl1h);
|
||||
talloc_free(fl1h);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* femto_l1_if.h
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LC15_L1_IF_H
|
||||
#define _LC15_L1_IF_H
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include "lc15bts.h"
|
||||
|
||||
enum {
|
||||
MQ_SYS_READ,
|
||||
MQ_L1_READ,
|
||||
MQ_TCH_READ,
|
||||
MQ_PDTCH_READ,
|
||||
_NUM_MQ_READ
|
||||
};
|
||||
|
||||
enum {
|
||||
MQ_SYS_WRITE,
|
||||
MQ_L1_WRITE,
|
||||
MQ_TCH_WRITE,
|
||||
MQ_PDTCH_WRITE,
|
||||
_NUM_MQ_WRITE
|
||||
};
|
||||
|
||||
struct lc15l1_hdl {
|
||||
struct gsm_time gsm_time;
|
||||
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
|
||||
uint32_t dsp_trace_f;
|
||||
struct llist_head wlc_list;
|
||||
|
||||
struct gsmtap_inst *gsmtap;
|
||||
uint32_t gsmtap_sapi_mask;
|
||||
|
||||
uint8_t trx_no;
|
||||
|
||||
struct osmo_timer_list alive_timer;
|
||||
unsigned int alive_prim_cnt;
|
||||
|
||||
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
|
||||
struct osmo_wqueue write_q[_NUM_MQ_WRITE];
|
||||
|
||||
struct {
|
||||
int trx_nr; /* <1-2> */
|
||||
} hw_info;
|
||||
};
|
||||
|
||||
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
|
||||
#define msgb_sysprim(msg) ((Litecell15_Prim_t *)(msg)->l1h)
|
||||
|
||||
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data);
|
||||
|
||||
/* send a request primitive to the L1 and schedule completion call-back */
|
||||
int l1if_req_compl(struct lc15l1_hdl *fl1h, struct msgb *msg,
|
||||
int is_system_prim, l1if_compl_cb *cb, void *data);
|
||||
|
||||
int l1if_reset(struct lc15l1_hdl *hdl);
|
||||
int l1if_activate_rf(struct lc15l1_hdl *hdl, int on);
|
||||
int l1if_set_trace_flags(struct lc15l1_hdl *hdl, uint32_t flags);
|
||||
int l1if_set_txpower(struct lc15l1_hdl *fl1h, float tx_power);
|
||||
|
||||
struct msgb *l1p_msgb_alloc(void);
|
||||
struct msgb *sysp_msgb_alloc(void);
|
||||
|
||||
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan);
|
||||
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2);
|
||||
|
||||
int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg);
|
||||
int l1if_handle_l1prim(int wq, struct lc15l1_hdl *fl1h, struct msgb *msg);
|
||||
|
||||
/* tch.c */
|
||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
|
||||
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
|
||||
|
||||
/*
|
||||
* The implementation of these functions is selected by either compiling and
|
||||
* linking sysmo_l1_hw.c or sysmo_l1_fwd.c
|
||||
*/
|
||||
int l1if_transport_open(int q, struct lc15l1_hdl *hdl);
|
||||
int l1if_transport_close(int q, struct lc15l1_hdl *hdl);
|
||||
|
||||
#endif /* _SYSMO_L1_IF_H */
|
|
@ -0,0 +1,332 @@
|
|||
/* NuRAN Wireless Litecell 1.5 L1 API related definitions */
|
||||
|
||||
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
|
||||
* based on:
|
||||
* sysmobts.c
|
||||
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <nrw/litecell15/litecell15.h>
|
||||
#include <nrw/litecell15/gsml1const.h>
|
||||
#include <nrw/litecell15/gsml1dbg.h>
|
||||
|
||||
#include "lc15bts.h"
|
||||
|
||||
enum l1prim_type lc15bts_get_l1prim_type(GsmL1_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case GsmL1_PrimId_MphInitReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphCloseReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphConnectReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphDisconnectReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphActivateReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphDeactivateReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphConfigReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphMeasureReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphInitCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphCloseCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphConnectCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphDisconnectCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphActivateCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphDeactivateCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphConfigCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_MphMeasureCnf: return L1P_T_CONF;
|
||||
case GsmL1_PrimId_PhEmptyFrameReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_PhDataReq: return L1P_T_REQ;
|
||||
case GsmL1_PrimId_MphTimeInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_MphSyncInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhConnectInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhReadyToSendInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhDataInd: return L1P_T_IND;
|
||||
case GsmL1_PrimId_PhRaInd: return L1P_T_IND;
|
||||
default: return L1P_T_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1] = {
|
||||
{ GsmL1_PrimId_MphInitReq, "MPH-INIT.req" },
|
||||
{ GsmL1_PrimId_MphCloseReq, "MPH-CLOSE.req" },
|
||||
{ GsmL1_PrimId_MphConnectReq, "MPH-CONNECT.req" },
|
||||
{ GsmL1_PrimId_MphDisconnectReq,"MPH-DISCONNECT.req" },
|
||||
{ GsmL1_PrimId_MphActivateReq, "MPH-ACTIVATE.req" },
|
||||
{ GsmL1_PrimId_MphDeactivateReq,"MPH-DEACTIVATE.req" },
|
||||
{ GsmL1_PrimId_MphConfigReq, "MPH-CONFIG.req" },
|
||||
{ GsmL1_PrimId_MphMeasureReq, "MPH-MEASURE.req" },
|
||||
{ GsmL1_PrimId_MphInitCnf, "MPH-INIT.conf" },
|
||||
{ GsmL1_PrimId_MphCloseCnf, "MPH-CLOSE.conf" },
|
||||
{ GsmL1_PrimId_MphConnectCnf, "MPH-CONNECT.conf" },
|
||||
{ GsmL1_PrimId_MphDisconnectCnf,"MPH-DISCONNECT.conf" },
|
||||
{ GsmL1_PrimId_MphActivateCnf, "MPH-ACTIVATE.conf" },
|
||||
{ GsmL1_PrimId_MphDeactivateCnf,"MPH-DEACTIVATE.conf" },
|
||||
{ GsmL1_PrimId_MphConfigCnf, "MPH-CONFIG.conf" },
|
||||
{ GsmL1_PrimId_MphMeasureCnf, "MPH-MEASURE.conf" },
|
||||
{ GsmL1_PrimId_MphTimeInd, "MPH-TIME.ind" },
|
||||
{ GsmL1_PrimId_MphSyncInd, "MPH-SYNC.ind" },
|
||||
{ GsmL1_PrimId_PhEmptyFrameReq, "PH-EMPTY_FRAME.req" },
|
||||
{ GsmL1_PrimId_PhDataReq, "PH-DATA.req" },
|
||||
{ GsmL1_PrimId_PhConnectInd, "PH-CONNECT.ind" },
|
||||
{ GsmL1_PrimId_PhReadyToSendInd,"PH-READY_TO_SEND.ind" },
|
||||
{ GsmL1_PrimId_PhDataInd, "PH-DATA.ind" },
|
||||
{ GsmL1_PrimId_PhRaInd, "PH-RA.ind" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
GsmL1_PrimId_t lc15bts_get_l1prim_conf(GsmL1_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case GsmL1_PrimId_MphInitReq: return GsmL1_PrimId_MphInitCnf;
|
||||
case GsmL1_PrimId_MphCloseReq: return GsmL1_PrimId_MphCloseCnf;
|
||||
case GsmL1_PrimId_MphConnectReq: return GsmL1_PrimId_MphConnectCnf;
|
||||
case GsmL1_PrimId_MphDisconnectReq: return GsmL1_PrimId_MphDisconnectCnf;
|
||||
case GsmL1_PrimId_MphActivateReq: return GsmL1_PrimId_MphActivateCnf;
|
||||
case GsmL1_PrimId_MphDeactivateReq: return GsmL1_PrimId_MphDeactivateCnf;
|
||||
case GsmL1_PrimId_MphConfigReq: return GsmL1_PrimId_MphConfigCnf;
|
||||
case GsmL1_PrimId_MphMeasureReq: return GsmL1_PrimId_MphMeasureCnf;
|
||||
default: return -1; // Weak
|
||||
}
|
||||
}
|
||||
|
||||
enum l1prim_type lc15bts_get_sysprim_type(Litecell15_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case Litecell15_PrimId_SystemInfoReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_SystemInfoCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_SystemFailureInd: return L1P_T_IND;
|
||||
case Litecell15_PrimId_ActivateRfReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_ActivateRfCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_DeactivateRfReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_DeactivateRfCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_SetTraceFlagsReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_Layer1ResetReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_Layer1ResetCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_SetCalibTblReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_SetCalibTblCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_MuteRfReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_MuteRfCnf: return L1P_T_CONF;
|
||||
case Litecell15_PrimId_SetRxAttenReq: return L1P_T_REQ;
|
||||
case Litecell15_PrimId_SetRxAttenCnf: return L1P_T_CONF;
|
||||
default: return L1P_T_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1] = {
|
||||
{ Litecell15_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
|
||||
{ Litecell15_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
|
||||
{ Litecell15_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
|
||||
{ Litecell15_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
|
||||
{ Litecell15_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
|
||||
{ Litecell15_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
|
||||
{ Litecell15_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
|
||||
{ Litecell15_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
|
||||
{ Litecell15_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
|
||||
{ Litecell15_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
|
||||
{ Litecell15_PrimId_SetCalibTblReq, "SET-CALIB.req" },
|
||||
{ Litecell15_PrimId_SetCalibTblCnf, "SET-CALIB.cnf" },
|
||||
{ Litecell15_PrimId_MuteRfReq, "MUTE-RF.req" },
|
||||
{ Litecell15_PrimId_MuteRfCnf, "MUTE-RF.cnf" },
|
||||
{ Litecell15_PrimId_SetRxAttenReq, "SET-RX-ATTEN.req" },
|
||||
{ Litecell15_PrimId_SetRxAttenCnf, "SET-RX-ATTEN-CNF.cnf" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
Litecell15_PrimId_t lc15bts_get_sysprim_conf(Litecell15_PrimId_t id)
|
||||
{
|
||||
switch (id) {
|
||||
case Litecell15_PrimId_SystemInfoReq: return Litecell15_PrimId_SystemInfoCnf;
|
||||
case Litecell15_PrimId_ActivateRfReq: return Litecell15_PrimId_ActivateRfCnf;
|
||||
case Litecell15_PrimId_DeactivateRfReq: return Litecell15_PrimId_DeactivateRfCnf;
|
||||
case Litecell15_PrimId_Layer1ResetReq: return Litecell15_PrimId_Layer1ResetCnf;
|
||||
case Litecell15_PrimId_SetCalibTblReq: return Litecell15_PrimId_SetCalibTblCnf;
|
||||
case Litecell15_PrimId_MuteRfReq: return Litecell15_PrimId_MuteRfCnf;
|
||||
case Litecell15_PrimId_SetRxAttenReq: return Litecell15_PrimId_SetRxAttenCnf;
|
||||
default: return -1; // Weak
|
||||
}
|
||||
}
|
||||
|
||||
const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
|
||||
{ GsmL1_Sapi_Idle, "IDLE" },
|
||||
{ GsmL1_Sapi_Fcch, "FCCH" },
|
||||
{ GsmL1_Sapi_Sch, "SCH" },
|
||||
{ GsmL1_Sapi_Sacch, "SACCH" },
|
||||
{ GsmL1_Sapi_Sdcch, "SDCCH" },
|
||||
{ GsmL1_Sapi_Bcch, "BCCH" },
|
||||
{ GsmL1_Sapi_Pch, "PCH" },
|
||||
{ GsmL1_Sapi_Agch, "AGCH" },
|
||||
{ GsmL1_Sapi_Cbch, "CBCH" },
|
||||
{ GsmL1_Sapi_Rach, "RACH" },
|
||||
{ GsmL1_Sapi_TchF, "TCH/F" },
|
||||
{ GsmL1_Sapi_FacchF, "FACCH/F" },
|
||||
{ GsmL1_Sapi_TchH, "TCH/H" },
|
||||
{ GsmL1_Sapi_FacchH, "FACCH/H" },
|
||||
{ GsmL1_Sapi_Nch, "NCH" },
|
||||
{ GsmL1_Sapi_Pdtch, "PDTCH" },
|
||||
{ GsmL1_Sapi_Pacch, "PACCH" },
|
||||
{ GsmL1_Sapi_Pbcch, "PBCCH" },
|
||||
{ GsmL1_Sapi_Pagch, "PAGCH" },
|
||||
{ GsmL1_Sapi_Ppch, "PPCH" },
|
||||
{ GsmL1_Sapi_Pnch, "PNCH" },
|
||||
{ GsmL1_Sapi_Ptcch, "PTCCH" },
|
||||
{ GsmL1_Sapi_Prach, "PRACH" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1] = {
|
||||
{ GsmL1_Status_Success, "Success" },
|
||||
{ GsmL1_Status_Generic, "Generic error" },
|
||||
{ GsmL1_Status_NoMemory, "Not enough memory" },
|
||||
{ GsmL1_Status_Timeout, "Timeout" },
|
||||
{ GsmL1_Status_InvalidParam, "Invalid parameter" },
|
||||
{ GsmL1_Status_Busy, "Resource busy" },
|
||||
{ GsmL1_Status_NoRessource, "No more resources" },
|
||||
{ GsmL1_Status_Uninitialized, "Trying to use uninitialized resource" },
|
||||
{ GsmL1_Status_NullInterface, "Trying to call a NULL interface" },
|
||||
{ GsmL1_Status_NullFctnPtr, "Trying to call a NULL function ptr" },
|
||||
{ GsmL1_Status_BadCrc, "Bad CRC" },
|
||||
{ GsmL1_Status_BadUsf, "Bad USF" },
|
||||
{ GsmL1_Status_InvalidCPS, "Invalid CPS field" },
|
||||
{ GsmL1_Status_UnexpectedBurst, "Unexpected burst" },
|
||||
{ GsmL1_Status_UnavailCodec, "AMR codec is unavailable" },
|
||||
{ GsmL1_Status_CriticalError, "Critical error" },
|
||||
{ GsmL1_Status_OverheatError, "Overheat error" },
|
||||
{ GsmL1_Status_DeviceError, "Device error" },
|
||||
{ GsmL1_Status_FacchError, "FACCH / TCH order error" },
|
||||
{ GsmL1_Status_AlreadyDeactivated, "Lchan already deactivated" },
|
||||
{ GsmL1_Status_TxBurstFifoOvrn, "FIFO overrun" },
|
||||
{ GsmL1_Status_TxBurstFifoUndr, "FIFO underrun" },
|
||||
{ GsmL1_Status_NotSynchronized, "Not synchronized" },
|
||||
{ GsmL1_Status_Unsupported, "Unsupported feature" },
|
||||
{ GsmL1_Status_ClockError, "System clock error" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_tracef_names[29] = {
|
||||
{ DBG_DEBUG, "DEBUG" },
|
||||
{ DBG_L1WARNING, "L1_WARNING" },
|
||||
{ DBG_ERROR, "ERROR" },
|
||||
{ DBG_L1RXMSG, "L1_RX_MSG" },
|
||||
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE" },
|
||||
{ DBG_L1TXMSG, "L1_TX_MSG" },
|
||||
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE" },
|
||||
{ DBG_MPHCNF, "MPH_CNF" },
|
||||
{ DBG_MPHIND, "MPH_IND" },
|
||||
{ DBG_MPHREQ, "MPH_REQ" },
|
||||
{ DBG_PHIND, "PH_IND" },
|
||||
{ DBG_PHREQ, "PH_REQ" },
|
||||
{ DBG_PHYRF, "PHY_RF" },
|
||||
{ DBG_PHYRFMSGBYTE, "PHY_MSG_BYTE" },
|
||||
{ DBG_MODE, "MODE" },
|
||||
{ DBG_TDMAINFO, "TDMA_INFO" },
|
||||
{ DBG_BADCRC, "BAD_CRC" },
|
||||
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
|
||||
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
|
||||
{ DBG_DEVICEMSG, "DEVICE_MSG" },
|
||||
{ DBG_RACHINFO, "RACH_INFO" },
|
||||
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
|
||||
{ DBG_MEMORY, "MEMORY" },
|
||||
{ DBG_PROFILING, "PROFILING" },
|
||||
{ DBG_TESTCOMMENT, "TEST_COMMENT" },
|
||||
{ DBG_TEST, "TEST" },
|
||||
{ DBG_STATUS, "STATUS" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_tracef_docs[29] = {
|
||||
{ DBG_DEBUG, "Debug Region" },
|
||||
{ DBG_L1WARNING, "L1 Warning Region" },
|
||||
{ DBG_ERROR, "Error Region" },
|
||||
{ DBG_L1RXMSG, "L1_RX_MSG Region" },
|
||||
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE Region" },
|
||||
{ DBG_L1TXMSG, "L1_TX_MSG Region" },
|
||||
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE Region" },
|
||||
{ DBG_MPHCNF, "MphConfirmation Region" },
|
||||
{ DBG_MPHIND, "MphIndication Region" },
|
||||
{ DBG_MPHREQ, "MphRequest Region" },
|
||||
{ DBG_PHIND, "PhIndication Region" },
|
||||
{ DBG_PHREQ, "PhRequest Region" },
|
||||
{ DBG_PHYRF, "PhyRF Region" },
|
||||
{ DBG_PHYRFMSGBYTE, "PhyRF Message Region" },
|
||||
{ DBG_MODE, "Mode Region" },
|
||||
{ DBG_TDMAINFO, "TDMA Info Region" },
|
||||
{ DBG_BADCRC, "Bad CRC Region" },
|
||||
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
|
||||
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
|
||||
{ DBG_DEVICEMSG, "Device Message Region" },
|
||||
{ DBG_RACHINFO, "RACH Info" },
|
||||
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
|
||||
{ DBG_MEMORY, "Memory Region" },
|
||||
{ DBG_PROFILING, "Profiling Region" },
|
||||
{ DBG_TESTCOMMENT, "Test Comments" },
|
||||
{ DBG_TEST, "Test Region" },
|
||||
{ DBG_STATUS, "Status Region" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_tch_pl_names[] = {
|
||||
{ GsmL1_TchPlType_NA, "N/A" },
|
||||
{ GsmL1_TchPlType_Fr, "FR" },
|
||||
{ GsmL1_TchPlType_Hr, "HR" },
|
||||
{ GsmL1_TchPlType_Efr, "EFR" },
|
||||
{ GsmL1_TchPlType_Amr, "AMR(IF2)" },
|
||||
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
|
||||
{ GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" },
|
||||
{ GsmL1_TchPlType_Amr_Ratscch, "AMR(RATSCCH)" },
|
||||
{ GsmL1_TchPlType_Amr_SidUpdateInH, "AMR(SID_UPDATE INH)" },
|
||||
{ GsmL1_TchPlType_Amr_SidFirstP1, "AMR(SID_FIRST P1)" },
|
||||
{ GsmL1_TchPlType_Amr_SidFirstP2, "AMR(SID_FIRST P2)" },
|
||||
{ GsmL1_TchPlType_Amr_SidFirstInH, "AMR(SID_FIRST INH)" },
|
||||
{ GsmL1_TchPlType_Amr_RatscchMarker, "AMR(RATSCCH MARK)" },
|
||||
{ GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_dir_names[] = {
|
||||
{ GsmL1_Dir_TxDownlink, "TxDL" },
|
||||
{ GsmL1_Dir_TxUplink, "TxUL" },
|
||||
{ GsmL1_Dir_RxUplink, "RxUL" },
|
||||
{ GsmL1_Dir_RxDownlink, "RxDL" },
|
||||
{ GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink, "BOTH" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string lc15bts_chcomb_names[] = {
|
||||
{ GsmL1_LogChComb_0, "dummy" },
|
||||
{ GsmL1_LogChComb_I, "tch_f" },
|
||||
{ GsmL1_LogChComb_II, "tch_h" },
|
||||
{ GsmL1_LogChComb_IV, "ccch" },
|
||||
{ GsmL1_LogChComb_V, "ccch_sdcch4" },
|
||||
{ GsmL1_LogChComb_VII, "sdcch8" },
|
||||
{ GsmL1_LogChComb_XIII, "pdtch" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const uint8_t pdch_msu_size[_NUM_PDCH_CS] = {
|
||||
[PDCH_CS_1] = 23,
|
||||
[PDCH_CS_2] = 34,
|
||||
[PDCH_CS_3] = 40,
|
||||
[PDCH_CS_4] = 54,
|
||||
[PDCH_MCS_1] = 27,
|
||||
[PDCH_MCS_2] = 33,
|
||||
[PDCH_MCS_3] = 42,
|
||||
[PDCH_MCS_4] = 49,
|
||||
[PDCH_MCS_5] = 60,
|
||||
[PDCH_MCS_6] = 78,
|
||||
[PDCH_MCS_7] = 118,
|
||||
[PDCH_MCS_8] = 142,
|
||||
[PDCH_MCS_9] = 154
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
#ifndef LC15BTS_H
|
||||
#define LC15BTS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <nrw/litecell15/litecell15.h>
|
||||
#include <nrw/litecell15/gsml1const.h>
|
||||
|
||||
/*
|
||||
* Depending on the firmware version either GsmL1_Prim_t or Litecell15_Prim_t
|
||||
* is the bigger struct. For earlier firmware versions the GsmL1_Prim_t was the
|
||||
* bigger struct.
|
||||
*/
|
||||
#define LC15BTS_PRIM_SIZE \
|
||||
(OSMO_MAX(sizeof(Litecell15_Prim_t), sizeof(GsmL1_Prim_t)) + 128)
|
||||
|
||||
enum l1prim_type {
|
||||
L1P_T_INVALID, /* this must be 0 to detect uninitialized elements */
|
||||
L1P_T_REQ,
|
||||
L1P_T_CONF,
|
||||
L1P_T_IND,
|
||||
};
|
||||
|
||||
enum l1prim_type lc15bts_get_l1prim_type(GsmL1_PrimId_t id);
|
||||
const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1];
|
||||
GsmL1_PrimId_t lc15bts_get_l1prim_conf(GsmL1_PrimId_t id);
|
||||
|
||||
enum l1prim_type lc15bts_get_sysprim_type(Litecell15_PrimId_t id);
|
||||
const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1];
|
||||
Litecell15_PrimId_t lc15bts_get_sysprim_conf(Litecell15_PrimId_t id);
|
||||
|
||||
const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1];
|
||||
const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1];
|
||||
|
||||
const struct value_string lc15bts_tracef_names[29];
|
||||
const struct value_string lc15bts_tracef_docs[29];
|
||||
|
||||
const struct value_string lc15bts_tch_pl_names[15];
|
||||
|
||||
const struct value_string lc15bts_clksrc_names[10];
|
||||
|
||||
const struct value_string lc15bts_dir_names[6];
|
||||
|
||||
enum pdch_cs {
|
||||
PDCH_CS_1,
|
||||
PDCH_CS_2,
|
||||
PDCH_CS_3,
|
||||
PDCH_CS_4,
|
||||
PDCH_MCS_1,
|
||||
PDCH_MCS_2,
|
||||
PDCH_MCS_3,
|
||||
PDCH_MCS_4,
|
||||
PDCH_MCS_5,
|
||||
PDCH_MCS_6,
|
||||
PDCH_MCS_7,
|
||||
PDCH_MCS_8,
|
||||
PDCH_MCS_9,
|
||||
_NUM_PDCH_CS
|
||||
};
|
||||
|
||||
const uint8_t pdch_msu_size[_NUM_PDCH_CS];
|
||||
|
||||
#endif /* LC15BTS_H */
|
|
@ -13,6 +13,7 @@
|
|||
#include <sysmo_l1_if.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <pcu_l1_if.h>
|
||||
#include <bts.h>
|
||||
|
||||
extern void *tall_pcu_ctx;
|
||||
|
||||
|
@ -109,23 +110,6 @@ static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct femtol1_hdl
|
|||
return &prim->u;
|
||||
}
|
||||
|
||||
struct sapi_dir {
|
||||
GsmL1_Sapi_t sapi;
|
||||
GsmL1_Dir_t dir;
|
||||
};
|
||||
|
||||
static const struct sapi_dir pdtch_sapis[] = {
|
||||
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink },
|
||||
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink },
|
||||
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_TxDownlink },
|
||||
{ GsmL1_Sapi_Prach, GsmL1_Dir_RxUplink },
|
||||
#if 0
|
||||
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_RxUplink },
|
||||
{ GsmL1_Sapi_Pacch, GsmL1_Dir_TxDownlink },
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/* connect PDTCH */
|
||||
int l1if_connect_pdch(void *obj, uint8_t ts)
|
||||
{
|
||||
|
@ -155,8 +139,8 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
|
|||
switch (rts_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
rc = pcu_rx_rts_req_pdtch((long)fl1h->priv, rts_ind->u8Tn,
|
||||
rts_ind->u16Arfcn, rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
|
||||
rts_ind->u32Fn, rts_ind->u8BlockNbr);
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
default:
|
||||
|
@ -206,6 +190,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
|
|||
data_ind->msgUnitParam.u8Size-1);
|
||||
|
||||
get_meas(&meas, &data_ind->measParam);
|
||||
bts_update_tbf_ta("PH-DATA", data_ind->u32Fn, fl1h->trx_no,
|
||||
data_ind->u8Tn, qta2ta(meas.bto));
|
||||
|
||||
switch (data_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
|
@ -215,7 +201,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,
|
||||
|
@ -237,33 +223,14 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
|
|||
|
||||
static int handle_ph_ra_ind(struct femtol1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
|
||||
{
|
||||
uint8_t acc_delay;
|
||||
|
||||
pcu_rx_ra_time(ra_ind->u16Arfcn, ra_ind->u32Fn, ra_ind->u8Tn);
|
||||
|
||||
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
|
||||
bts_update_tbf_ta("PH-RA", ra_ind->u32Fn, fl1h->trx_no, ra_ind->u8Tn,
|
||||
qta2ta(ra_ind->measParam.i16BurstTiming));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -357,7 +324,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 +334,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
|
|||
return NULL;
|
||||
|
||||
fl1h->hLayer1 = hlayer1;
|
||||
fl1h->priv = priv;
|
||||
fl1h->trx_no = trx_no;
|
||||
fl1h->clk_cal = 0;
|
||||
/* default clock source: OCXO */
|
||||
fl1h->clk_src = SuperFemto_ClkSrcId_Ocxo;
|
|
@ -38,7 +38,7 @@ struct femtol1_hdl {
|
|||
struct gsmtap_inst *gsmtap;
|
||||
uint32_t gsmtap_sapi_mask;
|
||||
|
||||
void *priv; /* user reference */
|
||||
uint8_t trx_no;
|
||||
|
||||
struct osmo_timer_list alive_timer;
|
||||
unsigned int alive_prim_cnt;
|
|
@ -35,7 +35,7 @@ extern "C" {
|
|||
#include <pcu_l1_if.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <pcuif_proto.h>
|
||||
#include <osmocom/pcu/pcuif_proto.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -38,13 +38,14 @@ extern "C" {
|
|||
#include <pcu_l1_if.h>
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <pcuif_proto.h>
|
||||
#include <osmocom/pcu/pcuif_proto.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
|
||||
// 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,8 @@ 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
|
||||
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
|
||||
#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);
|
||||
|
@ -136,7 +138,6 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
|
|||
return;
|
||||
}
|
||||
#endif
|
||||
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
|
||||
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
|
||||
msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
|
@ -147,7 +148,8 @@ 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
|
||||
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
|
||||
#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);
|
||||
|
@ -155,7 +157,6 @@ void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
|
|||
return;
|
||||
}
|
||||
#endif
|
||||
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PACCH, 0, fn, 0, 0, msg->data, msg->len);
|
||||
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
|
||||
msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
|
@ -213,7 +214,15 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind, struct gsmtap_inst
|
|||
int rc;
|
||||
pcu_l1_meas meas;
|
||||
meas.set_rssi(data_ind->rssi);
|
||||
|
||||
#ifndef ENABLE_DIRECT_PHY
|
||||
/* convert BER to % value */
|
||||
meas.set_ber(data_ind->ber10k / 100);
|
||||
meas.set_bto(data_ind->ta_offs_qbits);
|
||||
meas.set_link_qual(data_ind->lqual_cb / 10);
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Data indication with raw measurements "
|
||||
"received: BER10k = %d, BTO = %d, Q = %d\n", data_ind->ber10k,
|
||||
data_ind->ta_offs_qbits, data_ind->lqual_cb);
|
||||
#endif
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
|
||||
"block=%d data=%s\n", data_ind->sapi,
|
||||
data_ind->arfcn, data_ind->block_nr,
|
||||
|
@ -263,11 +272,11 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
|
|||
}
|
||||
|
||||
// FIXME: remove this, when changed from c++ to c.
|
||||
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
|
||||
uint32_t fn, uint8_t block_nr)
|
||||
{
|
||||
return gprs_rlcmac_rcv_rts_block(bts_main_data(),
|
||||
trx, ts, arfcn, fn, block_nr);
|
||||
trx, ts, fn, block_nr);
|
||||
}
|
||||
|
||||
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
|
||||
|
@ -281,7 +290,7 @@ static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
|
|||
switch (rts_req->sapi) {
|
||||
case PCU_IF_SAPI_PDTCH:
|
||||
pcu_rx_rts_req_pdtch(rts_req->trx_nr, rts_req->ts_nr,
|
||||
rts_req->arfcn, rts_req->fn, rts_req->block_nr);
|
||||
rts_req->fn, rts_req->block_nr);
|
||||
break;
|
||||
case PCU_IF_SAPI_PTCCH:
|
||||
/* FIXME */
|
||||
|
@ -313,7 +322,8 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
|
|||
case PCU_IF_SAPI_RACH:
|
||||
rc = BTS::main_bts()->rcv_rach(
|
||||
rach_ind->ra, rach_ind->fn,
|
||||
rach_ind->qta);
|
||||
rach_ind->qta, rach_ind->is_11bit,
|
||||
(ph_burst_type)rach_ind->burst_type);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
|
||||
|
@ -331,7 +341,7 @@ static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
|
|||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct in_addr ia;
|
||||
int rc = 0;
|
||||
int trx, ts;
|
||||
unsigned int trx, ts;
|
||||
int i;
|
||||
|
||||
if (info_ind->version != PCU_IF_VERSION) {
|
||||
|
@ -347,9 +357,9 @@ static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
|
|||
LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
|
||||
bssgp_failed:
|
||||
/* free all TBF */
|
||||
for (trx = 0; trx < 8; trx++) {
|
||||
for (trx = 0; trx < ARRAY_SIZE(bts->trx); trx++) {
|
||||
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
|
||||
for (ts = 0; ts < 8; ts++)
|
||||
for (ts = 0; ts < ARRAY_SIZE(bts->trx[0].pdch); ts++)
|
||||
bts->trx[trx].pdch[ts].free_resources();
|
||||
}
|
||||
gprs_bssgp_destroy();
|
||||
|
@ -441,16 +451,16 @@ bssgp_failed:
|
|||
bts->initial_cs_ul = bts->initial_cs_dl;
|
||||
}
|
||||
|
||||
for (trx = 0; trx < 8; trx++) {
|
||||
for (trx = 0; trx < ARRAY_SIZE(bts->trx); trx++) {
|
||||
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) {
|
||||
|
@ -466,12 +476,12 @@ bssgp_failed:
|
|||
#endif
|
||||
}
|
||||
|
||||
for (ts = 0; ts < 8; ts++) {
|
||||
for (ts = 0; ts < ARRAY_SIZE(bts->trx[0].pdch); ts++) {
|
||||
pdch = &bts->trx[trx].pdch[ts];
|
||||
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(
|
||||
|
|
|
@ -33,6 +33,15 @@ extern "C" {
|
|||
}
|
||||
#endif
|
||||
|
||||
static inline uint8_t qta2ta(int16_t qta)
|
||||
{
|
||||
if (qta < 0)
|
||||
return 0;
|
||||
if (qta > 252)
|
||||
qta = 252;
|
||||
return qta >> 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* L1 Measurement values
|
||||
*/
|
||||
|
@ -48,7 +57,8 @@ struct pcu_l1_meas_ts {
|
|||
}
|
||||
|
||||
pcu_l1_meas_ts() :
|
||||
have_ms_i_level(0)
|
||||
have_ms_i_level(0),
|
||||
ms_i_level(0)
|
||||
{}
|
||||
#endif
|
||||
};
|
||||
|
@ -100,7 +110,14 @@ struct pcu_l1_meas {
|
|||
have_ms_rx_qual(0),
|
||||
have_ms_c_value(0),
|
||||
have_ms_sign_var(0),
|
||||
have_ms_i_level(0)
|
||||
have_ms_i_level(0),
|
||||
rssi(0),
|
||||
ber(0),
|
||||
bto(0),
|
||||
link_qual(0),
|
||||
ms_rx_qual(0),
|
||||
ms_c_value(0),
|
||||
ms_sign_var(0)
|
||||
{}
|
||||
#endif
|
||||
};
|
||||
|
@ -124,7 +141,7 @@ int pcu_sock_send(struct msgb *msg);
|
|||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
|
||||
uint32_t fn, uint8_t block_nr);
|
||||
|
||||
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
|
||||
|
|
|
@ -32,6 +32,7 @@ extern "C" {
|
|||
#include "pcu_vty.h"
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
|
@ -46,7 +47,7 @@ void *tall_pcu_ctx;
|
|||
extern void *bv_tall_ctx;
|
||||
static int quit = 0;
|
||||
static int rt_prio = -1;
|
||||
static char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
|
||||
static const char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
|
@ -189,8 +190,8 @@ int main(int argc, char *argv[])
|
|||
bts->cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
|
||||
bts->max_cs_ul = 4;
|
||||
bts->max_cs_dl = 4;
|
||||
bts->max_mcs_ul = 4;
|
||||
bts->max_mcs_dl = 4;
|
||||
bts->max_mcs_ul = MAX_GPRS_CS;
|
||||
bts->max_mcs_dl = MAX_GPRS_CS;
|
||||
/* CS-1 to CS-4 */
|
||||
bts->cs_lqual_ranges[0].low = -256;
|
||||
bts->cs_lqual_ranges[0].high = 6;
|
||||
|
@ -210,10 +211,17 @@ int main(int argc, char *argv[])
|
|||
bts->dl_tbf_idle_msec = 2000;
|
||||
bts->llc_idle_ack_csec = 10;
|
||||
|
||||
/*
|
||||
* By default resegmentation is supported in DL
|
||||
* can also be configured through VTY
|
||||
*/
|
||||
bts->dl_arq_type = EGPRS_ARQ1;
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
osmo_stats_init(tall_pcu_ctx);
|
||||
rate_ctr_init(tall_pcu_ctx);
|
||||
gprs_ns_set_log_ss(DNS);
|
||||
bssgp_set_log_ss(DBSSGP);
|
||||
|
||||
|
@ -244,7 +252,8 @@ int main(int argc, char *argv[])
|
|||
fprintf(stderr, "No config file: '%s' Using default config.\n",
|
||||
config_file);
|
||||
|
||||
rc = telnet_init(tall_pcu_ctx, NULL, 4240);
|
||||
rc = telnet_init_dynif(tall_pcu_ctx, NULL, vty_get_bind_addr(),
|
||||
OSMO_VTY_PORT_PCU);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing telnet\n");
|
||||
exit(1);
|
||||
|
|
|
@ -129,6 +129,10 @@ static int config_write_pcu(struct vty *vty)
|
|||
vty_out(vty, " window-size %d %d%s", bts->ws_base, bts->ws_pdch,
|
||||
VTY_NEWLINE);
|
||||
|
||||
if (bts->dl_arq_type)
|
||||
vty_out(vty, " egprs dl arq-type arq2%s",
|
||||
VTY_NEWLINE);
|
||||
|
||||
if (bts->force_llc_lifetime == 0xffff)
|
||||
vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
|
||||
else if (bts->force_llc_lifetime)
|
||||
|
@ -187,12 +191,6 @@ DEFUN(cfg_pcu_egprs,
|
|||
|
||||
bts->egprs_enabled = 1;
|
||||
|
||||
vty_out(vty, "%%Note that EGPRS support is in an experimental state "
|
||||
"and the PCU will currently fail to use a TBF if the MS is capable "
|
||||
"to do EGPRS. You may want to disable this feature by entering "
|
||||
"the \"no egprs\" command. "
|
||||
"Do not use this in production!%s", VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -480,6 +478,25 @@ DEFUN(cfg_pcu_no_mcs_max,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DL_STR "downlink specific configuration\n"
|
||||
|
||||
DEFUN(cfg_pcu_dl_arq_type,
|
||||
cfg_pcu_dl_arq_cmd,
|
||||
"egprs dl arq-type (spb|arq2)",
|
||||
EGPRS_STR DL_STR "ARQ options\n"
|
||||
"enable SPB(ARQ1) support\n"
|
||||
"enable ARQ2 support")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
if (!strcmp(argv[0], "arq2"))
|
||||
bts->dl_arq_type = 1;
|
||||
else
|
||||
bts->dl_arq_type = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_window_size,
|
||||
cfg_pcu_window_size_cmd,
|
||||
"window-size <0-1024> [<0-256>]",
|
||||
|
@ -954,6 +971,7 @@ int pcu_vty_init(const struct log_info *cat)
|
|||
install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_mcs_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_dl_arq_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_mcs_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_mcs_max_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_mcs_max_cmd);
|
||||
|
|
|
@ -61,6 +61,8 @@ static void tbf_print_vty_info(struct vty *vty, gprs_rlcmac_tbf *tbf)
|
|||
if (tbf->pdch[i])
|
||||
vty_out(vty, "%d%s ", i, is_ctrl ? "!" : "");
|
||||
}
|
||||
if (tbf->trx != NULL)
|
||||
vty_out(vty, " TRX_ID=%d", tbf->trx->trx_no);
|
||||
vty_out(vty, " CS=%s WS=%d",
|
||||
tbf->current_cs().name(), tbf->window()->ws());
|
||||
|
||||
|
@ -68,12 +70,26 @@ static void tbf_print_vty_info(struct vty *vty, gprs_rlcmac_tbf *tbf)
|
|||
gprs_rlc_ul_window *win = &ul_tbf->m_window;
|
||||
vty_out(vty, " V(Q)=%d V(R)=%d",
|
||||
win->v_q(), win->v_r());
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
vty_out(vty, " TBF Statistics:%s", VTY_NEWLINE);
|
||||
if(GprsCodingScheme::GPRS == tbf->ms()->mode()) {
|
||||
vty_out_rate_ctr_group(vty, " ", ul_tbf->m_ul_gprs_ctrs);
|
||||
} else {
|
||||
vty_out_rate_ctr_group(vty, " ", ul_tbf->m_ul_egprs_ctrs);
|
||||
}
|
||||
}
|
||||
if (dl_tbf) {
|
||||
gprs_rlc_dl_window *win = &dl_tbf->m_window;
|
||||
vty_out(vty, " V(A)=%d V(S)=%d nBSN=%d%s",
|
||||
win->v_a(), win->v_s(), win->resend_needed(),
|
||||
win->window_stalled() ? " STALLED" : "");
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
vty_out_rate_ctr_group(vty, " ", tbf->m_ctrs);
|
||||
if(GprsCodingScheme::GPRS == tbf->ms()->mode()) {
|
||||
vty_out_rate_ctr_group(vty, " ", dl_tbf->m_dl_gprs_ctrs);
|
||||
} else {
|
||||
vty_out_rate_ctr_group(vty, " ", dl_tbf->m_dl_egprs_ctrs);
|
||||
}
|
||||
}
|
||||
vty_out(vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
|
||||
}
|
||||
|
@ -102,7 +118,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 +152,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",
|
||||
|
@ -169,16 +185,22 @@ static int show_ms(struct vty *vty, GprsMs *ms)
|
|||
vty_out(vty, " MS I level (slot %d): %d dB%s",
|
||||
i, ms->l1_meas()->ts[i].ms_i_level, VTY_NEWLINE);
|
||||
}
|
||||
vty_out(vty, " RLC/MAC DL Control Msg: %d%s", ms->dl_ctrl_msg(),
|
||||
VTY_NEWLINE);
|
||||
if (ms->ul_tbf())
|
||||
vty_out(vty, " Uplink TBF: TFI=%d, state=%s%s",
|
||||
ms->ul_tbf()->tfi(),
|
||||
ms->ul_tbf()->state_name(),
|
||||
VTY_NEWLINE);
|
||||
if (ms->dl_tbf())
|
||||
if (ms->dl_tbf()) {
|
||||
vty_out(vty, " Downlink TBF: TFI=%d, state=%s%s",
|
||||
ms->dl_tbf()->tfi(),
|
||||
ms->dl_tbf()->state_name(),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Current DL Throughput: %d Kbps %s",
|
||||
ms->dl_tbf()->m_bw.dl_throughput,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
llist_for_each(i_tbf, &ms->old_tbfs())
|
||||
vty_out(vty, " Old %-19s TFI=%d, state=%s%s",
|
||||
|
|
173
src/rlc.cpp
173
src/rlc.cpp
|
@ -33,6 +33,9 @@ uint8_t *gprs_rlc_data::prepare(size_t block_data_len)
|
|||
memset(block, 0x0, sizeof(block));
|
||||
memset(block, 0x2b, block_data_len);
|
||||
|
||||
/* Initial value of puncturing scheme */
|
||||
next_ps = EGPRS_PS_1;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
|
@ -80,6 +83,32 @@ int gprs_rlc_dl_window::mark_for_resend()
|
|||
return resend;
|
||||
}
|
||||
|
||||
/* Update the receive block bitmap */
|
||||
uint16_t gprs_rlc_ul_window::update_egprs_rbb(uint8_t *rbb)
|
||||
{
|
||||
int i;
|
||||
uint16_t bsn;
|
||||
uint16_t bitmask = 0x80;
|
||||
int8_t pos = 0;
|
||||
int8_t bit_pos = 0;
|
||||
for (i = 0, bsn = (v_q()+1); ((bsn < (v_r())) && (i < ws())); i++,
|
||||
bsn = this->mod_sns(bsn + 1)) {
|
||||
if (m_v_n.is_received(bsn)) {
|
||||
rbb[pos] = rbb[pos] | bitmask;
|
||||
} else {
|
||||
rbb[pos] = rbb[pos] & (~bitmask);
|
||||
}
|
||||
bitmask = bitmask >> 1;
|
||||
bit_pos++;
|
||||
bit_pos = bit_pos % 8;
|
||||
if (bit_pos == 0) {
|
||||
pos++;
|
||||
bitmask = 0x80;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int gprs_rlc_dl_window::count_unacked()
|
||||
{
|
||||
uint16_t unacked = 0;
|
||||
|
@ -102,7 +131,9 @@ void gprs_rlc_dl_window::update(BTS *bts, const struct bitvec *rbb,
|
|||
uint16_t first_bsn, uint16_t *lost,
|
||||
uint16_t *received)
|
||||
{
|
||||
unsigned num_blocks = rbb->cur_bit;
|
||||
unsigned dist = distance();
|
||||
unsigned num_blocks = rbb->cur_bit > dist
|
||||
? dist : rbb->cur_bit;
|
||||
unsigned bsn;
|
||||
|
||||
/* first_bsn is in range V(A)..V(S) */
|
||||
|
@ -214,6 +245,8 @@ void gprs_rlc_window::set_sns(uint16_t sns)
|
|||
|
||||
void gprs_rlc_window::set_ws(uint16_t ws)
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "ws(%d)\n",
|
||||
ws);
|
||||
OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
|
||||
OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
|
||||
m_ws = ws;
|
||||
|
@ -224,7 +257,7 @@ void gprs_rlc_ul_window::update_rbb(char *rbb)
|
|||
{
|
||||
int i;
|
||||
for (i=0; i < ws(); i++) {
|
||||
if (m_v_n.is_received(ssn()-1-i))
|
||||
if (m_v_n.is_received((ssn()-1-i) & mod_sns()))
|
||||
rbb[ws()-1-i] = 'R';
|
||||
else
|
||||
rbb[ws()-1-i] = 'I';
|
||||
|
@ -282,7 +315,8 @@ bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
|
|||
}
|
||||
|
||||
static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding, unsigned int header_bits)
|
||||
GprsCodingScheme cs, bool with_padding, unsigned int header_bits,
|
||||
const unsigned int spb)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int padding_bits = with_padding ? cs.optionalPaddingBits() : 0;
|
||||
|
@ -297,7 +331,7 @@ static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
|
|||
|
||||
for (i = 0; i < rlc->num_data_blocks; i++) {
|
||||
gprs_rlc_data_block_info_init(&rlc->block_info[i], cs,
|
||||
with_padding);
|
||||
with_padding, spb);
|
||||
|
||||
rlc->data_offs_bits[i] =
|
||||
header_bits + padding_bits +
|
||||
|
@ -307,21 +341,25 @@ static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
|
|||
}
|
||||
|
||||
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding)
|
||||
GprsCodingScheme cs, bool with_padding, const unsigned int spb)
|
||||
{
|
||||
return gprs_rlc_data_header_init(rlc, cs, with_padding,
|
||||
cs.numDataHeaderBitsDL());
|
||||
cs.numDataHeaderBitsDL(), spb);
|
||||
}
|
||||
|
||||
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding)
|
||||
{
|
||||
/*
|
||||
* last parameter is sent as 0 since common function used
|
||||
* for both DL and UL
|
||||
*/
|
||||
return gprs_rlc_data_header_init(rlc, cs, with_padding,
|
||||
cs.numDataHeaderBitsUL());
|
||||
cs.numDataHeaderBitsUL(), 0);
|
||||
}
|
||||
|
||||
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
|
||||
GprsCodingScheme cs, bool with_padding)
|
||||
GprsCodingScheme cs, bool with_padding, const unsigned int spb)
|
||||
{
|
||||
unsigned int data_len = cs.maxDataBlockBytes();
|
||||
if (with_padding)
|
||||
|
@ -333,24 +371,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 +434,87 @@ void gprs_rlc_mcs_cps_decode(unsigned int cps,
|
|||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the PS value for retransmission with MCS change,
|
||||
* retransmission with no MCS change, fresh transmission cases.
|
||||
* The return value shall be used for current transmission only
|
||||
* 44.060 9.3.2.1 defines the PS selection for MCS change case
|
||||
* cs_current is the output of MCS selection algorithm for retx
|
||||
* cs is coding scheme of previous transmission of RLC data block
|
||||
*/
|
||||
enum egprs_puncturing_values gprs_get_punct_scheme(
|
||||
enum egprs_puncturing_values punct,
|
||||
const GprsCodingScheme &cs,
|
||||
const GprsCodingScheme &cs_current,
|
||||
const enum egprs_rlcmac_dl_spb spb)
|
||||
{
|
||||
|
||||
/*
|
||||
* 10.4.8b of TS 44.060
|
||||
* If it is second segment of the block
|
||||
* dont change the puncturing scheme
|
||||
*/
|
||||
if (spb == EGPRS_RLCMAC_DL_SEC_SEG)
|
||||
return punct;
|
||||
|
||||
/* TS 44.060 9.3.2.1.1 */
|
||||
if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS9) &&
|
||||
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS6)) {
|
||||
if ((punct == EGPRS_PS_1) || (punct == EGPRS_PS_3))
|
||||
return EGPRS_PS_1;
|
||||
else if (punct == EGPRS_PS_2)
|
||||
return EGPRS_PS_2;
|
||||
} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6) &&
|
||||
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS9)) {
|
||||
if (punct == EGPRS_PS_1)
|
||||
return EGPRS_PS_3;
|
||||
else if (punct == EGPRS_PS_2)
|
||||
return EGPRS_PS_2;
|
||||
} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS7) &&
|
||||
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS5))
|
||||
return EGPRS_PS_1;
|
||||
else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS5) &&
|
||||
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS7))
|
||||
return EGPRS_PS_2;
|
||||
else if (cs != cs_current)
|
||||
return EGPRS_PS_1;
|
||||
/* TS 44.060 9.3.2.1.1 ends here */
|
||||
/*
|
||||
* Below else will handle fresh transmission, retransmission with no
|
||||
* MCS change case
|
||||
*/
|
||||
else
|
||||
return punct;
|
||||
return EGPRS_PS_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function calculates puncturing scheme for retransmission of a RLC
|
||||
* block with same MCS. The computed value shall be used for next transmission
|
||||
* of the same RLC block
|
||||
* TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
|
||||
*/
|
||||
void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
|
||||
const GprsCodingScheme &cs)
|
||||
{
|
||||
switch (GprsCodingScheme::Scheme(cs)) {
|
||||
case GprsCodingScheme::MCS1 :
|
||||
case GprsCodingScheme::MCS2 :
|
||||
case GprsCodingScheme::MCS5 :
|
||||
case GprsCodingScheme::MCS6 :
|
||||
*punct = ((enum egprs_puncturing_values)((*punct + 1) %
|
||||
EGPRS_MAX_PS_NUM_2));
|
||||
break;
|
||||
case GprsCodingScheme::MCS3 :
|
||||
case GprsCodingScheme::MCS4 :
|
||||
case GprsCodingScheme::MCS7 :
|
||||
case GprsCodingScheme::MCS8 :
|
||||
case GprsCodingScheme::MCS9 :
|
||||
*punct = ((enum egprs_puncturing_values)((*punct + 1) %
|
||||
EGPRS_MAX_PS_NUM_3));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
212
src/rlc.h
212
src/rlc.h
|
@ -27,7 +27,6 @@
|
|||
|
||||
#define RLC_GPRS_SNS 128 /* GPRS, must be power of 2 */
|
||||
#define RLC_GPRS_WS 64 /* max window size */
|
||||
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
|
||||
#define RLC_EGPRS_MIN_WS 64 /* min window size */
|
||||
#define RLC_EGPRS_MAX_WS 1024 /* min window size */
|
||||
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
|
||||
|
@ -56,6 +55,88 @@ enum gprs_rlc_dl_bsn_state {
|
|||
GPRS_RLC_DL_BSN_MAX,
|
||||
};
|
||||
|
||||
/*
|
||||
* EGPRS resegment status information for UL
|
||||
* When only first split block is received bsn state
|
||||
* will be set to EGPRS_RESEG_FIRST_SEG_RXD and when
|
||||
* only second segment is received the state will be
|
||||
* set to EGPRS_RESEG_SECOND_SEG_RXD. When both Split
|
||||
* blocks are received the state will be set to
|
||||
* EGPRS_RESEG_DEFAULT
|
||||
* The EGPRS resegmentation feature allows MS to retransmit
|
||||
* RLC blocks of HeaderType1, HeaderType2 by segmenting
|
||||
* them to 2 HeaderType3 blocks(Example MCS5 will be
|
||||
* retransmitted as 2 MCS2 blocks). Table 10.4.8b.1 of 44.060
|
||||
* explains the possible values of SPB in HeadrType3 for UL
|
||||
* direction. When the MCS is changed at the PCU, PCU directs the
|
||||
* changed MCS to MS by PUAN or UPLINK ASSIGNMENT message along
|
||||
* with RESEGMENT flag, Then MS may decide to retransmit the
|
||||
* blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
|
||||
* The retransmission MCS is calculated based on current MCS of
|
||||
* the Block and demanded MCS by PCU. Section 10.3a.4.3 of 44.060
|
||||
* shows the HeadrType3 with SPB field present in it
|
||||
*/
|
||||
enum egprs_rlc_ul_reseg_bsn_state {
|
||||
EGPRS_RESEG_DEFAULT = 0,
|
||||
EGPRS_RESEG_FIRST_SEG_RXD = 0x01,
|
||||
EGPRS_RESEG_SECOND_SEG_RXD = 0x02,
|
||||
EGPRS_RESEG_INVALID = 0x04
|
||||
};
|
||||
|
||||
/*
|
||||
* EGPRS resegment status information for DL
|
||||
* When only first segment is sent, bsn state
|
||||
* will be set to EGPRS_RESEG_FIRST_SEG_SENT and when
|
||||
* second segment is sent the state will be
|
||||
* set to EGPRS_RESEG_SECOND_SEG_SENT.
|
||||
* EGPRS_RESEG_DL_INVALID is set to 8 considering there is a scope for
|
||||
* 3rd segment according to Table 10.4.8b.2 of 44.060
|
||||
* The EGPRS resegmentation feature allows PCU to retransmit
|
||||
* RLC blocks of HeaderType1, HeaderType2 by segmenting
|
||||
* them to 2 HeaderType3 blocks(Example MCS5 will be
|
||||
* retransmitted as 2 MCS2 blocks). Table 10.4.8b.2 of 44.060
|
||||
* explains the possible values of SPB in HeadrType3 for DL
|
||||
* direction.The PCU decides to retransmit the
|
||||
* blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
|
||||
* The retransmission MCS is calculated based on current MCS of
|
||||
* the Block and demanded MCS by PCU. Section 10.3a.3.3 of 44.060
|
||||
* shows the HeadrType3 with SPB field present in it
|
||||
*/
|
||||
enum egprs_rlc_dl_reseg_bsn_state {
|
||||
EGPRS_RESEG_DL_DEFAULT = 0,
|
||||
EGPRS_RESEG_FIRST_SEG_SENT = 0x01,
|
||||
EGPRS_RESEG_SECOND_SEG_SENT = 0x02,
|
||||
EGPRS_RESEG_DL_INVALID = 0x08
|
||||
};
|
||||
|
||||
/* Table 10.4.8b.2 of 44.060 */
|
||||
enum egprs_rlcmac_dl_spb {
|
||||
EGPRS_RLCMAC_DL_NO_RETX = 0,
|
||||
EGPRS_RLCMAC_DL_FIRST_SEG = 2,
|
||||
EGPRS_RLCMAC_DL_SEC_SEG = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* Valid puncturing scheme values
|
||||
* TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
|
||||
*/
|
||||
enum egprs_puncturing_values {
|
||||
EGPRS_PS_1,
|
||||
EGPRS_PS_2,
|
||||
EGPRS_PS_3,
|
||||
EGPRS_PS_INVALID,
|
||||
};
|
||||
|
||||
/*
|
||||
* EGPRS_MAX_PS_NUM_2 is valid for MCS 1,2,5,6.
|
||||
* And EGPRS_MAX_PS_NUM_3 is valid for MCS 3,4,7,8,9
|
||||
* TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
|
||||
*/
|
||||
enum egprs_puncturing_types {
|
||||
EGPRS_MAX_PS_NUM_2 = 2,
|
||||
EGPRS_MAX_PS_NUM_3,
|
||||
EGPRS_MAX_PS_NUM_INVALID,
|
||||
};
|
||||
|
||||
static inline uint16_t mod_sns_half()
|
||||
{
|
||||
|
@ -89,30 +170,64 @@ struct gprs_rlc_data_info {
|
|||
struct gprs_rlc_data_block_info block_info[2];
|
||||
};
|
||||
|
||||
/* holds the current status of the block w.r.t UL/DL split blocks */
|
||||
union split_block_status {
|
||||
egprs_rlc_ul_reseg_bsn_state block_status_ul;
|
||||
egprs_rlc_dl_reseg_bsn_state block_status_dl;
|
||||
};
|
||||
|
||||
struct gprs_rlc_data {
|
||||
uint8_t *prepare(size_t block_data_length);
|
||||
void put_data(const uint8_t *data, size_t len);
|
||||
|
||||
/* block history */
|
||||
/* block data including LI headers */
|
||||
uint8_t block[RLC_MAX_LEN];
|
||||
/* block len of history */
|
||||
/* block data len including LI headers*/
|
||||
uint8_t len;
|
||||
|
||||
struct gprs_rlc_data_block_info block_info;
|
||||
GprsCodingScheme cs;
|
||||
/*
|
||||
* cs_current_trans is variable to hold the cs_last value for
|
||||
* current transmission. cs_current_trans is same as cs_last during
|
||||
* transmission case. during retransmission cs_current_trans is
|
||||
* fetched from egprs_mcs_retx_tbl table based on
|
||||
* cs and demanded cs.reference is 44.060 Table
|
||||
* 8.1.1.1 and Table 8.1.1.2
|
||||
* For UL. cs_last shall be used everywhere.
|
||||
*/
|
||||
GprsCodingScheme cs_current_trans;
|
||||
GprsCodingScheme cs_last;
|
||||
|
||||
/*
|
||||
* The MCS of initial transmission of a BSN
|
||||
* This variable is used for split block
|
||||
* processing in DL
|
||||
*/
|
||||
GprsCodingScheme cs_init;
|
||||
|
||||
/* puncturing scheme value to be used for next transmission*/
|
||||
enum egprs_puncturing_values next_ps;
|
||||
|
||||
/* holds the status of the block w.r.t UL/DL split blocks*/
|
||||
union split_block_status spb_status;
|
||||
};
|
||||
|
||||
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding);
|
||||
GprsCodingScheme cs, bool with_padding, const unsigned int spb);
|
||||
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
|
||||
GprsCodingScheme cs, bool with_padding);
|
||||
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
|
||||
GprsCodingScheme cs, bool with_padding);
|
||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
|
||||
int with_padding);
|
||||
GprsCodingScheme cs, bool with_padding, const unsigned int spb);
|
||||
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, enum egprs_puncturing_values
|
||||
punct, enum egprs_puncturing_values punct2, int with_padding);
|
||||
void gprs_rlc_mcs_cps_decode(unsigned int cps, GprsCodingScheme cs,
|
||||
int *punct, int *punct2, int *with_padding);
|
||||
|
||||
enum egprs_puncturing_values gprs_get_punct_scheme(enum egprs_puncturing_values
|
||||
punct, const GprsCodingScheme &cs,
|
||||
const GprsCodingScheme &cs_current_trans,
|
||||
const enum egprs_rlcmac_dl_spb spb);
|
||||
void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
|
||||
const GprsCodingScheme &cs);
|
||||
/*
|
||||
* I hold the currently transferred blocks and will provide
|
||||
* the routines to manipulate these arrays.
|
||||
|
@ -185,7 +300,7 @@ struct gprs_rlc_dl_window: public gprs_rlc_window {
|
|||
const uint16_t v_s() const;
|
||||
const uint16_t v_s_mod(int offset) const;
|
||||
const uint16_t v_a() const;
|
||||
const int16_t distance() const;
|
||||
const uint16_t distance() const;
|
||||
|
||||
/* Methods to manage reception */
|
||||
int resend_needed();
|
||||
|
@ -226,12 +341,16 @@ struct gprs_rlc_ul_window: public gprs_rlc_window {
|
|||
const uint16_t v_r() const;
|
||||
const uint16_t v_q() const;
|
||||
|
||||
const void set_v_r(int);
|
||||
const void set_v_q(int);
|
||||
|
||||
const uint16_t ssn() const;
|
||||
|
||||
bool is_in_window(uint16_t bsn) const;
|
||||
bool is_received(uint16_t bsn) const;
|
||||
|
||||
void update_rbb(char *rbb);
|
||||
uint16_t update_egprs_rbb(uint8_t *rbb);
|
||||
void raise_v_r_to(int moves);
|
||||
void raise_v_r(const uint16_t bsn);
|
||||
uint16_t raise_v_q();
|
||||
|
@ -287,67 +406,6 @@ struct rlc_li_field_egprs {
|
|||
uint8_t e:1,
|
||||
li:7;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_ul_header_egprs_3 {
|
||||
uint8_t r:1,
|
||||
si:1,
|
||||
cv:4,
|
||||
tfi_a:2;
|
||||
uint8_t tfi_b:3,
|
||||
bsn1_a:5;
|
||||
uint8_t bsn1_b:6,
|
||||
cps_a:2;
|
||||
uint8_t cps_b:2,
|
||||
spb:2,
|
||||
rsb:1,
|
||||
pi:1,
|
||||
spare:1,
|
||||
dummy:1;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_dl_header_egprs_1 {
|
||||
uint8_t usf:3,
|
||||
es_p:2,
|
||||
rrbp:2,
|
||||
tfi_a:1;
|
||||
uint8_t tfi_b:4,
|
||||
pr:2,
|
||||
bsn1_a:2;
|
||||
uint8_t bsn1_b:8;
|
||||
uint8_t bsn1_c:1,
|
||||
bsn2_a:7;
|
||||
uint8_t bsn2_b:3,
|
||||
cps:5;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_dl_header_egprs_2 {
|
||||
uint8_t usf:3,
|
||||
es_p:2,
|
||||
rrbp:2,
|
||||
tfi_a:1;
|
||||
uint8_t tfi_b:4,
|
||||
pr:2,
|
||||
bsn1_a:2;
|
||||
uint8_t bsn1_b:8;
|
||||
uint8_t bsn1_c:1,
|
||||
cps:3,
|
||||
dummy:4;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gprs_rlc_dl_header_egprs_3 {
|
||||
uint8_t usf:3,
|
||||
es_p:2,
|
||||
rrbp:2,
|
||||
tfi_a:1;
|
||||
uint8_t tfi_b:4,
|
||||
pr:2,
|
||||
bsn1_a:2;
|
||||
uint8_t bsn1_b:8;
|
||||
uint8_t bsn1_c:1,
|
||||
cps:4,
|
||||
spb:2,
|
||||
dummy:1;
|
||||
} __attribute__ ((packed));
|
||||
#else
|
||||
# error "Only little endian headers are supported yet. TODO: add missing structs"
|
||||
#endif
|
||||
|
@ -485,7 +543,7 @@ inline void gprs_rlc_dl_window::raise(int moves)
|
|||
m_v_a = (m_v_a + moves) & mod_sns();
|
||||
}
|
||||
|
||||
inline const int16_t gprs_rlc_dl_window::distance() const
|
||||
inline const uint16_t gprs_rlc_dl_window::distance() const
|
||||
{
|
||||
return (m_v_s - m_v_a) & mod_sns();
|
||||
}
|
||||
|
@ -516,6 +574,16 @@ inline bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
|
|||
return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
|
||||
}
|
||||
|
||||
inline const void gprs_rlc_ul_window::set_v_r(int v_r)
|
||||
{
|
||||
m_v_r = v_r;
|
||||
}
|
||||
|
||||
inline const void gprs_rlc_ul_window::set_v_q(int v_q)
|
||||
{
|
||||
m_v_q = v_q;
|
||||
}
|
||||
|
||||
inline const uint16_t gprs_rlc_ul_window::v_r() const
|
||||
{
|
||||
return m_v_r;
|
||||
|
|
44
src/sba.cpp
44
src/sba.cpp
|
@ -26,6 +26,7 @@
|
|||
|
||||
extern "C" {
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -48,32 +49,39 @@ int SBAController::alloc(
|
|||
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct gprs_rlcmac_sba *sba;
|
||||
int8_t trx, ts;
|
||||
int8_t ts;
|
||||
uint32_t fn;
|
||||
bool trxs[8];
|
||||
int selected_trx;
|
||||
int ret;
|
||||
|
||||
ret = m_bts.get_possible_trxs_sba(trxs);
|
||||
|
||||
if (ret == -EINVAL)
|
||||
return -EINVAL;
|
||||
|
||||
selected_trx = m_bts.get_suitable_trx(trxs);
|
||||
|
||||
for (ts = 7; ts >= 0; ts--) {
|
||||
pdch = &m_bts.bts_data()->trx[selected_trx].pdch[ts];
|
||||
if (!pdch->is_enabled())
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (ts < 0)
|
||||
return -EINVAL;
|
||||
|
||||
sba = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_sba);
|
||||
if (!sba)
|
||||
return -ENOMEM;
|
||||
|
||||
for (trx = 0; trx < 8; trx++) {
|
||||
for (ts = 7; ts >= 0; ts--) {
|
||||
pdch = &m_bts.bts_data()->trx[trx].pdch[ts];
|
||||
if (!pdch->is_enabled())
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (ts >= 0)
|
||||
break;
|
||||
}
|
||||
if (trx == 8) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
|
||||
talloc_free(sba);
|
||||
if (!gsm48_ta_is_valid(ta))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648;
|
||||
|
||||
sba->trx_no = trx;
|
||||
sba->trx_no = selected_trx;
|
||||
sba->ts_no = ts;
|
||||
sba->fn = fn;
|
||||
sba->ta = ta;
|
||||
|
@ -81,9 +89,11 @@ int SBAController::alloc(
|
|||
llist_add(&sba->list, &m_sbas);
|
||||
m_bts.sba_allocated();
|
||||
|
||||
*_trx = trx;
|
||||
*_trx = selected_trx;
|
||||
*_ts = ts;
|
||||
*_fn = fn;
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, " sba fn=%d ts = %d trx = %d\n", fn, ts, selected_trx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
249
src/tbf.cpp
249
src/tbf.cpp
|
@ -33,6 +33,7 @@
|
|||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -42,6 +43,88 @@ extern void *tall_pcu_ctx;
|
|||
|
||||
static void tbf_timer_cb(void *_tbf);
|
||||
|
||||
static const struct rate_ctr_desc tbf_ctr_description[] = {
|
||||
{ "rlc.nacked", "RLC Nacked " },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc tbf_dl_gprs_ctr_description[] = {
|
||||
{ "gprs.downlink.cs1", "CS1 " },
|
||||
{ "gprs.downlink.cs2", "CS2 " },
|
||||
{ "gprs.downlink.cs3", "CS3 " },
|
||||
{ "gprs.downlink.cs4", "CS4 " },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc tbf_dl_egprs_ctr_description[] = {
|
||||
{ "egprs.downlink.mcs1", "MCS1 " },
|
||||
{ "egprs.downlink.mcs2", "MCS2 " },
|
||||
{ "egprs.downlink.mcs3", "MCS3 " },
|
||||
{ "egprs.downlink.mcs4", "MCS4 " },
|
||||
{ "egprs.downlink.mcs5", "MCS5 " },
|
||||
{ "egprs.downlink.mcs6", "MCS6 " },
|
||||
{ "egprs.downlink.mcs7", "MCS7 " },
|
||||
{ "egprs.downlink.mcs8", "MCS8 " },
|
||||
{ "egprs.downlink.mcs9", "MCS9 " },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc tbf_ul_gprs_ctr_description[] = {
|
||||
{ "gprs.uplink.cs1", "CS1 " },
|
||||
{ "gprs.uplink.cs2", "CS2 " },
|
||||
{ "gprs.uplink.cs3", "CS3 " },
|
||||
{ "gprs.uplink.cs4", "CS4 " },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc tbf_ul_egprs_ctr_description[] = {
|
||||
{ "egprs.uplink.mcs1", "MCS1 " },
|
||||
{ "egprs.uplink.mcs2", "MCS2 " },
|
||||
{ "egprs.uplink.mcs3", "MCS3 " },
|
||||
{ "egprs.uplink.mcs4", "MCS4 " },
|
||||
{ "egprs.uplink.mcs5", "MCS5 " },
|
||||
{ "egprs.uplink.mcs6", "MCS6 " },
|
||||
{ "egprs.uplink.mcs7", "MCS7 " },
|
||||
{ "egprs.uplink.mcs8", "MCS8 " },
|
||||
{ "egprs.uplink.mcs9", "MCS9 " },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc tbf_ctrg_desc = {
|
||||
"pcu.tbf",
|
||||
"TBF Statistics",
|
||||
OSMO_STATS_CLASS_SUBSCRIBER,
|
||||
ARRAY_SIZE(tbf_ctr_description),
|
||||
tbf_ctr_description,
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc tbf_dl_gprs_ctrg_desc = {
|
||||
"tbf.gprs",
|
||||
"Data Blocks",
|
||||
OSMO_STATS_CLASS_SUBSCRIBER,
|
||||
ARRAY_SIZE(tbf_dl_gprs_ctr_description),
|
||||
tbf_dl_gprs_ctr_description,
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc tbf_dl_egprs_ctrg_desc = {
|
||||
"tbf.egprs",
|
||||
"Data Blocks",
|
||||
OSMO_STATS_CLASS_SUBSCRIBER,
|
||||
ARRAY_SIZE(tbf_dl_egprs_ctr_description),
|
||||
tbf_dl_egprs_ctr_description,
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc tbf_ul_gprs_ctrg_desc = {
|
||||
"tbf.gprs",
|
||||
"Data Blocks",
|
||||
OSMO_STATS_CLASS_SUBSCRIBER,
|
||||
ARRAY_SIZE(tbf_ul_gprs_ctr_description),
|
||||
tbf_ul_gprs_ctr_description,
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc tbf_ul_egprs_ctrg_desc = {
|
||||
"tbf.egprs",
|
||||
"Data Blocks",
|
||||
OSMO_STATS_CLASS_SUBSCRIBER,
|
||||
ARRAY_SIZE(tbf_ul_egprs_ctr_description),
|
||||
tbf_ul_egprs_ctr_description,
|
||||
};
|
||||
|
||||
gprs_rlcmac_tbf::Meas::Meas() :
|
||||
rssi_sum(0),
|
||||
rssi_num(0)
|
||||
|
@ -74,11 +157,12 @@ gprs_rlcmac_tbf::gprs_rlcmac_tbf(BTS *bts_, gprs_rlcmac_tbf_direction dir) :
|
|||
m_tfi(0),
|
||||
m_created_ts(0),
|
||||
m_ms(NULL),
|
||||
m_ta(0),
|
||||
m_ta(GSM48_TA_INVALID),
|
||||
m_ms_class(0),
|
||||
m_list(this),
|
||||
m_ms_list(this),
|
||||
m_egprs_enabled(false)
|
||||
m_egprs_enabled(false),
|
||||
m_ctrs(NULL)
|
||||
{
|
||||
/* The classes of these members do not have proper constructors yet.
|
||||
* Just set them to 0 like talloc_zero did */
|
||||
|
@ -151,7 +235,8 @@ void gprs_rlcmac_tbf::set_ta(uint8_t ta)
|
|||
if (ms())
|
||||
ms()->set_ta(ta);
|
||||
|
||||
m_ta = ta;
|
||||
if (gsm48_ta_is_valid(ta))
|
||||
m_ta = ta;
|
||||
}
|
||||
|
||||
uint8_t gprs_rlcmac_tbf::ms_class() const
|
||||
|
@ -188,6 +273,13 @@ const gprs_llc_queue *gprs_rlcmac_tbf::llc_queue() const
|
|||
return m_ms ? m_ms->llc_queue() : NULL;
|
||||
}
|
||||
|
||||
int gprs_rlcmac_tbf::llc_queue_size() const
|
||||
{
|
||||
/* m_ms->llc_queue() never returns NULL: GprsMs::m_llc_queue is a
|
||||
* member instance. */
|
||||
return m_ms ? m_ms->llc_queue()->size() : 0;
|
||||
}
|
||||
|
||||
void gprs_rlcmac_tbf::set_ms(GprsMs *ms)
|
||||
{
|
||||
if (m_ms == ms)
|
||||
|
@ -303,7 +395,7 @@ static void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf)
|
|||
for (ts = 0; ts < 8; ts++) {
|
||||
if (!tbf->pdch[ts])
|
||||
continue;
|
||||
|
||||
tbf->trx->current_load--;
|
||||
tbf->pdch[ts]->detach_tbf(tbf);
|
||||
tbf->pdch[ts] = NULL;
|
||||
}
|
||||
|
@ -313,10 +405,19 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf)
|
|||
{
|
||||
/* update counters */
|
||||
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
|
||||
gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
|
||||
tbf->bts->tbf_ul_freed();
|
||||
if (tbf->state_is(GPRS_RLCMAC_FLOW))
|
||||
tbf->bts->tbf_ul_aborted();
|
||||
rate_ctr_group_free(ul_tbf->m_ul_egprs_ctrs);
|
||||
rate_ctr_group_free(ul_tbf->m_ul_gprs_ctrs);
|
||||
} else {
|
||||
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
|
||||
if (tbf->is_egprs_enabled()) {
|
||||
rate_ctr_group_free(dl_tbf->m_dl_egprs_ctrs);
|
||||
} else {
|
||||
rate_ctr_group_free(dl_tbf->m_dl_gprs_ctrs);
|
||||
}
|
||||
tbf->bts->tbf_dl_freed();
|
||||
if (tbf->state_is(GPRS_RLCMAC_FLOW))
|
||||
tbf->bts->tbf_dl_aborted();
|
||||
|
@ -352,6 +453,8 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf)
|
|||
if (tbf->ms())
|
||||
tbf->set_ms(NULL);
|
||||
|
||||
rate_ctr_group_free(tbf->m_ctrs);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF ends here **********\n");
|
||||
talloc_free(tbf);
|
||||
}
|
||||
|
@ -375,6 +478,12 @@ int gprs_rlcmac_tbf::update()
|
|||
return -rc;
|
||||
}
|
||||
|
||||
if (is_egprs_enabled()) {
|
||||
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(this);
|
||||
if (dl_tbf)
|
||||
dl_tbf->egprs_calc_window_size();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -394,7 +503,6 @@ int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf)
|
|||
const char *gprs_rlcmac_tbf::tbf_state_name[] = {
|
||||
"NULL",
|
||||
"ASSIGN",
|
||||
"WAIT ASSIGN",
|
||||
"FLOW",
|
||||
"FINISHED",
|
||||
"WAIT RELEASE",
|
||||
|
@ -495,12 +603,14 @@ void gprs_rlcmac_tbf::poll_timeout()
|
|||
}
|
||||
ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
|
||||
bts->rlc_ack_timedout();
|
||||
bts->pkt_ul_ack_nack_poll_timedout();
|
||||
if (state_is(GPRS_RLCMAC_FINISHED)) {
|
||||
gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(this);
|
||||
ul_tbf->m_n3103++;
|
||||
if (ul_tbf->m_n3103 == ul_tbf->bts->bts_data()->n3103) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"- N3103 exceeded\n");
|
||||
bts->pkt_ul_ack_nack_poll_failed();
|
||||
ul_tbf->set_state(GPRS_RLCMAC_RELEASING);
|
||||
tbf_timer_start(ul_tbf, 3169, ul_tbf->bts->bts_data()->t3169, 0);
|
||||
return;
|
||||
|
@ -520,11 +630,13 @@ void gprs_rlcmac_tbf::poll_timeout()
|
|||
ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
|
||||
n3105++;
|
||||
bts->rlc_ass_timedout();
|
||||
bts->pua_poll_timedout();
|
||||
if (n3105 == bts_data()->n3105) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
|
||||
set_state(GPRS_RLCMAC_RELEASING);
|
||||
tbf_timer_start(this, 3195, bts_data()->t3195, 0);
|
||||
bts->rlc_ass_failed();
|
||||
bts->pua_poll_failed();
|
||||
return;
|
||||
}
|
||||
/* reschedule UL assignment */
|
||||
|
@ -540,11 +652,13 @@ void gprs_rlcmac_tbf::poll_timeout()
|
|||
dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
|
||||
n3105++;
|
||||
bts->rlc_ass_timedout();
|
||||
bts->pda_poll_timedout();
|
||||
if (n3105 == bts->bts_data()->n3105) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
|
||||
set_state(GPRS_RLCMAC_RELEASING);
|
||||
tbf_timer_start(this, 3195, bts_data()->t3195, 0);
|
||||
bts->rlc_ass_failed();
|
||||
bts->pda_poll_failed();
|
||||
return;
|
||||
}
|
||||
/* reschedule DL assignment */
|
||||
|
@ -561,12 +675,15 @@ void gprs_rlcmac_tbf::poll_timeout()
|
|||
dl_tbf->n3105++;
|
||||
if (dl_tbf->state_is(GPRS_RLCMAC_RELEASING))
|
||||
bts->rlc_rel_timedout();
|
||||
else
|
||||
else {
|
||||
bts->rlc_ack_timedout();
|
||||
bts->pkt_dl_ack_nack_poll_timedout();
|
||||
}
|
||||
if (dl_tbf->n3105 == dl_tbf->bts->bts_data()->n3105) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
|
||||
dl_tbf->set_state(GPRS_RLCMAC_RELEASING);
|
||||
tbf_timer_start(dl_tbf, 3195, dl_tbf->bts_data()->t3195, 0);
|
||||
bts->pkt_dl_ack_nack_poll_failed();
|
||||
bts->rlc_ack_failed();
|
||||
return;
|
||||
}
|
||||
|
@ -624,6 +741,8 @@ static int setup_tbf(struct gprs_rlcmac_tbf *tbf,
|
|||
"Allocated %s: trx = %d, ul_slots = %02x, dl_slots = %02x\n",
|
||||
tbf->name(), tbf->trx->trx_no, tbf->ul_slots(), tbf->dl_slots());
|
||||
|
||||
tbf->m_ctrs = rate_ctr_group_alloc(tbf, &tbf_ctrg_desc, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -632,7 +751,9 @@ gprs_rlcmac_ul_tbf::gprs_rlcmac_ul_tbf(BTS *bts_) :
|
|||
m_rx_counter(0),
|
||||
m_n3103(0),
|
||||
m_contention_resolution_done(0),
|
||||
m_final_ack_sent(0)
|
||||
m_final_ack_sent(0),
|
||||
m_ul_gprs_ctrs(NULL),
|
||||
m_ul_egprs_ctrs(NULL)
|
||||
{
|
||||
memset(&m_usf, 0, sizeof(m_usf));
|
||||
}
|
||||
|
@ -687,20 +808,25 @@ struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
|
|||
if (egprs_ms_class > 0 && bts->egprs_enabled) {
|
||||
tbf->enable_egprs();
|
||||
tbf->m_window.set_sns(RLC_EGPRS_SNS);
|
||||
/* TODO: Allow bigger UL windows when CRBB encoding is supported */
|
||||
tbf->m_window.set_ws(RLC_EGPRS_MIN_WS);
|
||||
setup_egprs_mode(bts, ms);
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Enabled EGPRS for %s, mode %s\n",
|
||||
tbf->name(), GprsCodingScheme::modeName(ms->mode()));
|
||||
}
|
||||
|
||||
rc = setup_tbf(tbf, ms, use_trx, ms_class, egprs_ms_class, single_slot);
|
||||
|
||||
if (tbf->is_egprs_enabled())
|
||||
tbf->egprs_calc_ulwindow_size();
|
||||
|
||||
/* if no resource */
|
||||
if (rc < 0) {
|
||||
talloc_free(tbf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tbf->m_ul_egprs_ctrs = rate_ctr_group_alloc(tbf, &tbf_ul_egprs_ctrg_desc, 0);
|
||||
tbf->m_ul_gprs_ctrs = rate_ctr_group_alloc(tbf, &tbf_ul_gprs_ctrg_desc, 0);
|
||||
|
||||
llist_add(&tbf->list(), &bts->bts->ul_tbfs());
|
||||
tbf->bts->tbf_ul_created();
|
||||
|
||||
|
@ -710,7 +836,8 @@ struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
|
|||
gprs_rlcmac_dl_tbf::BandWidth::BandWidth() :
|
||||
dl_bw_octets(0),
|
||||
dl_loss_lost(0),
|
||||
dl_loss_received(0)
|
||||
dl_loss_received(0),
|
||||
dl_throughput(0)
|
||||
{
|
||||
timerclear(&dl_bw_tv);
|
||||
timerclear(&dl_loss_tv);
|
||||
|
@ -722,7 +849,9 @@ gprs_rlcmac_dl_tbf::gprs_rlcmac_dl_tbf(BTS *bts_) :
|
|||
m_wait_confirm(0),
|
||||
m_dl_ack_requested(false),
|
||||
m_last_dl_poll_fn(0),
|
||||
m_last_dl_drained_fn(0)
|
||||
m_last_dl_drained_fn(0),
|
||||
m_dl_gprs_ctrs(NULL),
|
||||
m_dl_egprs_ctrs(NULL)
|
||||
{
|
||||
memset(&m_llc_timer, 0, sizeof(m_llc_timer));
|
||||
}
|
||||
|
@ -781,18 +910,10 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
|
|||
}
|
||||
|
||||
if (tbf->is_egprs_enabled()) {
|
||||
unsigned int num_pdch = pcu_bitcount(tbf->dl_slots());
|
||||
unsigned int ws = bts->ws_base + num_pdch * bts->ws_pdch;
|
||||
ws = (ws / 32) * 32;
|
||||
ws = OSMO_MAX(64, ws);
|
||||
if (num_pdch == 1)
|
||||
ws = OSMO_MIN(192, ws);
|
||||
else
|
||||
ws = OSMO_MIN(128 * num_pdch, ws);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "%s: Setting EGPRS window size to %d\n",
|
||||
tbf->name(), ws);
|
||||
tbf->m_window.set_ws(ws);
|
||||
tbf->egprs_calc_window_size();
|
||||
tbf->m_dl_egprs_ctrs = rate_ctr_group_alloc(tbf, &tbf_dl_egprs_ctrg_desc, 0);
|
||||
} else {
|
||||
tbf->m_dl_gprs_ctrs = rate_ctr_group_alloc(tbf, &tbf_dl_gprs_ctrg_desc, 0);
|
||||
}
|
||||
|
||||
llist_add(&tbf->list(), &bts->bts->dl_tbfs());
|
||||
|
@ -824,12 +945,6 @@ void gprs_rlcmac_tbf::handle_timeout()
|
|||
case 0: /* assignment */
|
||||
if ((state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))) {
|
||||
if (state_is(GPRS_RLCMAC_ASSIGN)) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "%s releasing due to "
|
||||
"PACCH assignment timeout (not yet sent).\n",
|
||||
tbf_name(this));
|
||||
tbf_free(this);
|
||||
return;
|
||||
} else if (state_is(GPRS_RLCMAC_WAIT_ASSIGN)) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "%s releasing due to "
|
||||
"PACCH assignment timeout.\n", tbf_name(this));
|
||||
tbf_free(this);
|
||||
|
@ -841,7 +956,7 @@ void gprs_rlcmac_tbf::handle_timeout()
|
|||
if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) {
|
||||
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(this);
|
||||
dl_tbf->m_wait_confirm = 0;
|
||||
if (dl_tbf->state_is(GPRS_RLCMAC_WAIT_ASSIGN)) {
|
||||
if (dl_tbf->state_is(GPRS_RLCMAC_ASSIGN)) {
|
||||
tbf_assign_control_ts(dl_tbf);
|
||||
|
||||
if (!dl_tbf->upgrade_to_multislot) {
|
||||
|
@ -996,14 +1111,13 @@ struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn, uint8_t ts)
|
|||
encode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "\n");
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Downlink Assignment -------------------------\n");
|
||||
bts->pkt_dl_assignemnt();
|
||||
bitvec_pack(ass_vec, msgb_put(msg, 23));
|
||||
bitvec_free(ass_vec);
|
||||
talloc_free(mac_control_block);
|
||||
|
||||
if (poll_ass_dl) {
|
||||
set_polling(new_poll_fn, ts);
|
||||
if (new_dl_tbf->state_is(GPRS_RLCMAC_ASSIGN))
|
||||
new_dl_tbf->set_state(GPRS_RLCMAC_WAIT_ASSIGN);
|
||||
dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK;
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"%s Scheduled DL Assignment polling on FN=%d, TS=%d\n",
|
||||
|
@ -1020,6 +1134,35 @@ struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn, uint8_t ts)
|
|||
return msg;
|
||||
}
|
||||
|
||||
struct msgb *gprs_rlcmac_tbf::create_packet_access_reject()
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_alloc(23, "rlcmac_ul_ass_rej");
|
||||
|
||||
bitvec *packet_access_rej = bitvec_alloc(23);
|
||||
|
||||
bitvec_unhex(packet_access_rej,
|
||||
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
||||
|
||||
Encoding::write_packet_access_reject(
|
||||
packet_access_rej, tlli());
|
||||
|
||||
bts->pkt_access_reject();
|
||||
|
||||
bitvec_pack(packet_access_rej, msgb_put(msg, 23));
|
||||
|
||||
bitvec_free(packet_access_rej);
|
||||
ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
|
||||
|
||||
/* Start Tmr only if it is UL TBF */
|
||||
if (direction == GPRS_RLCMAC_UL_TBF)
|
||||
tbf_timer_start(this, 0, Treject_pacch);
|
||||
|
||||
return msg;
|
||||
|
||||
}
|
||||
|
||||
struct msgb *gprs_rlcmac_tbf::create_ul_ass(uint32_t fn, uint8_t ts)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
@ -1071,13 +1214,12 @@ struct msgb *gprs_rlcmac_tbf::create_ul_ass(uint32_t fn, uint8_t ts)
|
|||
decode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
|
||||
LOGPC(DCSN1, LOGL_NOTICE, "\n");
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Uplink Assignment -------------------------\n");
|
||||
bts->pkt_ul_assignment();
|
||||
bitvec_free(ass_vec);
|
||||
talloc_free(mac_control_block);
|
||||
|
||||
set_polling(new_poll_fn, ts);
|
||||
ul_ass_state = GPRS_RLCMAC_UL_ASS_WAIT_ACK;
|
||||
if (new_tbf->state_is(GPRS_RLCMAC_ASSIGN))
|
||||
new_tbf->set_state(GPRS_RLCMAC_WAIT_ASSIGN);
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"%s Scheduled UL Assignment polling on FN=%d, TS=%d\n",
|
||||
name(), poll_fn, poll_ts);
|
||||
|
@ -1176,14 +1318,11 @@ int gprs_rlcmac_tbf::set_tlli_from_ul(uint32_t new_tlli)
|
|||
|
||||
const char *tbf_name(gprs_rlcmac_tbf *tbf)
|
||||
{
|
||||
return tbf->name();
|
||||
return tbf ? tbf->name() : "(no TBF)";
|
||||
}
|
||||
|
||||
const char *gprs_rlcmac_tbf::name() const
|
||||
{
|
||||
if (this == NULL)
|
||||
return "(no TBF)";
|
||||
|
||||
snprintf(m_name_buf, sizeof(m_name_buf) - 1,
|
||||
"TBF(TFI=%d TLLI=0x%08x DIR=%s STATE=%s%s)",
|
||||
m_tfi, tlli(),
|
||||
|
@ -1249,3 +1388,37 @@ bool gprs_rlcmac_tbf::is_control_ts(uint8_t ts) const
|
|||
{
|
||||
return ts == control_ts;
|
||||
}
|
||||
|
||||
struct gprs_rlcmac_ul_tbf *handle_tbf_reject(struct gprs_rlcmac_bts *bts,
|
||||
GprsMs *ms, uint32_t tlli, uint8_t trx_no, uint8_t ts)
|
||||
{
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf = NULL;
|
||||
struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
|
||||
|
||||
ul_tbf = talloc(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
|
||||
if (!ul_tbf)
|
||||
return ul_tbf;
|
||||
|
||||
talloc_set_destructor(ul_tbf, ul_tbf_dtor);
|
||||
new (ul_tbf) gprs_rlcmac_ul_tbf(bts->bts);
|
||||
if (!ms)
|
||||
ms = bts->bts->ms_alloc(0, 0);
|
||||
|
||||
ms->set_tlli(tlli);
|
||||
|
||||
llist_add(&ul_tbf->list(), &bts->bts->ul_tbfs());
|
||||
ul_tbf->bts->tbf_ul_created();
|
||||
ul_tbf->set_state(GPRS_RLCMAC_ASSIGN);
|
||||
ul_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
|
||||
|
||||
ul_tbf->set_ms(ms);
|
||||
ul_tbf->update_ms(tlli, GPRS_RLCMAC_UL_TBF);
|
||||
ul_tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ;
|
||||
ul_tbf->control_ts = ts;
|
||||
ul_tbf->trx = trx;
|
||||
ul_tbf->m_ctrs = rate_ctr_group_alloc(ul_tbf, &tbf_ctrg_desc, 0);
|
||||
ul_tbf->m_ul_egprs_ctrs = rate_ctr_group_alloc(ul_tbf, &tbf_ul_egprs_ctrg_desc, 0);
|
||||
ul_tbf->m_ul_gprs_ctrs = rate_ctr_group_alloc(ul_tbf, &tbf_ul_gprs_ctrg_desc, 0);
|
||||
|
||||
return ul_tbf;
|
||||
}
|
||||
|
|
100
src/tbf.h
100
src/tbf.h
|
@ -40,11 +40,11 @@ class GprsMs;
|
|||
|
||||
#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */
|
||||
#define Tassign_pacch 2,0 /* timeout for pacch assigment */
|
||||
#define Treject_pacch 0,2000 /* timeout for tbf reject for PRR*/
|
||||
|
||||
enum gprs_rlcmac_tbf_state {
|
||||
GPRS_RLCMAC_NULL = 0, /* new created TBF */
|
||||
GPRS_RLCMAC_ASSIGN, /* wait for DL transmission */
|
||||
GPRS_RLCMAC_WAIT_ASSIGN,/* wait for confirmation */
|
||||
GPRS_RLCMAC_ASSIGN, /* wait for downlink assignment */
|
||||
GPRS_RLCMAC_FLOW, /* RLC/MAC flow, resource needed */
|
||||
GPRS_RLCMAC_FINISHED, /* flow finished, wait for release */
|
||||
GPRS_RLCMAC_WAIT_RELEASE,/* wait for release or restart of DL TBF */
|
||||
|
@ -65,6 +65,7 @@ enum gprs_rlcmac_tbf_dl_ass_state {
|
|||
enum gprs_rlcmac_tbf_ul_ass_state {
|
||||
GPRS_RLCMAC_UL_ASS_NONE = 0,
|
||||
GPRS_RLCMAC_UL_ASS_SEND_ASS, /* send uplink assignment on next RTS */
|
||||
GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ, /* send assignment reject next RTS */
|
||||
GPRS_RLCMAC_UL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
|
||||
};
|
||||
|
||||
|
@ -79,6 +80,48 @@ enum gprs_rlcmac_tbf_direction {
|
|||
GPRS_RLCMAC_UL_TBF
|
||||
};
|
||||
|
||||
enum tbf_counters {
|
||||
TBF_CTR_RLC_NACKED,
|
||||
};
|
||||
|
||||
enum tbf_gprs_counters {
|
||||
TBF_CTR_GPRS_DL_CS1,
|
||||
TBF_CTR_GPRS_DL_CS2,
|
||||
TBF_CTR_GPRS_DL_CS3,
|
||||
TBF_CTR_GPRS_DL_CS4,
|
||||
};
|
||||
|
||||
enum tbf_egprs_counters {
|
||||
TBF_CTR_EGPRS_DL_MCS1,
|
||||
TBF_CTR_EGPRS_DL_MCS2,
|
||||
TBF_CTR_EGPRS_DL_MCS3,
|
||||
TBF_CTR_EGPRS_DL_MCS4,
|
||||
TBF_CTR_EGPRS_DL_MCS5,
|
||||
TBF_CTR_EGPRS_DL_MCS6,
|
||||
TBF_CTR_EGPRS_DL_MCS7,
|
||||
TBF_CTR_EGPRS_DL_MCS8,
|
||||
TBF_CTR_EGPRS_DL_MCS9,
|
||||
};
|
||||
|
||||
enum tbf_gprs_ul_counters {
|
||||
TBF_CTR_GPRS_UL_CS1,
|
||||
TBF_CTR_GPRS_UL_CS2,
|
||||
TBF_CTR_GPRS_UL_CS3,
|
||||
TBF_CTR_GPRS_UL_CS4,
|
||||
};
|
||||
|
||||
enum tbf_egprs_ul_counters {
|
||||
TBF_CTR_EGPRS_UL_MCS1,
|
||||
TBF_CTR_EGPRS_UL_MCS2,
|
||||
TBF_CTR_EGPRS_UL_MCS3,
|
||||
TBF_CTR_EGPRS_UL_MCS4,
|
||||
TBF_CTR_EGPRS_UL_MCS5,
|
||||
TBF_CTR_EGPRS_UL_MCS6,
|
||||
TBF_CTR_EGPRS_UL_MCS7,
|
||||
TBF_CTR_EGPRS_UL_MCS8,
|
||||
TBF_CTR_EGPRS_UL_MCS9,
|
||||
};
|
||||
|
||||
#define GPRS_RLCMAC_FLAG_CCCH 0 /* assignment on CCCH */
|
||||
#define GPRS_RLCMAC_FLAG_PACCH 1 /* assignment on PACCH */
|
||||
#define GPRS_RLCMAC_FLAG_UL_DATA 2 /* uplink data received */
|
||||
|
@ -104,6 +147,7 @@ struct gprs_rlcmac_tbf {
|
|||
|
||||
struct msgb *create_dl_ass(uint32_t fn, uint8_t ts);
|
||||
struct msgb *create_ul_ass(uint32_t fn, uint8_t ts);
|
||||
struct msgb *create_packet_access_reject();
|
||||
|
||||
GprsMs *ms() const;
|
||||
void set_ms(GprsMs *ms);
|
||||
|
@ -142,8 +186,7 @@ struct gprs_rlcmac_tbf {
|
|||
uint8_t ms_class() const;
|
||||
void set_ms_class(uint8_t);
|
||||
GprsCodingScheme current_cs() const;
|
||||
gprs_llc_queue *llc_queue();
|
||||
const gprs_llc_queue *llc_queue() const;
|
||||
int llc_queue_size() const;
|
||||
|
||||
time_t created_ts() const;
|
||||
uint8_t dl_slots() const;
|
||||
|
@ -225,13 +268,18 @@ struct gprs_rlcmac_tbf {
|
|||
uint8_t m_tfi;
|
||||
time_t m_created_ts;
|
||||
|
||||
struct rate_ctr_group *m_ctrs;
|
||||
|
||||
protected:
|
||||
gprs_rlcmac_bts *bts_data() const;
|
||||
|
||||
int set_tlli_from_ul(uint32_t new_tlli);
|
||||
void merge_and_clear_ms(GprsMs *old_ms);
|
||||
|
||||
static const char *tbf_state_name[7];
|
||||
gprs_llc_queue *llc_queue();
|
||||
const gprs_llc_queue *llc_queue() const;
|
||||
|
||||
static const char *tbf_state_name[6];
|
||||
|
||||
class GprsMs *m_ms;
|
||||
|
||||
|
@ -262,6 +310,9 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
|
|||
|
||||
void tbf_free(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
struct gprs_rlcmac_ul_tbf *handle_tbf_reject(struct gprs_rlcmac_bts *bts,
|
||||
GprsMs *ms, uint32_t tlli, uint8_t trx_no, uint8_t ts_no);
|
||||
|
||||
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
|
||||
|
||||
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
|
||||
|
@ -316,7 +367,10 @@ inline bool gprs_rlcmac_tbf::is_tfi_assigned() const
|
|||
{
|
||||
/* The TBF is established or has been assigned by a IMM.ASS for
|
||||
* download */
|
||||
return state > GPRS_RLCMAC_ASSIGN;
|
||||
return state > GPRS_RLCMAC_ASSIGN ||
|
||||
(direction == GPRS_RLCMAC_DL_TBF &&
|
||||
state == GPRS_RLCMAC_ASSIGN &&
|
||||
(state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)));
|
||||
}
|
||||
|
||||
inline uint8_t gprs_rlcmac_tbf::tfi() const
|
||||
|
@ -372,6 +426,9 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
|
|||
int release();
|
||||
int abort();
|
||||
|
||||
void egprs_calc_window_size();
|
||||
void update_coding_scheme_counter_dl(const GprsCodingScheme cs);
|
||||
|
||||
/* TODO: add the gettimeofday as parameter */
|
||||
struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
|
||||
|
||||
|
@ -390,6 +447,7 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
|
|||
struct BandWidth {
|
||||
struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
|
||||
uint32_t dl_bw_octets; /* number of octets since bw_tv */
|
||||
uint32_t dl_throughput; /* throughput to be displayed in stats */
|
||||
|
||||
struct timeval dl_loss_tv; /* timestamp for loss calculation */
|
||||
uint16_t dl_loss_lost; /* sum of lost packets */
|
||||
|
@ -398,6 +456,9 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
|
|||
BandWidth();
|
||||
} m_bw;
|
||||
|
||||
struct rate_ctr_group *m_dl_gprs_ctrs;
|
||||
struct rate_ctr_group *m_dl_egprs_ctrs;
|
||||
|
||||
protected:
|
||||
struct ana_result {
|
||||
unsigned received_packets;
|
||||
|
@ -421,6 +482,11 @@ protected:
|
|||
int analyse_errors(char *show_rbb, uint8_t ssn, ana_result *res);
|
||||
void schedule_next_frame();
|
||||
|
||||
enum egprs_rlc_dl_reseg_bsn_state egprs_dl_get_data
|
||||
(int bsn, uint8_t **block_data);
|
||||
unsigned int get_egprs_dl_spb_status(int bsn);
|
||||
enum egprs_rlcmac_dl_spb get_egprs_dl_spb(int bsn);
|
||||
|
||||
struct osmo_timer_list m_llc_timer;
|
||||
};
|
||||
|
||||
|
@ -439,6 +505,25 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
|
|||
int assemble_forward_llc(const gprs_rlc_data *data);
|
||||
int snd_ul_ud();
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_spb(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx);
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_first_seg(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx);
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_second_seg(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx);
|
||||
|
||||
void egprs_calc_ulwindow_size();
|
||||
|
||||
void update_coding_scheme_counter_ul(const GprsCodingScheme cs);
|
||||
|
||||
/* 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
|
||||
|
@ -451,6 +536,9 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
|
|||
uint8_t m_contention_resolution_done; /* set after done */
|
||||
uint8_t m_final_ack_sent; /* set if we sent final ack */
|
||||
|
||||
struct rate_ctr_group *m_ul_gprs_ctrs;
|
||||
struct rate_ctr_group *m_ul_egprs_ctrs;
|
||||
|
||||
protected:
|
||||
void maybe_schedule_uplink_acknack(const gprs_rlc_data_info *rlc);
|
||||
};
|
||||
|
|
401
src/tbf_dl.cpp
401
src/tbf_dl.cpp
|
@ -34,6 +34,7 @@
|
|||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gprs/gprs_bssgp_bss.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -43,11 +44,6 @@ extern "C" {
|
|||
/* After sending these frames, we poll for ack/nack. */
|
||||
#define POLL_ACK_AFTER_FRAMES 20
|
||||
|
||||
extern "C" {
|
||||
int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
|
||||
uint8_t num_frames, uint32_t num_octets);
|
||||
}
|
||||
|
||||
static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf,
|
||||
const uint8_t ms_class)
|
||||
{
|
||||
|
@ -124,7 +120,7 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
|
|||
{
|
||||
uint8_t ss;
|
||||
int8_t use_trx;
|
||||
uint16_t ta = 0;
|
||||
uint16_t ta = GSM48_TA_INVALID;
|
||||
struct gprs_rlcmac_ul_tbf *ul_tbf = NULL, *old_ul_tbf;
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
|
||||
GprsMs *ms;
|
||||
|
@ -218,7 +214,7 @@ int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts,
|
|||
LOGP(DRLCMAC, LOGL_NOTICE,
|
||||
"%s IMSI %s: "
|
||||
"moving DL TBF to new MS object\n",
|
||||
dl_tbf->name(), imsi);
|
||||
ms_old->dl_tbf()->name(), imsi);
|
||||
dl_tbf = ms_old->dl_tbf();
|
||||
/* Move the DL TBF to the new MS */
|
||||
dl_tbf->set_ms(ms);
|
||||
|
@ -314,7 +310,7 @@ drop_frame:
|
|||
LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU "
|
||||
"because lifetime limit reached, "
|
||||
"count=%u new_queue_size=%zu\n",
|
||||
tbf_name(this), frames, llc_queue()->size());
|
||||
tbf_name(this), frames, llc_queue_size());
|
||||
if (frames > 0xff)
|
||||
frames = 0xff;
|
||||
if (octets > 0xffffff)
|
||||
|
@ -359,13 +355,12 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
|||
{
|
||||
int bsn;
|
||||
int data_len2, force_data_len = -1;
|
||||
GprsCodingScheme cs2;
|
||||
GprsCodingScheme force_cs;
|
||||
|
||||
bsn = m_window.resend_needed();
|
||||
|
||||
if (previous_bsn >= 0) {
|
||||
force_cs = m_rlc.block(previous_bsn)->cs;
|
||||
force_cs = m_rlc.block(previous_bsn)->cs_current_trans;
|
||||
if (!force_cs.isEgprs())
|
||||
return -1;
|
||||
force_data_len = m_rlc.block(previous_bsn)->len;
|
||||
|
@ -379,7 +374,27 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
|||
m_window.mod_sns(bsn - previous_bsn) > RLC_EGPRS_MAX_BSN_DELTA)
|
||||
return -1;
|
||||
|
||||
cs2 = m_rlc.block(bsn)->cs;
|
||||
if (is_egprs_enabled()) {
|
||||
/* Table 8.1.1.2 and Table 8.1.1.1 of 44.060 */
|
||||
m_rlc.block(bsn)->cs_current_trans =
|
||||
GprsCodingScheme::get_retx_mcs(
|
||||
m_rlc.block(bsn)->cs_init,
|
||||
ms()->current_cs_dl(),
|
||||
bts->bts_data()->dl_arq_type);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"- initial_cs_dl(%d) last_mcs(%d)"
|
||||
" demanded_mcs(%d) cs_trans(%d)"
|
||||
" arq_type(%d) bsn(%d)\n",
|
||||
m_rlc.block(bsn)->cs_init.to_num(),
|
||||
m_rlc.block(bsn)->cs_last.to_num(),
|
||||
ms()->current_cs_dl().to_num(),
|
||||
m_rlc.block(bsn)->cs_current_trans.to_num(),
|
||||
bts->bts_data()->dl_arq_type, bsn);
|
||||
} else
|
||||
m_rlc.block(bsn)->cs_current_trans =
|
||||
m_rlc.block(bsn)->cs_last;
|
||||
|
||||
data_len2 = m_rlc.block(bsn)->len;
|
||||
if (force_data_len > 0 && force_data_len != data_len2)
|
||||
return -1;
|
||||
|
@ -402,13 +417,14 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
|||
if (restart_bsn_cycle())
|
||||
return take_next_bsn(fn, previous_bsn, may_combine);
|
||||
} else if (have_data()) {
|
||||
GprsCodingScheme new_cs;
|
||||
/* New blocks may be send */
|
||||
cs2 = force_cs ? force_cs : current_cs();
|
||||
new_cs = force_cs ? force_cs : current_cs();
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"- Sending new block at BSN %d, CS=%s\n",
|
||||
m_window.v_s(), cs2.name());
|
||||
m_window.v_s(), new_cs.name());
|
||||
|
||||
bsn = create_new_bsn(fn, cs2);
|
||||
bsn = create_new_bsn(fn, new_cs);
|
||||
} else if (!m_window.window_empty()) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
|
||||
"because all blocks have been transmitted (FLOW).\n",
|
||||
|
@ -422,7 +438,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) {
|
||||
|
@ -430,10 +446,11 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
|
|||
LOGP(DRLCMACDL, LOGL_DEBUG,
|
||||
"- Nothing else to send, Re-transmit final block!\n");
|
||||
bsn = m_window.v_s_mod(-1);
|
||||
bts->rlc_final_block_resent();
|
||||
bts->rlc_resent();
|
||||
}
|
||||
|
||||
*may_combine = cs2.numDataBlocks() > 1;
|
||||
*may_combine = m_rlc.block(bsn)->cs_current_trans.numDataBlocks() > 1;
|
||||
|
||||
return bsn;
|
||||
}
|
||||
|
@ -504,7 +521,13 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
/* now we still have untransmitted LLC data, so we fill mac block */
|
||||
rlc_data = m_rlc.block(bsn);
|
||||
data = rlc_data->prepare(block_data_len);
|
||||
rlc_data->cs = cs;
|
||||
rlc_data->cs_last = cs;
|
||||
rlc_data->cs_current_trans = cs;
|
||||
|
||||
/* Initialise the variable related to DL SPB */
|
||||
rlc_data->spb_status.block_status_dl = EGPRS_RESEG_DL_DEFAULT;
|
||||
rlc_data->cs_init = cs;
|
||||
|
||||
rlc_data->len = block_data_len;
|
||||
|
||||
rdbi = &(rlc_data->block_info);
|
||||
|
@ -517,8 +540,11 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
|
||||
do {
|
||||
bool is_final;
|
||||
int payload_written = 0;
|
||||
|
||||
if (m_llc.frame_length() == 0) {
|
||||
/* nothing to sent - delay the release of the TBF */
|
||||
|
||||
int space = block_data_len - write_offset;
|
||||
/* A header will need to by added, so we just need
|
||||
* space-1 octets */
|
||||
|
@ -539,10 +565,13 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
m_llc.frame_length(), frames_since_last_drain(fn));
|
||||
}
|
||||
|
||||
is_final = llc_queue()->size() == 0 && !keep_open(fn);
|
||||
is_final = llc_queue_size() == 0 && !keep_open(fn);
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(rdbi, cs,
|
||||
&m_llc, &write_offset, &num_chunks, data, is_final);
|
||||
&m_llc, &write_offset, &num_chunks, data, is_final, &payload_written);
|
||||
|
||||
if (payload_written > 0)
|
||||
bts->rlc_dl_payload_bytes(payload_written);
|
||||
|
||||
if (ar == Encoding::AR_NEED_MORE_BLOCKS)
|
||||
break;
|
||||
|
@ -550,6 +579,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s"
|
||||
"len=%d\n", tbf_name(this), m_llc.frame_length());
|
||||
gprs_rlcmac_dl_bw(this, m_llc.frame_length());
|
||||
bts->llc_dl_bytes(m_llc.frame_length());
|
||||
m_llc.reset();
|
||||
|
||||
if (is_final) {
|
||||
|
@ -562,7 +592,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
|
|||
} while (ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "data block (BSN %d, %s): %s\n",
|
||||
bsn, rlc_data->cs.name(),
|
||||
bsn, rlc_data->cs_last.name(),
|
||||
osmo_hexdump(rlc_data->block, block_data_len));
|
||||
/* raise send state and set ack state array */
|
||||
m_window.m_v_b.mark_unacked(bsn);
|
||||
|
@ -589,8 +619,12 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
GprsCodingScheme cs;
|
||||
int bsns[ARRAY_SIZE(rlc.block_info)];
|
||||
unsigned num_bsns;
|
||||
int punct[ARRAY_SIZE(rlc.block_info)];
|
||||
enum egprs_puncturing_values punct[ARRAY_SIZE(rlc.block_info)];
|
||||
bool need_padding = false;
|
||||
enum egprs_rlcmac_dl_spb spb = EGPRS_RLCMAC_DL_NO_RETX;
|
||||
unsigned int spb_status = get_egprs_dl_spb_status(index);
|
||||
|
||||
memset(punct, EGPRS_PS_INVALID, sizeof(punct));
|
||||
|
||||
/*
|
||||
* TODO: This is an experimental work-around to put 2 BSN into
|
||||
|
@ -600,7 +634,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 +644,29 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
num_bsns += 1;
|
||||
}
|
||||
|
||||
if (num_bsns == 1) {
|
||||
/* TODO: remove the conditional when MCS-6 padding isn't
|
||||
* failing to be decoded by MEs anymore */
|
||||
if (cs != GprsCodingScheme(GprsCodingScheme::MCS8))
|
||||
cs.decToSingleBlock(&need_padding);
|
||||
update_coding_scheme_counter_dl(cs);
|
||||
/*
|
||||
* if the intial mcs is 8 and retransmission mcs is either 6 or 3
|
||||
* we have to include the padding of 6 octets in first segment
|
||||
*/
|
||||
if ((GprsCodingScheme::Scheme(cs_init) == GprsCodingScheme::MCS8) &&
|
||||
(GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6 ||
|
||||
GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS3)) {
|
||||
if (spb_status == EGPRS_RESEG_DL_DEFAULT ||
|
||||
spb_status == EGPRS_RESEG_SECOND_SEG_SENT)
|
||||
need_padding = true;
|
||||
} else if (num_bsns == 1) {
|
||||
cs.decToSingleBlock(&need_padding);
|
||||
}
|
||||
|
||||
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding);
|
||||
spb = get_egprs_dl_spb(index);
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- need_padding %d spb_status %d spb %d"
|
||||
"(BSN1 %d BSN2 %d)\n",
|
||||
need_padding,
|
||||
spb_status, spb, index, index2);
|
||||
|
||||
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding, spb);
|
||||
|
||||
rlc.usf = 7; /* will be set at scheduler */
|
||||
rlc.pr = 0; /* FIXME: power reduction */
|
||||
|
@ -630,14 +680,17 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
|
||||
msg_data = msgb_put(dl_msg, msg_len);
|
||||
|
||||
OSMO_ASSERT(rlc.num_data_blocks <= ARRAY_SIZE(rlc.block_info));
|
||||
OSMO_ASSERT(rlc.num_data_blocks > 0);
|
||||
|
||||
/* Copy block(s) to RLC message */
|
||||
for (data_block_idx = 0; data_block_idx < rlc.num_data_blocks;
|
||||
data_block_idx++)
|
||||
{
|
||||
int bsn;
|
||||
GprsCodingScheme cs_enc;
|
||||
uint8_t *block_data;
|
||||
gprs_rlc_data_block_info *rdbi, *block_info;
|
||||
enum egprs_rlc_dl_reseg_bsn_state reseg_status;
|
||||
|
||||
/* Check if there are more blocks than BSNs */
|
||||
if (data_block_idx < num_bsns)
|
||||
|
@ -645,25 +698,45 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
else
|
||||
bsn = bsns[0];
|
||||
|
||||
cs_enc = m_rlc.block(bsn)->cs;
|
||||
/* Get current puncturing scheme from block */
|
||||
|
||||
/* get data and header from current block */
|
||||
block_data = m_rlc.block(bsn)->block;
|
||||
m_rlc.block(bsn)->next_ps = gprs_get_punct_scheme(
|
||||
m_rlc.block(bsn)->next_ps,
|
||||
m_rlc.block(bsn)->cs_last, cs, spb);
|
||||
|
||||
/* TODO: Use real puncturing values */
|
||||
punct[data_block_idx] = data_block_idx;
|
||||
if (cs.isEgprs()) {
|
||||
OSMO_ASSERT(m_rlc.block(bsn)->next_ps >= EGPRS_PS_1);
|
||||
OSMO_ASSERT(m_rlc.block(bsn)->next_ps <= EGPRS_PS_3);
|
||||
}
|
||||
punct[data_block_idx] = m_rlc.block(bsn)->next_ps;
|
||||
|
||||
rdbi = &rlc.block_info[data_block_idx];
|
||||
block_info = &m_rlc.block(bsn)->block_info;
|
||||
|
||||
if(rdbi->data_len != m_rlc.block(bsn)->len) {
|
||||
LOGP(DRLCMACDL, LOGL_ERROR,
|
||||
"ERROR: Expected len = %d for %s instead of "
|
||||
"%d in data unit %d (BSN %d, %s)\n",
|
||||
rdbi->data_len, cs.name(), m_rlc.block(bsn)->len,
|
||||
data_block_idx, bsn, cs_enc.name());
|
||||
OSMO_ASSERT(rdbi->data_len == m_rlc.block(bsn)->len);
|
||||
/*
|
||||
* get data and header from current block
|
||||
* function returns the reseg status
|
||||
*/
|
||||
reseg_status = egprs_dl_get_data(bsn, &block_data);
|
||||
m_rlc.block(bsn)->spb_status.block_status_dl = reseg_status;
|
||||
|
||||
/*
|
||||
* If it is first segment of the split block set the state of
|
||||
* bsn to nacked. If it is the first segment dont update the
|
||||
* next ps value of bsn. since next segment also needs same cps
|
||||
*/
|
||||
if (spb == EGPRS_RLCMAC_DL_FIRST_SEG)
|
||||
m_window.m_v_b.mark_nacked(bsn);
|
||||
else {
|
||||
/*
|
||||
* TODO: Need to handle 2 same bsns
|
||||
* in header type 1
|
||||
*/
|
||||
gprs_update_punct_scheme(&m_rlc.block(bsn)->next_ps,
|
||||
cs);
|
||||
}
|
||||
|
||||
m_rlc.block(bsn)->cs_last = cs;
|
||||
rdbi->e = block_info->e;
|
||||
rdbi->cv = block_info->cv;
|
||||
rdbi->bsn = bsn;
|
||||
|
@ -742,8 +815,6 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
|
|||
/* Increment TX-counter */
|
||||
m_tx_counter++;
|
||||
|
||||
bts->rlc_sent();
|
||||
|
||||
return dl_msg;
|
||||
}
|
||||
|
||||
|
@ -765,6 +836,11 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
|
|||
unsigned received_packets = 0, lost_packets = 0;
|
||||
unsigned num_blocks = strlen(show_rbb);
|
||||
|
||||
unsigned distance = m_window.distance();
|
||||
|
||||
num_blocks = num_blocks > distance
|
||||
? distance : num_blocks;
|
||||
|
||||
/* SSN - 1 is in range V(A)..V(S)-1 */
|
||||
for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
|
||||
bool is_received;
|
||||
|
@ -796,7 +872,7 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
|
|||
|
||||
/* Get statistics for current CS */
|
||||
|
||||
if (rlc_data->cs != current_cs()) {
|
||||
if (rlc_data->cs_last != current_cs()) {
|
||||
/* This block has already been encoded with a different
|
||||
* CS, so it doesn't help us to decide, whether the
|
||||
* current CS is ok. Ignore it. */
|
||||
|
@ -837,13 +913,15 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
|
|||
int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
|
||||
const struct bitvec *rbb)
|
||||
{
|
||||
int16_t dist; /* must be signed */
|
||||
unsigned dist;
|
||||
uint16_t lost = 0, received = 0;
|
||||
char show_v_b[RLC_MAX_SNS + 1];
|
||||
char show_rbb[RLC_MAX_SNS + 1];
|
||||
int error_rate;
|
||||
struct ana_result ana_res;
|
||||
unsigned num_blocks = rbb->cur_bit;
|
||||
dist = m_window.distance();
|
||||
unsigned num_blocks = rbb->cur_bit > dist
|
||||
? dist : rbb->cur_bit;
|
||||
unsigned behind_last_bsn = m_window.mod_sns(first_bsn + num_blocks);
|
||||
|
||||
Decoding::extract_rbb(rbb, show_rbb);
|
||||
|
@ -852,31 +930,13 @@ int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
|
|||
"(BSN=%d) R=ACK I=NACK\n", first_bsn,
|
||||
show_rbb, m_window.mod_sns(behind_last_bsn - 1));
|
||||
|
||||
/* apply received array to receive state (first_bsn..behind_last_bsn-1) */
|
||||
if (num_blocks > 0) {
|
||||
/* calculate distance of ssn from V(S) */
|
||||
dist = m_window.mod_sns(m_window.v_s() - behind_last_bsn);
|
||||
/* check if distance is less than distance V(A)..V(S) */
|
||||
if (dist >= m_window.distance()) {
|
||||
/* this might happpen, if the downlink assignment
|
||||
* was not received by ms and the ack refers
|
||||
* to previous TBF
|
||||
* FIXME: we should implement polling for
|
||||
* control ack!
|
||||
* TODO: check whether this FIXME still makes sense
|
||||
*/
|
||||
LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of "
|
||||
"V(A)..V(S) range %s Free TBF!\n", tbf_name(this));
|
||||
return 1; /* indicate to free TBF */
|
||||
}
|
||||
}
|
||||
|
||||
error_rate = analyse_errors(show_rbb, behind_last_bsn, &ana_res);
|
||||
|
||||
if (bts_data()->cs_adj_enabled && ms())
|
||||
ms()->update_error_rate(this, error_rate);
|
||||
|
||||
m_window.update(bts, rbb, first_bsn, &lost, &received);
|
||||
rate_ctr_add(&m_ctrs->ctr[TBF_CTR_RLC_NACKED], lost);
|
||||
|
||||
/* report lost and received packets */
|
||||
gprs_rlcmac_received_lost(this, received, lost);
|
||||
|
@ -935,6 +995,7 @@ int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb)
|
|||
|
||||
m_window.update(bts, show_rbb, ssn,
|
||||
&lost, &received);
|
||||
rate_ctr_add(&m_ctrs->ctr[TBF_CTR_RLC_NACKED], lost);
|
||||
|
||||
/* report lost and received packets */
|
||||
gprs_rlcmac_received_lost(this, received, lost);
|
||||
|
@ -968,7 +1029,7 @@ int gprs_rlcmac_dl_tbf::maybe_start_new_window()
|
|||
release();
|
||||
|
||||
/* check for LLC PDU in the LLC Queue */
|
||||
if (llc_queue()->size() > 0)
|
||||
if (llc_queue_size() > 0)
|
||||
/* we have more data so we will re-use this tbf */
|
||||
establish_dl_tbf_on_pacch();
|
||||
|
||||
|
@ -1086,7 +1147,7 @@ bool gprs_rlcmac_dl_tbf::need_control_ts() const
|
|||
bool gprs_rlcmac_dl_tbf::have_data() const
|
||||
{
|
||||
return m_llc.chunk_size() > 0 ||
|
||||
(llc_queue() && llc_queue()->size() > 0);
|
||||
(llc_queue_size() > 0);
|
||||
}
|
||||
|
||||
int gprs_rlcmac_dl_tbf::frames_since_last_poll(unsigned fn) const
|
||||
|
@ -1125,3 +1186,209 @@ bool gprs_rlcmac_dl_tbf::keep_open(unsigned fn) const
|
|||
keep_time_frames = msecs_to_frames(bts_data()->dl_tbf_idle_msec);
|
||||
return frames_since_last_drain(fn) <= keep_time_frames;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the pointer to data which needs
|
||||
* to be copied. Also updates the status of the block related to
|
||||
* Split block handling in the RLC/MAC block.
|
||||
*/
|
||||
enum egprs_rlc_dl_reseg_bsn_state
|
||||
gprs_rlcmac_dl_tbf::egprs_dl_get_data(int bsn, uint8_t **block_data)
|
||||
{
|
||||
gprs_rlc_data *rlc_data = m_rlc.block(bsn);
|
||||
egprs_rlc_dl_reseg_bsn_state *block_status_dl =
|
||||
&rlc_data->spb_status.block_status_dl;
|
||||
|
||||
GprsCodingScheme &cs_current_trans = m_rlc.block(bsn)->cs_current_trans;
|
||||
GprsCodingScheme &cs_init = m_rlc.block(bsn)->cs_init;
|
||||
*block_data = &rlc_data->block[0];
|
||||
|
||||
/*
|
||||
* Table 10.3a.0.1 of 44.060
|
||||
* MCS6,9: second segment starts at 74/2 = 37
|
||||
* MCS5,7: second segment starts at 56/2 = 28
|
||||
* MCS8: second segment starts at 31
|
||||
* MCS4: second segment starts at 44/2 = 22
|
||||
*/
|
||||
if (cs_current_trans.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3) {
|
||||
if (*block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
|
||||
switch (GprsCodingScheme::Scheme(cs_init)) {
|
||||
case GprsCodingScheme::MCS6 :
|
||||
case GprsCodingScheme::MCS9 :
|
||||
*block_data = &rlc_data->block[37];
|
||||
break;
|
||||
case GprsCodingScheme::MCS7 :
|
||||
case GprsCodingScheme::MCS5 :
|
||||
*block_data = &rlc_data->block[28];
|
||||
break;
|
||||
case GprsCodingScheme::MCS8 :
|
||||
*block_data = &rlc_data->block[31];
|
||||
break;
|
||||
case GprsCodingScheme::MCS4 :
|
||||
*block_data = &rlc_data->block[22];
|
||||
break;
|
||||
default:
|
||||
LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
|
||||
"--%s hit invalid condition. headerType(%d) "
|
||||
" blockstatus(%d) cs(%s) PLEASE FIX!\n", name(),
|
||||
cs_current_trans.headerTypeData(),
|
||||
*block_status_dl, cs_init.name());
|
||||
break;
|
||||
|
||||
}
|
||||
return EGPRS_RESEG_SECOND_SEG_SENT;
|
||||
} else if ((cs_init.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
|
||||
(cs_init.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
|
||||
return EGPRS_RESEG_FIRST_SEG_SENT;
|
||||
} else if ((GprsCodingScheme::Scheme(cs_init) ==
|
||||
GprsCodingScheme::MCS4) &&
|
||||
(GprsCodingScheme::Scheme(cs_current_trans) ==
|
||||
GprsCodingScheme::MCS1)) {
|
||||
return EGPRS_RESEG_FIRST_SEG_SENT;
|
||||
}
|
||||
}
|
||||
return EGPRS_RESEG_DL_DEFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the status of split block
|
||||
* for RLC/MAC block.
|
||||
*/
|
||||
unsigned int gprs_rlcmac_dl_tbf::get_egprs_dl_spb_status(const int bsn)
|
||||
{
|
||||
const gprs_rlc_data *rlc_data = m_rlc.block(bsn);
|
||||
|
||||
return rlc_data->spb_status.block_status_dl;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the spb value to be sent OTA
|
||||
* for RLC/MAC block.
|
||||
*/
|
||||
enum egprs_rlcmac_dl_spb gprs_rlcmac_dl_tbf::get_egprs_dl_spb(const int bsn)
|
||||
{
|
||||
struct gprs_rlc_data *rlc_data = m_rlc.block(bsn);
|
||||
egprs_rlc_dl_reseg_bsn_state block_status_dl =
|
||||
rlc_data->spb_status.block_status_dl;
|
||||
|
||||
GprsCodingScheme &cs_current_trans = m_rlc.block(bsn)->cs_current_trans;
|
||||
GprsCodingScheme &cs_init = m_rlc.block(bsn)->cs_init;
|
||||
|
||||
/* Table 10.4.8b.1 of 44.060 */
|
||||
if (cs_current_trans.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3) {
|
||||
/*
|
||||
* if we are sending the second segment the spb should be 3
|
||||
* other wise it should be 2
|
||||
*/
|
||||
if (block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
|
||||
|
||||
/* statistics */
|
||||
bts->spb_downlink_second_segment();
|
||||
return EGPRS_RLCMAC_DL_SEC_SEG;
|
||||
} else if ((cs_init.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
|
||||
(cs_init.headerTypeData() ==
|
||||
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
|
||||
bts->spb_downlink_first_segment();
|
||||
return EGPRS_RLCMAC_DL_FIRST_SEG;
|
||||
} else if ((GprsCodingScheme::Scheme(cs_init) ==
|
||||
GprsCodingScheme::MCS4) &&
|
||||
(GprsCodingScheme::Scheme(cs_current_trans) ==
|
||||
GprsCodingScheme::MCS1)) {
|
||||
bts->spb_downlink_first_segment();
|
||||
return EGPRS_RLCMAC_DL_FIRST_SEG;
|
||||
}
|
||||
}
|
||||
/* Non SPB cases 0 is reurned */
|
||||
return EGPRS_RLCMAC_DL_NO_RETX;
|
||||
}
|
||||
|
||||
void gprs_rlcmac_dl_tbf::egprs_calc_window_size()
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data = bts->bts_data();
|
||||
unsigned int num_pdch = pcu_bitcount(dl_slots());
|
||||
unsigned int ws = bts_data->ws_base + num_pdch * bts_data->ws_pdch;
|
||||
|
||||
ws = (ws / 32) * 32;
|
||||
ws = OSMO_MAX(64, ws);
|
||||
|
||||
if (num_pdch == 1)
|
||||
ws = OSMO_MIN(192, ws);
|
||||
else
|
||||
ws = OSMO_MIN(128 * num_pdch, ws);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_INFO, "%s: Setting EGPRS window size to %d\n",
|
||||
name(), ws);
|
||||
|
||||
m_window.set_ws(ws);
|
||||
}
|
||||
|
||||
void gprs_rlcmac_dl_tbf::update_coding_scheme_counter_dl(const GprsCodingScheme cs)
|
||||
{
|
||||
uint8_t coding_scheme = 0;
|
||||
|
||||
coding_scheme = GprsCodingScheme::Scheme(cs);
|
||||
if (cs.isGprs()) {
|
||||
switch (coding_scheme) {
|
||||
case GprsCodingScheme::CS1 :
|
||||
bts->gprs_dl_cs1();
|
||||
rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS1]);
|
||||
break;
|
||||
case GprsCodingScheme::CS2 :
|
||||
bts->gprs_dl_cs2();
|
||||
rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS2]);
|
||||
break;
|
||||
case GprsCodingScheme::CS3 :
|
||||
bts->gprs_dl_cs3();
|
||||
rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS3]);
|
||||
break;
|
||||
case GprsCodingScheme::CS4 :
|
||||
bts->gprs_dl_cs4();
|
||||
rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS4]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (coding_scheme) {
|
||||
case GprsCodingScheme::MCS1 :
|
||||
bts->egprs_dl_mcs1();
|
||||
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS1]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS2 :
|
||||
bts->egprs_dl_mcs2();
|
||||
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS2]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS3 :
|
||||
bts->egprs_dl_mcs3();
|
||||
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS3]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS4 :
|
||||
bts->egprs_dl_mcs4();
|
||||
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS4]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS5 :
|
||||
bts->egprs_dl_mcs5();
|
||||
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS5]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS6 :
|
||||
bts->egprs_dl_mcs6();
|
||||
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS6]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS7 :
|
||||
bts->egprs_dl_mcs7();
|
||||
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS7]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS8 :
|
||||
bts->egprs_dl_mcs8();
|
||||
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS8]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS9 :
|
||||
bts->egprs_dl_mcs9();
|
||||
rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS9]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
283
src/tbf_ul.cpp
283
src/tbf_ul.cpp
|
@ -29,6 +29,8 @@
|
|||
#include <decoding.h>
|
||||
#include <pcu_l1_if.h>
|
||||
|
||||
#include "pcu_utils.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
@ -51,7 +53,7 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
|
|||
const uint8_t *data = _data->block;
|
||||
uint8_t len = _data->len;
|
||||
const struct gprs_rlc_data_block_info *rdbi = &_data->block_info;
|
||||
GprsCodingScheme cs = _data->cs;
|
||||
GprsCodingScheme cs = _data->cs_last;
|
||||
|
||||
Decoding::RlcData frames[16], *frame;
|
||||
int i, num_frames = 0;
|
||||
|
@ -60,25 +62,32 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
|
|||
LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
|
||||
|
||||
num_frames = Decoding::rlc_data_from_ul_data(
|
||||
rdbi, cs, data, &(frames[0]), sizeof(frames),
|
||||
rdbi, cs, data, &(frames[0]), ARRAY_SIZE(frames),
|
||||
&dummy_tlli);
|
||||
|
||||
/* create LLC frames */
|
||||
for (i = 0; i < num_frames; i++) {
|
||||
frame = frames + i;
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset %d, "
|
||||
"length=%d, is_complete=%d\n",
|
||||
i + 1, frame->offset, frame->length, frame->is_complete);
|
||||
if (frame->length) {
|
||||
bts->rlc_ul_payload_bytes(frame->length);
|
||||
|
||||
m_llc.append_frame(data + frame->offset, frame->length);
|
||||
m_llc.consume(frame->length);
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d "
|
||||
"starts at offset %d, "
|
||||
"length=%d, is_complete=%d\n",
|
||||
i + 1, frame->offset, frame->length,
|
||||
frame->is_complete);
|
||||
|
||||
m_llc.append_frame(data + frame->offset, frame->length);
|
||||
m_llc.consume(frame->length);
|
||||
}
|
||||
|
||||
if (frame->is_complete) {
|
||||
/* send frame to SGSN */
|
||||
LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n",
|
||||
tbf_name(this) , m_llc.frame_length());
|
||||
snd_ul_ud();
|
||||
bts->llc_ul_bytes(m_llc.frame_length());
|
||||
m_llc.reset();
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +147,7 @@ struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn, uint8_t ts)
|
|||
return msg;
|
||||
}
|
||||
|
||||
/*! \brief receive data from PDCH/L1 */
|
||||
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
|
||||
const struct gprs_rlc_data_info *rlc,
|
||||
uint8_t *data, struct pcu_l1_meas *meas)
|
||||
|
@ -167,7 +177,7 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
|
|||
|
||||
/* Increment RX-counter */
|
||||
this->m_rx_counter++;
|
||||
|
||||
update_coding_scheme_counter_ul(rlc->cs);
|
||||
/* Loop over num_blocks */
|
||||
for (block_idx = 0; block_idx < rlc->num_data_blocks; block_idx++) {
|
||||
int num_chunks;
|
||||
|
@ -219,36 +229,28 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
|
|||
rdbi->bsn, m_window.v_q(),
|
||||
m_window.mod_sns(m_window.v_q() + ws - 1));
|
||||
block = m_rlc.block(rdbi->bsn);
|
||||
block->block_info = *rdbi;
|
||||
block->cs = rlc->cs;
|
||||
OSMO_ASSERT(rdbi->data_len < sizeof(block->block));
|
||||
OSMO_ASSERT(rdbi->data_len <= sizeof(block->block));
|
||||
rlc_data = &(block->block[0]);
|
||||
/* TODO: Handle SPB != 0 -> Set length to 2*len, add offset if
|
||||
* 2nd part. Note that resegmentation is currently disabled
|
||||
* within the UL assignment.
|
||||
*/
|
||||
if (rdbi->spb) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"Got SPB != 0 but resegmentation has been "
|
||||
"disabled, skipping %s data block with BSN %d, "
|
||||
"TFI=%d.\n", rlc->cs.name(), rdbi->bsn,
|
||||
rlc->tfi);
|
||||
continue;
|
||||
}
|
||||
|
||||
block->len =
|
||||
Decoding::rlc_copy_to_aligned_buffer(rlc, block_idx, data,
|
||||
rlc_data);
|
||||
if (rdbi->spb) {
|
||||
egprs_rlc_ul_reseg_bsn_state assemble_status;
|
||||
|
||||
assemble_status = handle_egprs_ul_spb(rlc,
|
||||
block, data, block_idx);
|
||||
|
||||
if (assemble_status != EGPRS_RESEG_DEFAULT)
|
||||
return 0;
|
||||
} else {
|
||||
block->block_info = *rdbi;
|
||||
block->cs_last = rlc->cs;
|
||||
block->len =
|
||||
Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data, rlc_data);
|
||||
}
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"%s: data_length=%d, data=%s\n",
|
||||
name(), block->len, osmo_hexdump(rlc_data, block->len));
|
||||
|
||||
/* TODO: Handle SPB != 0 -> set state to partly received
|
||||
* (upper/lower) and continue with the loop, unless the other
|
||||
* part is already present.
|
||||
*/
|
||||
|
||||
/* Get/Handle TLLI */
|
||||
if (rdbi->ti) {
|
||||
num_chunks = Decoding::rlc_data_from_ul_data(
|
||||
|
@ -389,3 +391,220 @@ int gprs_rlcmac_ul_tbf::snd_ul_ud()
|
|||
return 0;
|
||||
}
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_second_seg(
|
||||
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx)
|
||||
{
|
||||
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
|
||||
union split_block_status *spb_status = &block->spb_status;
|
||||
uint8_t *rlc_data = &block->block[0];
|
||||
|
||||
bts->spb_uplink_second_segment();
|
||||
|
||||
if (spb_status->block_status_ul &
|
||||
EGPRS_RESEG_FIRST_SEG_RXD) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"---%s: Second seg is received "
|
||||
"first seg is already present "
|
||||
"set the status to complete\n", name());
|
||||
spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
|
||||
|
||||
block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data, rlc_data + block->len);
|
||||
block->block_info.data_len += rdbi->data_len;
|
||||
} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"---%s: Second seg is received "
|
||||
"first seg is not received "
|
||||
"set the status to second seg received\n",
|
||||
name());
|
||||
|
||||
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data,
|
||||
rlc_data + rlc->block_info[block_idx].data_len);
|
||||
|
||||
spb_status->block_status_ul = EGPRS_RESEG_SECOND_SEG_RXD;
|
||||
block->block_info = *rdbi;
|
||||
}
|
||||
return spb_status->block_status_ul;
|
||||
}
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_first_seg(
|
||||
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx)
|
||||
{
|
||||
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
|
||||
uint8_t *rlc_data = &block->block[0];
|
||||
union split_block_status *spb_status = &block->spb_status;
|
||||
|
||||
bts->spb_uplink_first_segment();
|
||||
|
||||
if (spb_status->block_status_ul & EGPRS_RESEG_SECOND_SEG_RXD) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"---%s: First seg is received "
|
||||
"second seg is already present "
|
||||
"set the status to complete\n", name());
|
||||
|
||||
block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data, rlc_data);
|
||||
|
||||
block->block_info.data_len = block->len;
|
||||
spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
|
||||
} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"---%s: First seg is received "
|
||||
"second seg is not received "
|
||||
"set the status to first seg "
|
||||
"received\n", name());
|
||||
|
||||
spb_status->block_status_ul = EGPRS_RESEG_FIRST_SEG_RXD;
|
||||
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
|
||||
block_idx, data, rlc_data);
|
||||
block->block_info = *rdbi;
|
||||
}
|
||||
return spb_status->block_status_ul;
|
||||
}
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_spb(
|
||||
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
|
||||
uint8_t *data, const uint8_t block_idx)
|
||||
{
|
||||
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
|
||||
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"--%s: Got SPB(%d) "
|
||||
"cs(%s) data block with BSN (%d), "
|
||||
"TFI(%d).\n", name(), rdbi->spb, rlc->cs.name(), rdbi->bsn,
|
||||
rlc->tfi);
|
||||
|
||||
egprs_rlc_ul_reseg_bsn_state assemble_status = EGPRS_RESEG_INVALID;
|
||||
|
||||
/* Section 10.4.8b of 44.060*/
|
||||
if (rdbi->spb == 2)
|
||||
assemble_status = handle_egprs_ul_first_seg(rlc,
|
||||
block, data, block_idx);
|
||||
else if (rdbi->spb == 3)
|
||||
assemble_status = handle_egprs_ul_second_seg(rlc,
|
||||
block, data, block_idx);
|
||||
else {
|
||||
LOGP(DRLCMACUL, LOGL_ERROR,
|
||||
"--%s: spb(%d) Not supported SPB for this EGPRS "
|
||||
"configuration\n",
|
||||
name(), rdbi->spb);
|
||||
}
|
||||
|
||||
/*
|
||||
* When the block is successfully constructed out of segmented blocks
|
||||
* upgrade the MCS to the type 2
|
||||
*/
|
||||
if (assemble_status == EGPRS_RESEG_DEFAULT) {
|
||||
switch (GprsCodingScheme::Scheme(rlc->cs)) {
|
||||
case GprsCodingScheme::MCS3 :
|
||||
block->cs_last = GprsCodingScheme::MCS6;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"--%s: Upgrading to MCS6\n", name());
|
||||
break;
|
||||
case GprsCodingScheme::MCS2 :
|
||||
block->cs_last = GprsCodingScheme::MCS5;
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"--%s: Upgrading to MCS5\n", name());
|
||||
break;
|
||||
case GprsCodingScheme::MCS1 :
|
||||
LOGP(DRLCMACUL, LOGL_DEBUG,
|
||||
"--%s: Upgrading to MCS4\n", name());
|
||||
block->cs_last = GprsCodingScheme::MCS4;
|
||||
break;
|
||||
default:
|
||||
LOGP(DRLCMACUL, LOGL_ERROR,
|
||||
"--%s: cs(%s) Error in Upgrading to higher MCS\n",
|
||||
name(), rlc->cs.name());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return assemble_status;
|
||||
}
|
||||
|
||||
void gprs_rlcmac_ul_tbf::update_coding_scheme_counter_ul(const GprsCodingScheme cs)
|
||||
{
|
||||
uint8_t coding_scheme = 0;
|
||||
|
||||
coding_scheme = GprsCodingScheme::Scheme(cs);
|
||||
if (cs.isGprs()) {
|
||||
switch (coding_scheme) {
|
||||
case GprsCodingScheme::CS1 :
|
||||
bts->gprs_ul_cs1();
|
||||
rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS1]);
|
||||
break;
|
||||
case GprsCodingScheme::CS2 :
|
||||
bts->gprs_ul_cs2();
|
||||
rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS2]);
|
||||
break;
|
||||
case GprsCodingScheme::CS3 :
|
||||
bts->gprs_ul_cs3();
|
||||
rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS3]);
|
||||
break;
|
||||
case GprsCodingScheme::CS4 :
|
||||
bts->gprs_ul_cs4();
|
||||
rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS4]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (coding_scheme) {
|
||||
case GprsCodingScheme::MCS1 :
|
||||
bts->egprs_ul_mcs1();
|
||||
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS1]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS2 :
|
||||
bts->egprs_ul_mcs2();
|
||||
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS2]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS3 :
|
||||
bts->egprs_ul_mcs3();
|
||||
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS3]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS4 :
|
||||
bts->egprs_ul_mcs4();
|
||||
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS4]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS5 :
|
||||
bts->egprs_ul_mcs5();
|
||||
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS5]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS6 :
|
||||
bts->egprs_ul_mcs6();
|
||||
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS6]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS7 :
|
||||
bts->egprs_ul_mcs7();
|
||||
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS7]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS8 :
|
||||
bts->egprs_ul_mcs8();
|
||||
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS8]);
|
||||
break;
|
||||
case GprsCodingScheme::MCS9 :
|
||||
bts->egprs_ul_mcs9();
|
||||
rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS9]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gprs_rlcmac_ul_tbf::egprs_calc_ulwindow_size()
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data = bts->bts_data();
|
||||
unsigned int num_pdch = pcu_bitcount(ul_slots());
|
||||
unsigned int ws = bts_data->ws_base + num_pdch * bts_data->ws_pdch;
|
||||
ws = (ws / 32) * 32;
|
||||
ws = OSMO_MAX(64, ws);
|
||||
|
||||
if (num_pdch == 1)
|
||||
ws = OSMO_MIN(192, ws);
|
||||
else
|
||||
ws = OSMO_MIN(128 * num_pdch, ws);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "%s: Setting EGPRS window size to %d, base(%d) slots(%d) ws_pdch(%d)\n",
|
||||
name(), ws, bts_data->ws_base, num_pdch, bts_data->ws_pdch);
|
||||
|
||||
m_window.set_ws(ws);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/
|
||||
AM_LDFLAGS = -lrt
|
||||
|
||||
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test edge/EdgeTest
|
||||
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test edge/EdgeTest bitcomp/BitcompTest
|
||||
noinst_PROGRAMS = emu/pcu_emu
|
||||
|
||||
rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp
|
||||
|
@ -26,6 +26,11 @@ tbf_TbfTest_LDADD = \
|
|||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
bitcomp_BitcompTest_SOURCES = bitcomp/BitcompTest.cpp ../src/egprs_rlc_compression.cpp
|
||||
bitcomp_BitcompTest_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
edge_EdgeTest_SOURCES = edge/EdgeTest.cpp
|
||||
edge_EdgeTest_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
|
@ -108,6 +113,7 @@ EXTRA_DIST = \
|
|||
rlcmac/RLCMACTest.ok rlcmac/RLCMACTest.err \
|
||||
alloc/AllocTest.ok alloc/AllocTest.err \
|
||||
tbf/TbfTest.ok tbf/TbfTest.err \
|
||||
bitcomp/BitcompTest.ok bitcomp/BitcompTest.err \
|
||||
types/TypesTest.ok types/TypesTest.err \
|
||||
ms/MsTest.ok ms/MsTest.err \
|
||||
llc/LlcTest.ok llc/LlcTest.err \
|
||||
|
|
|
@ -792,6 +792,56 @@ static void test_many_connections()
|
|||
test_many_connections(alloc_algorithm_dynamic, 160, "algorithm dynamic");
|
||||
}
|
||||
|
||||
static void test_2_consecutive_dl_tbfs()
|
||||
{
|
||||
BTS the_bts;
|
||||
struct gprs_rlcmac_bts *bts;
|
||||
struct gprs_rlcmac_trx *trx;
|
||||
uint8_t ms_class = 11;
|
||||
uint8_t egprs_ms_class = 11;
|
||||
gprs_rlcmac_tbf *dl_tbf1, *dl_tbf2;
|
||||
uint8_t numTs1 = 0, numTs2 = 0;
|
||||
|
||||
printf("Testing DL TS allocation for Multi UEs\n");
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
bts->alloc_algorithm = alloc_algorithm_b;
|
||||
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[4].enable();
|
||||
trx->pdch[5].enable();
|
||||
trx->pdch[6].enable();
|
||||
trx->pdch[7].enable();
|
||||
|
||||
dl_tbf1 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf1);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (dl_tbf1->pdch[i])
|
||||
numTs1++;
|
||||
}
|
||||
OSMO_ASSERT(numTs1 == 4);
|
||||
printf("TBF1: numTs(%d)\n", numTs1);
|
||||
|
||||
dl_tbf2 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf2);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (dl_tbf2->pdch[i])
|
||||
numTs2++;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: currently 2nd DL TBF gets 3 TS
|
||||
* This behaviour will be fixed in subsequent patch
|
||||
*/
|
||||
printf("TBF2: numTs(%d)\n", numTs2);
|
||||
OSMO_ASSERT(numTs2 == 3);
|
||||
|
||||
tbf_free(dl_tbf1);
|
||||
tbf_free(dl_tbf2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile AllocTest context");
|
||||
|
@ -809,6 +859,7 @@ int main(int argc, char **argv)
|
|||
test_alloc_b();
|
||||
test_successive_allocation();
|
||||
test_many_connections();
|
||||
test_2_consecutive_dl_tbfs();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -10793,3 +10793,6 @@ Going to test assignment with many connections, algorithm dynamic
|
|||
TBF[31] class 14 reserves ......C.
|
||||
TBF[31] class 15 reserves .......C
|
||||
Successfully allocated 160 TBFs
|
||||
Testing DL TS allocation for Multi UEs
|
||||
TBF1: numTs(4)
|
||||
TBF2: numTs(3)
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rlc.h"
|
||||
#include "gprs_debug.h"
|
||||
#include <gprs_rlcmac.h>
|
||||
#include "egprs_rlc_compression.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/bitvec.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
}
|
||||
|
||||
#define NEW 1
|
||||
#define MASK(n) (0xFF << (8-n))
|
||||
#define MAX_CRBB_LEN 23
|
||||
#define MAX_URBB_LEN 40
|
||||
|
||||
void *tall_pcu_ctx;
|
||||
|
||||
struct test_data {
|
||||
int8_t crbb_len;
|
||||
uint8_t cc;
|
||||
uint8_t crbb_data[MAX_CRBB_LEN]; /* compressed data */
|
||||
uint8_t ucmp_data[MAX_URBB_LEN]; /* uncompressed data */
|
||||
int ucmp_len;
|
||||
int verify;
|
||||
} test[] = {
|
||||
{ .crbb_len = 67, .cc = 1,
|
||||
.crbb_data = {
|
||||
0x02, 0x0c, 0xa0, 0x30, 0xcb, 0x1a, 0x0c, 0xe3, 0x6c
|
||||
},
|
||||
.ucmp_data = {
|
||||
0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xff, 0xff,
|
||||
0xff, 0xf8, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe,
|
||||
0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xdb
|
||||
},
|
||||
.ucmp_len = 194, .verify = 1
|
||||
},
|
||||
{ .crbb_len = 40, .cc = 1,
|
||||
.crbb_data = {
|
||||
0x53, 0x06, 0xc5, 0x40, 0x6d
|
||||
},
|
||||
.ucmp_data = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03
|
||||
},
|
||||
.ucmp_len = 182, .verify = 1
|
||||
},
|
||||
{ .crbb_len = 8, .cc = 1,
|
||||
.crbb_data = {0x02},
|
||||
.ucmp_data = {0xff, 0xff, 0xff, 0xf8},
|
||||
.ucmp_len = 29, .verify = 1
|
||||
},
|
||||
{ .crbb_len = 103, .cc = 1,
|
||||
.crbb_data = {
|
||||
0x02, 0x0c, 0xe0, 0x41, 0xa0, 0x0c, 0x36, 0x0d, 0x03,
|
||||
0x71, 0xb0, 0x6e, 0x24
|
||||
},
|
||||
.ucmp_data = {
|
||||
0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xff,
|
||||
0xf8, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00,
|
||||
0x0f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x7f, 0xff,
|
||||
0xff, 0xff, 0x80, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff
|
||||
},
|
||||
.ucmp_len = 288, .verify = 1
|
||||
},
|
||||
/* Test vector from libosmocore test */
|
||||
{ .crbb_len = 35, .cc = 0,
|
||||
.crbb_data = {0xde, 0x88, 0x75, 0x65, 0x80},
|
||||
.ucmp_data = {0x37, 0x47, 0x81, 0xf0},
|
||||
.ucmp_len = 28, .verify = 1
|
||||
},
|
||||
{ .crbb_len = 18, .cc = 1,
|
||||
.crbb_data = {0xdd, 0x41, 0x00},
|
||||
.ucmp_data = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x00, 0x00
|
||||
},
|
||||
.ucmp_len = 90, .verify = 1
|
||||
},
|
||||
/*Invalid inputs*/
|
||||
{ .crbb_len = 18, .cc = 1,
|
||||
.crbb_data = {0x1E, 0x70, 0xc0},
|
||||
.ucmp_data = {0x0},
|
||||
.ucmp_len = 0, .verify = 0
|
||||
},
|
||||
{ .crbb_len = 14, .cc = 1,
|
||||
.crbb_data = {0x00, 0x1E, 0x7c},
|
||||
.ucmp_data = {0x0},
|
||||
.ucmp_len = 0, .verify = 0
|
||||
},
|
||||
{ .crbb_len = 24, .cc = 0,
|
||||
.crbb_data = {0x00, 0x00, 0x00},
|
||||
.ucmp_data = {0x0},
|
||||
.ucmp_len = 0, .verify = 0
|
||||
}
|
||||
};
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
|
||||
{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_DEBUG, 1},
|
||||
{"DRLCMAC", "\033[0;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Downlink (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_DEBUG, 1},
|
||||
{"DRLCMACMEAS", "\033[1;31m", "GPRS RLC/MAC layer Measurements (RLCMAC)", LOGL_INFO, 1},
|
||||
{"DNS", "\033[1;34m", "GPRS Network Service Protocol (NS)", LOGL_INFO, 1},
|
||||
{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO, 1},
|
||||
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
|
||||
};
|
||||
|
||||
static int filter_fn(const struct log_context *ctx,
|
||||
struct log_target *tar)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* To verify the result with expected result */
|
||||
int check_result(bitvec bits, uint8_t *exp_data, int exp_len)
|
||||
{
|
||||
if (bits.cur_bit != exp_len)
|
||||
return 0;
|
||||
size_t n = (exp_len / 8);
|
||||
int rem = (exp_len % 8);
|
||||
|
||||
if (memcmp(exp_data, bits.data, n) == 0) {
|
||||
if (rem == 0)
|
||||
return 1;
|
||||
if ((bits.data[n] & MASK(rem)) == ((*(exp_data + n)) & MASK(rem)))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* To test decoding of compressed bitmap by Tree based method
|
||||
* and to verify the result with expected result
|
||||
* for invalid input verfication is suppressed
|
||||
*/
|
||||
static void test_EPDAN_decode_tree(void)
|
||||
{
|
||||
bitvec dest;
|
||||
int init_flag = 1;
|
||||
int itr;
|
||||
int rc;
|
||||
uint8_t bits_data[RLC_EGPRS_MAX_WS/8];
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
for (itr = 0 ; itr < (sizeof(test) / sizeof(test_data)) ; itr++) {
|
||||
dest.data = bits_data;
|
||||
dest.data_len = sizeof(bits_data);
|
||||
dest.cur_bit = 0;
|
||||
memset(dest.data, 0, sizeof(bits_data));
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTest:%d\nTree based decoding:"
|
||||
"\nuncompressed data = %s\nlen = %d\n", itr + 1,
|
||||
osmo_hexdump(test[itr].crbb_data,
|
||||
(test[itr].crbb_len + 7)/8), test[itr].crbb_len
|
||||
);
|
||||
rc = egprs_compress::decompress_crbb(test[itr].crbb_len,
|
||||
test[itr].cc, test[itr].crbb_data, &dest);
|
||||
if (rc < 0) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE,
|
||||
"\nFailed to decode CRBB: length %d, data %s",
|
||||
test[itr].crbb_len, osmo_hexdump(
|
||||
test[itr].crbb_data, (test[itr].crbb_len + 7)/8));
|
||||
}
|
||||
if (init_flag)
|
||||
init_flag = 0;
|
||||
if (test[itr].verify) {
|
||||
if (check_result(dest, test[itr].ucmp_data,
|
||||
test[itr].ucmp_len) == 0) {
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTree based decoding"
|
||||
":Error\nexpected data = %s\nexpected"
|
||||
" len = %d\ndecoded data = %s\n"
|
||||
"decoded len = %d\n",
|
||||
osmo_hexdump(test[itr].ucmp_data,
|
||||
(test[itr].ucmp_len + 7)/8),
|
||||
test[itr].ucmp_len, osmo_hexdump(dest.data,
|
||||
(dest.cur_bit + 7)/8), dest.cur_bit
|
||||
);
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "\nexpected data = %s\nexpected len = %d"
|
||||
"\ndecoded data = %s\ndecoded len = %d\n",
|
||||
osmo_hexdump(test[itr].ucmp_data,
|
||||
(test[itr].ucmp_len + 7)/8),
|
||||
test[itr].ucmp_len, osmo_hexdump(dest.data,
|
||||
(dest.cur_bit + 7)/8), dest.cur_bit
|
||||
);
|
||||
}
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
const struct log_info debug_log_info = {
|
||||
filter_fn,
|
||||
(struct log_info_cat *)default_categories,
|
||||
ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging(&debug_log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
|
||||
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile bitcompTest context");
|
||||
if (!tall_pcu_ctx)
|
||||
abort();
|
||||
|
||||
test_EPDAN_decode_tree();
|
||||
|
||||
if (getenv("TALLOC_REPORT_FULL"))
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
talloc_free(tall_pcu_ctx);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* stubs that should not be reached
|
||||
*/
|
||||
extern "C" {
|
||||
void l1if_pdch_req() { abort(); }
|
||||
void l1if_connect_pdch() { abort(); }
|
||||
void l1if_close_pdch() { abort(); }
|
||||
void l1if_open_pdch() { abort(); }
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
|
||||
Test:1
|
||||
Tree based decoding:
|
||||
uncompressed data = 02 0c a0 30 cb 1a 0c e3 6c
|
||||
len = 67
|
||||
Run_length = 29
|
||||
Run_length = 26
|
||||
Run_length = 30
|
||||
Run_length = 27
|
||||
Run_length = 31
|
||||
Run_length = 19
|
||||
Run_length = 32
|
||||
|
||||
expected data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff db
|
||||
expected len = 194
|
||||
decoded data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff db
|
||||
decoded len = 194
|
||||
|
||||
Test:2
|
||||
Tree based decoding:
|
||||
uncompressed data = 53 06 c5 40 6d
|
||||
len = 40
|
||||
Run_length = 50
|
||||
Run_length = 40
|
||||
Run_length = 51
|
||||
Run_length = 41
|
||||
|
||||
expected data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 03
|
||||
expected len = 182
|
||||
decoded data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 03
|
||||
decoded len = 182
|
||||
|
||||
Test:3
|
||||
Tree based decoding:
|
||||
uncompressed data = 02
|
||||
len = 8
|
||||
Run_length = 29
|
||||
|
||||
expected data = ff ff ff f8
|
||||
expected len = 29
|
||||
decoded data = ff ff ff f8
|
||||
decoded len = 29
|
||||
|
||||
Test:4
|
||||
Tree based decoding:
|
||||
uncompressed data = 02 0c e0 41 a0 0c 36 0d 03 71 b0 6e 24
|
||||
len = 103
|
||||
Run_length = 29
|
||||
Run_length = 19
|
||||
Run_length = 29
|
||||
Run_length = 20
|
||||
Run_length = 30
|
||||
Run_length = 21
|
||||
Run_length = 31
|
||||
Run_length = 22
|
||||
Run_length = 32
|
||||
Run_length = 22
|
||||
Run_length = 33
|
||||
|
||||
expected data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
|
||||
expected len = 288
|
||||
decoded data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
|
||||
decoded len = 288
|
||||
|
||||
Test:5
|
||||
Tree based decoding:
|
||||
uncompressed data = de 88 75 65 80
|
||||
len = 35
|
||||
Run_length = 2
|
||||
Run_length = 2
|
||||
Run_length = 1
|
||||
Run_length = 3
|
||||
Run_length = 1
|
||||
Run_length = 1
|
||||
Run_length = 3
|
||||
Run_length = 4
|
||||
Run_length = 6
|
||||
Run_length = 5
|
||||
|
||||
expected data = 37 47 81 f0
|
||||
expected len = 28
|
||||
decoded data = 37 47 81 f0
|
||||
decoded len = 28
|
||||
|
||||
Test:6
|
||||
Tree based decoding:
|
||||
uncompressed data = dd 41 00
|
||||
len = 18
|
||||
Run_length = 64
|
||||
Run_length = 16
|
||||
Run_length = 10
|
||||
|
||||
expected data = ff ff ff ff ff ff ff ff ff ff 00 00
|
||||
expected len = 90
|
||||
decoded data = ff ff ff ff ff ff ff ff ff ff 00 00
|
||||
decoded len = 90
|
||||
|
||||
Test:7
|
||||
Tree based decoding:
|
||||
uncompressed data = 1e 70 c0
|
||||
len = 18
|
||||
Run_length = 1
|
||||
Run_length = 1
|
||||
Run_length = 2
|
||||
Run_length = 15
|
||||
|
||||
expected data =
|
||||
expected len = 0
|
||||
decoded data =
|
||||
decoded len = 19
|
||||
|
||||
Test:8
|
||||
Tree based decoding:
|
||||
uncompressed data = 00 1e
|
||||
len = 14
|
||||
|
||||
Failed to decode CRBB: length 14, data 00 1e
|
||||
expected data =
|
||||
expected len = 0
|
||||
decoded data =
|
||||
decoded len = 0
|
||||
|
||||
Test:9
|
||||
Tree based decoding:
|
||||
uncompressed data = 00 00 00
|
||||
len = 24
|
||||
|
||||
Failed to decode CRBB: length 24, data 00 00 00
|
||||
expected data =
|
||||
expected len = 0
|
||||
decoded data =
|
||||
decoded len = 0
|
|
@ -0,0 +1,2 @@
|
|||
=== start test_EPDAN_decode_tree ===
|
||||
=== end test_EPDAN_decode_tree ===
|
|
@ -26,7 +26,7 @@
|
|||
#include "encoding.h"
|
||||
#include "rlc.h"
|
||||
#include "llc.h"
|
||||
|
||||
#include "bts.h"
|
||||
extern "C" {
|
||||
#include "pcu_vty.h"
|
||||
|
||||
|
@ -35,6 +35,7 @@ extern "C" {
|
|||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/gprs/protocol/gsm_04_60.h>
|
||||
}
|
||||
|
||||
#include <errno.h>
|
||||
|
@ -495,6 +496,24 @@ static void test_rlc_unit_decoder()
|
|||
OSMO_ASSERT(chunks[2].length == 1);
|
||||
OSMO_ASSERT(!chunks[2].is_complete);
|
||||
|
||||
rdbi.e = 0;
|
||||
rdbi.ti = 0;
|
||||
rdbi.cv = 1;
|
||||
tlli = 0;
|
||||
offs = 0;
|
||||
data[offs++] = 1;
|
||||
num_chunks = Decoding::rlc_data_from_ul_data(&rdbi, cs, data,
|
||||
chunks, ARRAY_SIZE(chunks), &tlli);
|
||||
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
OSMO_ASSERT(chunks[0].offset == 1);
|
||||
OSMO_ASSERT(chunks[0].length == 0);
|
||||
OSMO_ASSERT(chunks[0].is_complete);
|
||||
|
||||
OSMO_ASSERT(chunks[1].offset == 1);
|
||||
OSMO_ASSERT(chunks[1].length == 43);
|
||||
OSMO_ASSERT(!chunks[1].is_complete);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
|
@ -506,6 +525,7 @@ static void test_rlc_unit_encoder()
|
|||
uint8_t llc_data[1500] = {0,};
|
||||
int num_chunks = 0;
|
||||
int write_offset;
|
||||
int count_payload;
|
||||
struct gprs_llc llc;
|
||||
Encoding::AppendResult ar;
|
||||
|
||||
|
@ -515,43 +535,49 @@ static void test_rlc_unit_encoder()
|
|||
|
||||
/* TS 44.060, B.1 */
|
||||
cs = GprsCodingScheme::CS4;
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 11);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 11);
|
||||
OSMO_ASSERT(count_payload == 11);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 26);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 2 + 11 + 26);
|
||||
OSMO_ASSERT(count_payload == 26);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 11);
|
||||
OSMO_ASSERT(num_chunks == 3);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((11 << 2) | (1 << 1) | (0 << 0)));
|
||||
|
@ -562,50 +588,57 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 20);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 19);
|
||||
OSMO_ASSERT(count_payload == 19);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((0 << 2) | (0 << 1) | (1 << 0)));
|
||||
OSMO_ASSERT(data[1] == 0);
|
||||
|
||||
/* Block 2 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
OSMO_ASSERT(llc.chunk_size() == 1);
|
||||
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 1);
|
||||
OSMO_ASSERT(count_payload == 1);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 1 + 18);
|
||||
OSMO_ASSERT(count_payload == 18);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((1 << 2) | (1 << 1) | (1 << 0)));
|
||||
|
@ -615,31 +648,35 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 7);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 7);
|
||||
OSMO_ASSERT(count_payload == 7);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 11);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 2 + 7 + 11);
|
||||
OSMO_ASSERT(count_payload == 11);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((7 << 2) | (1 << 1) | (0 << 0)));
|
||||
|
@ -650,20 +687,22 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 1);
|
||||
OSMO_ASSERT(write_offset == 20);
|
||||
OSMO_ASSERT(count_payload == 20);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
|
||||
|
@ -673,20 +712,22 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 20);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 1);
|
||||
OSMO_ASSERT(write_offset == 20);
|
||||
OSMO_ASSERT(count_payload == 20);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
|
||||
|
@ -696,49 +737,55 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::CS1;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 30);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 1);
|
||||
OSMO_ASSERT(write_offset == 20);
|
||||
OSMO_ASSERT(count_payload == 20);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
OSMO_ASSERT(data[0] == 0);
|
||||
|
||||
/* Block 2 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
OSMO_ASSERT(llc.chunk_size() == 10);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 10);
|
||||
OSMO_ASSERT(count_payload == 10);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 10 + 9);
|
||||
OSMO_ASSERT(count_payload == 9);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((10 << 2) | (1 << 1) | (1 << 0)));
|
||||
|
@ -748,43 +795,49 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS4;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 11);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 11);
|
||||
OSMO_ASSERT(count_payload == 11);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 26);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 2 + 11 + 26);
|
||||
OSMO_ASSERT(count_payload == 26);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 99);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 5);
|
||||
OSMO_ASSERT(num_chunks == 3);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((11 << 1) | (0 << 0)));
|
||||
|
@ -800,75 +853,86 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS2;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 15);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 15);
|
||||
OSMO_ASSERT(count_payload == 15);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 12);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
/* no LI here, becaues there are exact 12 bytes left. Put LI into next frame */
|
||||
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 12);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((15 << 1) | (1 << 0)));
|
||||
OSMO_ASSERT(data[1] == 0);
|
||||
|
||||
/* Block 2 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
OSMO_ASSERT(llc.chunk_size() == 0);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 0);
|
||||
OSMO_ASSERT(count_payload == 0);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 7);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == 2 + 0 + 7);
|
||||
OSMO_ASSERT(count_payload == 7);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 18);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv != 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 18);
|
||||
OSMO_ASSERT(num_chunks == 3);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((0 << 1) | (0 << 0)));
|
||||
|
@ -877,32 +941,36 @@ static void test_rlc_unit_encoder()
|
|||
OSMO_ASSERT(data[3] == 0);
|
||||
|
||||
/* Block 3 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 6);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, false);
|
||||
&llc, &write_offset, &num_chunks, data, false, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(write_offset == 1 + 6);
|
||||
OSMO_ASSERT(count_payload == 6);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, 12);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == 12);
|
||||
OSMO_ASSERT(num_chunks == 3);
|
||||
|
||||
OSMO_ASSERT(data[0] == ((6 << 1) | (0 << 0)));
|
||||
|
@ -917,21 +985,23 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS2;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, rdbi.data_len);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 1);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == rdbi.data_len);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
OSMO_ASSERT(data[0] == 0);
|
||||
|
@ -941,21 +1011,23 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS2;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, rdbi.data_len - 1);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == rdbi.data_len - 1);
|
||||
OSMO_ASSERT(num_chunks == 1);
|
||||
|
||||
OSMO_ASSERT(data[0] == (((rdbi.data_len-1) << 1) | (1 << 0)));
|
||||
|
@ -966,21 +1038,23 @@ static void test_rlc_unit_encoder()
|
|||
cs = GprsCodingScheme::MCS2;
|
||||
|
||||
/* Block 1 */
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false);
|
||||
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
|
||||
num_chunks = 0;
|
||||
write_offset = 0;
|
||||
memset(data, 0, sizeof(data));
|
||||
|
||||
llc.reset();
|
||||
llc.put_frame(llc_data, rdbi.data_len - 2);
|
||||
count_payload = -1;
|
||||
|
||||
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
|
||||
&llc, &write_offset, &num_chunks, data, true);
|
||||
&llc, &write_offset, &num_chunks, data, true, &count_payload);
|
||||
|
||||
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
|
||||
OSMO_ASSERT(rdbi.e == 0);
|
||||
OSMO_ASSERT(rdbi.cv == 0);
|
||||
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
|
||||
OSMO_ASSERT(count_payload == rdbi.data_len - 2);
|
||||
OSMO_ASSERT(num_chunks == 2);
|
||||
|
||||
OSMO_ASSERT(data[0] == (((rdbi.data_len-2) << 1) | (0 << 0)));
|
||||
|
@ -1018,7 +1092,7 @@ static void test_rlc_unaligned_copy()
|
|||
block_idx++)
|
||||
{
|
||||
struct gprs_rlc_data_info rlc;
|
||||
gprs_rlc_data_info_init_dl(&rlc, cs, false);
|
||||
gprs_rlc_data_info_init_dl(&rlc, cs, false, 0);
|
||||
|
||||
memset(bits, pattern, sizeof(bits));
|
||||
Decoding::rlc_copy_to_aligned_buffer(
|
||||
|
@ -1062,12 +1136,14 @@ static void test_rlc_info_init()
|
|||
struct gprs_rlc_data_info rlc;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
gprs_rlc_data_info_init_dl(&rlc, GprsCodingScheme(GprsCodingScheme::CS1), false);
|
||||
gprs_rlc_data_info_init_dl(&rlc,
|
||||
GprsCodingScheme(GprsCodingScheme::CS1), false, 0);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.data_offs_bits[0] == 24);
|
||||
OSMO_ASSERT(rlc.block_info[0].data_len == 20);
|
||||
|
||||
gprs_rlc_data_info_init_dl(&rlc, GprsCodingScheme(GprsCodingScheme::MCS1), false);
|
||||
gprs_rlc_data_info_init_dl(&rlc,
|
||||
GprsCodingScheme(GprsCodingScheme::MCS1), false, 0);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.data_offs_bits[0] == 33);
|
||||
OSMO_ASSERT(rlc.block_info[0].data_len == 22);
|
||||
|
@ -1101,6 +1177,250 @@ const struct log_info debug_log_info = {
|
|||
ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
static void setup_bts(BTS *the_bts, uint8_t ts_no, uint8_t cs = 1)
|
||||
{
|
||||
gprs_rlcmac_bts *bts;
|
||||
gprs_rlcmac_trx *trx;
|
||||
|
||||
bts = the_bts->bts_data();
|
||||
bts->egprs_enabled = true;
|
||||
bts->alloc_algorithm = alloc_algorithm_a;
|
||||
bts->initial_cs_dl = cs;
|
||||
bts->initial_cs_ul = cs;
|
||||
trx = &bts->trx[0];
|
||||
trx->pdch[ts_no].enable();
|
||||
}
|
||||
static void uplink_header_type_2_parsing_test(BTS *the_bts,
|
||||
uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
|
||||
uint8_t ms_class)
|
||||
{
|
||||
GprsMs *ms;
|
||||
struct pcu_l1_meas meas;
|
||||
int tfi = 0;
|
||||
gprs_rlcmac_bts *bts;
|
||||
RlcMacUplink_t ulreq = {0};
|
||||
uint8_t data[79] = {0};
|
||||
struct gprs_rlc_ul_header_egprs_2 *egprs2 = NULL;
|
||||
|
||||
egprs2 = (struct gprs_rlc_ul_header_egprs_2 *) data;
|
||||
bts = the_bts->bts_data();
|
||||
|
||||
tfi = 1;
|
||||
|
||||
struct gprs_rlc_data_info rlc;
|
||||
GprsCodingScheme cs;
|
||||
int rc, offs;
|
||||
|
||||
/*without padding*/
|
||||
cs = GprsCodingScheme::MCS5;
|
||||
egprs2 = (struct gprs_rlc_ul_header_egprs_2 *) data;
|
||||
egprs2->r = 1;
|
||||
egprs2->si = 1;
|
||||
egprs2->cv = 7;
|
||||
egprs2->tfi_hi = tfi & 0x03;
|
||||
egprs2->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs2->bsn1_hi = 0;
|
||||
egprs2->bsn1_lo = 0;
|
||||
egprs2->cps_hi = 3;
|
||||
egprs2->cps_lo = 0;
|
||||
egprs2->rsb = 0;
|
||||
egprs2->pi = 0;
|
||||
data[4] = 0x20; /* Setting E field */
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
offs = rlc.data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(offs == 4);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
|
||||
/* with padding case */
|
||||
cs = GprsCodingScheme::MCS6;
|
||||
egprs2 = (struct gprs_rlc_ul_header_egprs_2 *) data;
|
||||
egprs2->r = 1;
|
||||
egprs2->si = 1;
|
||||
egprs2->cv = 7;
|
||||
egprs2->tfi_hi = tfi & 0x03;
|
||||
egprs2->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs2->bsn1_hi = 0;
|
||||
egprs2->bsn1_lo = 0;
|
||||
egprs2->cps_hi = 3;
|
||||
egprs2->cps_lo = 0;
|
||||
egprs2->rsb = 0;
|
||||
egprs2->pi = 0;
|
||||
data[10] = 0x20; /* Setting E field */
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
offs = rlc.data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(offs == 10);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
|
||||
egprs2->r = 1;
|
||||
egprs2->si = 1;
|
||||
egprs2->cv = 7;
|
||||
egprs2->tfi_hi = tfi & 0x03;
|
||||
egprs2->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs2->bsn1_hi = 1;
|
||||
egprs2->bsn1_lo = 0;
|
||||
egprs2->cps_hi = 2;
|
||||
egprs2->cps_lo = 0;
|
||||
egprs2->rsb = 0;
|
||||
egprs2->pi = 0;
|
||||
data[10] = 0x20; /* Setting E field */
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
offs = rlc.data_offs_bits[0] / 8;
|
||||
OSMO_ASSERT(offs == 10);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 1);
|
||||
}
|
||||
|
||||
static void uplink_header_type2_test(void)
|
||||
{
|
||||
BTS the_bts;
|
||||
int ts_no = 7;
|
||||
uint32_t fn = 2654218;
|
||||
uint16_t qta = 31;
|
||||
uint32_t tlli = 0xf1223344;
|
||||
const char *imsi = "0011223344";
|
||||
uint8_t ms_class = 1;
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
GprsMs *ms;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
setup_bts(&the_bts, ts_no, 10);
|
||||
|
||||
uplink_header_type_2_parsing_test(&the_bts, ts_no,
|
||||
tlli, &fn, qta, ms_class);
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void uplink_header_type_1_parsing_test(BTS *the_bts,
|
||||
uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
|
||||
uint8_t ms_class)
|
||||
{
|
||||
uint8_t trx_no = 0;
|
||||
int tfi = 0;
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
gprs_rlcmac_bts *bts;
|
||||
uint8_t data[155] = {0};
|
||||
struct gprs_rlc_ul_header_egprs_1 *egprs1 = NULL;
|
||||
struct gprs_rlc_data_info rlc;
|
||||
GprsCodingScheme cs;
|
||||
int rc, offs;
|
||||
|
||||
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
|
||||
bts = the_bts->bts_data();
|
||||
|
||||
tfi = 1;
|
||||
|
||||
/* MCS 7 */
|
||||
cs = GprsCodingScheme::MCS7;
|
||||
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
|
||||
egprs1->si = 1;
|
||||
egprs1->r = 1;
|
||||
egprs1->cv = 7;
|
||||
egprs1->tfi_hi = tfi & 0x03;
|
||||
egprs1->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs1->bsn1_hi = 0;
|
||||
egprs1->bsn1_lo = 0;
|
||||
egprs1->bsn2_hi = 1;
|
||||
egprs1->bsn2_lo = 0;
|
||||
egprs1->cps = 15;
|
||||
egprs1->rsb = 0;
|
||||
egprs1->pi = 0;
|
||||
data[5] = 0xc0;
|
||||
data[5 + 57] = 1;
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 2);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
OSMO_ASSERT(rlc.block_info[1].bsn == 1);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
|
||||
/* MCS 8 */
|
||||
cs = GprsCodingScheme::MCS8;
|
||||
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
|
||||
egprs1->si = 1;
|
||||
egprs1->r = 1;
|
||||
egprs1->cv = 7;
|
||||
egprs1->tfi_hi = tfi & 0x03;
|
||||
egprs1->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs1->bsn1_hi = 0;
|
||||
egprs1->bsn1_lo = 0;
|
||||
egprs1->bsn2_hi = 1;
|
||||
egprs1->bsn2_lo = 0;
|
||||
egprs1->cps = 15;
|
||||
egprs1->rsb = 0;
|
||||
egprs1->pi = 0;
|
||||
data[5] = 0xc0;
|
||||
data[5 + 69] = 1;
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 2);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
OSMO_ASSERT(rlc.block_info[1].bsn == 1);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
|
||||
/* MCS 9 */
|
||||
cs = GprsCodingScheme::MCS9;
|
||||
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
|
||||
egprs1->si = 1;
|
||||
egprs1->r = 1;
|
||||
egprs1->cv = 7;
|
||||
egprs1->tfi_hi = tfi & 0x03;
|
||||
egprs1->tfi_lo = (tfi & 0x1c) >> 2;
|
||||
egprs1->bsn1_hi = 0;
|
||||
egprs1->bsn1_lo = 0;
|
||||
egprs1->bsn2_hi = 1;
|
||||
egprs1->bsn2_lo = 0;
|
||||
egprs1->cps = 15;
|
||||
egprs1->rsb = 0;
|
||||
egprs1->pi = 0;
|
||||
data[5] = 0xc0;
|
||||
data[5 + 75] = 1;
|
||||
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
|
||||
OSMO_ASSERT(rlc.num_data_blocks == 2);
|
||||
OSMO_ASSERT(rlc.block_info[0].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[0].ti == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].e == 1);
|
||||
OSMO_ASSERT(rlc.block_info[1].ti == 0);
|
||||
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
|
||||
OSMO_ASSERT(rlc.block_info[1].bsn == 1);
|
||||
OSMO_ASSERT(rlc.tfi == 1);
|
||||
}
|
||||
|
||||
void uplink_header_type1_test(void)
|
||||
{
|
||||
BTS the_bts;
|
||||
int ts_no = 7;
|
||||
uint32_t fn = 2654218;
|
||||
uint16_t qta = 31;
|
||||
uint32_t tlli = 0xf1223344;
|
||||
const char *imsi = "0011223344";
|
||||
uint8_t ms_class = 1;
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
GprsMs *ms;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
setup_bts(&the_bts, ts_no, 12);
|
||||
uplink_header_type_1_parsing_test(&the_bts, ts_no, tlli, &fn,
|
||||
qta, ms_class);
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct vty_app_info pcu_vty_info = {0};
|
||||
|
@ -1123,6 +1443,9 @@ int main(int argc, char **argv)
|
|||
test_rlc_unaligned_copy();
|
||||
test_rlc_unit_encoder();
|
||||
|
||||
uplink_header_type2_test();
|
||||
uplink_header_type1_test();
|
||||
|
||||
if (getenv("TALLOC_REPORT_FULL"))
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
return EXIT_SUCCESS;
|
||||
|
|
|
@ -6,3 +6,7 @@
|
|||
=== end test_rlc_unit_decoder ===
|
||||
=== start test_rlc_unit_encoder ===
|
||||
=== end test_rlc_unit_encoder ===
|
||||
=== start uplink_header_type2_test ===
|
||||
=== end uplink_header_type2_test ===
|
||||
=== start uplink_header_type1_test ===
|
||||
=== end uplink_header_type1_test ===
|
||||
|
|
|
@ -30,6 +30,7 @@ extern const struct log_info gprs_log_info;
|
|||
#include "pcu_vty.h"
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
}
|
||||
using namespace std;
|
||||
|
@ -89,9 +90,12 @@ void testRlcMacDownlink()
|
|||
|
||||
std::string testData[] = {
|
||||
"4e082500e3f1a81d080820800b2b2b2b2b2b2b2b2b2b2b", // Packet Downlink Assignment
|
||||
"48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
|
||||
"48282407a6a07422720100032b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
|
||||
"47240c00400000000000000079eb2ac9402b2b2b2b2b2b", // Packet Uplink Ack Nack
|
||||
"47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b" // Packet Uplink Assignment
|
||||
"47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
|
||||
"400820001a3904df0680efb3300b2b2b2b2b2b2b2b2b2b", // Packet Downlink Assignment (EGPRS)
|
||||
"40284f0000001009810c826f4406809dcecb2b2b2b2b2b", // Packet Uplink Assignment (EGPRS)
|
||||
"4024030f2f0000000087b0042b2b2b2b2b2b2b2b2b2b2b" // Packet Uplink Ack Nack (EGPRS)
|
||||
"4913e00850884013a8048b2b2b2b2b2b2b2b2b2b2b2b2b"
|
||||
"412430007fffffffffffffffefd19c7ba12b2b2b2b2b2b"
|
||||
"41942b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
|
||||
|
@ -152,9 +156,10 @@ void testRlcMacUplink()
|
|||
bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
||||
|
||||
std::string testData[] = {
|
||||
"400e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Dummy Control Block
|
||||
"400b8020000000000000002480e00b2b2b2b2b2b2b2b2b", // Packet Downlink Ack/Nack
|
||||
"4016713dc094270ca2ae57ef909006aa0fc0001f80222b" // Packet Resource Request
|
||||
"400e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Dummy Control Block
|
||||
"400b8020000000000000002480e0032b2b2b2b2b2b2b2b", // Packet Downlink Ack/Nack
|
||||
"4016713dc094270ca2ae57ef909006aa0fc0001f80222b", // Packet Resource Request
|
||||
"40200ffc0021ec010b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // EPDAN
|
||||
"400a9020000000000000003010012a0800132b2b2b2b2b"
|
||||
};
|
||||
|
||||
|
@ -207,6 +212,21 @@ void testRlcMacUplink()
|
|||
bitvec_free(resultVector);
|
||||
}
|
||||
|
||||
void testCsnLeftAlignedVarBmpBounds()
|
||||
{
|
||||
bitvec *vector = bitvec_alloc(23);
|
||||
|
||||
bitvec_unhex(vector, "40200bffd161003e0e519ffffffb800000000000000000");
|
||||
RlcMacUplink_t data;
|
||||
|
||||
EGPRS_AckNack_Desc_t *urbb =
|
||||
&data.u.Egprs_Packet_Downlink_Ack_Nack.EGPRS_AckNack.Desc;
|
||||
decode_gsm_rlcmac_uplink(vector, &data);
|
||||
|
||||
OSMO_ASSERT(!strcmp(osmo_hexdump(urbb->URBB, 13),
|
||||
"7f ff ff ee 00 00 00 00 00 00 00 00 00 "));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
|
@ -214,5 +234,5 @@ int main(int argc, char *argv[])
|
|||
//printSizeofRLCMAC();
|
||||
testRlcMacDownlink();
|
||||
testRlcMacUplink();
|
||||
|
||||
testCsnLeftAlignedVarBmpBounds();
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
|
|||
vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 = 4828247a6a07422721032b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 = 4828247a6a07422721032b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4828247a6a07422721032b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4724c040000000079eb2ac9402b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
|
@ -31,22 +31,46 @@ vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
|
|||
vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
UPLINK
|
||||
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 = 4082001a394df680efb330b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 = 4082001a394df680efb330b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4082001a394df680efb330b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
|
||||
vector1 = 40284f00010981c826f446809dcecb2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
|
||||
vector1 = 40284f00010981c826f446809dcecb2b2b2b2b2b
|
||||
vector2 = 40284f00010981c826f446809dcecb2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 40243f2f000087b042b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40243f2f000087b042b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40243f2f000087b042b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
UPLINK
|
||||
vector1 = 40e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 40e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 40b802000000002480e032b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40b802000000002480e032b2b2b2b2b2b2b2b
|
||||
vector2 = 40b802000000002480e032b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
||||
=========Start DECODE===========
|
||||
|
@ -56,3 +80,19 @@ vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
|||
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
||||
vector2 = 4016713dc09427ca2ae57ef90906aafc001f80222b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 4020ffc021ec1b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 4020ffc021ec1b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector2 = 4020ffc021ec1b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
vector1 = 40a90200000000301012a80132b2b2b2b2b
|
||||
=========Start DECODE===========
|
||||
+++++++++Finish DECODE++++++++++
|
||||
=========Start ENCODE=============
|
||||
+++++++++Finish ENCODE+++++++++++
|
||||
vector1 = 40a90200000000301012a80132b2b2b2b2b
|
||||
vector2 = 40a90200000000301012a80132b2b2b2b2b
|
||||
vector1 == vector2 : TRUE
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -32,6 +32,8 @@
|
|||
=== end test_tbf_ws ===
|
||||
=== start test_tbf_egprs_two_phase ===
|
||||
=== end test_tbf_egprs_two_phase ===
|
||||
=== start test_tbf_egprs_two_phase_spb ===
|
||||
=== end test_tbf_egprs_two_phase_spb ===
|
||||
=== start test_tbf_egprs_dl ===
|
||||
Testing MCS-1
|
||||
Testing MCS-2
|
||||
|
@ -43,3 +45,51 @@ Testing MCS-7
|
|||
Testing MCS-8
|
||||
Testing MCS-9
|
||||
=== end test_tbf_egprs_dl ===
|
||||
=== start test_tbf_egprs_retx_dl ===
|
||||
Testing retx for MCS 6 - 6
|
||||
Testing retx for MCS 1 - 9
|
||||
Testing retx for MCS 2 - 8
|
||||
Testing retx for MCS 5 - 7
|
||||
Testing retx for MCS 6 - 9
|
||||
Testing retx for MCS 7 - 5
|
||||
Testing retx for MCS 9 - 6
|
||||
=== end test_tbf_egprs_retx_dl ===
|
||||
=== start test_tbf_egprs_spb_dl ===
|
||||
Testing retx for MCS 6 to reseg_mcs 3
|
||||
Testing retx for MCS 5 to reseg_mcs 2
|
||||
Testing retx for MCS 4 to reseg_mcs 1
|
||||
Testing retx for MCS 6 to reseg_mcs 3
|
||||
=== end test_tbf_egprs_spb_dl ===
|
||||
=== start test_tbf_puan_urbb_len ===
|
||||
=== end test_tbf_puan_urbb_len ===
|
||||
=== start test_tbf_update_ws ===
|
||||
=== end test_tbf_update_ws ===
|
||||
=== start test_tbf_li_decoding ===
|
||||
=== end test_tbf_li_decoding ===
|
||||
=== start test_tbf_epdan_out_of_rx_window ===
|
||||
=== end test_tbf_epdan_out_of_rx_window ===
|
||||
=== start test_immediate_assign_rej_multi_block ===
|
||||
=== end test_immediate_assign_rej_multi_block ===
|
||||
=== start test_immediate_assign_rej_single_block ===
|
||||
=== end test_immediate_assign_rej_single_block ===
|
||||
=== start test_packet_access_rej_epdan ===
|
||||
packet reject: 40 84 7f f7 6e e6 41 4b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
|
||||
=== end test_packet_access_rej_epdan ===
|
||||
=== start test_tbf_egprs_two_phase_puan ===
|
||||
=== end test_tbf_egprs_two_phase_puan ===
|
||||
=== start test_packet_access_rej_prr ===
|
||||
=== end test_packet_access_rej_prr ===
|
||||
=== start test_packet_access_rej_prr_no_other_tbfs ===
|
||||
=== end test_packet_access_rej_prr_no_other_tbfs ===
|
||||
=== start test_multi_trx_test ===
|
||||
=== start test_multi_trx_test_same_capacity ===
|
||||
=== end test_multi_trx_test_same_capacity ===
|
||||
=== start test_multi_trx_test_1_2_capacity ===
|
||||
=== end test_multi_trx_test_1_2_capacity ===
|
||||
=== start test_multi_trx_test_1_4_capacity ===
|
||||
=== end test_multi_trx_test_1_4_capacity ===
|
||||
=== start test_multi_trx_test_release_alloc ===
|
||||
=== end test_multi_trx_test_release_alloc ===
|
||||
=== start test_multi_trx_test_same_capacity_dl_ul_combined ===
|
||||
=== end test_multi_trx_test_same_capacity_dl_ul_combined ===
|
||||
=== end test_multi_trx_test ===
|
||||
|
|
|
@ -23,6 +23,13 @@ cat $abs_srcdir/tbf/TbfTest.err > experr
|
|||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/tbf/TbfTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([bitcomp])
|
||||
AT_KEYWORDS([bitcomp])
|
||||
cat $abs_srcdir/bitcomp/BitcompTest.ok > expout
|
||||
cat $abs_srcdir/bitcomp/BitcompTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/bitcomp/BitcompTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([edge])
|
||||
AT_KEYWORDS([edge])
|
||||
cat $abs_srcdir/edge/EdgeTest.ok > expout
|
||||
|
|
|
@ -322,6 +322,23 @@ static void test_rlc_dl_ul_basic()
|
|||
ul_win.receive_bsn(4);
|
||||
count = ul_win.raise_v_q();
|
||||
OSMO_ASSERT(count == 0);
|
||||
|
||||
/*
|
||||
* SSN wrap around case
|
||||
* Should not expect any BSN as nacked.
|
||||
*/
|
||||
rbb = "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR";
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
ul_win.receive_bsn(i);
|
||||
ul_win.raise_v_q();
|
||||
}
|
||||
ul_win.receive_bsn(0);
|
||||
ul_win.raise_v_q();
|
||||
ul_win.receive_bsn(1);
|
||||
ul_win.raise_v_q();
|
||||
ul_win.update_rbb(win_rbb);
|
||||
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
|
||||
OSMO_ASSERT(ul_win.ssn() == 2);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -410,6 +427,42 @@ static void test_rlc_dl_ul_basic()
|
|||
}
|
||||
}
|
||||
|
||||
void test_immediate_assign_rej()
|
||||
{
|
||||
uint8_t plen;
|
||||
bitvec *immediate_assignment_rej = bitvec_alloc(22);
|
||||
|
||||
bitvec_unhex(immediate_assignment_rej,
|
||||
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
||||
plen = Encoding::write_immediate_assignment_reject(
|
||||
immediate_assignment_rej, 112, 100,
|
||||
GSM_L1_BURST_TYPE_ACCESS_1);
|
||||
|
||||
printf("assignment reject: %s\n",
|
||||
osmo_hexdump(immediate_assignment_rej->data, 22));
|
||||
|
||||
OSMO_ASSERT(plen == 19);
|
||||
/* RA value */
|
||||
OSMO_ASSERT(immediate_assignment_rej->data[3] == 0x7f);
|
||||
/* Extended RA value */
|
||||
OSMO_ASSERT(immediate_assignment_rej->data[19] == 0xc0);
|
||||
|
||||
bitvec_unhex(immediate_assignment_rej,
|
||||
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
|
||||
|
||||
plen = Encoding::write_immediate_assignment_reject(
|
||||
immediate_assignment_rej, 112, 100,
|
||||
GSM_L1_BURST_TYPE_ACCESS_0);
|
||||
|
||||
printf("assignment reject: %s\n",
|
||||
osmo_hexdump(immediate_assignment_rej->data, 22));
|
||||
|
||||
OSMO_ASSERT(plen == 19);
|
||||
/* RA value */
|
||||
OSMO_ASSERT(immediate_assignment_rej->data[3] == 0x70);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
osmo_init_logging(&gprs_log_info);
|
||||
|
@ -422,6 +475,7 @@ int main(int argc, char **argv)
|
|||
test_rlc_v_b();
|
||||
test_rlc_v_n();
|
||||
test_rlc_dl_ul_basic();
|
||||
test_immediate_assign_rej();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,3 +6,5 @@ rbb: 00 00 00 00 00 00 00 31
|
|||
rbb: 10 00 00 00 00 00 00 01
|
||||
show_rbb: RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
show_rbb: IIRRIIIR
|
||||
assignment reject: 06 3a 10 7f 06 36 14 7f 06 36 14 7f 06 36 14 7f 06 36 14 c0 2b 2b
|
||||
assignment reject: 06 3a 10 70 06 36 14 70 06 36 14 70 06 36 14 70 06 36 14 0b 2b 2b
|
||||
|
|
Loading…
Reference in New Issue