Compare commits

...

517 Commits

Author SHA1 Message Date
Pau Espin Pedrol
f1a334be63 Bump version: 0.4.0.115-513c-dirty → 0.5.0
Change-Id: I6ce6fb40690a66b0980eba4fa03b47da2f59ee6e
2018-05-03 16:20:01 +02:00
Stefan Sperling
513c9bca17 improve documentation of Encoding::write_paging_request()
Add pointers to relevant parts of the spec. Tweak comments to be
more specific about the values being written and abbreviations used.

Change-Id: Ia5bf3f7f8846198b7b4e25ff1accf6206764be74
2018-05-02 14:53:16 +02:00
Philipp Maier
33c52b6271 tbf: add frame number to log output
Currently, the TBF timer log messages lack the frame number in
the logoutput

- Add frame number to TBF timer related log-statements

Change-Id: I5a744dc5cd7c1de1baea13fffac026c83d091429
Related: SYS#4139
Patch-by: Octasic inc.
2018-04-12 12:46:44 +02:00
Philipp Maier
7e8e3978fe pcu_l1_if: add frame number to log output
Currently, the log output lacks the frame number.

 - make get_current_fn() public
 - add frame number to the log statements in pcu_l1_if.cpp

Change-Id: Idce994dbf86a2bbf861907d75418a2a3867244db
Related: SYS#4139
Patch-by: Octasic inc.
2018-04-10 13:35:24 +02:00
Philipp Maier
8ccf704608 cosmetic: remove runaway semicolon
Change-Id: I33c335dc8d564a8357ffb5b1163c2c4b1a578b49
Related: SYS#4139
Patch-by: Octasic inc.
2018-04-10 13:35:18 +02:00
Neels Hofmeyr
42f2d61ac9 use osmo_init_logging2() with proper talloc ctx
There is a duality of initialization: early_init() in bts.cpp wants to init
logging even before static instances get initialized. Make sure that
tall_pcu_ctx is initialized during early_init() as well. There is a build
context that does not seem to include bts.cpp (osmo-pcu-remote), so to be sure,
init tall_pcu_ctx as NULL and both in early_init() as well as pcu_main.cpp,
init both tall_pcu_ctx and logging if it is still NULL.

Change-Id: I2199b62d0270bd35dec2283e8f5b364b7c63915b
2018-04-01 16:57:42 +02:00
Neels Hofmeyr
e6e4898027 Revert "Rewrite EGPRS Packet Uplink Assignment"
This reverts commit 529ce88545,
I2139fb347b3290621bbc3f6a031f7f213d372e65.

Commit I52ec9b07413daabba8cd5f1fba5c7b3af6a33389 /
896574e92b was found (empirically) to be a
regression, rendering GPRS service fatally unreliable.

This reverted commit seems to be related to the regression and is reverted
along with it.

Related: OS#3013
Change-Id: I3e8cc0e8ba3ba5bd444124fd4cb95ef92a71fdfb
2018-03-30 14:30:13 +00:00
Neels Hofmeyr
9f06cbff6d configure: properly quote CFLAGS in lc15 check
Change-Id: I7bb7a7d7b5b9340cb41c95f848774ee378c59212
2018-03-29 22:10:57 +00:00
Neels Hofmeyr
a661bcd086 configure: fix --enable-sysmocom-dsp and --with-sysmobts flags
Fix multiple problems around the sysmobts DSP access and headers:

- Use the proper variable name to detect the choice: $enable_sysmocom_bts was
  not set anywhere and would actually be used from the current user env, if
  present, instead of from configure args.

- Quote the $CPPFLAGS when assigning to oldCPPFLAGS and back.

- When checking SYSMOBTS_INCDIR, do not allow an empty "-I" without a dir.

- Ensure the --with-sysmobts path is used as an absolute path.

- Error out if --with-sysmobts is paired with --disable-sysmocom-dsp.

Also tweak reporting.

The resulting behavior now is:

./configure --disable-sysmocom-dsp
checking whether to enable direct DSP access for PDCH of sysmocom-bts... no

./configure --enable-sysmocom-dsp
checking whether to enable direct DSP access for PDCH of sysmocom-bts... yes
checking for sysmocom/femtobts/superfemto.h... no
configure: error: sysmocom/femtobts/superfemto.h can not be found, see --with-sysmobts

./configure --disable-sysmocom-dsp --with-sysmobts=../../../sysmobts/layer1-api
checking whether to enable direct DSP access for PDCH of sysmocom-bts... error
configure: error: --with-sysmobts does not work with --disable-sysmocom-dsp

./configure --enable-sysmocom-dsp --with-sysmobts=../../../sysmobts/layer1-api
checking whether to enable direct DSP access for PDCH of sysmocom-bts... yes, using -I/n/s/sysmobts/layer1-api
checking for sysmocom/femtobts/superfemto.h... yes

./configure --with-sysmobts=../../../sysmobts/layer1-api
checking whether to enable direct DSP access for PDCH of sysmocom-bts... yes, using -I/n/s/sysmobts/layer1-api
checking for sysmocom/femtobts/superfemto.h... yes

./configure --with-sysmobts=/nonexisting/path
checking whether to enable direct DSP access for PDCH of sysmocom-bts... yes, using -I/nonexisting/path
checking for sysmocom/femtobts/superfemto.h... no
configure: error: sysmocom/femtobts/superfemto.h can not be found in -I/nonexisting/path, see --with-sysmobts

./configure --with-sysmobts=/var/log
checking whether to enable direct DSP access for PDCH of sysmocom-bts... yes, using -I/var/log
checking for sysmocom/femtobts/superfemto.h... no
configure: error: sysmocom/femtobts/superfemto.h can not be found in -I/var/log, see --with-sysmobts

Change-Id: I2f5988730dbbcf3b21d8c647c499623843ed3da9
2018-03-29 17:14:22 +02:00
Neels Hofmeyr
39f845848c Revert "Rewrite Packet Downlink Assignment"
This reverts commit 896574e92b,
I52ec9b07413daabba8cd5f1fba5c7b3af6a33389.

This commit was found (empirically) to be a regression, rendering GPRS service
fatally unreliable.

Related: OS#3013
Change-Id: Idcba0381f70eb7f7c9aefdee9dfeafd5de96a9be
2018-03-28 14:34:55 +00:00
Neels Hofmeyr
782da2cf95 Revert "Rewrite Packet Uplink Assignment"
This reverts commit 93d947f5e8,
I44db2eeea7448ff67e688ae716487bc6dbfc96a3.

Commit I52ec9b07413daabba8cd5f1fba5c7b3af6a33389 /
896574e92b was found (empirically) to be a
regression, rendering GPRS service fatally unreliable.

This reverted commit seems to follow after the regression and is reverted along
with it.

Related: OS#3013
Change-Id: If7038127e9a663c93006475b3add961adc0b1922
2018-03-28 14:34:55 +00:00
Neels Hofmeyr
89b85e078e Revert "Use Timing Advance Index in UL assignments"
This reverts commit 6298fbb7b2,
I8b17be78a46c0bc17516b7c90f35aa4768010ae4.

Commit I52ec9b07413daabba8cd5f1fba5c7b3af6a33389 /
896574e92b was found (empirically) to be a
regression, rendering GPRS service fatally unreliable.

This reverted commit seems to follow after the regression and is reverted along
with it.

Related: OS#3013
Change-Id: I5e0fd8c9c3b89e519e7382e3d0bb24e0aeddeff6
2018-03-28 14:34:55 +00:00
Neels Hofmeyr
f75381498c mslot_class: two more: use uint32_t to shift 1 << 31
Avoid runtime error seen on jenkins admin-deb9build slave, when building
osmo-pcu with_dsp=None,with_vty=False, during ts_alloc and tbf regression
tests:

+../../../src/mslot_class.c:271:36: runtime error: left shift of 1 by 31 places cannot be represented in type 'int'
+../../../src/mslot_class.c:272:22: runtime error: left shift of 1 by 31 places cannot be represented in type 'int'

This time it seems that these are all.

The master-osmo-pcu breaks on this since moving to a debian9 build slave.

Change-Id: I976a1dca9da19a05afc85a17b7ba60545b7bc1e5
2018-03-28 14:44:47 +02:00
Neels Hofmeyr
8b4bd46b95 mslot_class: find_free_tfi(): use uint32_t to shift 1 << 31
Avoid runtime error seen on jenkins admin-deb9build slave, when building
osmo-pcu with_dsp=None,with_vty=False, during ts_alloc and tbf regression
tests:

+../../../src/mslot_class.c:242:22: runtime error: left shift of 1 by 31 places cannot be represented in type 'int'

The master-osmo-pcu breaks on this since moving to a debian9 build slave.

Change-Id: I0cdf10e5fbc1173a7a09bd4fed8a66d06f80aeb1
2018-03-26 23:24:16 +02:00
Pau Espin Pedrol
74906224ca gprs_bssgp_pcu.cpp: Comment unused function parse_ra_cap
Commit 741d25cb6f commented the only user
of the function but forgot to comment too the function itself.

Change-Id: I8b291b45aaedfb0421cd28c0d9e24cefa5547b09
2018-03-17 01:43:14 +01:00
Neels Hofmeyr
5a5919435e configure: add --enable-werror
Provide a sane means of adding the -Werror compiler flag.

Currently, some of our jenkins.sh add -Werror by passing 'CFLAGS="-Werror"',
but that actually *overwrites* all the other CFLAGS we might want to have set.

Maintain these exceptions from -Werror:
a) deprecation (allow upstream to mark deprecation without breaking builds);
b) "#warning" pragmas (allow to remind ourselves of errors without breaking
   builds)

As a last configure step before generating the output files, print the complete
CFLAGS and CPPFLAGS by means of AC_MSG_RESULT.

Change-Id: I0f735913fc3bbda695c4e66449dcfc94f417dafb
2018-03-13 00:03:38 +00:00
Neels Hofmeyr
bdc55fad62 implement support for 3-digit MNC with leading zeros
Receive the mnc_3_digits flag from the PCU interface.

Bump the PCU interface to 9.
This is one part of the three identical pcuif_proto.h patches:
- I49cd762c3c9d7ee6a82451bdf3ffa2a060767947 (osmo-bts)
- I787fed84a7b613158a5618dd5cffafe4e4927234 (osmo-pcu)
- I78f30aef7aa224b2e9db54c3a844d8f520b3aee0 (osmo-bsc)

Add 3-digit flags and use the new RAI and LAI API from libosmocore throughout
the code base to be able to handle an MNC < 100 that has three digits (leading
zeros).

Depends: Id2240f7f518494c9df6c8bda52c0d5092f90f221 (libosmocore),
         Ib7176b1d65a03b76f41f94bc9d3293a8a07d24c6 (libosmocore)

Change-Id: I787fed84a7b613158a5618dd5cffafe4e4927234
2018-03-11 00:47:14 +01:00
Alexander Couzens
8343b4adbb pcuif_proto: add version 8 features
Add PCU_IF_MSG_DATA_CNF_DT and PCU_IF_SAPI_AGCH_DT to bring the
pccif_proto into sync. Both commands are required to support the
rb11 with an osmo-bsc co-located pcu.

Change-Id: Ieaf151447e5556b911be7e2483b7c154fc5ec42e
2018-02-28 03:03:49 +01:00
Alexander Couzens
d2a219e644 pcuif_proto.h: fix whitespaces and indention
Change-Id: I290967346af4e2707cfdfb62dccaccd43d195443
2018-02-28 03:03:01 +01:00
Max
731e2bb328 Simplify TS alloc: move slot check into functions
Move timeslot applicability check outside of nested for loop into
separate functions and document them. Add corresponding tests.

This allows us to clarify types used in TS-related computations.

Change-Id: Ic39e848da47dc11357782362fdf6206d2c1457c2
Related: OS#2282
2018-02-21 12:08:40 +00:00
Max
77988d469d Simplify TS alloc: move slot assignment
Move into separate functions:
* move timeslot reservation
* move UL timeslot assignment
* move DL timeslot assignment

Change-Id: I64cf78c5cfc78664766f9769dd5cde632dab92b0
Related: OS#2282
2018-02-21 12:08:38 +00:00
Max
847ed9f8cd TBF: make network counters internal
* store N310* counters in shared array similar to corresponding timers
* add functions to increment/reset counters

This avoids direct access to TBF counters from PDCH.

Change-Id: I8ffff9c7186f74bde7e6ac5f6e98f0b3e4c35274
Related: OS#1539
2018-02-20 18:16:11 +01:00
Max
4da385998a Simplify TS alloc: constify max dl slot func
Constify parameters of gprs_alloc_max_dl_slots_per_ms().

Change-Id: Ic90930d98560459eab0054cb9e1625cb99db61c8
Related: OS#2282
2018-02-20 10:20:55 +01:00
Max
c5407c775a Simplify TS alloc: don't use PDCH for free TFI
Don't use PDCH from free TFI lookup routine. This allows for simpler
function which can be moved to mslot_class.c alongside with other
similar helpers.

Change-Id: Ie154866900453d232a890f7b9a30911b451525a1
Related: OS#2282
2018-02-20 10:19:25 +01:00
Max
6dc90b8c86 Move PDCH-related functions into separate files
The PDCH class and corresponding functions are rather self-contained and
independent from BTS implementation. Let's move them into separate file
to make bts.cpp more manageable. As additional benefit it allow us to
somewhat untangle all the different cross-dependent includes.

Change-Id: Ie05e25361e6741a81b024679f9675c98d4923683
Related: OS#1539
2018-02-19 17:41:24 +01:00
Max
2afec6dba5 Simplify TS alloc: split USF/UL allocation
* move USF allocation into separate function
* document USF allocation

This allows to clearly see where selected UL TS is forced into single TS
in algorithm B allocator.

Change-Id: I563dc10827ce68295553f88f3bf2e1fc0ba595c1
Related: OS#2282
2018-02-19 09:00:21 +00:00
Max
0cc7212cfd Simplify TS alloc: split allocation
* generalize TS allocation and move it into separate function
* move single-slot allocation into separate function
* use common functions for TS allocation on both UL and DL

Change-Id: Ied45ae380c345bc76fe9d6fd9a6184d1109f83f2
Related: OS#2282
2018-02-19 09:00:20 +00:00
Max
adca67bcbb Simplify TS alloc: separate capacity computation
Move TRX capacity computation into separate function and document it.

Change-Id: Ifd88fc7ff818ea2a041eae61c5d457926a0df0f2
Related: OS#2282
2018-02-19 09:00:20 +00:00
Max
f633b8d8b2 Simplify TS alloc: split off RX mask computation
Move computation of RX mask into separate function and document it. This
allows to significantly shrink find_multi_slot() function and overall
improve code readability.

Since the test output requires cosmetic adjustment anyway due to change
in the sequence of log messages, use this opportunity to better group
and format log message.

Change-Id: I731726a096bba7ee97499e5cbe3e7401869d7392
Related: OS#2282
2018-02-19 09:00:19 +00:00
Max
1187a7719c Update header includes
Many files include unnecessary headers and don't include headers which
are actually used. Because of that combined with the fact that OsmoPCU
is a mixture of C and C++, it makes it hard to modularize code. Fix
this (using iwyu [1] tool):

* add missing headers
* remove unused headers

[1] https://include-what-you-use.org/

Related: OS#1539
Change-Id: I8c9f488a43b099c72b2d30d3245e7ba50872fc00
2018-02-19 08:43:46 +00:00
Max
910a387b0e Move include guard to the top
Having explicit include above the douible-include guard defines is
potential source for hard to track bugs. Let's move it inside the guard
statement.

Change-Id: I5114a63ce00b03c8eed23565d52969250bd505cc
Related: OS#1539
2018-02-19 08:43:45 +00:00
Max
4382e4e8fe Move paging generation into PDCH
Previously paging was prepared inside BTS function and than handed over
to PDCH function. Move the actual preparation into PDCH to better
decouple PDCH from BTS.

Related: OS#1539
Change-Id: I389fb16b6e54040770c21f88edbcb8e045636928
2018-02-19 08:43:33 +00:00
Max
735e435e8e Use explicit type for pcu_lsb()
It's only used for byte-long input so we can specify input and output
types explicitly.

Change-Id: Id0bef691e17e4331c7c4b491661e36173d85388a
2018-02-19 08:33:04 +00:00
Stefan Sperling
5b22fb7953 Make osmo-pcu wait for BTS to become available at start-up time.
After the PCU socket becomes available, the BTS might send an
INFO_IND message with the 'ACTIVE' flag cleared. If this happens,
do not exit immediately, but keep retrying until an INFO_IND
message with the 'ACTIVE' flag arrives.

Note that this change only affects behaviour at process start-up time.
If the BTS switches from active to inactive state then osmo-pcu will
still exit. If this behaviour should be changed as well it could be
done in a follow-up patch.

Tested against osom-bsc + osmo-bts-virtual.

Change-Id: Ic42a5601a43b81d260721fef5d9fa52447f9d309
Related: OS#2689
2018-02-14 19:55:05 +01:00
Max
c907b88ecd emu: use libosmocore definitions
Change-Id: I4eade528faeb3841549ad7a6c78e8c1357909614
2018-02-13 12:53:55 +01:00
Max
4c112dc5a6 TBF: move common test code into functions
* move common code into functions
* print error instead of failing test right away

This allows the tests to continue till completion even in case of
intermediate error which simplifies troubleshooting by allowing to
gather more errors in a single test run.

Change-Id: I1c4ad1dc94542835f15bd666f0821e0ccfcc78c1
Related: OS#1759
2018-02-08 09:40:57 +00:00
Max
137fd59bf4 RACH: improve single block detection
Replace unreadable if-else ladder in is_single_block() with regular
switch-case. This enables implementation of 11-bit RACH support in
follow-up patches.

Related: OS#1548
Change-Id: I9180478152f9341f11bb3dffe61671da683f24d8
2018-02-07 17:25:42 +01:00
Max
c9ce6f916e vty: drop unused function
Change-Id: I01f3773ca6a9b6d4e28ca2f59c944c6d48918dd1
2018-02-03 15:05:29 +00:00
Max
5441e1f2f0 TBF: show assignment kind in vty
Change-Id: Ic4e40d9c141ab7ee3f7c4dceec007dbe16359f93
Related: OS#1759
2018-02-03 15:05:29 +00:00
Max
5d7f757e49 TBF: add helpers for assignment type handling
* add function to set/unset given assignment type
* log assignment type flag changes
* update tests output with additional logs

This enables us to carefully track the TBF assignment type transitions.

Change-Id: I3fe9d52472be8b7f257e8326b2f84e8e7d7bd1f4
Related: OS#1759
2018-02-03 15:05:29 +00:00
Max
0fdaa9d383 TBF: decrease logging verbosity for traffic
Change-Id: If43aa9895abf58602556c986a633ff93a6f00b06
2018-02-03 15:05:28 +00:00
Max
7e4921d8e2 Simplify TS alloc: internalize TRX check
Move TRX check inside local tfi_find_free() wrapper to make main
algorithm easier to follow.

Change-Id: I02da2b8ba8c9c8815dae0e39e1fed277ca0df171
Related: OS#2282
2018-01-31 11:21:28 +01:00
Max
69d585e148 TS alloc: print suggested TRX on allocation errors
If TS allocation fails due to unavailable TFI, print TRX which was
suggested to allocator. This simplifies allocator debugging but requires
cosmetic modifications to test output.

Change-Id: Icaf97d71d71985d52dc0bda448c26b19fe5645e7
Related: OS#2282
2018-01-31 11:21:27 +01:00
Max
a76a7d0c6c Simplify TS alloc: adjust function signatures
* document used parameters and return values
* use consistent formatting
* constify function parameters where appropriate (adjusting parameter
  types if necessary)

Change-Id: I211b10b4da59c73d509b719346774515c761886a
Related: OS#2282
2018-01-26 12:57:05 +01:00
Max
d000d80968 Simplify TS alloc: use defines for constants
* define and use constant for occupied TFI instead copying the same
  magic number all over the place
* use libosmocore's define for bit pretty-printer

Change-Id: I2699ceebf0cbec01652a02fa68ccc9e9419d0293
Related: OS#2282
2018-01-26 12:57:05 +01:00
Max
92e9c17aec Simplify TS alloc: avoid TS reassignment
Assign reserved_*_slots only when multislot masks are found to avoid
reassignment and make code easier to follow.

Change-Id: I9b0482f4ea75ead9855cd78e33c8e70d0ccf4484
Related: OS#2282
2018-01-26 12:56:27 +01:00
Max
92b7a50605 Simplify TS alloc: fix allocation calls
Using the semantic patch below, adjust allocation-related calls to match
updated allocator signatures.

// spatch --c++ --dir src -I src --sp-file callfix.spatch --in-place --recursive-includes
// spatch --c++ --dir tests -I src --sp-file callfix.spatch --in-place --recursive-includes
@@ expression A, B, C, D, E; @@
tbf_alloc_ul_tbf(A, B, C, D, E,
(
- 1
+ true
|
- 0
+ false
)
 )
@@ expression A, B, C, D, E; @@
tbf_alloc_dl_tbf(A, B, C, D, E,
(
- 1
+ true
|
- 0
+ false
)
 )

Change-Id: I43c76cb49093b40eb854d324e898e821270053dc
Related: OS#2282
2018-01-26 12:55:59 +01:00
Max
e9fe0e3d06 Simplify TS alloc: adjust allocator signatures
* drop unused parameters (from both functions and structs)
* document used parameters and return values
* tighten types used for parameters
* use consistent formatting

Tests are adjusted accordingly but test results are left untouched to
avoid regressions.

Change-Id: I39d81ab64ff790b9c4c2d0312a574485cd83e755
Related: OS#2282
2018-01-26 12:55:29 +01:00
Max
a296118e6d TBF: override send function via linker option
Use --wrap linker facility to override pcu_sock_send() similar to other
Osmo* projects.

Change-Id: Ia3d436bd3d1fb0ce8e98526bd7457f4c57667ceb
2018-01-25 19:54:57 +01:00
Max
164b59d757 TBF: decrease L1 logging verbosity in test
Don't clutter output with low-level details.

Change-Id: I451f2472070dea2387bfaea45ca5bdd9e3b2276d
2018-01-25 19:54:57 +01:00
Max
d2d51ed109 cosmetic: fix whitespace issue with include files
Change-Id: I401fe88f5bd1665becd6fe6d4204b3877d548ccc
2018-01-25 16:24:34 +00:00
Max
01bd0cc42f Add multislot classes from latest spec
The table B.1 is copy-pasted from 3GPP TS 45.002 and reformatted via
Emacs macros into C struct to avoid typos. The test output expanded
accordingly.

The allocation test expectations and output are adjusted accordingly.

Note: classes 35-45 which need TA offset are not properly supported
yet. This can be extended once we have such devices available for tests.

Change-Id: I1ef2eb99c517f25e7d1e71b985a3e0eb3879eb2c
Related: OS#2282
2018-01-25 16:12:58 +00:00
Max
9f46071409 AllocTest: remove assumption on max MS class
So far the allocation was only tested up to hardcoded MS class 29. Drop
that assumption and test for all supported MS classes. Adjust expected
test output as necessary.

Note: using mslot_class_max() forces allocation for MS classes 30 and 31
for which no actual data is available (will be added in follow-up
patches) which current implementation treats differently depending on
TX/RX direction - see gprs_alloc_max_dl_slots_per_ms(). Because of that
we have to adjust the expected number of allocations in
test_successive_allocation() as well.

Change-Id: I7737f303d97197ef159b14a19c3312a11f07b433
Related: OS#2282
2018-01-25 16:12:57 +00:00
Max
c59ef12e51 AllocTest: expand test output
* print MS classes
* unify and print test mode description
* print additional info on test completion

This only changes meta info about test run but not the actual test
output.

Change-Id: I30a4b8f561a9677f4e9ded33a051a249bd15a6a2
Related: OS#2282
2018-01-25 16:12:57 +00:00
Max
2ecf0fdfc2 AllocTest: adjust test_alloc_b()
This function contains 3 independent test cases. Let's split them into
separate functions to simplify further modifications:

* split test cases into separate functions
* use them for mass test as well
* change function names to avoid confusion
* make individual test cases return error instead of failing via assert
  on allocation failure

The top-level test_alloc_b() is used as part of exhaustion tests in
test_all_alloc_b() for example, so it's expected that allocation might
fail (due to TFI or USF exhaustion for example) eventually. In this case
it's better to indicate it to caller instead of failing entire program.

The test output does not require any adjustements because we do not
exhaust to the point of allocation failure yet.

Change-Id: Id7e03a85ce96e7d617cecee963759bae589a3a1a
Related: OS#2282
2018-01-25 16:12:57 +00:00
Max
46fbfceac6 Add tests for find_multi_slots()
* make function public
* add tests

Change-Id: I4174703808335c19341cd5b5f4422496d958967f
2018-01-25 16:12:56 +00:00
Max
fdd79e9828 TBF: adjust test log levels
* enable debugging for DTBF*
* disable excessive DRLCMAC*

Change-Id: I122620941e7939d513742c8589a75e0ab76f79ab
2018-01-24 11:07:01 +01:00
Max
cac6b66638 TBF: make poll state internal
* add functions/macros for setting TBF's poll state
* add function for checking TBF's poll state

Change-Id: I6db1c4e7bd0a49aeb5e391afe371c36b96c6a702
Related: OS#1539
2018-01-24 11:06:55 +01:00
Max
088c7df571 TBF: make UL ack state internal
* add functions/macros for setting TBF's UL ack state
* add functions for checking TBF's UL ack state

N. B: this should not be confused with TBF-UL state.

Change-Id: I144483447d4b0b93e775da0e926ee45eb8ab39f3
Related: OS#1539
2018-01-24 11:06:37 +01:00
Max
0e5998087e TBF: make UL/DL state internal
* add functions/macros for setting TBF's UL/DL state
* add functions for checking TBF's UL/DL state
* move pre-free check into separate function

N. B: this should not be confused with TBF-UL or TBF-DL state.

Change-Id: Idcbf5775d17b1247f2ed01788f9b0788ce66e871
Related: OS#1539
2018-01-24 11:06:22 +01:00
Max
0524e38d9e TBF: add dedicated log categories
Previously all TBF-related events were logged as part of DRLCMAC which
is too broad to make it practically useful due to excessive amount of
log messages generated. Introduce dedicated log categories for
TBF-related events. Adjust test output as necessary.

Change-Id: I64d660e5971263d5c63d2ba95d50625c16a594aa
2018-01-19 18:49:16 +01:00
Max
d81b3bf360 Set V_N and V_B to known initial state
Reset V_N and V_B in UL/DL window class constructors to make sure we
always start from known initial state.

Related: OS#1759
Change-Id: I8e14ffa913b49c5394229220de9165cdfaabdf19
Fixes: CID70468, CID70469.
2018-01-17 15:57:26 +00:00
Max
4cb6e04914 jenkins.sh: Disable building doxygen for deps
Don't clutter build logs with irrelevant output.

Change-Id: If1784baa519c10ab0ab3e600f373c27a6c8ae4c6
2018-01-17 15:56:57 +00:00
Max
2399b1dbfc TBF: log source of state transitions
We use the same approach for osmo_fsm: when state transition happens,
it's not very useful to always log the transition function itself, it's
much more useful to see where the actual transition comes from.

Change-Id: I348ba89bdda2b44c7019e9c893c764ee08c80bec
Related: OS#1759
2018-01-17 15:47:32 +00:00
Max
186206cff2 Allow specifying sysmocom headers explicitly
The headers for LC1.5 are specified explicitly. Add corresponding option
to specify sysmoBTS headers location and use it in jenkins build. While
at it, unify header fixup code with the one used in OsmoBTS.

Change-Id: I5248e8b389fd240b4d5a0bcf6c954d6115262462
2018-01-17 11:17:43 +00:00
Max
6298fbb7b2 Use Timing Advance Index in UL assignments
Write TAI (if available) when generating Rest Octets for UL
Assignment. This should not affect actual PCU behavior because TAI is
not yet supported by upper layers but we have to adjust corresponding
tests anyway.

Change-Id: I8b17be78a46c0bc17516b7c90f35aa4768010ae4
2018-01-17 10:50:14 +00:00
Max
93d947f5e8 Rewrite Packet Uplink Assignment
Use bitvec_set_*() directly without external write pointer tracking to
simplify the code. This is part of IA Rest Octets (3GPP TS 44.018
§10.5.2.16) which is the last part of the message so it should not
interfere with the rest of encoding functions.

The tests are adjusted accordingly.

Change-Id: I44db2eeea7448ff67e688ae716487bc6dbfc96a3
Related: OS#1526
2018-01-17 10:50:14 +00:00
Max
896574e92b Rewrite Packet Downlink Assignment
Use bitvec_set_*() directly without external write pointer tracking to
simplify the code. This is part of IA Rest Octets (3GPP TS 44.018
§10.5.2.16) which is the last part of the message so it should not
interfere with the rest of encoding functions.

The tests are adjusted accordingly.

Change-Id: I52ec9b07413daabba8cd5f1fba5c7b3af6a33389
Related: OS#1526
2018-01-17 10:50:14 +00:00
Max
529ce88545 Rewrite EGPRS Packet Uplink Assignment
Use bitvec_set_*() directly without external write pointer tracking to
simplify the code. This is part of IA Rest Octets (3GPP TS 44.018
§10.5.2.16) which is the last part of the message so it should not
interfere with the rest of encoding functions.

Reusable fragments are split into static helpers.

Change-Id: I2139fb347b3290621bbc3f6a031f7f213d372e65
Related: OS#1526
2018-01-17 10:50:14 +00:00
Max
2141962baf Fix sanitizer build
Add sanitizer flags to linker as well to resolve linker error.

Change-Id: I695baaf8ce78ed938f6f71c40d17120fa690338b
2018-01-15 17:23:17 +01:00
Max
71affcedba Allocate global context for TypesTest
Missing allocation leads to LSAN error:
==24997==ERROR: LeakSanitizer: detected memory leaks

Indirect leak of 230 byte(s) in 2 object(s) allocated from:
    #0 0x7feaa1b2fb50 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50)
    #1 0x7feaa13dbc80 in _talloc_zero (/usr/lib/x86_64-linux-gnu/libtalloc.so.2+0x5c80)

Change-Id: I62c03dad353f459abcdb7a18a69b7782da38dfb7
SUMMARY: AddressSanitizer: 230 byte(s) leaked in 2 allocation(s).
2018-01-15 17:23:17 +01:00
Max
3e659ea9b7 Fix jenkins.sh to match jenkins job axis filter
The 'yes/no' values are automatically converted to True/False upon
jenkins job instantiation. Let's use those directly.

Change-Id: Ib2100c8345d1f07f488de8170348fec9f877dd9b
2018-01-12 17:07:13 +00:00
Max
00fd0df046 Don't access TBF internals in vty functions
Obtain corresponding window object of UL/DL TBF by using proper accessor
function instead of direct access to private member.

Change-Id: I89bcd2c2b0b6f120d40d20fd43c1e516de3e3950
2018-01-12 16:37:45 +01:00
Max
9d7357e4fe TBF: unify EGPRS window calculation
Move actual calculation into shared function and use it to set window
size for TBF. TBT test output requires cosmetic adjuestements due to
extended debug output.

Change-Id: Ib9f4a277082da3c71007f5f3b4f2acac8b994540
Related: OS#1759
2018-01-12 15:29:42 +01:00
Max
d0532b53eb TBF-DL: move priority computation into function
Improve readability by moving priority computation into separate
function.

Change-Id: Icdca0106a544036eaa94a25f0d4f84e4282f4568
2018-01-12 15:29:41 +01:00
Max
ea98b7d784 TBF: move window parameters to UL/DL level
The UL and DL TBF use different classes implementing window
management. Hence it's better to use it explicitly instead of using the
common window management superclass inside common TBF superclass. While
at it, also remove the direct access to window class - use accessor
functions instead.

Related: OS#1759
Change-Id: I0b55aa8947db65f7206adcf53ea32b74a831d9e6
2018-01-12 15:29:41 +01:00
Max
7d32f55e4e Avoid code duplication in TBF test
Move repetitive checks into corresponding macros to avoid copy-pasted
code. This also enables strickter checks some of which were apparently
omitted while copy-pasting.

This is part of preparation work to move to separate UL/DL windows.

Related: OS#1759
Change-Id: If7aa72f5aa66c5e9c255542c066b5494c098aab2
2018-01-12 15:29:41 +01:00
Max
2617bf2016 TBF-UL: add simpler test helper
Add function to set both V_R and V_Q values to 0 which is useful for TBF
test.
Related: OS#1759
Change-Id: I719abfbd5b88c694cbbd69d5c4dcb42baaca91b2
2018-01-12 14:26:28 +00:00
Max
58818585bc Clarify RACH-related interfaces
* make is_11bit parameter into bool
* remove is_single_block() from public interface and mark it as static
* move logging outside of if ladder
* move side-effects from is_single_block() into separate static
  functions
* simplify UL-TBF allocation in case of 11-bit RACH

This immediately makes it obvious that priority is never actually used
despite being computed - seems like a leftover from merge of incomplete
patch series.

Change-Id: If189b7166a29a87ffb17a7a9bc560f674851fd53
Related: OS#1548
2018-01-12 14:24:00 +00:00
Max
8dce1de6d2 TBF: cleanup state flag handling
* introduce generic function to check whether particular flag was set
   for'a TBF and clear it if necessary. Use this instead of
   clear_poll_timeout_flag()
 * add function to explicitly set assignment and appropriate state flags

Overall this makes the code easier to read and debug.

Related: OS#1759
Change-Id: Ic4560280c72f91700f2e19c6c7f6658dc29625c2
2018-01-12 14:17:52 +00:00
Max
5081806f4d Make TBF state private
Let's make sure no external function can mess with the TBF state.

Change-Id: I217f4c4bac21dd584c8682928a080a1a6e9507e1
2018-01-12 14:17:10 +00:00
Max
b3a17d6074 cosmetic: clarify coding scheme and puncturing
* use appropriate types for coding scheme parameters
* add comment regarding possible number of RLCMAC blocks

The code in create_dl_acked_block() has underlying assumption that
rlc.num_data_blocks can never be more than 2, which is true and is
enforced by appropriate asserts but is not obvious when looking at the
function code alone. It's equally hard for Coverity which leads to false
positives in scan.

Lets' make this assumption explicit by putting it into for(;;) condition
alongside with corresponding comment.

Fixes: CID143070
Change-Id: If599a6c8a6ef56d847604fcf41bb71decccd8a78
2018-01-04 16:35:55 +00:00
Max
a4f570fe7a window: move encoding into functions
* move window size encoding and writing into separate functions
* introduce necessary TBF wrappers to avoid direct m_window access

This is part of preparation work to move to separate UL/DL windows.

Related: OS#1759
Change-Id: I60184d5049bc7d7b119df5a9eb82d1c4b788c840
2018-01-04 10:15:59 +00:00
Max
7df82d412e TBF-DL: mark rcvd_dl_ack() parameters as boolean
The final(_ack) parameter of rcvd_dl_ack() only used as boolean - mark
it as such.

Change-Id: Icc4d68f049a45d4b42c5594f50594ff0d44c1bac
2018-01-04 10:15:59 +00:00
Max
869c0c2e55 Fix llc_queue_size() type
It either returns 0 or LLC queue size() which has size_t return
type. This means it can never be negative - hence it's better to use
size_t as return type.

Change-Id: I2a6e849d349ab12854976bd0d68537a370a9c83d
Fixes: CID181478
2018-01-03 12:00:36 +01:00
Max
0bc982e714 TBF: bail out for unknown timers
Return right after logging error if attempting to start or stop unknown
timer.

Change-Id: Ie6ae564d41a5e03270685c6bafb3504278eb3551
Fixes: CID181512, CID181514
2018-01-02 07:26:05 +00:00
Max
467f633b16 TBF: log timer invocation source
When troubleshooting TBF timers we're not only interested in timer
duration but also in the code which triggered it. Let's use LOGPSRC to
log it: wrap t_start() in a macro for convenience.

Change-Id: If5f883ae52c469e5158bad24da9904fdc455582f
Related: OS#2407
2018-01-02 07:26:05 +00:00
Max
b2de1f7888 TBF: unify timer handling
Use generic timer handling infrastracture to handle assignment/reject
internal timer. Rename timer array accordingly. Use defines with
explicit second/microsecond values to make it more readable.

Change-Id: I63fb7e6f0695383a83472c836a381a055f64690b
2018-01-02 07:26:04 +00:00
Max
effdec6e13 Add optional profiling support
This facilitates the use of programs like uftrace. It's disabled by
default due to associated overhead.

Change-Id: I5c16988cefa46e0b958030c0f3bff9efc5b4979d
2017-12-28 14:18:02 +01:00
Max
20c7c46bce Add tests for pcu_lsb()
This utility functions is used by TBF allocation routines and only
tested indirectly through allocation test. Let's add proper exhaustive
test which checks all uint8_t values.

This also requires adding missing include to pcu_utils.h

Change-Id: If08a7f0d31f0e5ad8a5efa5885880aed19c329ab
2017-12-22 14:20:05 +01:00
Max
406a1f0acf Enable sanitize for CI test
Change-Id: Ia33ffb9b25df587706367bc24925cf9cead3b9a0
2017-12-21 17:34:19 +01:00
Max
b2f0b62cd4 Add --enable-sanitize configure option
Change-Id: Idb2c1d6057012ed2f032e7504387a0767d02d75b
2017-12-21 17:27:34 +01:00
Max
327e121a0f Add function to get max supported MS class
It's useful for allocation tests.

Change-Id: I31d503af700ec3364042ff7e661710953cacf9f8
Related: OS#2282
2017-12-21 15:13:25 +01:00
Max
1714aeaa67 Fix warnings
Fix warnings detected by compiler and coverity scan.

Change-Id: If463c7f8769e18d3df74837f0cb0f545cca9b23e
Fixes: CID181479
2017-12-21 11:19:39 +01:00
Max
59e4a4fee1 TBF: add N3101 counter
Properly reset the counter when receiving valid RLCMAC block and update
it when no data is received as per 3GPP TS 44.060 §8.1.1.1

Change-Id: I2f79c6153dc4073c9d293b2824979e6381576682
Fixes: OS#2407
2017-12-20 18:01:55 +01:00
Max
ee5be3a009 TBF: implement independent T31xx timers
Previously TBF got single timer so the pending timer was automatically
cancelled when new one was scheduled. Let's make it more robust by
implementing independent T31 xx timers from 3GPP TS 44.060 §13.2 with
corresponding start/stop functions and counters.

The semantics of the timers is preserved as before: pending timers are
restarted unconditionally. It might be neecessary to change this later on
after spec review.

N. B. T0: used for assign/reject timeouts, have to be properly
attributed and documented first.

Change-Id: I0305873ca47534f53441247217881da59625e1f7
Related: OS#2407
2017-12-20 17:49:25 +01:00
Max
c21f007277 Introduce LOGTBF* for consistent logging
When troubleshooting complex issues with TBF lifecycle, it's much easier
to follow the logs which are consistently formatted. Add LOGTBF*() macro
similar to struct-specific log routines we use in other Osmocom project
and use it to log TBF-related messages in a unified way. Tweak test
output accordingly.

Related: OS#2407
Change-Id: I388249afefc32d2f6e5cb5e5abc6daf4dbd284ea
2017-12-20 14:14:40 +00:00
Max
ea9968f685 Fix tests after rate_ctr change
Recent change lin libosmocore disallow registering rate_ctr with the
same name and indexing multiple times. To accommodate to this check if
rate counters arealready allocated (by static allocator of BTS singleton
for example) and register rate counter with different index.

This fixes the tests for now but eventually we'll remove the BTS singleton
which will allow us to remove this hack.

Change-Id: I7c552ce653b44ec3a31049641728926adc07361d
Related: OS#2757
2017-12-18 22:58:28 +00:00
Max
ef784e4e9e Remove unused includes and forward declarations
Change-Id: I59da04edd1b8ff965bbfbe00ccae1f7c9b6e5301
2017-12-18 22:05:22 +00:00
Max
912131803b TBF: remove unused variable
The num_T_exp is write-only so it can be safely dropped.

Change-Id: I94d83ca8c9b2f0732b53fdf42b17ba93cd7f1c15
2017-12-15 17:44:47 +01:00
Max
1a11d1db09 TBF-DL: fix misleading idle time check
The dl_tbf_idle_msec is uint32_t so it cannot be < 0.

Change-Id: Ic88cb4698bcb9be52a5179529f81b8728bf4f93f
2017-12-15 14:28:34 +00:00
Max
25a3ca4e59 TBF: move EGPRS enablement into (U|D)L-TBF
This is preparation patch for transition to separate UL/DL window
variables instead of current shared generic window. The setting of
window parameters is performed in functions specific to UL/DL TBFs but
the general EGPRS flag remains the same and is set via the same function
which is now marked as protected since it's only meant to be used by
UL/DL subclasses.

Related: OS#1759
Change-Id: I6056194b28a1eb9d69093d1dfdc65a11bc1fc579
2017-12-14 12:20:53 +01:00
Max
ead08aae35 DL window: constify resend_needed() function
It doesn't change any state so mark it as const.

Change-Id: I5d672bfd654198aebb187772de464c52b3209435
2017-12-13 18:25:36 +01:00
Max
39eb95f130 vty: print class and TBFs for each MS
It's handy for troubleshooting to get quick overview of per-MS TBF
allocations and MS [EGPRS] classes.

Change-Id: Ie79c20f86da6db4565654b0f5856f4fddd83ef96
2017-12-06 13:15:32 +01:00
Max
cea806e5b9 TBF: expand timer logging
* log timer values
* log start/stop cause
* update test output as necessary

This simplifies debugging issues with TBF timers.

Related: OS#2407
Change-Id: Ib8e537416af9bec5d447356286f44e9e8bbf1b7a
2017-12-05 18:47:22 +00:00
Max
b4d368b576 TBF: fix compiler warning in test
tbf/TbfTest.cpp: In function ‘void egprs_spb_to_normal_validation(BTS*, unsigned int, unsigned int)’:
tbf/TbfTest.cpp:2788:26: warning: ‘<<’ in boolean context, did you mean ‘<’ ? [-Wint-in-bool-context]
  bsn1 = (egprs2->bsn1_hi << 9) || (egprs2->bsn1_mid << 1)
         ~~~~~~~~~~~~~~~~~^~~~~
tbf/TbfTest.cpp:2788:53: warning: ‘<<’ in boolean context, did you mean ‘<’ ? [-Wint-in-bool-context]
  bsn1 = (egprs2->bsn1_hi << 9) || (egprs2->bsn1_mid << 1)
                                   ~~~~~~~~~~~~~~~~~~^~~~~
tbf/TbfTest.cpp:2825:26: warning: ‘<<’ in boolean context, did you mean ‘<’ ? [-Wint-in-bool-context]
  bsn2 = (egprs3->bsn1_hi << 9) || (egprs3->bsn1_mid << 1) ||
         ~~~~~~~~~~~~~~~~~^~~~~
tbf/TbfTest.cpp:2825:53: warning: ‘<<’ in boolean context, did you mean ‘<’ ? [-Wint-in-bool-context]
  bsn2 = (egprs3->bsn1_hi << 9) || (egprs3->bsn1_mid << 1) ||
                                   ~~~~~~~~~~~~~~~~~~^~~~~
tbf/TbfTest.cpp:2844:26: warning: ‘<<’ in boolean context, did you mean ‘<’ ? [-Wint-in-bool-context]
  bsn3 = (egprs2->bsn1_hi << 9) || (egprs2->bsn1_mid << 1) ||
         ~~~~~~~~~~~~~~~~~^~~~~
tbf/TbfTest.cpp:2844:53: warning: ‘<<’ in boolean context, did you mean ‘<’ ? [-Wint-in-bool-context]
  bsn3 = (egprs2->bsn1_hi << 9) || (egprs2->bsn1_mid << 1) ||
                                   ~~~~~~~~~~~~~~~~~~^~~~~

Change-Id: Idf9e5f15faa7810411ed9d68ed43cf907eea2545
2017-12-05 18:47:21 +00:00
Pau Espin Pedrol
da0a194b57 Print error cause of pcu socket connect failure
This log is useful to quickly debug scenarions in which pcu never
connects to bts. For instance, if bts is started as root and pcu is not,
pcu will fail to connect to the socket and will fail with "Permission
Denied".

Change-Id: I6fd5736b5916cbad72b96f064929bb667ff97ded
2017-12-05 18:44:06 +01:00
Max
59f50c2718 TBF: log timer override
Currently TBF support only single Txxxx timer so scheduling another
timer will cancel out the one which is already running. Until the proper
fix is in place, let's at least log this situation as error.

Note: cosmetic adjustement to test output is required - we do not report
restart of the same timer twice because "restarting" assumes it anyway.

Change-Id: I462464a1e6df937b72cad65d19cd48e95dc4db45
Related: OS#2407
2017-12-04 10:55:47 +01:00
Max
701afa4b3a Fix compiler warning
Move function declarations which use gprs_rlcmac_ul_tbf into tbf.h to
avoid compiler warning:

In file included from pcu_vty.c:15:0:
bts.h:166:27: warning: ‘struct gprs_rlcmac_ul_tbf’ declared inside parameter list will not be visible outside of this definition or declaration
 void update_tbf_ta(struct gprs_rlcmac_ul_tbf *tbf, int8_t ta_delta);
                           ^~~~~~~~~~~~~~~~~~
bts.h:167:24: warning: ‘struct gprs_rlcmac_ul_tbf’ declared inside parameter list will not be visible outside of this definition or declaration
 void set_tbf_ta(struct gprs_rlcmac_ul_tbf *tbf, uint8_t ta);
                        ^~~~~~~~~~~~~~~~~~

Change-Id: Ic34c72c8bff6d7c775f56bb6026fec5425f7dcb4
2017-12-01 17:19:04 +01:00
Max
a5eb67d91c Replace '.' in counter names with ':'
The '.' is an illegal character in counter names, as they are exported
via CTRL interface, where '.' has a special meaning that cannot be used
by strings comprising the variable name.

Change-Id: I5ef60152a31dea25cb839c47edc93d5337ec3a3e
2017-11-21 20:24:54 +01:00
Max
842d781b5e Move multislot table to separate file
To facilitate testing and addition of support for new multislot classes,
hide multislot class struct internals:

* introduce mslot_class_get_*() functions
* use those functions instead of direct access to array of structs
* use ms_class as a parameter to find_multi_slot() instead of entire
  object

Change-Id: Id796bcff1322b1e273a0e3236c66c23b9da8fac6
2017-11-21 10:45:24 +00:00
Max
b709144f1b Remove unused parameter
Change-Id: Ifd6e04a29e27b1862cf9e98dec7481d3e0efcd48
2017-11-21 10:45:22 +00:00
Minh-Quang Nguyen
8d55563523 PCU: display TA information in TBF stats
Change-Id: I26886224c2ad6d5a29e92203635b8bf7459730a2
2017-11-16 10:06:58 -05:00
Minh-Quang Nguyen
1bcfa9aacf PCU: Fix TA adjustment
Promblem:
 TA provided from L1 PH-DATA-IND is a relative amount of TA adjustment to actual TA
 being used for given TBF. The current TA update algorithm in PCU simply applies the relative
 amount of TA to given TBF but does not take into account of current TA.
 As a result, the PCU will request wrong TA jump for given TBF if the MS is moving away from
 BTS more than 2 km.

 Related issue: http://osmocom.org/issues/2611

Fixes:
- The PCU needs increase or decrease current TA of given TBF on receiving of relative
  amount of TA adjustment provided by PH-DATA-IND from L1
- The PCU needs to set absolute TA of given TBF on receiving absolute TA provided by
  PH-RA-IND from L1.

Change-Id: I65212f8203f1a35278890f51db038d689b2493d5
2017-11-16 10:06:41 -05:00
Neels Hofmeyr
bfc54b551b vty: skip installing cmds now always installed by default
vty_install_default() and install_default() will soon be deprecated.

Depends: I5021c64a787b63314e0f2f1cba0b8fc7bff4f09b
Change-Id: I6c9f928f4a4d7fd6bf37c64a64ee5d843ad5bb7a
2017-11-01 00:51:51 +01:00
Neels Hofmeyr
49beba49eb jenkins: use osmo-clean-workspace.sh before and after build
See osmo-ci change I2409b2928b4d7ebbd6c005097d4ad7337307dd93 for rationale.

Depends: I2409b2928b4d7ebbd6c005097d4ad7337307dd93
Change-Id: If8aa657c4bf62ef62549fbe9dc15ce3fb018d8d9
2017-10-30 04:22:10 +00:00
Harald Welte
d34ec1b969 Tag/Release Version 0.4.0
Change-Id: I8559585a4513dddf1516c2a2b08968556c69b7ec
2017-10-29 12:10:13 +01:00
Harald Welte
47c682937c Debian: upgrade to debhelper 9 / Standards 3.9.8
Change-Id: I1fd274d85b8fd344517d62dd9e6adc4af6de3e26
2017-10-29 12:10:13 +01:00
Harald Welte
e64917a932 Debian: migrate from DEB_BUILD_HARDENING to DEB_BUILD_MAINT_OPTIONS
Change-Id: Ic400c509ecd0c6e8485e9433f144528f6abc600d
2017-10-29 12:10:13 +01:00
Harald Welte
9dab1baef8 Debian: print test results in case of failure + clean-up autotest
Change-Id: Id912a106d42bbd9d2ad89b67d16d52cb2344eb6d
2017-10-29 12:10:13 +01:00
Harald Welte
f826e8ab2b Debian: Cosmetic changes to control file; add better Description
Change-Id: I0a8bf134757f6ed754bfefd45a9fdac255447e43
2017-10-29 12:10:13 +01:00
Harald Welte
3e51d3e5bd Call osmo_init_logging() before static BTS constructor
The BTS constructor uses functions of libosmocore that could in turn
want to log something.  This requires the logging to be initialized
before.

The only way to achieve this is to add an __attribute__((constructor))
function *before* the BTS constructor is being run.

This solution might not be elegant, but  I guess it's the only way to
initialize a C library before calling C++ constructors of global static
instance of a class.

In case anyone comes up with a better / cleaner approach, we can always
change later.

This change requires libosmocore >= 0.10.1, as only that permits
multiple calls to osmo_init_logging() which may now occur.

Change-Id: I28dc4f0db229518348c92413959fed5ae85d753d
2017-10-29 11:02:34 +01:00
Harald Welte
ac0490ad2a tests: Don't use private version of log_info but global gprs_log_info
There's no need for each test case to carry their own log_info and
filter function.  They can simply use the global gprs_log_info and
configure the stderr log verbosity according to their needs.

Change-Id: I8706a624e5d06e062d1198711aa197fbd0860769
2017-10-29 11:02:34 +01:00
Philipp Maier
4c9ec22546 gb: allow only packets from a specific SGSN
Each PCU has a specifically assigned SGSN, which may send
packets to the PCU. Ensure that no one else except the
configured SGSN can send packets to the PCU.

Change-Id: Ic2009039fab7cf0fba916556239747ae5b410366
Depends: libosmocore Ifeb201d9006eec275a46708007ff342cdfc14e45
2017-10-24 10:50:17 +00:00
Max
a4f4822784 cosmetic: reformat multislot classes table
Add header similar to the one used in the standard, reformat to
facilitate further extention.

Change-Id: I786df6b154c0668d2cefa0ea84d7dea336b0da1d
Related: OS#2282
2017-10-10 19:02:43 +02:00
Max
5b0df1f1c5 TS alloc: properly count UL slots
Add cycle to mark multiple allocated UL slots similar to the way we
count DL slots in AllocTest. Until multislot UL allocation is
implemented it does not affect test output.

Change-Id: I2705405119421da3066c6c6bdd5830df4c133a36
Related: OS#2282
2017-09-12 11:58:29 +02:00
Max
5759a19020 TBF-DL: extend index check for RLC block copy
Log number of RLC blocks to copy and assert if trying to copy too many
blocks.

Change-Id: I01cbc26ec67400a44e9fff3f9a30d729320380f9
Fixes: CID143069
2017-09-08 12:58:28 +00:00
Max
1962136a33 Assert valid CS
The coding scheme converted to number make sense only if it's
valid. This is implicitly assumed by the code using this conversion as
non-zero value. Make those assumptions explicit with OSMO_ASSERT().

Change-Id: I8f62627b7b7b89dfa1b0d1a7e71b95b2c40fdffa
Fixes: CID70466
2017-09-08 12:51:03 +02:00
Max
5a6bcfb797 cosmetic: convert explicit warnings to fixme/todo
We do not use this style (#warning as an issue tracker replacement) in
any other Osmocom project. Also those warnings clutter compiler output
making it harder to spot warnings for the actual code.

Change-Id: I72070e2a027e60e8b80c12ccfa23ff075434689f
2017-09-04 13:06:01 +00:00
Max
8bfa659087 Move gsmtap and accounting into separate function
Change-Id: I3609da1850244f25bd4611c9d25795ca379d6325
2017-09-01 11:08:06 +02:00
Max
84bf0faed9 Support receiving SI13 from BTS
* store SI13 in BTS struct
* check and handle BCCH SAPI

Change-Id: I610a93ce23725b182ec14e3507331295bd542f74
Related: OS#2400
2017-09-01 11:08:06 +02:00
Max
b216c6b165 cosmetic: tighten direct-phy related code
* move the code to related SAPI case
* get rid of 'unused variable' warning if direct-phy is not used

Change-Id: If8cae6f3579cfdecc25bbe1d08fa88a4f664a03b
2017-09-01 11:08:06 +02:00
Max
10e37a5089 Use value string check from osmo-ci
Change-Id: Ib9c595ef80cb6b0d126d4da8244f6435e0526095
2017-08-24 19:04:44 +00:00
Max
d78adfb577 Facilitate future releases
* use release helper from libosmocore
* use semantic versioning

Change-Id: Ie0a7f5977550bd0a1ba8b03bdb7e2d619a398e4e
Related: OS#1861
2017-08-24 13:53:25 +02:00
Minh-Quang Nguyen
1f18909335 EDGE: Fix UL link adaptation.
We have seen that UL MCS5- MCS9 link adaptation has not been implemented in current PCU implementation.
If the MS slowly moves far away from the BTS, the UL MCS will always stick at MCS9 no matter UL link quality values leading to poor data service experience.
The UL MCS is expected to adapt from MCS9 -> MCSx due to bad UL quality.

Below PCU traces indicate that UL MCS is quickly increasing to MCS9 (max MCS 9 was used in this test) and it never changes to other UL MCS due zero thresholds.

<0004> gprs_ms.cpp:670 MS (IMSI ): Link quality 23dB (23dB) left window [0, 0], modifying uplink CS level: MCS-6 -> MCS-7
<0004> gprs_ms.cpp:670 MS (IMSI 000): Link quality 23dB (23dB) left window [0, 0], modifying uplink CS level: MCS-7 -> MCS-8
<0004> gprs_ms.cpp:670 MS (IMSI 000): Link quality 23dB (23dB) left window [0, 0], modifying uplink CS level: MCS-8 -> MCS-9

Change-Id: I9272c337ad6399da4a47cc6e2736e25f24e099d8
2017-08-18 18:09:24 +00:00
Harald Welte
b1be6112bb GSMTAP: fix category checks in pcu_rx_data_ind and pcu_l1if_tx_ptcch
We needto shift the PCU_GSMTAP_C_* constants before comparing against
the mask of enabled GSMTAP categories.

Change-Id: Ieb9332c65ed7bd57baf1aeab5ab722f92fc23b24
2017-08-17 00:50:24 +02:00
Max
f60cf62f4f Simplify polling troubleshooting
* introduce enum describing poll kind and use it in set_polling()
* move state change into set_polling()
* move logging into set_polling() and unify output
* move duplicated code into static function
* adjust tests to match unified logging output

Change-Id: I14074207f8bbc18b3ebd60875bb99a0a3a4b399d
Related: OS#1524
2017-08-14 15:19:08 +00:00
Max
a10c39866b Move DL assignment to TBF-DL
This function does not really belongs to BTS and it heavily relies on
direct access to TBF-DL members anyway.

Change-Id: I04584103018675a2f35cfb565473bfd81a208d7c
Closes: OS#1540
2017-08-14 15:50:47 +02:00
Max
341dccd7e2 Move common code into functions
* separate channel request responder into inline function
* move generic TBF poll check into inline function

Change-Id: I9ec3ab8de100f0bc75044f55ac769d1083d52806
Related: OS#1539
2017-08-14 15:49:59 +02:00
Max
fd13f6c199 Encapsulate handling of UL ACK timeout
Use helper methods instead checking and manipulating flag directly.

Change-Id: Ia3f009c52118db95b38a077e08eecda844e7f8d1
Related: OS#1539
2017-08-14 15:48:37 +02:00
Pau Espin Pedrol
c4178e55ea Add pcu-socket vty config
osmo-bts already supports configuring a different path for the bts<->pcu
socket by using the 'pcu-socket' config field.

Change-Id: I9b3e1171da467519750b201849ec892a1e318129
2017-08-09 12:17:39 +02:00
Harald Welte
0cd8e4eade README: Clarify that fixed allocations have been revmoved from 3GPP specs
In Release 5, Fixed Allocations were removed as part of a "GPRS
simplification project" inside 3PGPP.  This means that any MS compatible
with Rel-5 or higher may not support it, which makes implementation
in the PCU af historical interest only.  See Tdoc GP-021277 for the
actual removal, it happened at 5.0.0 time.

Change-Id: I8138bc0a47d3468b67cec866447fd4b0fc69d4c5
2017-07-29 22:15:02 +02:00
Harald Welte
bc219d5450 GSMTAP: Ad PTCCH as separate gsmtap category
This allows us to send GSMTAP for PTCCH only if requested by user/vty

Change-Id: Id720f4bebdce7f6152fbddddbe05036638c5866e
2017-07-29 13:43:30 +02:00
Harald Welte
cd34dd3b2b GSMTAP: Fix logical channel of downlink PTCCH
Change-Id: I29ecc968d56d4d0165cffa206297c42d6fb02cf4
2017-07-29 13:43:30 +02:00
Harald Welte
f7740aa44b GSMTAP: Fix channel type for data blocks: PDTCH instead of PACCH
PACCH is used only for RLC/MAC control messages, while PDTCH is used
for data.

Change-Id: I6c912e17d8c8d4178096679a541e61eeeb4b6643
2017-07-29 13:43:30 +02:00
Harald Welte
05d7b5dd59 BSSGP: Improve logging of received messages
We now differentiate clearly between messages that

a) we don't expect based on our reading of the spec
b) we have not implemented yet (but should)
c) we do not even know of

Also, unify the log string formats and provide BVCI whenever possible.

Change-Id: Ie32f5771d49960547ec5d7611f96a74facc1b035
2017-07-29 10:21:24 +02:00
Harald Welte
18a17aa487 Remove #warnings that have been adressed since 2015
In commit 9399046729 the lookup by
TLLI, TLLI-OLD and IMSI has been implemented, but the corresponding
compile-time warning was never removed.

Change-Id: I7a1767bc7cab01048e851fd4e63112bc676d6c78
2017-07-29 10:21:24 +02:00
Max
2813f931dd BSSGP: Use libosmocore for BVC-RESET
Implement proper BVC RESET procedure by using libosmocore code to handle
BVCI reset and initiate PTP BVC reset if necessary.

Requires I9bf8f4dd784ccddbb9926492a85fff3293a0e913 in libosmocore.
Related: OS#1638
Change-Id: I718c949759688cb34ce6bcbb3da2092fcdfa6989
2017-07-28 17:42:45 +00:00
Harald Welte
717cdf5405 Introduce GSMTAP categories
When looking at GSMTAP output so far, one is easily overwhelmed by way
too much information being presented.  A lot of is consists of DUMMY
frames, which are probably of lowest interest, ever.

A concept similar to the "gsmtap-sapi" of OsmoBTS is introduced, by
which the user can configure which particular categories (uplink or
downlink control or data, gprs or egprs, ...) he actually wants to
see in his logs.

Change-Id: I297183690e98a7234dfc1608c18847d8981306e4
2017-07-21 22:15:28 +02:00
Harald Welte
9530a404ce check for missing result of rate_ctr_group_alloc()
In case the counter group allocation fails, we must handle this
gracefully and fail the allocation of the parent object, too.

Change-Id: Id6d780c67b4af15aaa5c6f2b8b00f2a0b70a7385
Related: OS#2361
2017-07-12 00:47:10 +02:00
Harald Welte
8c8027c307 jenkins.sh: Proper error message if local environment isn't set up
Change-Id: Ibd24ba6024714f3d7aac14ef661acf52de2a3825
2017-07-10 14:01:07 +00:00
Max
aae1bfbbe0 Remove TBF knowledge from rcv_control*dl_ack_nack
Do not access TBF internals directly from rcv_control*dl_ack_nack() -
wrap corresponding code into TBF-DL method.

Change-Id: I3d1b5782001e45617b4a960612fcfc249904b37c
Related: OS#1539
2017-07-10 09:39:53 +00:00
Max
5579595464 Ignore test binaries using mask
Change-Id: Ic7930c6e715447e91572f7ed6b0d2bb2238cf367
2017-07-10 10:49:02 +02:00
Max
241f5bcb00 Copy sysmopcu.service to osmo-pcu
This way the name of systemd service file will match the name of the
binary similar to OsmoBTS. Add aliases so the user can use both old and
new names regardless of which file is installed. Once the corresponding
changes to OE recipes are applied old file can be removed.

Based on work by Pau Espin Pedrol <pespin@sysmocom.de>

Change-Id: I2ca6f6c486bd6fcf4d5b3d0a05d25dc04f020c26
2017-07-03 18:50:49 +02:00
Max
3741f14689 Remove comment warning
It's unclear why the warning was placed next to commented log statement
to begin with, so let's just follow that warning's advice and drop it.

Change-Id: I3ef7a45d015a28fdadf75f97294bc5d4f825b8ae
2017-06-26 12:10:12 +02:00
Max
865436dee0 lc15: use environment for firmware version
This allows to specify firmware version to use by jenkins builder via
environment variable. If environment variable does not exist than
default master branch is used.

Change-Id: If8b249aa00270e60a0449f089a16823976e54d54
Related: SYS#3683
2017-06-22 17:00:46 +00:00
Max
a468cfaf2e tests: remove unused definition
Fix compilation warning.

Change-Id: I1c95c6ec8bee68773643f9646b0319a83fbc6cfa
2017-06-22 13:28:39 +02:00
Max
062dfa1d0c lc15: use generic L1 headers helper
* use generic L1 headers helper for both sysmocom-dsp and lc15bts-phy
  options
* use sh instead of bash

Related: SYS#3683
Change-Id: I3dc621731f47650cbc15a5f17b9e899e9ed2770f
2017-06-19 12:53:26 +02:00
Harald Welte
49b78229ca use tlvp_val16be() rather than manual pointer-cast + ntohs()
Change-Id: Ib77cb703bb1710da396db3a939700515b5c20235
2017-06-11 11:40:19 +02:00
Harald Welte
c136be04f7 lc15: further fixes regarding --with-litecell15
The fix in 0fb294a8dd was only partially
valid, as it unconditionally used $includedir, without any prefix.  This
polluted the include path with host include files in cross-compiling
builds.

Let's take a different approach and simply define LITECELL15_CFLAGS
(similar to what pkgconfig does), which makes the "-I" go away if no
--with-litecell15 has been specified.

Change-Id: I63393decfe42a24dab56c7654f716c1580416ab2
2017-06-11 11:40:18 +02:00
Harald Welte
0fb294a8dd lc15: fix configure.ac variable substitution causing compile error
When "--enable-lc15bts-phy" is passed to './configure' without specifying
an explicit header file path using "--with-litecell15=", we ended up
generating an empty string as LITECELL15_INCDIR and rendered something
like "-DENABLE_DIRECT_PHY  -I -I../../git/src/osmo-bts-litecell15" as
part of the compiler invocation, where the -I with no argument will hide
the second -I, as the second one is supposed to be the optarg for the
first include.

This in turn made the "#include <lc15_l1_if.h>" fail, when using
separate source and build directories.

This patch fixes the configur script to use $includedir, rather than the
non-existant $incdir as default for LITECELL15_INCDIR

Change-Id: I483e62f8331e7867a92f8055c4d450fdd5288cb6
2017-06-11 10:35:56 +02:00
Max
9dabfa2c2b Cleanup FN scheduling
* replace magic number with defined constant
* move copy-pasted code to inline functions
* remove unused code

Change-Id: I6fee0714453d0c3c3f3f875f88daea2d9c477331
Related: OS#1524
2017-05-26 07:55:52 +00:00
Harald Welte
356ac618f1 Fix format string error (string needs %s)
In a49475b5a8 we introduce the use of
bssgp_pdu_str() and change from printing the numeric code to the
stringified version of the message code.  However, the format string
was not updated accordingly :/

Change-Id: I7173b692fb1f222aab44cd4f44a482038d0f51dc
Fixes: Coverity CID 169684
2017-05-25 19:05:32 +02:00
Max
e8284a7f92 Fix typo in logging
DL-TBD was errorneously printed as UL-TBF.

Change-Id: I94a224c0339a062e4c8d5aa6c4c858f3f0298a0d
2017-05-24 22:19:43 +00:00
Max
a49475b5a8 Print human-readable BSSGP PDU type
Change-Id: Ief4b5ce4e4020edaf771eaa24f4382ec368dd18c
2017-05-23 17:53:38 +02:00
Alexander Couzens
ccde5c9557 remove pcu own bitvector implementation
The osmocore bitvec is exact the same, but use a pointer instead of
a reference.

Change-Id: Id8f797631d89aa12b6e48efb2dc153a3e2f059f7
2017-05-15 12:46:33 +00:00
Neels Hofmeyr
3de6d0602f fix PACCH paging: don't return early in case of NULL TBF
Commit b78a4a6dfe tried to fix a NULL dereference
error, but apparently was overly eager to return, because it looked like all
code paths would dereference the tbf.

In fact the code path further above, for msg != NULL, has "always" dereferenced
the tbf, but the lower code path, the one effecting the paging, has only
started to dereference tbf since shortly before the overly eager fix: in
da7250ad2c, to "update the dl ctrl msg counter
for ms". It seems that this tbf dereference in the paging path is bogus and the
cause for the segfault that made me write the early exit fix.

Fix that fix:

Do not exit early if tbf == NULL, stay in there to be able to reach the paging
path below.

In case of a message to be sent, assume that tbf is present, and verify: print
an error message and abort if there is a msg but no tbf, so that we will see
the error if I'm wrong there. If a tbf is missing, free the msg.

In case of no message, go on to send pending pagings, but do not attempt to
count ctrl messages for a tbf -- IIUC there will never be a tbf if we're
paging.

This should avoid segfaults while keeping PACCH paging intact.

Tweak a comment for and add a blank line above the paging section.

Related: OS#2176 CID#158969
Change-Id: Ib79f4a945e211a13ac7d1e511cc37b0940ac6202
2017-05-15 10:40:34 +00:00
Alexander Couzens
333d7e6345 tbf.cpp: use new tlli instead of old tlli
The old tlli might be 0x00000000.

Change-Id: I2fd6fec022506e203d05e91c36ccd9e020ff816c
2017-05-01 16:57:48 +00:00
Neels Hofmeyr
e6d26ec09c bitcomp test: use expected rc instead of 'verify' flag
The 'verify' flag is useless, we always want to verify everything.  Replace
'verify' with 'expect_rc', expecting a specific decoding result per test set.

When an error code was returned, cut short the loop and skip printing expected
vs. decoded bits.

This uncovers the fact that the first test marked as "invalid inputs" does not
seem to be invalid after all, or at least does not produce an error to be
returned. For now, only uncover this fact, the fix shall be submitted later.

Change-Id: Icf815a8f1acb0e275463408450171df046879847
2017-03-27 00:24:28 +02:00
Neels Hofmeyr
5382e0fc1f bitcomp test: fix: also verify bits after decoded data
Before this, the expected data had seemingly random bits set after the end of
the expected decoding result. Make the test expect these extra bits as zero,
matching with the buffer initialization to zero.

In result_matches(), compare the full length of bytes instead of masking the
bits after the end of the decoded data (which caused us to not catch the wrong
expectation until now).

This fixes the underlying issues found in
http://lists.osmocom.org/pipermail/osmocom-net-gprs/2017-March/000876.html
  [osmo-pcu 0.2.896-0a8f] testsuite: 4 failed
  from: Arnaud ZANETTI
  on: Fri Mar 24 09:53:53 UTC 2017

Change-Id: I2501208e2f8b4f709efbcadbd1057c086472c9e6
2017-03-27 00:24:28 +02:00
Neels Hofmeyr
dd1700a397 bitcomp test: fix: only one hexdump per log; use printf
The test wants to write multiline results, so it should use printf instead of
the logging system.

Split logs to only one hexdump per printf(). One cannot use osmo_hexdump twice
in one printf(); before this, one of the two hexdumps won, both dumps would
show as the same. Very bad for a regression test!

This uncovers a discrepancy between expected and produced results, proving that
the expected stderr output was not capable of uncovering test failures. The
test's check_result() function *has* always verified the decoded data, but only
up to the last decoded bit. Our expected data contains seemingly random bits
after the end of the decoded bits, but check_result() never compares those,
hence we don't catch that error. The extra bits should definitely be zero,
because the destination buffer is pre-initialized to zero -- fixed in a
subsequent patch.

This should cosmetically fix the build failure found in:
http://lists.osmocom.org/pipermail/osmocom-net-gprs/2017-March/000876.html
  [osmo-pcu 0.2.896-0a8f] testsuite: 4 failed
  from: Arnaud ZANETTI
  on: Fri Mar 24 09:53:53 UTC 2017
The real fix will follow.

Change-Id: I24fc32eb55baaf22f9c6fdda917bfb8395d02b1c
2017-03-27 00:24:28 +02:00
Neels Hofmeyr
7783964bb9 cosmetic: BitcompTest: make readable
In order to understand what the bitcomp test is logging, cosmetically rearrange
the code:

- memset bits_data before assigning to destination bitvec.
- use macro CEIL_DIV_8 to clarify what (x+7)/8 does.
- rename check_result() to result_matches() and return a bool,
  also constify result_matches() args and pass a bitvec reference instead of
  copying the bitvec struct.
- rearrange logging lines to make readable what is going on there.
- drop unused 'init_flag'

There are obviously errors like double hexdumps per log line, multiple newlines
in a LOGP statement and so forth: these shall be fixed by subsequent patches.

Change-Id: Id0da9d9b67f4713d3a67e3532ed44b8cb1bd1d08
2017-03-27 00:24:03 +02:00
Max
0a8fae8d14 Support sending OML Alerts via BTS
* extend BTS <-> PCU protocol with TXT messages
* use it to implement OML alerts support
* use it to implement version message
* add function to transmit both of them them
* send alerts for internal encoding problems as an example
* send version when BTS socket is connected

Note: requires corresponding change
If57459c0610f2c7b36d599b13087c8deef8bdd9e in libosmocore.

Related: OS#1614, 1615
Change-Id: If4ea5b3f7409df2fb030681ad468df6b711790a7
2017-03-17 17:01:28 +00:00
Harald Welte
db84235a0b Update README file with general project info and use Markdown
Also, it seems the readme was so far not included in any tarballs (make
dist).  Let's change that.

Change-Id: I1967fbbdeadb967c0c0dce2c112ac692c539da1d
2017-03-17 17:24:55 +01:00
Neels Hofmeyr
20827374e9 jenkins: add value_string termination check
Change-Id: I27217c9162efd800eebcb403eab770f4528d21ac
Depends: libosmocore change-id I2bc93ab4781487e7685cfb63091a489cd126b1a8
2017-03-16 19:01:04 +00:00
Max
b86a30dc22 tests: include headers from include/
In addition to .h files from src/ add include/ as well: some headers are
now public and reside in separate directory.

Change-Id: I09c02a171fb3b2f2791ce938725db7d4ff397e95
2017-03-13 13:43:22 +00:00
Max
5dd8d1bbd8 bts.cpp: Fix overloading ambiguity
Fix error introduced in 1275a3f91a by
using signed 32 bit integer which is enough for Frame Number in
GSM. Also, mark parameter constraints more explicitly:
- add assert for expected FN values
- don't perform computation for non-relative FN

The error was:
bts.cpp: In member function ‘uint32_t BTS::rfn_to_fn(uint32_t)’:
bts.cpp:554:25: error: call of overloaded ‘abs(uint32_t)’ is ambiguous
  if (abs(rfn - m_cur_rfn) > RFN_THRESHOLD) {
                         ^
In file included from /usr/include/c++/6/cstdlib:75:0,
                 from /usr/include/c++/6/stdlib.h:36,
                 from /usr/include/osmocom/core/linuxrbtree.h:97,
                 from /usr/include/osmocom/core/timer.h:35,
                 from ./bts.h:29,
                 from bts.cpp:21:
/usr/include/stdlib.h:735:12: note: candidate: int abs(int)
 extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
            ^~~
In file included from /usr/include/c++/6/stdlib.h:36:0,
                 from /usr/include/osmocom/core/linuxrbtree.h:97,
                 from /usr/include/osmocom/core/timer.h:35,
                 from ./bts.h:29,
                 from bts.cpp:21:
/usr/include/c++/6/cstdlib:185:3: note: candidate: __int128 std::abs(__int128)
   abs(__GLIBCXX_TYPE_INT_N_0 __x) { return __x >= 0 ? __x : -__x; }
   ^~~
/usr/include/c++/6/cstdlib:180:3: note: candidate: long long int std::abs(long long int)
   abs(long long __x) { return __builtin_llabs (__x); }
   ^~~
/usr/include/c++/6/cstdlib:172:3: note: candidate: long int std::abs(long int)
   abs(long __i) { return __builtin_labs(__i); }

Change-Id: Ib6d895a97aa35414f245ea4406c6e78f1b4fb5b8
2017-03-13 11:25:14 +00:00
Max
727295f206 Add pkg-config file
We're installing header file pcuif_proto.h so it's better to use
pkg-config for proper version tracking similar to the way it's done for
OpenBSC.

Change-Id: I0520045e5655794df152b98b9755d7cbbd334049
2017-03-09 12:17:35 +01:00
Philipp Maier
1275a3f91a BTS: Convert relative frame numbers to absolute frame numbers
The implementation of the method rcv_rach() in class BTS, restores
the absolute frame number of the incoming RACH-Request by using
the relative frame number (RFn = Fn mod 42432) from the rach
request and the already known internal absolute frame number
m_cur_fn, which is continusly updated by the CCU interface.

In some rare cases, a RACH request might be received by the BTS,
a very short time before the frame number wraps in its 42432.
Depending on the PCU location, RACH request might be received
by the BSC, which forwards it to the PCU. It is then likely
that, while the RACH request is being forwarded to the PCU, the
PCU internal absolute frame number wraps before the RACH
can be processed. The relative frame number from the rach
request would then be interpreted as if it were received after
the wrapping of the internal frame number modulos.

This commit adds logic to detect and resolve this race condition.
Also a unit test is added to check some cornercases.

Change-Id: I74f00c11e5739d49f370ce6c357149e81d9aa759
2017-03-05 12:23:56 +00:00
Neels Hofmeyr
a01e2ee177 logging fixup: shorter names for LOGGING_FILTER_* and LOGGING_CTX_*
In libosmocore, my patch was merged to master a bit too soon. To accomodate the
request for naming that matches the general "LOG" prefix instead of "LOGGING",
a fixup was committed to libosmocore. Adjust for that.

Original patch: change-id I5c343630020f4b108099696fd96c2111614c8067
The fixup: change-id I424fe3f12ea620338902b2bb8230544bde3f1a93

Change-Id: I4db4a668f2be07f3d55f848d38d1b490d8a7a685
2017-02-23 18:11:44 +01:00
Neels Hofmeyr
d0a887b28b gprs_debug.h: remove unused cruft / cosmetic tweaks
Change-Id: Ied1ffc320332a605b140d23910eb0a13ef9a7a75
2017-02-20 15:30:18 +00:00
Neels Hofmeyr
4ae5406959 logging: use central filter and ctx consts from libosmocore
Change-Id: I7b41a5a26527864177c63403ad171d2987f0ff6a
Depends: libosmocore change-id I5c343630020f4b108099696fd96c2111614c8067
2017-02-17 17:23:36 +01:00
Neels Hofmeyr
b609190369 dl tbf: initialize punct values and verify
Solves a sanitizer issue where punct2 is unset when passed to
gprs_rlc_mcs_cps() and thus takes a value not defined in the enum.

Change-Id: I004cbbab15e6ffa2749f4b7f1df651517c2ae693
2017-02-14 12:20:57 +01:00
Neels Hofmeyr
49b83ec3a3 dl tbf: calculate CPS only for EGPRS
Patch-by: Aravind Sirsikar <Arvind.Sirsikar@radisys.com>
Change-Id: I81b8e1d10bfe9efba3a9f04bced66f87d93285dd
2017-02-14 12:19:18 +01:00
Neels Hofmeyr
78ce59137f main, tests: use msgb_talloc_ctx_init() (new)
msgb_set_talloc_ctx() is deprecated since libosmocore commit
f45334be29016a36594aacc07c90e262e4994525 / change-id
I747fbbf977c4d2c868c8dead64cfc5fd86eb8d4c

Change-Id: I8d40abec428b739460ed545c9983d1b63021bd08
2017-02-08 17:37:55 +01:00
Neels Hofmeyr
de9da39b33 tests: edge, tbf: assert return values
Numerous calls assign a return value without asserting its value. Add
assertions and thus also eliminate compiler warnings about unused values.

Change-Id: I7f14198cfd747dae68b8aaa3b8d6ff7fc49ab824
2017-02-08 17:37:55 +01:00
Neels Hofmeyr
d34646a865 Fix dozens of compiler warnings across the board
Change-Id: I166109dc05d3323b92cd2a42f0c7e6009950e15d
2017-02-08 17:37:55 +01:00
Philipp Maier
53f0b4deb6 cosmetic: Fix log output
In BTS::rcv_rach() the log output is messed up because of a stray
"\n". This commit removes that.

Change-Id: I40d01c71982ad83589f070cf0047a4ae04695411
2017-02-01 14:19:04 +00:00
Philipp
d935d88a8c BTS: accept also relative frame numbers with rach requst
The rach request contains a relative frame number (Fn % 42432),
while BTS::rcv_rach() accepts the full frame number only.

Since the BTS is always aware of the full frame number this is
not a problem. But for BSC co-located PCU schemes it is a problem
since the rach request only contains the relative frame number
as mentioned above.

The pcu continusly receives frame number updates with the GSM time
indication message. It is simple to re-calculate the full frame
number from that information.

This patch makes BTS::rcv_rach() compatible with relative frame
numbers, while not breaking the compatibility for full frame
numbers

Change-Id: Iaa182d8d29c6a0f5fa06064c2eb48b21b1ba2775
2017-01-26 11:19:21 +01:00
aravind sirsikar
e9a138e111 Handle packet access reject during packet resource request
When Packet resource request is received, PCU will generate the
packet access reject if no resources are present. The encoding is done
based on section 7.1.3.2.1 and 8.1.2.5 of 44.060 version 7.27.0 Release 7.
This patch also includes the test case to validate the generated
packet access reject message.

This patch is integration tested on Osmo-trx setup with Ettus B210 board
and LG F70 MS with some simulation code changes in Osmo-pcu.

Change-Id: I05ff25124b58905586caa0c0c37023d69724f121
2017-01-24 13:11:51 +00:00
sivasankari
1d8744ce96 Add test case for testing PUAN
This test case is for testing generation of
EGPRS PUAN. Corresponding log files .ok and .err
are modified.

Change-Id: I18e6d4a9e90fd6453fe14187beab27dfeae8dbe9
2017-01-24 15:53:35 +05:30
sivasankari
8adfcd06a1 Add compression support in EGPRS PUAN
This adds compression of bitmap in PUAN. The compressed bitmap
is used only if the number of bits in the bitmap does not fit in
the message and there is a gain after compression.
The algorithm is part of libosmocore and so there is dependency
on the libosmocore for compilation.
The algorithm is tested on integration setup by forcing compression.

Change-Id: Id2eec4b5eb6da0ebd24054b541b09b700b9b40ba
2017-01-23 12:26:09 +00:00
Max
127a1e0750 Log additional info for radio errors
Change-Id: I936a07ce87f05d9c3dc351dc3bdc4f00d78265e0
Related: OS#1553
2017-01-16 10:10:01 +01:00
Max
b3df58660f Log socket path on connection
Change-Id: I81c5c1068a8b59ee30399dac90b0f7e730fc19f4
2017-01-06 17:20:57 +01:00
Neels Hofmeyr
b78a4a6dfe fix segfault: check for NULL tbf in sched_select_ctrl_msg()
Apparently fixes a corrupted stack looking like this on sysmobts:

  (gdb) run
  Starting program: /usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
  [Thread debugging using libthread_db enabled]
  Using host libthread_db library "/lib/libthread_db.so.1".
  <000b> telnet_interface.c:95 telnet at 127.0.0.1 4240
  <0001> osmobts_sock.cpp:227 Opening OsmoPCU L1 interface to OsmoBTS
  <0001> osmobts_sock.cpp:285 osmo-bts PCU socket has been connected
  <0001> pcu_l1_if.cpp:368 BTS available
  <0008> gprs_ns.c:233 NSVCI=65534 Creating NS-VC
  <0008> gprs_ns.c:233 NSVCI=100 Creating NS-VC
  <0008> gprs_ns.c:1568 NSEI=100 RESET procedure based on API request
  <0008> gprs_ns.c:393 NSEI=100 Tx NS RESET (NSVCI=100, cause=O&M intervention)
  <0001> pcu_l1_if.cpp:83 Sending activate request: trx=0 ts=2
  <0001> pcu_l1_if.cpp:495 PDCH: trx=0 ts=2
  <0001> pcu_l1_if.cpp:83 Sending activate request: trx=0 ts=3
  <0001> pcu_l1_if.cpp:495 PDCH: trx=0 ts=3
  <0001> pcu_l1_if.cpp:83 Sending activate request: trx=0 ts=4
  <0001> pcu_l1_if.cpp:495 PDCH: trx=0 ts=4
  <0001> pcu_l1_if.cpp:83 Sending activate request: trx=0 ts=5
  <0001> pcu_l1_if.cpp:495 PDCH: trx=0 ts=5
  <0001> pcu_l1_if.cpp:83 Sending activate request: trx=0 ts=6
  <0001> pcu_l1_if.cpp:495 PDCH: trx=0 ts=6
  <0001> pcu_l1_if.cpp:83 Sending activate request: trx=0 ts=7
  <0001> pcu_l1_if.cpp:495 PDCH: trx=0 ts=7
  <0001> pcu_l1_if.cpp:319 RACH request received: sapi=1 qta=0, ra=120, fn=103198
  <0009> tbf_ul.cpp:373 LLC [PCU -> SGSN] TBF(TFI=0 TLLI=0x7f2dd569 DIR=UL STATE=FLOW) len=6
  <0008> gprs_ns.c:684 All NS-VCs for NSEI 100 are either dead or blocked!

  Program received signal SIGSEGV, Segmentation fault.
  gprs_rlcmac_rcv_rts_block (bts=0x60a08, trx=trx@entry=0 '\000', ts=ts@entry=4 '\004', fn=7, fn@entry=103272,
      block_nr=block_nr@entry=0 '\000') at gprs_rlcmac_sched.cpp:349
  349	gprs_rlcmac_sched.cpp: No such file or directory.
  (gdb) bt
  #0  gprs_rlcmac_rcv_rts_block (bts=0x60a08, trx=trx@entry=0 '\000', ts=ts@entry=4 '\004', fn=7, fn@entry=103272,
      block_nr=block_nr@entry=0 '\000') at gprs_rlcmac_sched.cpp:349
  #1  0x0001151c in pcu_rx_rts_req_pdtch (trx=<optimized out>, ts=<optimized out>, fn=103272, block_nr=<optimized out>)
      at pcu_l1_if.cpp:279
  #2  0x0000bfcc in handle_ph_readytosend_ind (fl1h=0xafa40, rts_ind=0xb03f8) at osmo-bts-sysmo/sysmo_l1_if.c:142
  #3  l1if_handle_l1prim (wq=<optimized out>, fl1h=0xafa40, msg=0xb0330) at osmo-bts-sysmo/sysmo_l1_if.c:259
  #4  0x4fcd6330 in osmo_fd_disp_fds (_eset=0xbefffb68, _wset=0xbefffae8, _rset=0xbefffa68) at select.c:149
  #5  osmo_select_main (polling=<optimized out>) at select.c:189
  #6  0x0000b2a0 in main (argc=<optimized out>, argv=0x66628 <_ZStL8__ioinit>) at pcu_main.cpp:295

Fixes: coverity CID#158969
Related: https://lists.osmocom.org/pipermail/osmocom-net-gprs/2016-December/000785.html
Change-Id: I357492e558e98cfdbf5bb3438b5013029195b02b
2017-01-06 13:54:04 +00:00
Pravin Kumarvel
06bdb3550c Refactoring write_packet_ack_nack_desc_egprs to prepare for CRBB support
Change-Id: Ie5c25b6ee30f2f1b613e923c234b03a6ffe12ae2
2017-01-06 10:36:07 +00:00
sivasankari
67b89cae08 Array indexing for SPB counters in bts statistics.
Array indexing mismatch is corrected for SPB counters.
 (bts_ctr_description with the bts counter declaration).

Change-Id: I9b17ca0f838a37d9405cebf2319e722a302f5ed9
2017-01-06 10:31:56 +00:00
Max
e66de5b5ae Improve logging
Add value_string describing UL and DL TBF states and use it for logging
errors while freeing TBFs.

Change-Id: I292ec81ab602c65ef86e6e3e85740182b63474b6
2017-01-05 18:39:25 +01:00
sivasankari
ee78bf0882 Adds rate_ctr_init in the startup of osmo-pcu
Issue:Though the rate_ctr framework is used in osmo-pcu for bts statistics,
      the interval counters are always 0.
Fix:rate_ctr_init is added in the startup which arms the timer and hence
    the rate ctr intervals is displayed with proper values.

Change-Id: Ib0f33d2de9406aa7436aa9aeb6a8dabdff96383b
2016-12-22 14:09:43 +00:00
sivasankari
da7250ad2c Add counter at BTS level And statistics at TBF/MS level.
Adds spb counters at BTS level(show bts statistics).
Adds RLC/MAC downlink control msg at ms level(show ms imsi <imsi_val>).
Adds the number of coding schemes counter for UL at TBF level.

Change-Id: Icbe4ba95e34bea89ee36f532d099db68204b7c38
2016-12-22 14:09:04 +00:00
Harald Welte
963cdaffd5 Fix uninitialized members in pcu_l1_meas()
Change-Id: I76a03c9f54be474ab9ece908ef782807d555c6ac
Fixes: Coverity CID 57952
2016-12-16 11:56:45 +00:00
Harald Welte
1f2bb6e93e struct pcu_l1_meas_ts: initialize ms_i_level
Change-Id: I93de7589d746b91ba26b1b36bf2690f125277cd0
Fixes: Coverity CID 57953
2016-12-16 11:56:44 +00:00
sivasankari
5395073fff Add statistics in the ms and tbf level.
Adds DL throughput in show ms imsi <imsi_value>.
Adds the number of coding schemes counter and rlc nacked counter at TBf level.

Change-Id: Ia95b0404989b00db0e7ba416bc40d09ef41fde1c
2016-12-09 12:05:43 +00:00
aravind sirsikar
cc4214a429 Sanitizer build fix for TbfTest
Change-Id: Ia6993fd6f89c9d9ed00ec6cb4b27953e72fa1f52
2016-12-09 16:12:42 +05:30
Mrinal Mishra
0e63644d14 Add debugging log for RLC data block decoding
Added debugging log for RLC UL Data Block decoding for both GPRS/EGPRS cases.

Change-Id: I8c197bdc4cd1330cbab0adfd188336d27682cec4
2016-12-02 09:15:53 +00:00
Neels Hofmeyr
34bfbdaf9e debian: fix: add pcuif_proto.h to osmo-pcu.install
Following 68fc12775f
'Install the pcuif_proto.h header file'
we need to add pcuif_proto.h to the debian install file.

Change-Id: Ib8e185900826baadcc96fcde1491903dbaf85f8b
2016-11-30 00:37:32 +01:00
sivasankari
168911b438 Add new BTS level counters
Adds counters for Immediate Assignment Reject, Packet Access Reject,
Channel Request Description and Final Block resend.

Change-Id: I23e326d4ea489aa4967e452fe02773b44ab146f7
2016-11-25 19:55:38 +05:30
Harald Welte
68fc12775f Install the pcuif_proto.h header file
So far, we used to keep a copy of the header file around in
both osmo-pcu and osmo-bts projects.  Before we start introducing
a third copy in openbsc, let's have the osmo-pcu install the header
file and make the other programs use that.

Change-Id: I60976c9be5488256d1ff55fdc5aa548e3705400d
2016-11-17 21:09:55 +01:00
Harald Welte
5d93f0f4ec Fix GSMTAP logging in case direct PHY access is enabled
In the existing code, GSMTAP messages were only generated in case no
direct PHY access was being used (i.e. in the case all user traffic goes
over the PCU socket).  I'm not quite sure what the reason is for that
would be and conclud this is not intentional.

Let's first send the message to GSMTAP and then decide whether to send
it via the direct PHY access or via the PCU socket into the BTS/BSC.

Change-Id: I5d2e018f7009cb947abc874881c0c440feca3ade
2016-11-17 21:09:55 +01:00
Harald Welte
bb47d957a8 pcu_l1_if: get rid of magic numbers and use ARRAY_SIZE() for array iteration
Change-Id: I61d00950b4eb0b8bcbaf386d5081be84580dac75
2016-11-17 21:09:55 +01:00
Mrinal Mishra
f86307e1e4 Add BTS level counters
Adds counters for MCS blocks, 11 bit Rach counters and others.

Change-Id: I605b0d66eb217decd35cbb8f87abfa577760245a
2016-11-14 01:15:16 +00:00
aravind sirsikar
ed3413e397 Handle packet access reject during EPDAN/PDAN with channel description
When PDAN/EPDAN with channel description is received, PCU will generate the
packet access reject if no resources are present. The encoding is done
based on section 7.1.3.2.1 and 8.1.2.5 of 44.060 version 7.27.0 Release 7.
This patch also includes the test case to validate the generated
packet access reject message.

This patch is integration tested on Osmo-trx setup with Ettus B210 board
and LG F70 MS with some simulation code changes in Osmo-pcu.

Change-Id: I096a3bb44a65533b9e9b091925dd5f70a8696d6
2016-11-11 17:15:10 +05:30
aravind sirsikar
c0c3afd079 Handle Immediate assignment reject
When RACH is received, PCU will generate the Immediate assignment reject
message if no resources are present. The encoding is done based on section
9.1.20 of 44.018 version 11.7.0 Release 11. This patch also includes the
test case to validate the generated Immediate assignment reject message.

This patch is integration tested on Osmo-trx setup with Ettus B210 board
and LG F70 MS with some simulation code changes in Osmo-pcu.

Change-Id: I3d33e2b9746fa4f338fad0e6b63b1c5f07de6f9b
2016-11-09 16:27:00 +05:30
Max
ae4838101a Handle Timing Advance IE properly
Move writing Timing Advance IE and Timing Advance Index into separate
functions to simplify adding PTCCH support. This also fixes previous
incorrect (and unused) code for writing Packet TA IE which has not set
TS for TA.

Change-Id: I786bf7fc999d401cc3d9e7f1e7a1fba953b5d458
Related: OS#1545
2016-11-09 09:30:49 +00:00
aravind sirsikar
fb41afaaf6 EGPRS: fix for EPDAN out of window
Fix alignment of EPDAN outside the RLC transmit window,
according to section 9.1.8.2.4 in 44.060 version 7.27.0 Release 7.
The specification explains that a bit within the uncompressed bitmap
whose corresponding BSN is not within the transmit window shall be
ignored. Without this fix PCU was dropping the EPDAN message and not
updating the status of BSNs which are inside the RLC window. This patch
updates the status of the BSNs which are inside the window and ignores
the remaining bits.

Related: OS#1789

Change-Id: Id07d178970f168f5389016c1eea31eb6b82057b6
2016-11-02 15:48:00 +05:30
aravind sirsikar
9434e52af9 Modify return type of gprs_rlc_dl_window::distance to uint16_t
Since there is a "&mod_sns()" present in this function, the outcome
is always unsigned.

Change-Id: I66f3db4dc27a6cbef146c832bf8b43f1492358a4
2016-11-02 15:43:10 +05:30
aravind sirsikar
f276138202 EGPRS: add test case to show EPDAN BSN out of window bug
This patch adds a test case test_tbf_epdan_out_of_rx_window,
which expects a current bug with EPDAN for interpretation of the
bitmap explained in section 9.1.8.2.4 in 44.060 version 7.27.0
Release 7. The specification explains that a bit within the
uncompressed bitmap whose corresponding BSN is not within the
transmit window shall be ignored. But current PCU implementation
drops the EPDAN and does not update status of the BSN which are
inside the window. The test's expectation is corrected along with
the bug fix in a subsequent commit.

Related: OS#1789

Change-Id: If32b67f5c05707155281128b776a90a1e3d587b2
2016-10-30 03:15:22 +00:00
Mrinal Mishra
d453eaa788 Add logging support
This commit adds the TRX_ID in the output of VTY command "show tbf all".

Change-Id: Ia5412dddb899e20963f884e02bdf796b6ea7ee6c
2016-10-26 15:41:56 +05:30
Neels Hofmeyr
f868bdbe76 jenkins.sh: use osmo-build-dep.sh, output testlogs
Also make cosmetically similar to the other jenkins.sh scripts in various osmo
repositories.

Change-Id: I34c19ed7c80aa56bd131f738f37324aed1cd73db
2016-10-20 11:42:03 +00:00
Max
d71e8b32e3 Use qbit-TA to update Timing Advance
Separate qbit-TA to TA conversion into separate function and use it for
computing and updating Timing Advance.

Note: the code was tested with TA=0 only to make sure it does not
introduce regressions.

Change-Id: I96fdbb20b09fb85fdd9fb6dcf3c25f6bee7f80e4
Fixes: OS#1531
2016-10-19 08:23:29 +00:00
Neels Hofmeyr
4ea452689d Revert "tbf: Add state WAIT_ASSIGN"
This reverts commit f1a7b8fc66.

Conflicts:
	tests/tbf/TbfTest.err

The commit broke GPRS service at least for osmo-bts-sysmo on a SysmoBTS 1002
with current master of osmo-bts (ef30f50d5d6d5f863fc147d05ccdceb89284934e).

The error observed is the following log output (was viewing both osmo-bts-sysmo
and osmo-pcu logs interleaved):

<0002> tbf.cpp:874 TBF(TFI=0 TLLI=0x00000000 DIR=UL STATE=WAIT ASSIGN) T3169 timeout during transsmission
<0002> tbf.cpp:893 - Assignment was on CCCH
<0002> tbf.cpp:899 - No uplink data received yet
<0007> l1sap.c:904 RACH for packet access
<0001> pcu_l1_if.cpp:311 RACH request received: sapi=1 qta=0, ra=121, fn=13653
[repeat]

When removing this single commit from current osmo-pcu master, GPRS service
works well on SysmoBTS, with current osmo-bts master.

The TbfTest.err expected output needed adjustment after the revert.

Disclaimer: I am not aware of adverse effects this commit may have. I have no
idea what the WAIT_ASSIGN state is used for -- further review is required.

Change-Id: I1532f8e93194368cdc1e3846f82afa6d68cd5fbd
2016-10-18 14:48:36 +02:00
Alexander Couzens
e4727a3591 llc: remove NULL-pointer check of gprs_llc_queue::size()/octets()
All callers now check the pointer before calling it.
gcc6 is optimizing `if (!this) {CODE}` as this is assumed to never be a
std::nullptr here.

Change-Id: I918a094e0dc59098a9eb00d152c9ae42d36b3a99
2016-10-18 11:51:02 +02:00
Alexander Couzens
d38b92e972 tbf: add llc_queue_size() to check llc_queue is valid before calling size()
gcc6 is optimizing if (!this) {CODE} as this is assumed to never be a
std::nullptr here. Move the null check to the caller. In preparation of
removing the check within llc_queue->size(), all callers must check the object
before calling it. Make sure of that: make the llc_queue() access function
protected and offer only a public llc_queue_size() function that incorporates
the NULL check. All current callers are only interested in the
llc_queue_size().

Tweaked-by: nhofmeyr
Change-Id: I88cc3180f8f86785e3f07981895dabddf50b60a2
2016-10-18 09:49:57 +00:00
Pravin Kumarvel
0a4a6c1200 EGPRS: Add EPDAN CRBB Tree based decoding
Implemented tree based algorithm to decode compressed bitmap in EPDAN
as described in section 9.1.10 of 3GPP 44.060.
This algorithm intends to improve the performance over existing method.
New Regression test is added under bitcomp directory.
Test case is added to validate decompressed result of the bitmap
Present in EPDAN.
Test is done for multiple bitmaps of varying length.
Invalid inputs are also part of the test vector.

Change-Id: Ieae1992ed4b02bb1e09eec2d3de1a030eabd16ce
2016-10-17 11:00:57 +05:30
Neels Hofmeyr
6348aea6a2 build: add -Wall
I noticed that unused variables are not complained about by the build. Let's
add -Wall.

I also noticed that the Makefile.ams include STD_DEFINES_AND_INCLUDES, which is
never set in configure.ac, so using that to add -Wall to all build contexts.

Change-Id: I16711cf5a1ef8bd611074b3dd486ed7a0ae9df64
2016-10-03 10:02:17 +00:00
Neels Hofmeyr
da66f71ffe configure: check for pkg-config presence
Change-Id: Iaaeb2a926fc3832793dfb3f02e4ced2500950997
2016-10-01 01:06:19 +02:00
Neels Hofmeyr
da933e0ff8 build: be robust against install-sh files above the root dir
Explicitly set AC_CONFIG_AUX_DIR.

To reproduce the error avoided by this patch:

  rm install-sh        # in case it was already generated.
  touch ../install-sh  # yes, outside this source tree
  autoreconf -fi

This will produce an error like

  ...
  configure.ac:16: error: required file '../ltmain.sh' not found
  configure.ac:5: installing '../missing'
  src/Makefile.am: installing '../depcomp'
  autoreconf: automake failed with exit status: 1

See also automake (vim `which automake`) and look for 'sub locate_aux_dir'.

Change-Id: Ie9a10f14c5e8c5e9b6ea4910b4b9abb7e70f5e04
2016-10-01 01:06:19 +02:00
Neels Hofmeyr
fd9e16ce97 heed VTY 'line vty'/'bind' command
Like most other osmo-* programs, bind the telnet VTY to the address specified
by the 'line vty'/'bind' command. This is added by vty_init(), so until now the
PCU offered this config but ignored it.

Change-Id: I4cca05a212ec0d493b906014dc3a83e687ebbb1d
2016-09-22 07:06:41 +02:00
bhargava
465f5bbb6f Update the function immediate assignment for EGPRS
Encode the EGPRS fields of immediate assignment message in uplink
when EGPRS PACKET CHANNEL REQUEST (11 bit RACH) is received.
The series of patches for 11 bit RACH are dependent on libosmocore
and osmo-bts patches for 11 bit RACH.

Change-Id: Ie5e309156e5dbbb6add74a1b4d257c4ee2332e52
2016-09-16 05:55:41 +00:00
bhargava
628dcfbc97 Handle EGPRS 11 bit RACH in osmo-pcu
A function is_single_block is added to get request type of RACH.
EGPRS 11 bit RACH is handled.

Change-Id: I61d74a32f7764644ed86f7fdf97fa3c2f61503f7
2016-09-16 05:54:15 +00:00
Aravind Sirsikar
0ee31cfa38 Fix EGPRS DL window calculation during tbf update
Earlier there was no handling for recalculation of DL window
size during tbf update. Which has been fixed in this patch.

Related: OS#1808
Change-Id: I41aa807068520460fd665a55e3529e60f6bbb630
2016-09-15 17:54:46 +05:30
Aravind Sirsikar
8e70bb5bb4 tbf_dl: factor out EGPRS DL window size calculation
A subsequent patch needs to call this from gprs_rlcmac_tbf::update(),
so to avoid code dup, put the calculation in a separate function.

Related: OS#1808

Change-Id: I7c7777d43f843bbd3421503fc2a8600f148ca035
2016-09-15 17:51:16 +05:30
Aravind Sirsikar
22a901905c EGPRS: Fix issue with row 4 of Table 10.4.14a.1 of 44.060 version 7.27.0 Release 7
row 4 of Table 10.4.14a.1 of Spec 44.060 version 7.27.0 Release 7. Says
"The previous RLC data block contains a Upper Layer PDU, or a part of it,
that fills precisely the previous data block and for which there is no
length indicator in that RLC data block.
The current RLC data block contains a Upper Layer PDU that either fills
the current RLC data block precisely or continues in the next RLC data block."
So when we receive block with 1st LI: value=0 and Value of E bit in the
same octet as 1, we expect 2 chunks with 1st chunk as length as 0 and complete
and 2nd chunk as length non zero. But with this bug we see only 1 chunk causing
incorrect assembling

This issue has been fixed in this patch.

Related: OS#1811

Change-Id: I2cd0fca3ed28a553ede3f4b8a7d3267284dd2c9b
2016-09-15 17:24:49 +05:30
Aravind Sirsikar
3463bd4adc EGPRS: add test case to show LI decoding bug
This patch adds a test case test_tbf_li_decoding which
expects a current bug with LI decoding for row 4 of Table 10.4.14a.1
in 44.060 version 7.27.0 Release 7.
The test's expectation is corrected along with the bug
fix in a subsequent commit

Related: OS#1811

Change-Id: Ida410dab1aa4b0cf3e15b2090586377eb19b2469
2016-09-15 17:19:54 +05:30
Aravind Sirsikar
e26ee01d56 DL TS allocation: add test case to show TS allocation bug for 2nd DL TBF
This patch adds a test case test_2_consecutive_dl_tbfs which
expects a current bug with TS allocation for 2nd DL TBF.
The test's expectation is corrected along with the bug fix in a
subsequent commit

Related: OS#1792

Change-Id: I890e4fbb2b64037e051433e70082a197e2a929a6
2016-09-14 11:55:32 +00:00
Neels Hofmeyr
0241526836 Fix CSN1 decoding: CSN_LEFT_ALIGNED_VAR_BMP bounds
Fix attempted read past vector boundaries in case of a starting bit offset !=
0, so that the last amount of bits read should be < 8. In the case of
CSN_LEFT_ALIGNED_VAR_BMP, the mod-8 calculation was flawed, and in the final
step, 8 bits were read instead of the remainder < 8. This lead to -EINVAL being
returned by bitvec_get_bit_pos() and bogus resulting data.

Instead, read 8 bits only as long as at least 8 bits remain, and read any
remaining bits < 8 in a final step. Drop unneeded nB1 variable and an obvious
comment.

Adjust the unit test assertion in testCsnLeftAlignedVarBmpBounds() in
RLCMACTest.cpp.

Based on a fix by Aravind Sirsikar <Arvind.Sirsikar@radisys.com>, but
implemented differently.

Related: OS#1805
Change-Id: I490498c8da6b531f54acb673379379f7b10907c0
2016-09-14 01:26:34 +00:00
Aravind Sirsikar
9f5f008aed CSN1 decoding: add test to show bug in CSN_LEFT_ALIGNED_VAR_BMP
CSN1 decoding currently contains an attempted read past vector boundaries in
case of a starting bit offset != 0, so that the last amount of bits read should
be < 8. In the case of CSN_LEFT_ALIGNED_VAR_BMP, the mod-8 calculation is
flawed, and in what should be the final step of reading n < 8 bits, 8 bits are
read instead of n (with an extraneous read of n bits following after that).
This leads to -EINVAL being returned by bitvec_get_bit_pos() and bogus
resulting data.

Add testCsnLeftAlignedVarBmpBounds() in RLCMACTest.cpp to show and expect this
bug. The test's expectation shall be corrected along with the bug fix in a
subsequent commit.

Related: OS#1805
Tweaked-by: Neels Hofmeyr <nhofmeyr@sysmocom.de>
Change-Id: I4641f5d1d49f66cb1a5cd813befb3a2a266001b0
2016-09-14 01:26:33 +00:00
Aravind Sirsikar
8d2d9e8985 TBF flow: unit test compilation error fix
The test failure was introduced by 9bbe1600cc
"Fix Timing Advance handling": between patch build checking and patch
submission, a new section was added to TbfTest.cpp which also needs adjustment.

Tweaked-by: Neels Hofmeyr <nhofmeyr@sysmocom.de>
Change-Id: If077da5f21fd5cba54556f1dead05a1bc4ea5540
2016-09-12 15:16:38 +02:00
Max
9bbe1600cc Fix Timing Advance handling
* initialize with invalid TA instead of making assumption that phone is
  located within 550 meters (TA=0)
* only set valid TA

Change-Id: Idfc40ff0c11bdac13d9e28fbfa4e95dfc6b735b0
Related: OS#1526
2016-09-09 06:37:04 +00:00
Minh-Quang Nguyen
16ddc90eab LC15: Change TRX numbering for the latest Litecell15 hardware
Change-Id: If3c4aff0366587dd3e5baa3d15b9e91d8ebe7753
2016-09-06 10:27:11 -04:00
Aravind Sirsikar
7c7a86c080 Fix GPRS PUAN encoding: wrong BSN status
Earlier there was an incorrect encoding of BSN status in GPRS PUAN message.
This was a bottle neck for GPRS performance testing for UL. Which has been fixed
in this patch.

Related: OS#1806

Change-Id: I98e586aa5cb9200cf03e092556304211d4d459aa
2016-09-02 06:47:09 +00:00
Aravind Sirsikar
a35c911a91 GPRS: PUAN encoding: add test case to show wrong BSNs status
This patch adds a test case which expects a current bug with
GPRS PUAN encoding. The test's expectation
is corrected along with the bug fix in a subsequent commit

Related: OS#1806

Change-Id: Ied0f1dd3037d8fac6a772f4e097defb72634f955
2016-09-02 06:45:32 +00:00
Aravind Sirsikar
3c2eaebd21 DL: add test case to show wrong window size
This patch adds a test case test_tbf_update_ws. Which expects a
current bug with DL window size calculation. The test's expectation
is corrected along with the bug fix in a subsequent commit

Related: OS#1808

Change-Id: I4659494c6f93ae89e4cc4ac79fff5fcaf2d23699
2016-08-30 15:40:19 +05:30
Aravind Sirsikar
fd71384104 TBF flow: unit test compilation error fix
Change-Id: I89638ba908e7d9964a5525061ce0cf26049be438
2016-08-28 17:55:05 +05:30
Aravind Sirsikar
b119198992 TBF flow: Coverity fix
Related: CID#1361925, CID:#1361924

Change-Id: Ib1f71a8940eed7ad74211092275dfa29aa353fc7
2016-08-28 11:55:01 +00:00
Neels Hofmeyr
01826c13b1 vty: use OSMO_VTY_PORT_PCU instead of number
Include vty/ports.h and use the proper constant.

Change-Id: I9c5b7683f76994c539da5551f40df32379dc685e
2016-08-27 02:19:48 +00:00
bhargava
959d1dee67 Change interface in osmo-pcu for 11 bit RACH
Interface structure between osmo-bts and osmo-pcu is updated with
the parameters to differentiate the type of RACH and further
support 11 bit RACH. The function prototype and definitions are
changed accordingly. Interface version number is increased.

Change-Id: I265c2d92d36d6cbcbeee60cdd8407dafe1da06a4
2016-08-27 01:22:48 +00:00
Aravind Sirsikar
eebcb1e3e8 Fix EGPRS PUAN encoding: use correct urbb_len
Earlier there was an incorrect encoding of PUAN when VQ is not equal
VR case for EGPRS UL RLC window. The PCU was encoding the same PUAN
message always irrespective of radio condition. This was a bottle neck
for performance testing. Which has been fixed in this patch.

Related: OS#1793

unit test assertion in the previous commit is fixed in this patch.

Change-Id: Iba7b1995028bd81749ffb080616b2ad5f2540d57
2016-08-25 16:40:23 +05:30
Aravind Sirsikar
02352b487a EGPRS: PUAN encoding: add test case to show wrong urbb_len issue
This patch adds a test case which expects a current bug with EGPRS PUAN
encoding when VQ != VR. The test's expectation is corrected along with
the bugfix in a subsequent commit

Adds test_tbf_puan_urbb_len to describe the following bug:
EGPRS PUAN encoding disregards the urbb_len, leading to identical PUAN
messages regardless of the urbb_len.

Related: OS#1793

Change-Id: I00662a564f64c0c83627401ae8f7bfef0f0a5de8
2016-08-25 16:37:30 +05:30
Aravind Sirsikar
50b097003b Modify EGPRS DL TBF flow to support SPB
Modify the EGPRS DL TBF flow to support Split block during
Retx. This patch will also Upgrade the test suite with test cases
to validate the EGPRS Downlink SPB for Retransmission

Scenarios like MCS6->MCS3, MCS4->MCS1, MCS5->MCS2, MCS9->MCS3
MCS7->MCS2, MCS8->MCS3 have been simulated and Integration tested
in NuRAN 1.0 hardware thoroughly.

Change-Id: I242afdd8ae7622dec8593b26382ad66bad5b9516
2016-08-25 10:41:33 +00:00
Aravind Sirsikar
e6cadb4e3c Add data structure to handle SPB for EGPRS DL
Modify the header files with necessary data structure to handle
Split block for EGPRS DL TBF.

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

Change-Id: I57673e53a9da2affa7e8aaa6551ac4b271c3d525
2016-08-25 10:34:00 +00:00
Aravind Sirsikar
1ec4d80176 Remove warning while using 'egprs only' command in VTY
This warning is not valid since the PCU is not failing when EGPRS is
activated. So removing this trace

Change-Id: I62278f998adc691b9a3563ac2a46d756e7bfb66c
2016-08-22 17:16:00 +00:00
Neels Hofmeyr
9876f4bb21 jenkins.sh: drop compat with old matrix params
Change-Id: I7b50a24cf5879cb473a5cf929768bdd30e863a26
2016-08-16 12:49:29 +02:00
Neels Hofmeyr
7fd177b91c jenkins.sh: change build matrix to $with_dsp and $with_vty
The new $with_dsp matrix parameter is defined as "sysmo" or empty/"none". The
lc15 DSP might be added in the future.

Fetch the sysmo layer 1 API only if with_dsp==sysmo.

The new $with_vty parameter is independent of $with_dsp, it is now up to
jenkins to define a matrix filter.

For compat, until jenkins is reconfigured with the new matrix parameters, use
$sysmodsp to init the new parameters to reflect previous behavior. The
$sysmobts matrix parameter made no sense, drop it.

Change-Id: Ia120f918342dc9563814252258b73bfb267e5253
2016-08-11 11:06:26 +00:00
Neels Hofmeyr
2d91260ea4 jenkins.sh: more quotes, cosmetics, less dup
Rename BTS_CONFIG to PCU_CONFIG.
More quotes.
Unify bash if-style.
Define *_PATH variables once globally instead of duping in every line.

Change-Id: If148632c3f340a8a395fa432135e593fecc41e82
2016-08-11 11:06:26 +00:00
Neels Hofmeyr
6bae2d11f1 jenkins.sh: use absolute paths instead of 'cd ..' and $PWD
Change-Id: If79d283fa0a559bb7ea319c513d09466eff523d1
2016-08-11 11:06:26 +00:00
Neels Hofmeyr
0b4da058ad jenkins.sh: ensure $MAKE is set
Change-Id: I2da8acdfe3abf79f68db4d00d04a7d162f0123ce
2016-08-11 11:06:26 +00:00
Max
79cb245157 LC: fix build error
Remove extra parameter which causes build to break. The error was
introduced in 878bd1f296

Change-Id: Id63187d925d448caa4fa85720582550919b1f216
2016-08-09 19:21:34 +02:00
Max
cbf9a721d6 Extend BTS <-> PCU protocol with measurement
Note: this increases the version of BTS <-> PCU protocol and thus
requires corresponding change in BTS.

Change-Id: Ide0e29b668ee38516605c1763fda85e87e867813
Related: OS#1616
2016-08-04 15:06:12 +00:00
Aravind Sirsikar
505a86d396 Add support for SPB handling for EGPRS UL TBF
This patch will modify the EGPRS UL TBF flow to support Split block
handling. This patch also contains test suite modification for SPB UL.
Scenarios like MCS6->MCS3, MCS4->MCS1, MCS5->MCS2, MCS9->MCS3
MCS7->MCS2, MCS8->MCS3 have been simulated and Integration tested
in NuRAN 1.0 hardware thoroughly. The scope of Unit testing is limited.

Change-Id: I39ca53218b6e0982abc2ab9c703c24c8bf0a09c0
2016-08-02 06:58:58 +00:00
Aravind Sirsikar
36bdc5f7a4 Add data structure for SPB in EGPRS UL
Modify header files with data structures required
to support split blocks for EGPRS UL TBF

This feature provides provision for 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.

Change-Id: I83ccd136bb361adcfd511c57c5a9d95ed72c36c2
2016-08-02 06:58:57 +00:00
Max
d572054ca7 Properly set TA_VALID bit
Check Timing Advance validity and set corresponding bit for Immediate
Assignment message. Previously !polling was errorneously used (polling
bit has nothing to do with TA validity according to 3GPP TS 44.018 Table
10.5.2.16.1) which lead to TA being always valid as polling is always 0
in other parts of the code.

Change-Id: I5d7ecc7f71402b945cae99332be2ebc0b17b9d44
Related: OS#1526
2016-07-28 06:20:41 +00:00
Max
878bd1f296 Remove useless ARFCN parameter
ARFCN is already part of TRX struct so there's no need to supply it
explicitly in a separate parameter. I've tested and those are the same
anyway.

Change-Id: I8e975c52cbc819427880093b1e5371fe1f8ce460
2016-07-26 00:20:23 +00:00
Max
1d7644b23a Cleanup readme
Remove note on PCCCH/PBCCH support because according to 3GPP TS 44.060
version 12.5.0 Release 12 § 1.6 "The network shall never enable PBCCH
and PCCCH".

The rationale behind this from GP-091955:

"Due to that P-channels are not deployed by any operator and are not
 expected ever to be, it has decided to remove the requirement on
 mandatory support of P-channels for the mobile stations in A/Gb mode."

Change-Id: I2b16413e1b6ce8f2bc2e8183165fb6b3aa14f2d0
2016-07-23 19:24:41 +00:00
Max
2ec6b8e758 Remove unused definitions
Those structs are not used anywhere (which was the case in the commit
which introduced them as well) but give false-positives while grepping
through the code. Better to just drop them.

Change-Id: I0a0bb0c641e4e081a57f72187ff96e9beef16588
2016-07-20 18:30:10 +02:00
Tom Tsou
df69809b82 egprs: Use RLC/MAC headers from libosmocore
EGPRS Type 1, 2, and 3 headers are used by OsmoPCU and OsmoBTS.
Move the header definitions to libosmocore to be shared by both
packages.

Modify the struct variable naming to use *_hi/*_lo instead of
*_a/*_b in order to be consistent with existing naming used in
libosmocore.

Change-Id: I98687ad981d27502aec42729611937ba1caf207c
2016-07-14 06:56:19 +00:00
Holger Hans Peter Freyther
5d94b5455f bitvector: Remove code clone and fallback to C implementation
This routine has been moved from from here to libosmocore and as
part of the C++ -> C the reference got converted to a pointer. We
have a lot of code that calls the method with the reference and
instead of updating the callers, create a short inline wrapper to
call the C routine.

Change-Id: Idd16ce251a42bad4401c2bf3a8fa6af70fb600ff
2016-07-13 16:26:32 +00:00
Aravind Sirsikar
1a679127af Add test cases to support ARQ-II for EGPRS DL Retx
During MCS upgradation such as MCS6->MCS9, 2 blocks which
were sent separately as MCS6, will be clubbed into one MCS9
block during retransmission. Same holds good for
MCS5->MCS7 transistion. During MCS reduction such as
MCS9->MCS6,2 blocks which were sent together will be
sent separately during the retransmission case.
Same is verified through the generated log file. Currently
MCS8->MCS6 transition is not supported. The retransmission
MCS is being calculated from Table 8.1.1.2 of TS 44.060.
The same test cases are also integration tested on Nuran
1.0 platform.

Change-Id: Ia357acfe30f4dea95e00749916c6818354f93285
2016-07-13 13:50:36 +00:00
Aravind Sirsikar
cf2152b24c Modify DL tbf flow for ARQ-II in EGPRS DL Retx
Modify the DL TBF flow to support ARQ-II EGPRS DL retransmission

Change-Id: I7a845c98f2018795f0f62240f228411b0bc030c7
2016-07-13 13:50:20 +00:00
Aravind Sirsikar
e8ccafc63d Add Accessor functions for ARQ-II in EGPRS DL
Add accessor function in existing classes to support ARQ-II for
retransmission in EGPRS DL

Change-Id: Iefff956bf2dcfe8fb0b2f5a7a7a2122d5d555f9e
2016-07-13 13:50:04 +00:00
Aravind Sirsikar
914955209e Add data structure for ARQ-II in EGPRS DL
Modify the existing data structure to support ARQ-II for Retx in EGPRS DL.
This will also hadle compilation issue related to renaming the variable.

Change-Id: I734b1024bb32f2daa43af4adf59f4a17f2294afe
2016-07-12 14:17:12 +05:30
Harald Welte
899d36d813 systemd service file: Stop using deprecated '-e' option
In commit 6d8884de49 in 2014, we
made the '-e' command line option deprecated.  Stop using it from the
systemd srevice file.

Change-Id: I322cadbee8980b78fff2984765c4b0216c50412e
Related: SYS#2749
2016-06-30 19:14:49 +02:00
Neels Hofmeyr
d32aa03520 typo in warning
(actually committing just to test gerrit, and if it goes through it's still
a valid change.)

Change-Id: I2ca9a1cc2f250801fbe62f3c50b73dff7101ee08
2016-06-20 18:17:03 +02:00
Aravind Sirsikar
2c9f980163 Add test cases for Header type1 in EGPRS UL
Update test suite with test cases for Header type 1 in EGPRS UL

Change-Id: I21811bb126dbe151b0708a964d3143bc2fd52389
Reviewed-on: https://gerrit.osmocom.org/272
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-06-17 15:31:07 +00:00
Aravind Sirsikar
99ab0a8fa0 Add header type 1 support for EGPRS uplink
Function is added to parse the EGPRS header type 1 in uplink tbf path.
along with configuration parameter updation to reflect max mcs in UL

Change-Id: I13c250e2e07377982ac3f29745f3cffd4088552a
Reviewed-on: https://gerrit.osmocom.org/270
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Tested-by: Jenkins Builder
2016-06-16 14:32:07 +00:00
Aravind Sirsikar
550a54184b Add Header Type2 support in EGPRS UL
This patch will add support for MCS5,6 in EGPRS UL along with incorrect
assert correction to let MCS 6 work.

Change-Id: Iac2422c8acbdcefe20aafbba6a4eb87c9893e3ba
Reviewed-on: https://gerrit.osmocom.org/269
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-06-16 14:18:53 +00:00
Aravind Sirsikar
23617c001d Remove GMSK only check in EGPRS UL
Since we are supporting MCS 5-9 in this patch series for EGPRS UL,
This condition is not relevant. So removing it.

Change-Id: I567acc012d8ad49681715f0104ba7e91625e1e7a
Reviewed-on: https://gerrit.osmocom.org/268
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-06-16 14:17:17 +00:00
Aravind Sirsikar
189742b66c Add test cases for Header Type 2 in EGPRS UL
Updates the test suite to add test cases for Header type 2 parsing
in EGPRS UL.

Change-Id: I1dd46010065a6d6da21e8e45af71e6d5f649b0b0
Reviewed-on: https://gerrit.osmocom.org/271
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-06-15 14:05:28 +00:00
Alexander Couzens
e04fd0cf0f tbf: replace this == NULL check in tbf->name
All checks of (this == null) will be eliminated by GCC >= 6.1
(https://gcc.gnu.org/gcc-6/changes.html, Value range propagation now
assumes that the this pointer of C++ member functions is non-null.

Change-Id: Ifddaef70bb0a4402050c817b1000d515c3a7118b
Reviewed-on: https://gerrit.osmocom.org/136
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-06-10 11:14:10 +00:00
Alexander Couzens
6922bcd929 tbf_dl: correct tbf name in log message for moving a DL TBF
It makes no sense to call functions on null pointer object. Use
the name of the old tbf.

Change-Id: I93b8c07a0b2de40a11e94fd6c212897cbe3b50ef
Reviewed-on: https://gerrit.osmocom.org/212
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-06-10 11:13:23 +00:00
Alexander Couzens
7fdbf89ef3 add KPI counter to count bytes for RLC and LLC frames
rlc.dl_bytes		bytes before sending rlc
rlc.dl_payload_bytes	count data w/o LI
rlc.ul_bytes		bytes when received rlc (only valid)
rlc.ul_payload_bytes	count data fragments w/o LI
llc.dl_bytes		complete encapsulated LLC PDUs
llc.ul_bytes		complete received LLC PDUs

Change-Id: I9a98a5a375d39b3f4990360056c4d6145e755f4d
Reviewed-on: https://gerrit.osmocom.org/145
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Reviewed-by: Holger Freyther <holger@freyther.de>
Tested-by: Jenkins Builder
2016-06-07 10:56:25 +00:00
Alexander Couzens
6f0dc96929 encoding/rlc_copy_from_aligned_buffer: export written payload bytes via an argument
Require to count statistics for rlc_dl_payload_bytes.

Change-Id: I0e622acb1f13f7489946baf049de4ba1cde6a1fc
Reviewed-on: https://gerrit.osmocom.org/142
Tested-by: Jenkins Builder
Reviewed-by: Holger Freyther <holger@freyther.de>
2016-06-07 10:48:35 +00:00
Alexander Couzens
1a5066112f tbf_dl: comment why we sent a dummy LLC packets to delay the release of the TBF
Change-Id: I1862674437dffef4de3ffa7b183ecf690020b0ec
Reviewed-on: https://gerrit.osmocom.org/143
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Tested-by: Jenkins Builder
2016-06-07 10:42:19 +00:00
Alexander Couzens
d302e4fb28 decoding: remove superfluous double-semicolon
Change-Id: I48ec24f2e10620279cbcbf39c70a4be6438f6b0f
Reviewed-on: https://gerrit.osmocom.org/140
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Tested-by: Jenkins Builder
2016-06-01 13:38:35 +00:00
Alexander Couzens
68e2c6375e rlc.h: correct gprs_rlc_data comment
It's the block data, not the history.
Also add including LI headers.

Change-Id: Id4d99d1d21c7fa372771fd569d87bbcf2c6b6d22
Reviewed-on: https://gerrit.osmocom.org/144
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Tested-by: Jenkins Builder
2016-06-01 13:38:28 +00:00
Alexander Couzens
cb846ecbbc encoding: add doxygen for rlc_data_to_dl_append*
Change-Id: I6ead0f1d14a91c657448227e17438b49a54e6c4a
Reviewed-on: https://gerrit.osmocom.org/141
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-05-31 11:52:19 +00:00
Alexander Couzens
b82bd92e57 decoding: improve and add comments
Change-Id: I45c9fc55243224909ca2fdece8cbfa686b0f444d
Reviewed-on: https://gerrit.osmocom.org/139
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-05-31 11:51:59 +00:00
Alexander Couzens
2fcfc29020 add comments to describe functions
Change-Id: Ie351632001abbeb82008a5eecae0d0323a8ef7d7
Reviewed-on: https://gerrit.osmocom.org/106
Reviewed-by: Harald Welte <laforge@gnumonks.org>
Tested-by: Jenkins Builder
2016-05-25 20:07:03 +00:00
Alexander Couzens
ce936f3cd4 tbf_ul: use correct size for chunk_size
The size of the hole array in bytes was used instead of the size of elements.

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

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

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

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

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

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

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

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

Change-Id: Ib1287375cb10a889625bbac8528fa60deed23a2b
Fixes: SYS#2443
Reviewed-on: https://gerrit.osmocom.org/61
Tested-by: Jenkins Builder
Reviewed-by: Harald Welte <laforge@gnumonks.org>
2016-05-20 16:26:20 +00:00
Max
58b6646750 Change internal API for consistency
Make TRX API (void *) consistent with the way it's used (integer). Use
uint8_t for TRX numbering everywhere (we don't expect hardware with
more than 256 transceivers in the near future). This change helps to
avoid unnecessary casts and make API much clearer.

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

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

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

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

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

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

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

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

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

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

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

Fixes: Coverity: CID 1351733
2016-02-25 14:03:49 +01:00
Holger Hans Peter Freyther
f3f1bde4fc alloc: Fix UBSAN for accessing the array at -16
This test will check that we exhaust the available TFIs, so the last
TFI is -16 and then we try to store to that address. It is surprising
that it worked!
2016-02-22 15:14:01 +01:00
Max
528ff3910f Add gsmtap support to generic bts
Instrument TX and RX functions dealing with regular BTS (without direct
DSP access) to use GSMTAP. Previously only DSP-related functions were
instrumented.
2016-02-22 14:50:58 +01:00
Max
2efdf69734 Introduce --gsmtap-ip/-i option
This option allows user to use custom IP address instead of default "localhost".
Correspondingly gsmtap init moved from sysmoBTS-specific code up to "bts" struct
level. This way it can be easier reused by other implementations. The
lack of regressions was verified by checking following command on
sysmoBTS: "./osmo-pcu -c osmo-pcu.cfg -r 1 -i 192.168.10.1" where
192.168.10.1 is the host which was running wireshark and netcat:
"nc -u -l 192.168.10.1 -p 4729" to accept gsmtap flow.
2016-02-22 14:00:17 +01:00
Max
9d5580b6dd Ignore files generated by cscope tool
Signed-off-by: Max <msuraev@sysmocom.de>
2016-02-22 13:58:54 +01:00
Holger Hans Peter Freyther
4f8438a6cd Merge remote-tracking branch 'origin/jerlbeck/master'
This adds EDGE support and at the same time is changing a lot of code
on GPRS support as well. Due my business decision of completing as much
as possible during the time we had the unit test coverage is not as
extensive as I had hoped for.

This is just the beginning. We do not support mixed GPRS/EDGE support
and have plenty of things to improve throughput.

Thanks to On-Waves for supporting a project with so many unknowns and
uncertainty and Jacob for leading the effort at sysmocom.
2016-02-22 09:44:39 +01:00
Harald Welte
7f4841b3ac Fix missing '-V / --version' in print_help() 2016-02-14 12:14:18 +01:00
Jacob Erlbeck
7f28c97fcc edge: Support all coding schemes for BSSGP flow control
Currently the MCS schemes are not supported when the leak rate is
being computed. This leads to a lower value for the leak rate, thus
limiting the throughput to GPRS-like ranges.

Use the payload size reported by GprsCodingScheme instead.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:40 +01:00
Jacob Erlbeck
be881c028f edge: Work-around to not use MCS-6 with padding
Currently single BSN blocks of size 68 (a single unit from MCS-8)
are put into a MCS-6 block with padding according to TS 44.060
Annex J. Unfortunately, these packets are ignored by the E71. This
might be related to the currently incorrect usage of the puncturing
schemes and the RSB bit (the puncturing schemes are not updated and
the RBS flag is never set).

Avoid downgrading from MCS-8 to MCS-6 if there is no second block
available. Send the block twice instead.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:40 +01:00
Jacob Erlbeck
64e7b83139 edge: Compare len instead of using cs.isCombinable
Comparing the coding scheme properties doesn't seem to be strong
enough, since create_dl_acked_block(fn, ts, index, index2) sometimes
had to combine data units of different size this way.

Check the registered size of the BSN blocks instead, since these must
match if two blocks are to be combined in a single RLC data message
with MCS7-9.

It is not yet clear, what exactly goes wrong with the current
implementation, but this commit fixes the problem which trigger the
assertion in create_dl_acked_block.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:40 +01:00
Jacob Erlbeck
d6752491e1 edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message,
even if MSC7-9 are used, which transport two independant (except for a
max BSN delta of 512) BSN blocks. In that case, the same block is
just put twice into the same message.

The current create_dl_acked_block(fn, ts) method handles
block selection (resend, new BSN, dummy block, ...) and restart
handling in one method and is too complex to extend it accordingly.

Therefore this commit move the block selection/creation handling into
a new method (take_next_bsn) which delivers the next BSN along with a
hint, whether it may be combined with another block. In that case,
the function can be called a second time (this time with a valid
previous BSN, that's the one returned by the first call) to get the
second BSN. The real block generation method
create_dl_acked_block(fn, ts, index, index2) is then called with both
BSNs as indices (the second must be -1, if there is only one BSN).

Note that every BSN returned by take_next_bsn should be passed to
create_dl_acked_block to avoid state inconsistencies.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:40 +01:00
Jacob Erlbeck
be314d9a54 edge: Refactor create_dl_acked_block for multi-block support
Currently the method is hard-coded to support a single BSN only.
MCS-7 to MCS-9 encode 2 data blocks into a single RLC data message.

This commit refactors create_dl_acked_block to process any number
of blocks internally.

Note that this does not extend the parameter list accordingly and
just duplicates the BSN if these MCS are being used.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:40 +01:00
Jacob Erlbeck
14e26cbca3 ms: Fix GprsMs::current_cs_dl()
Currently the queue length is overwritten by the remaining chunk
length, which should be added instead. This may result in a lower
CS/MCS value than expected.

Add the chunk length instead if the TBF is present.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:40 +01:00
Jacob Erlbeck
2d2efb13e7 tbf/tests: Add tests for EGPRS TBF establishment
Add a test for EGPRS two phase access based on RA caps.

Add a test for DL TBFs where data block are encoded with each MCS.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:40 +01:00
Jacob Erlbeck
0f5c6956dd rlc: Use the rlc structure to access the data unit in the RLC message
Currently most offsets are hard-coded which makes it difficult to
access the data units and their headers when padding has to be taken
into account. These offset are already provided by the
gprs_rlc_data_info_init_ul/dl functions.

Change the encoder/decoder to use these values.

Note that some assumptions (bit alignment) are still present in the
code and checked by assertions.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:40 +01:00
Jacob Erlbeck
fbd82e4e9f rlc: Add gprs_rlc_mcs_cps_decode
To access EGPRS data blocks, the optional padding must be taken into
account. Whether padding has been used must be dervied from the CPS
field in the header of the RLC EGPRS data message.

Add this function to decode the CPS value and extract that
information.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
b55f313735 rlc: Add with_padding argument to gprs_rlc_data_info_init_dl/ul
The offsets of the data areas change when padding is used (see TS
44.060, 9.3.2.1 and Annex J for details).

Extend the parameter lists to pass the with_padding flag and use
that information to compute the correct offsets.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
215e18c9d4 cs: Add GprsCodingScheme::optionalPaddingBits
Return the amount of optional padding bits, which is 6*8 for
MCS-3 and MCS-6 and 0 for all other coding schemes. The padding
is needed the encode 68 byte data blocks (MCS-8) with these schemes.
See TS 44.060, 9.3.2.1 and Annex J for details.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
2305afd86c cs: Add family related methods
Add family handling and the related methods family,
isFamilyCompatible, isCombinable, decToSingleBlock to
GprsCodingScheme.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
4cc46d3e2a edge/vty: Set initial MCS
This adds the following VTY command (config-pcu node):

  - mcs <1-9>         sets initial DL and UL MCS to the same value
  - mcs <1-9> <1-9>   sets initial DL and UL MCS separately
  - no mcs            sets the default values

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
9e8593917f rlc: Support encoding of EGPRS header type 1 + 2
Currently only header type 3 (MCS-1 to MCS-4) is supported.

Add header structs to rlc.h and extend
Encoding::rlc_write_dl_data_header accordingly.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
f1a7b8fc66 tbf: Add state WAIT_ASSIGN
Currently the state on the TBF is set to ASSIGN when it is created
and in general changed to FLOW when it is acknowledged by the MS. The
moment when the assignment is really transmitted to the MS (which can
take some time) is not reflected by the state. A TBF can considered
to be valid, when the assignment is received by the MS.

Add the state WAIT_ASSIGN that is entered when the assigment has been
send to the MS (more precisely: when the corresponding RTS has been
processed or the message has been sent to the BTS).

The TBF, its PDCH(s), and its TFI can be assumed to be valid, when the
TBF has a state of WAIT_ASSIGN or higher (assuming that the TBF
starting time has not been set, which is currently only done for SBA,
which are not associated with a TBF).

Note that due to queuing in the BTS there can still be a invalid
time period for immediate assignments, when the request is lingering
in the AGCH or PCH queues.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
7c72acaa94 ms: Add current_pacch_slots method
The PACCH is specific to an MS and may change when a TBF is
established or removed (see TS 44.060, 8.1.1.2.2). For multislot
class type 2 phones, more than one timeslot may be used to transmit
RLC/MAC control messages.

Add a method that returns a set of TS which can be used for the
PACCH.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
1ff70c26e3 sched: Do PACCH assignments for the same direction last
Currently the selection of a pending control message is done round
robin. It can possibly happen, that a DL assigment is sent on a DL
TBF while an UL assignment is pending. The MS will replace the old
DL TBF in that case, so that it can no longer be used for the PACCH
so that the UL assignment is lost.

Give assigment requests for the same direction a lower priority.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
7d5157ee17 tbf: Only free TBF if it was replaced in rcv_control_ack
Currently the TBF whose PACCH has been used to send an assigment
is freed if it is in state WAIT_RELEASE. Sometimes this TBF could
be used for several assignments (e.g. an 'old' DL TBF is used to
assign an UL and then a DL TBF). The second of these assigments will
never be sent in that case. On the other hand, the MS replaces a
TBF of the same direction, so the old one can be freed in that case.

Only free the TBF if it is in state WAIT_RELEASE and has the same
direction like the new one.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:39 +01:00
Jacob Erlbeck
be80c3670e edge: Support EGPRS in IMM ASSIGNMENT
Tell the MS to use EGPRS if EGPRS is enabled on the TBF.

Note that only downlink TBF will be supported so far. Thus
single-phase uplink assignments may not work properly.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:38 +01:00
Jacob Erlbeck
2647a337a8 encoding: Redesign Encoding::write_immediate_assignment API
The EGPRS support will need more information to encode the IMMEDIATE
ASSIGMENT. Instead of adding more parameters pass a pointer to the
TBF unless an SBA shall be done (indicated by tbf == NULL). All
values that can be derived from the TBF and are not needed for an SBA
are removed from the parameter list.

Return a negative value on error.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:38 +01:00
Jacob Erlbeck
18831c3ca9 encoding: Refactor write_immediate_assignment
Move the IA rest encoding into separate functions fpr uplink and
downlink.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:38 +01:00
Jacob Erlbeck
7505f1d636 encoding: Use explicit LH encoding in write_immediate_assignment
Currently bitvec_write_field is used which just sets the bits as
given, while the spec 44.018 assumes LH encoding.

Use the bitvec_write_field_lh function instead.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:38 +01:00
Jacob Erlbeck
4e7424d47b pcu: Add bitvec_write_field_lh
While the bitvec functions from libosmocore support LH encoding, the
C++ wrapper does not.

Add a bitvec_write_field_lh function that is similar to
bitvec_write_field, but writes L and H instead of ZERO and ONE.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:38 +01:00
Jacob Erlbeck
9876a3ba5d tbf: Don't change type from CCCH to PACCH without ack
Currently the CCCH flag is cleared and the PACCH flag is set when a
multislot upgrade is scheduled for a downlink TBF, even if the MS has
never confirmed in any way that the PACCH really exists. This can
happen if the MS did not receive the DL IMM.ASS. Since the CCCH flags
gets cleared in that case, the IMM.ASSS is never retried and all
subsequent PACKET DOWNLINK ASSIGNMENTS will fail.

This commit delays the update of these flags until the MS has
responded with a corresponding CONTROL ACK.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:38 +01:00
Jacob Erlbeck
6b356a58d1 tbf: Use TLLI as ID if TFI not yet assigned
Currently the old TFI is always used as ID when a
PACKET DOWNLINK ASSIGNMENT is generated. This fails
if the old TBF has not been fully assigned yet. The
MS will then ignore the PDA.

This commit changes write_packet_downlink_assignment to accept
an additional parameter old_tfi_is_valid and uses the new TBF's
TLLI instead of the olf TFI if that parameter is set to false.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:38 +01:00
Jacob Erlbeck
f2694b74c9 tbf: Add check_polling/set_polling
Currently the checks for and the actual polling is done in several
places by copy & paste of several lines of code. This hinders changes
of they polling is handled internally and also is likely source of
programming mistakes.

Separate this into a check_polling function, that checks whether
polling is possible and returns the FN and the RRBP to be used in
that case. Otherwise the cause is logged (LOGL_DEBUG) and a negative
error value is returned. There are no other side effect beside the
logging.

If the call is successful, the set_polling method can be used to
actually register the polling.

Extend the encoder functions' parameters lists by an rrbp parameter.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:38 +01:00
Jacob Erlbeck
8eb17143f2 tbf: Add and use tbf->poll_ts
Currently tbf->control_ts is used to look up an incoming poll
response. Since that may eventually change, poll timeouts could
theoretically happen in that case.

Store the real poll TS in tbf->poll_ts at all places where
tbf->poll_fn is set. Do not use tbf->control_ts to look up
outstanding polls.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:38 +01:00
Jacob Erlbeck
81a04f7d79 tbf: Mark control slots in VTY TBF out
Put an '!' after the PDCH number in the output of the 'show tbf'
command, if is_control_ts() yields true.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
646da1ba8d tbf: Use is_control_ts() instead of comparing TS values directly
Currently there are some places where tbf->control_ts != ts is
evaluated to check, whether ts is a control slot.

Replace these expressions by tbf->is_control_ts(ts) which does
the same whitout exposing internal fields.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
5a3c84d0fd sched: Pass the current TS to the control create functions
Currently the checks in that function are based on the different
internal TS values of a TBF. It is assumed that they match the TS
that the current RTS refers to.

This commit adds a TS parameter to create_ul_ass, create_dl_ass,
and create_ul_ack to make this more explicit.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
5f93f855a7 tbf: Do not reuse old TBF after RACH requests
Currently existing TBF can be reused after an MS has sent a RACH
request. Since the MS can be or most probably is in packet idle mode
in that case, the TBF are no longer usable even if they share the
PDCHs and the control TS with the new one.

There are occasional freezes where the MS does no longer react to
messages sent on the PACCH.

This change aborts all pending TBFs if a new TBF is or has been
established via RACH.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
f04a5b33ec tbf: Add abort method for downlink TBF
Currently the is a release() function which takes care of
statistics and shutdown of properly finished TBFs, but which
cannot be used for aborted TBFs.

This commit add an abort() method which handles unacked RLC blocks
as lost and doesn't start a release timer. This method will be
invoked by tbf_free() for downlink TBF.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
0316dc6e48 tbf: Add counters for aborted TBF in state FLOW
Increment CTR_TBF_DL_ABORTED/CTR_TBF_UL_ABORTED if a TBF gets freed
that is still in state GPRS_RLCMAC_FLOW.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
29e3a2f0f3 Revert "tbf: Use the control TS for Immediate Assignments"
According to spec, the lowest numbered PDCH of the TBF shall be used
for the PACCH, only when concurrent TBF are active, the lowest common
PDCH shall be used. An immediate assigment is in general only sent,
if no TBF is active, so the latter case never applies.

This reverts commit fdd8a1ba2312be3f33576ee994c0b5da6272e815.
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
56d06f3e1e tbf: Use the control TS for Immediate Assignments
Currently the first TS is used, which can be different from the
control TS on downlink TBFs.

Use the control TS instead.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
2ca86afdec tbf: Refactor calls to write_immediate_assignment
Make use of variables, log them to LOGL_DEBUG, and reduce code
duplication.

This should help to narrow down the cause why snd_dl_ass are not
answered by the MS sometimes.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
741d25cb6f bssgp: Ignore downlink BSSGP RA Cap IE
That IE may contain inaccurate or completely bogus data.

This commit disables the parsing.

Note that this should be replaced by a config option.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:37 +01:00
Jacob Erlbeck
f4bb42459c tbf: Low prio for BSSPG values for GPRS/EGPRS MS class
Currently the values in the BSSGP RA Cap IE are eventually use
overwrite the 'good' values obtained from the MS earlier.

Use the 'good' values when the are present, which is assumed if
at least one of ms->ms_class() or ms->egprs_ms_class() is set.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
4a07a3be11 edge: Get EGPRS MS class from downlink BSSGP
In case there is no MS object present that matches a DL UNITDATA,
the EGPRS class is currently assumed to be 0, since the Radio
Access Capabilities IE is not fully decoded if present.

This commit uses the CSN.1 decoder to parse the IE and uses the
same functions like the uplink related code does to get the GPRS
and EGPRS multislot classes.

Remove the old parse_ra_cap_ms_class function.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
0df80be95e rlc: Add decode_gsm_ra_cap to decode Radio Access Caps
This uses the CSN.1 decoder to fully parse the radio access
capabilities as defined by TS 24.008, 10.5.5.12a.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
acf66fb456 Revert "bssgp: Add hand-coded extended RA Cap parser"
Just keep this for later reference, since a CSN.1 decoder based
implementation will be used.

This reverts commit b37ab36bb14d2d2c1363fbe521cd19984d0c3d4c.
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
a35122c496 bssgp: Add hand-coded extended RA Cap parser
Add an extended parse_ra_cap function based on the existing
parse_ra_cap_ms_class which also parses up to the EGPRS related
fields.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
9f6867033f tbf: Show window parameters in VTY
Add V(Q), V(R), V(A), V(S) to the output of the "show tbf" command.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
36df7740dd edge: Make window size configurable
Currently the window size is fixed to 64 even for EGPRS.

Support dynamic window sizes depending on the number of PDCH. The
WS can be set to b + f * N_PDCH. If the result is not valid according
to TS 44.060, Table 9.1.9.2.1, the value will be corrected to use the
next lower valid value (or 64).

The following VTY commands are added (config-pcu node):

  window-size <0-1024>          set base (b) value and leave f unchanged
  window-size <0-1024> <0-256>  set base (b) and factor (f)

Sponsored-by: On-Waves ehf
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
08c72fb4a9 tbf/vty: Fix the CS output and show the EGPRS MS class
Currently only the GPRS MS class and the DL CS (even for UL TBFs)
are shown.

Add the EGPRS MS class and use the TBF's real CS.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
8cba7e9b6d utils: Add pcu_bitcount and pcu_lsb
These functions are currently defined in src/gprs_rlcmac_ts_alloc.cpp
but will be needed elsewhere.

Turn them into template functions to support different types and move
them to pcu_utils.h.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
13965aed74 tbf: Add gprs_rlcmac_tbf::window() method
This method returns a gprs_rlc_window independently of the TBF's
direction.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:36 +01:00
Jacob Erlbeck
db88380b76 rlc: Add unified gprs_rlc_window parent class
Currently gprs_rlc_ul_window and gprs_rlc_dl_window are completely
separate classes, containing several identical members and methods.

This commit add a shared parent class containing WS and SNS handling.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:35 +01:00
Jacob Erlbeck
3b1c553773 edge: Work-around to use EGPRS if there was no DL RA Cap
If the downlink BSSGP didn't contain a RA Capabilities IE, both
MS class values are 0. The phone will send valid RA Caps later on.
Currently in that case, the downlink TBF is not established
if EGPRS is enabled.

Just force egprs_ms_class to 1 if EGPRS (only) is enabled.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:35 +01:00
Jacob Erlbeck
9b3d7e0159 edge: Disable GPRS/EGPRS mixed mode
Currently the plain 'egprs' command enables EGPRS but doesn't prevent
phones from being served in GPRS mode if they do not support EGPRS.
This involves complex frame allocation implementations in dynamic
mode, especially if 8PSK is being used. This is due to the inability
of non-EGPRS phone to decode 8PSK USF and ES/P altogether. Since
polling has a higher priority than USF, collisions will have to be
prevented by the PCU by never using an GPRS USF if it refers to a FN
that is already being used for polling.

This commit just disables mixed usage by ignoring GPRS-only request
if EGPRS is enabled.

The following VTY command (config-pcu node) is changed:

  egprs     ->   egprs only

Sponsored-by: On-Waves ehf
2016-02-08 00:45:35 +01:00
Jacob Erlbeck
f9940ca8d7 edge: Fix MCS range in VTY
Change the range from MCS 1-4 to MCS 1-9.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:35 +01:00
Jacob Erlbeck
53bc1861c4 edge: Fix initial coding scheme selection
Currently the coding scheme gets reset when GprsMs::set_mode()
is called even if the mode did not change.

Make sure, that the CS is only reset, if it is not compliant to the
new mode.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:35 +01:00
Jacob Erlbeck
b4beb545f7 edge: Call update_window even if FINAL_ACK_INDICATION is set
The bitvec RBB is always valid, even when FINAL_ACK_INDICATION is
set. This is done by the ack/nack decoder function which fake a
bitmap starting with V(A) up to V(S)-1 in that case (see
Decoding::handle_final_ack).

Call gprs_rlcmac_dl_tbf::update_window unconditionally and only use
is_final for logging and TBF state changes in
gprs_rlcmac_dl_tbf::rcvd_dl_ack.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:35 +01:00
Jacob Erlbeck
d7630f2256 edge: Use bitvec based window methods for EGPRS
Currently a faked 'old' RBB with 64 ACKs is being used.

Use the new bitvec based methods instead.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:35 +01:00
Jacob Erlbeck
419b034975 tbf: Use bitvec based window methods for GPRS
Currently the old fixed 64 bit RBB based implementation is used for
GPRS.

Use the new bitvec based methods instead.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:35 +01:00
Jacob Erlbeck
b41262fa0b edge: Use num_blocks in gprs_rlcmac_dl_tbf::analyse_errors
Currently a window size of 64 is being hard coded.

Use the length of the show_rbb string instead.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:35 +01:00
Jacob Erlbeck
eb08f86c02 edge: Add bitvec based DL window updating methods
The current methods are based on the GRPS specific RBB and SSN values
and formats which are not compatible with EGPRS.

Add a second set of similar methods with the same semantics but
which are based on a bitvec and the first BSN instead.

The following methods are affected:

- gprs_rlc_dl_window::update
- gprs_rlcmac_dl_tbf::rcvd_dl_ack
- gprs_rlcmac_dl_tbf::update_window

Sponsored-by: On-Waves ehf
2016-02-08 00:45:34 +01:00
Jacob Erlbeck
f2f24b0959 edge: Add a bitvec based Decoding::extract_rbb function
This shall replace the old one after the transition to bitvec based
RBBs.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:34 +01:00
Jacob Erlbeck
192bf33ffb decode: Add bitvec based GPRS DL ACK/NACK decoder
Currently the RBB is used and passed directly to the window handling
methods. For EGPRS a more abstract bitvec is derived from the messages
and will passed around instead.

Add a similar function for GPRS so that the same window handling can
be used for GPRS and EGPRS.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:34 +01:00
Jacob Erlbeck
2bef4dbd43 edge: Enable CRBB decoding
Currently CRBB bitmaps are ignored if they are present.

This commit enables the decoding.

Note that this requires osmo_t4_decode in libosmocore.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:34 +01:00
Jacob Erlbeck
3fdcb2b84e edge: Add experimental support for uplink CRBB
Currently only uncompressed bitmaps (URBB) are supported in
PACKET UPLINK ACK/NACK messages.

Extend decode_egprs_acknack_bits to decode compressed bitmaps (CRBB),
too.

Note that this code is only active, if the macro WITH_CRBB_DECODING
is defined.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:34 +01:00
Jacob Erlbeck
ae9c575d2c edge: Handle EGPRS PACKET DOWNLINK ACK NACK
Currently this message is ignored.

Support decoding and handling of this message. Use a bitvec for the
decoder that just represents a BSN sequence without any encoding
details (first bit -> first BSN). Return the corresponding BSN range
(snsmod(bsn_begin + bits_in_bitvec) = bsn_end), so snsmod(bsn_end-1)
is the last BSN if there is at least 1. If bsn_begin == bsn_end, no
BSNs has been added.

Note that this bitvec is not yet used for RBB handling. It just calls
the old rcvd_dl_ack with a faked (all bits are 1) RBB map.

Sponsored-by: On-Waves ehf
2016-02-08 00:45:34 +01:00
Jacob Erlbeck
c2141c6064 edge: Workaround to fix decoding of EGPRS_AckNack_w_len_t
The presence of the LENGTH field adds an additional offset which
breaks the related M_SERIALIZE in gsm_rlcmac.cpp. In that case,
the Desc in EGPRS_AckNack_t is being cast to EGPRS_AckNack_w_len_t
and then ((EGPRS_AckNack_w_len_t *)Desc)->Desc is filled with the
parsed data, which is a platform dependant number of bytes apart the
real Desc struct.

Remove LENGTH field from EGPRS_AckNack_w_len_t so that the Desc field
is the first field.

Note that this is not a real fix. The rlcmac wireshark dissector
still has the same declaration but doesn't seem to suffer from this
problem.

Sponsored-by: On-Waves ehf
2016-02-05 18:27:10 +01:00
Jacob Erlbeck
70955c765c edge: Provide and use CS -> CPS conversion
The MS' RLC receiver needs a valid CPS field to decode the block.

Add the function gprs_rlc_mcs_cps to generate the CPS value based on
coding scheme, puncturing, and padding.

Sponsored-by: On-Waves ehf
2016-02-05 18:27:10 +01:00
Jacob Erlbeck
a88d065606 edge: Support MCS data block encoding
Currently only GPRS data block encoding is supported.

This commit adds rlc_data_to_dl_append_egprs which does the EGPRS
specific extension and chunk handling. It extends
Encoding::rlc_data_to_dl_append to use that function for MCS coding
schemes.

Sponsored-by: On-Waves ehf
2016-02-05 18:24:50 +01:00
Jacob Erlbeck
5058bd6e9e edge: Select implementation by mode in rlc_data_to_dl_append
Currently the GPRS data block encoding is applied to every
coding scheme, even if an MCS is selected.

This commit renames the actual encoding function to
rlc_data_to_dl_append_gprs (not exported) and puts
selection code into Encoding::rlc_data_to_dl_append. This
requires an additional cs argument.

Sponsored-by: On-Waves ehf
2016-02-05 13:46:21 +01:00
Jacob Erlbeck
fec94d1c5c edge: Use rlc_data_to_dl_append in create_new_bsn
Currently TBF related tasks (status changes, counter updates,
LLC dummy command insertion) as well as
RLC data block generation are done within create_new_bsn.
The data block creation part has already been copied to
the stateless Encoding::rlc_data_to_dl_append function.

This commit changes create_new_bsn to use the encoder function and
just care about the TBF related stuff.

Since the rlc_data_to_dl_append function has been validated against
the test cases being described in annex B of TS 44.060, this
commit fixes an encoder bug which leads to broken LLC frames in
a special case (see example B.2, the main header's E bit was
erroneously set to 1 in that case). When this happens, the LLC frame
will get discarded after reassembly, so that TCP will have to
retransmit the lost packet.

Sponsored-by: On-Waves ehf
2016-02-05 13:46:12 +01:00
Jacob Erlbeck
14bb0947b4 edge: Add Encoding::rlc_data_to_dl_append
This function appends a single chunk to an RLC downlink data block.
The implementation is basically taken from the
gprs_rlcmac_dl_tbf::create_new_bsn method without the TBF related
functionality and any side effects.

Note that it still only supports GRPS.

Sponsored-by: On-Waves ehf
2016-02-05 13:26:34 +01:00
Jacob Erlbeck
3a3b6a7e86 edge: Use RLC data block encoding functions
This commit removes the use of struct rlc_dl_header from
gprs_rlcmac_dl_tbf::create_dl_acked_block and
gprs_rlcmac_dl_tbf::create_new_bsn. Instead of patching the
data area directly, the RLC block encoding functions are used.

Note that the data unit encoding is still hard-coded to GPRS in
create_new_bsn, so using MCS 1-4 (albeit being supported by the
encoder) will not work yet.

Sponsored-by: On-Waves ehf
2016-02-05 13:26:34 +01:00
Jacob Erlbeck
53efa3eeb6 tbf/test: Add missing function name printfs
Some test function don't have the "=== start/end ===" printfs.

Sponsored-by: On-Waves ehf
2016-02-05 13:26:34 +01:00
Jacob Erlbeck
314ec4db40 tbf: Remove obsolete TLLI functions
This commit removes gprs_rlcmac_tbf::extract_tlli and
Decoding::tlli_from_ul_data.

Sponsored-by: On-Waves ehf
2016-02-05 13:26:34 +01:00
Jacob Erlbeck
f0e403911d edge: Add encoder for downlink RLC data blocks
Currently the (GPRS) RLC block encoding is done by setting the
header fields directly in gprs_rlcmac_dl_tbf::create_new_bsn.
This is much more complex with EGPRS, since the data fields are
not byte aligned, the header formats depend on the header type,
and the mapping of bits to bytes is LSB first.

This commit adds Encoding::rlc_write_dl_data_header which writes
the header according to the given gprs_rlc_data_header structure.
Encoding::rlc_copy_from_aligned_buffer is also added to copy
byte sequences into the message.

Note that the actual encoding of data units is not yet present.

Sponsored-by: On-Waves ehf
2016-02-05 13:26:34 +01:00
Jacob Erlbeck
6e9f9c20e9 edge: Add init functions for gprs_rlc_data_info
Add the functions gprs_rlc_data_info_init_dl/ul which initialise a
gprs_rlc_data_info structure depending on the coding scheme. The
fields num_data_blocks, data_offs_bits, cs, and the data_blocks are
valid after this call. The other fields are set to 0.

The data blocks are initialised to the correct data_len, e == 1
(no extension header field), cv == 15 (not a final block). The other
data block fields are set to 0.

The gprs_rlc_data_block_info can also be initialised separately
by using the gprs_rlc_data_block_info_init function.

Sponsored-by: On-Waves ehf
2016-02-05 13:26:34 +01:00
Jacob Erlbeck
cc34a5b43f rlc: Add info fields for downlink
This commit extends gprs_rlc_data_info and gprs_rlc_data_block_info
by the fields required for downlink RLC data messages.

Sponsored-by: On-Waves ehf
2016-02-05 13:26:34 +01:00
Jacob Erlbeck
22f8087b98 edge: Add numDataHeaderBitsUL/DL and numDataBlockHeaderBits methods
These methods are added to GprsCodingScheme to avoid related
switch statements in the RLC block encoder for EGPRS.

Sponsored-by: On-Waves ehf
2016-02-05 13:26:33 +01:00
Jacob Erlbeck
fc1b3e6c90 edge: Fix RLC message size
Currently the RLC message length that is obtained from the DSP is
reduced by 1 if the last byte of the buffer includes spare bits.
While this worked well with GPRS, these bits are being used to
encode RLC blocks in EGPRS mode. Thus this last byte must not be
chopped off. The functionality of the code is not affected by this,
since the modified length value is not used.

This commit adds GprsCodingScheme::usedSizeDL/UL to return
the number of bytes needed to encode the message block. If there are
single bits at the end that are to be used (EGPRS), the functions
return the number of full bytes plus 1 (which is the buffer size
reported by the DSP and returned by sizeUL/sizeDL). The commit also
removes the len parameter from rcv_data_block_acknowledged.

Sponsored-by: On-Waves ehf
2016-02-05 13:26:33 +01:00
Jacob Erlbeck
f2ba4cbf51 edge: Rename gprs_rlc_ul_header_egprs and gprs_rlc_ul_data_block_info
These struct names are more specific than necessary. They are used
for GPRS (uplink) already. In downlink direction, only a few fields
will be added to the header struct. Add addition,
gprs_rlc_ul_header_egprs does not map directly to an encoded
header, like many other 'header' structs do.

Change the names to fit both modes and both directions:

  gprs_rlc_ul_header_egprs    -> gprs_rlc_data_info
  gprs_rlc_ul_data_block_info -> gprs_rlc_data_block_info

Sponsored-by: On-Waves ehf
2016-02-05 13:26:33 +01:00
Jacob Erlbeck
7e7a261de0 edge: Remove int casting operator from GprsCodingScheme
This convenience operator rather hide implementation bugs and is thus
removed.

Sponsored-by: On-Waves ehf
2016-02-05 13:25:43 +01:00
Jacob Erlbeck
a47aaa4e7a edge: Add work-around to get DL EGPRS from MS object
The EGPRS multi-slot class need to be parsed from the CSN.1 RA
capability (see gprs_bssgp_pcu_rx_dl_ud).

This commit adds a workaround to get the EGPRS MS class from the MS
object if that is present.

Sponsored-by: On-Waves ehf
2016-02-05 13:25:43 +01:00
Jacob Erlbeck
08f631c197 edge: Enable EGPRS in downlink TBFs
Currently GPRS is always used for downlink, which violates TS 44.060
(concurrent TBF must have the same mode).

Enable EGPRS mode for downlink if the EGPRS MS class is != 0 and
EGRPS has been enabled.

Note that EGPRS Ack/Nack handling is not yet implemented, so enabling
EGPRS will not work still. But we will now get EGPRS DL ACK/NACK
messages now from the MS.

Sponsored-by: On-Waves ehf
2016-02-05 13:20:45 +01:00
Jacob Erlbeck
369c2fb7b4 tbf: Remove bogus gprs_rlcmac_dl_tbf::enable_egprs
This method is already present in gprs_rlcmac_tbf and should
not be present in gprs_rlcmac_dl_tbf.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:14 +01:00
Jacob Erlbeck
166c9fc827 edge: Support EGPRS in write_packet_downlink_assignment
Add an use_egprs parameter to write_packet_downlink_assignment
and add the EGPRS related fields if it is set to true. The
window size is fixed at 64 blocks, link quality measurement
reports have been disabled, and the other optional fields are not
present.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:14 +01:00
Jacob Erlbeck
7b57997874 edge: Show current mode in VTY
Add the current mode to the output of the 'show ms imsi' and
'show ms tlli' commands.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:14 +01:00
Jacob Erlbeck
cb7289094a edge: Replace integer cs by GprsCodingScheme
Currently the TBF and MS object use a plain integer value
(current_cs) to manage the coding scheme. This makes it difficult to
support the MCS schemes. GprsCodingScheme supports a partial ordering
of these values (CS and MCS) and provides safe increment and
decrement methods.

Use the GprsCodingScheme type instead of integer for cs fields and
variables. Add a 'mode' to GprsMs which can be set to either GPRS,
EGPRS, or EGPRS_GMSK which also set the initial values of
current_cs_ul/dl. Select the mode based on max_mcs_ul and max_mcs_dl.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:14 +01:00
Jacob Erlbeck
96ccea8436 edge: Add initial_mcs_dl and initial_mcs_ul config values
Provide the initial MCS values for EGPRS data blocks. They are set
to 1 (MCS-1) for each direction and there is no support to change
these configuration values yet (the automatic adaption still works,
so there is probably no need to set these values).

Sponsored-by: On-Waves ehf
2016-02-01 13:58:14 +01:00
Jacob Erlbeck
4c9e549aa3 edge: Add methods and operators to GprsCodingScheme
Add a few new operators and methods to support the use of
GprsCodingScheme instead of the plain integer currently used.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:14 +01:00
Jacob Erlbeck
0d05805b76 edge: Add max_mcs_ul and max_mcs_dl config
This sets the maximum MCS encoding used for EGPRS RLC data blocks
in either direction.

The following VTY command are added to node config-pcu:

 - mcs max <1-9>         set maximum for both, uplink and downlink
 - mcs max <1-9> <1-9>   set maximum for downlink and uplink (in that
                         order)
 - no mcs max            do not limit

Note that using a value of 4 or below for each direction implies
that a GMSK-only TBF may be assumed, which for instance would allow
the use of the GPRS MS class instead of the possibly more restrictive
EGPRS MS class.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
ed2dbf6954 tbf: Use LListHead instead of llist_pods
LListHead does basically the same like llist_pods, but more C++ish
and with type safety.

This commit turns the former list field of gprs_rlcmac_tbf into a
private field, provides accessors, moves the related code from
pcu_vty.c to pcu_vty_functions.cpp, and removes the llist_pods
type and related code.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
bf49f042d4 tbf/vty: Move tbf_print_vty_info to pcu_vty_functions.cpp
This function is similar to the show_ms function already present in
the target file. Since the TBF lists will be turned into LListHead
based lists, they will get an iteration function in
pcu_vty_functions.cpp, too.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
aa9daa1b9d tbf: Replace static casts by calls to as_ul_tbf/as_dl_tbf
Currently casts from gprs_rlcmac_tbf to gprs_rlcmac_ul_tbf and
gprs_rlcmac_dl_tbf are done by using static_cast. This doesn't provide
protection against converting a gprs_rlcmac_ul_tbf pointer to a
gprs_rlcmac_dl_tbf pointer and vice versa.

This commit provides two functions as_ul_tbf and as_dl_tbf, that
behave similar like dynamic_cast but use the direction field instead
of RTTI.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
38f18698b3 edge/test: Rename test_rlc_decoder to test_rlc_unit_decoder
This test only covers RLC data units and not whole RLC messages.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
5ffbb2744f encoding: Remove RlcMacDownlink_t based write_packet_uplink_ack
This is the CSN1-encoder based variant, which has been replaced and
is no longer being used.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
a24e1cd508 tbf: Use bitvec based write_packet_uplink_ack
Use the new bitvec based encoder for PACKET UPLINK ACK/NACK messages
and disable the old CSN.1 encoder based one.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
37005a165d encoding: Add bitvec based write_packet_uplink_ack
The current write_packet_uplink_ack implementation is based on the
CSN.1 encoder which makes it difficult to do the bitmap encoding for
EGPRS.

Add a new implementation based on bitvec functions to create the
PACKET UPLINK ACK/NACK messages.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
580edbc326 sched: Assert that the generated message is not empty
Currently the msg data is accessed and index 0 assuming the msg is
not empty. While this should be always the case, the msg can be
empty if there are software errors in the message creation functions.

This commit adds an assertion to catch this kind of bugs.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
8e323b39f9 edge: Set the EGPRS window parameters
Currently the GPRS parameters are used, which is ok for the WS but
not for the SNS.

This commit uses RLC_EGPRS_SNS and RLC_EGPRS_MIN_WS for the window
configuration.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
869449cdd0 edge: Move EGPRS setup from setup_tbf to tbf_alloc_ul_tbf
Currently the EGPRS mode is enabled in setup_tbf depending on the
values of egprs_ms_class and bts->egprs_enabled (both must be != 0).
This makes it difficult to set different values (like window
parameters) depending on the direction.

This commit moved the initialisation part to tbf_alloc_ul_tbf und
just leaves the setting of the ms_class to setup_tbf.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:13 +01:00
Jacob Erlbeck
8f8197f3fd rlc: Make WS and SNS variable
Currently the values for WS and SNS are fixed to 64 (WS) and 128
(SNS) which are the only values that can be used with GPRS. On the
other hand, EGPRS requires an SNS of 2014 and a WS in the range of
64 to 1024.

This commit adds member variables and setters to both window
classes. By default, the GPRS values are being used.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:12 +01:00
Jacob Erlbeck
a3a567e765 rlc: Add constructor to window classes
Currently the gprs_rlc_dl_window and gprs_rlc_ul_window do not have
constructors, but need to get initialized explicitly.

This commit adds constructors to both classes and removes explicit
external initialization code.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:12 +01:00
Jacob Erlbeck
e1ca87f057 rlc/edge: Consistently use uint16_t for BSNs and SSNs
Currently in some places uint8_t is being used to encode BSNs and
SSNs. This is inconsistent and (even worse) not enough for EPGRS
which uses an SNS of 2014.

This commit changes these to uint16_t.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:12 +01:00
Jacob Erlbeck
93c55d04e5 rlc: Add and use mod_sns(bsn) method
Currently there is only a mod_sns() method which is being used in
expression like bsn_expr & win.mod_sns(). This only works, because
it is known that mod_sns() is (sns() - 1) where sns() in turn is
always 2^n. This is error prone, hard to read, and relies on window
specifics that should be kept inside the respective module.

This commit adds a mod_sns(uint bsn) method to gprs_rlc_ul_window and
gprs_rlc_dl_window, that encapsulates and hides this optimized
computation.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:12 +01:00
Jacob Erlbeck
2b3121eebf edge: Support EGPRS uplink Ack/Nack messages
This commit adds EGPRS UL Ack/Nack encoding based on the CSN1 code.

Note that the bitmap encoding is still broken and not easy to fix
with the CSN1 framework, especially w.r.t. the length field.
Therefore this implementation will be abandoned and replaced by a
bitvec based one.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:12 +01:00
Jacob Erlbeck
2e3a81e4b3 rlc: Use a pointer instead of repeated selector chains
Currently the same selector chain is used several times in the same
function.

This commit adds a pointer to Packet_Uplink_Ack_Nack and uses that
instead.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:12 +01:00
Jacob Erlbeck
5c75480e90 edge: Move the GPRS UL Ack/Nack encoding into a separate function
Currently the Encoding::write_packet_uplink_ack function only
supports GPRS.

Split this function into a generic and a GPRS specific part.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:12 +01:00
Jacob Erlbeck
d88bb2e825 rlc: Dump RLC data for debugging
Log incoming RLC data messages and RLC data units to LOGL_DEBUG.

Sponsored-by: On-Waves ehf
2016-02-01 13:58:12 +01:00
Jacob Erlbeck
c362df25a2 pcu: Fix memory corruption bugs (ASAN)
ASAN has found improper deletion of objects. These only occur
on shutdown but makes it impossible to run the test cases with
full ASAN support.

This commit fixes some of them and deactivates the freeing of the_pcu.bctx
which may cause a corruption in BTS::~BTS() later on.

Note that the latter is only a work-aound and should be fixed
properly. It will leak bctx objects, but this is currently not
critical, since gprs_bssgp_destroy is only called once, immediately
before a call to exit().

Ticket: OW#1572
Sponsored-by: On-Waves ehf
2016-02-01 13:56:32 +01:00
Jacob Erlbeck
27dc941475 edge: Remove leftover comments from encoding.c
Sponsored-by: On-Waves ehf
2016-01-08 11:14:15 +01:00
Jacob Erlbeck
ead5e4724c edge: Fix data block decoder (Coverity)
Use a signed integer instead of an unsigned one for num_chunks
which can set to a negative value on error.

Ensure that chunks is not dereferenced if it is NULL. In fact
that will not happen currently, since num_chunks is now always
<= 0 if chunks == NULL.

Fixes: Coverity CID 1347433, 1347434, 1347435

Sponsored-by: On-Waves ehf
2016-01-08 10:14:50 +01:00
Jacob Erlbeck
6e75bc7fe3 sched: Change next_ctrl_prio increment
Currently the control block scheduler does not seem to be fair in all
cases. At least it happend while testing the EGPRS code, that a
Uplink Ack/Nack message got never be sent, because a Downlink
assignment took over all the times.

This commit changes the round robin code to always increment the
priority offset by 1 instead of (i + 1). The former definitely
ensures that every message type gets the highest priority after some
steps. The latter might be more fair in some situations, but that and
its correctness are not proven.

Sponsored-by: On-Waves ehf
2015-12-16 19:57:13 +01:00
Jacob Erlbeck
845c01ef3f edge: Remove unused GPRS functions
This commit removes the code that is no longer used due to the commit
"Use a single PDCH rcv_data_block method for GPRS and EGPRS".

Sponsored-by: On-Waves ehf
2015-12-16 19:57:13 +01:00
Jacob Erlbeck
554a835e90 edge: Use a single PDCH rcv_data_block method for GPRS and EGPRS
Currently GPRS is handled by the old code path while EGPRS already
uses the unified functions. The rcv_block_egprs is basically not
specific to EGPRS and just needs minor modifications to handle GPRS.

This commit turns gprs_rlcmac_pdch::rcv_block_egprs into a unified
rcv_data_block method and uses it for GPRS, too.

Note that the logging messages of the new parser are different.

Sponsored-by: On-Waves ehf
2015-12-16 19:57:09 +01:00
Jacob Erlbeck
39c6c7f738 edge: Implement gprs_rlcmac_pdch::rcv_block_egprs
This commit replaces the stub by a method that decodes the block
first, and passes it to the TBF object associated with the TFI.

Sponsored-by: On-Waves ehf
2015-12-16 19:55:52 +01:00
Jacob Erlbeck
b3100e187b edge: Add methods for unified GPRS/EGPRS UL data block handling
The current rcv_data_block_acknowledged_gprs method is tightly
coupled to GPRS.

This commit adds variants of the involved methods that support
EGPRS and GPRS RLC encodings likewise.

Sponsored-by: On-Waves ehf
2015-12-16 19:55:18 +01:00
Jacob Erlbeck
e8f5fe5255 tbf: Refactor parts of extract_tlli into set_tlli_from_ul
Currently gprs_rlcmac_tbf::extract_tlli takes care of decoding and
the TBF update. These are really different things and doing the
decoding in extract_tlli makes EGPRS support more complex.

This commit moves the TBF state related part into a new method
gprs_rlcmac_tbf::set_tlli_from_ul.

Sponsored-by: On-Waves ehf
2015-12-16 19:37:49 +01:00
Jacob Erlbeck
ce1beb423c edge: Store GprsCodingScheme in gprs_rlc_data
Currently the coding scene is stored as number N, where there scheme
is CS-N.

This commit replaces this by a GprsCodingScheme type cs value. The
gprs_rlcmac_cs table is no longer needed and thus removed.

Sponsored-by: On-Waves ehf
2015-12-16 19:37:08 +01:00
Jacob Erlbeck
784a0bd000 edge: Add is_received and invalidate_bsn to gprs_rlc_ul_window
These methods will be needed for EGPRS decoding.

The is_received method returns true iff a block with the given BSN
has already been received in the current window. A call to
invalidate_bsn marks the block as not received.

Sponsored-by: On-Waves ehf
2015-12-16 19:37:08 +01:00
Jacob Erlbeck
d87e1d6ab7 rlc: Do not raise_v_q in receive_bsn
Currently gprs_rlc_ul_window::receive_bsn calls raise_v_q and returns
the number of RLC data blocks that can be taken from the queue. This
does not fit the EGPRS feature to put 2 independant data blocks in a
single RLC block.

This commit removes raise_v_q from receive_bsn, hence it must be
called explicitely to get the number of processable data blocks.

Sponsored-by: On-Waves ehf
2015-12-16 19:37:08 +01:00
Jacob Erlbeck
6167925147 edge: Add test cases for rlc_data_from_ul_data
This checks the example test cases given in appendix B of
TS 44.060.

Sponsored-by: On-Waves ehf
2015-12-16 19:36:10 +01:00
Jacob Erlbeck
4abc686d76 edge: Add unified decoder methods for GPRS/EGPRS
This commit adds new RLC block decoder functions that support both
GPRS and EGPRS. The code path is selected based on the value of the
GprsCodingScheme cs object.

- rlc_parse_ul_data_header
        parses the header of an RLC data block including the E and FBI/TI
        flags (currently supported CS-1 - CS-4, MCS-1 - MCS-4).

- rlc_copy_to_aligned_buffer
        copies an RLC data unit to a byte aligned buffer and returns
        the unit's length.

- rlc_get_data_aligned
        is a convenience wrapper around rlc_copy_to_aligned_buffer
        that avoids copying if the data unit is already byte aligned.

Sponsored-by: On-Waves ehf
2015-12-16 19:36:09 +01:00
Jacob Erlbeck
392a545336 edge: Add information about data blocks to GprsCodingScheme
This commit adds the methods maxDataBlockBytes and numDataBlocks
which provide information about the data areas within RLC messages.
In these areas, the extension bytes, TLLI, and the LLC data are
stored.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:43 +01:00
Jacob Erlbeck
4aa78a8bea rlc: Check endianness for bit field declarations
Currently the declarations of rlc_ul_header, rlc_dl_header, and
rlc_li_field silently assume that a gcc for a little endian platform
is being used.

This commit adds '#if OSMO_IS_LITTLE_ENDIAN' the ensure the correct
byte ordering.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:43 +01:00
Jacob Erlbeck
6c3dc61db5 edge: Add header type property to GprsCodingScheme
The header type depends on the coding scheme, for GPRS there is a
single data header type per direction, for EGPRS there are 3 per
direction. In addition, control block header types are used with CS-1
only, so there is one of the per direction altogether for GRPS and
EGPRS.

This commit adds the header type enum and two methods headerTypeData
and headerTypeControl.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:43 +01:00
Jacob Erlbeck
3b802e3c4a edge: Rename rcv_data_block_acknowledged
This commit renames rcv_data_block_acknowledged to
rcv_data_block_acknowledged_gprs to separate it from EGPRS data block
decoding.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:43 +01:00
Jacob Erlbeck
690a734ebf edge: Add gprs_rlcmac_pdch::rcv_block_egprs stub
This stub function gets called when an EGPRS data package arrives.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:42 +01:00
Jacob Erlbeck
9e862e1e7f edge: Use GprsCodingScheme to adjust the UL RLC block size
Currently the block size is mapped by a switch statement to strip
extra bits that are not used for RLC blocks. That information is
already available via the GprsCodingScheme class.

This commit moves the CS/MCS detection to the rcv_block message and
passes the cs object via rcv_block_gprs, where the length gets
adjusted, to gprs_rlcmac_pdch::rcv_data_block_acknowledged. There the
switch statement is removed.

Note that the TbfTest.err changes due to an additional log message.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:42 +01:00
Jacob Erlbeck
d0222cfe2d edge: Add test for GprsCodingScheme
This test checks constructors, predicates, and operators of the
GprsCodingScheme class.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:42 +01:00
Jacob Erlbeck
409f980a18 edge: Add GprsCodingScheme class
Currently the coding scheme is checked and compared at different
places which makes in cumbersome to extend it for EGPRS.

This class encapsules the coding scheme and provides required meta
information like sizes as well as helper methods.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:42 +01:00
Jacob Erlbeck
14e00f8f66 edge: Extend gprs_rlcmac_dl_tbf::handle by egprs_ms_class
The multislot (MS) class and the EGPRS MS class can also be passed
via BSSGP in an MS Radio Access Capability element which can
optionally be contained in a DL-UNITDATA PDU. While this case is fully
supported for GPRS, the EGPRS MS class in BSSGP messages is ignored.

This commit extends gprs_rlcmac_dl_tbf::handle to pass the EGPRS MS
class, too.

Note, that the EGPRS class is not yet taken from the CSN.1 RA
capability and is always set to 0. Note also, that append_data
still uses ms_class only.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:42 +01:00
Jacob Erlbeck
5265f59525 edge: Enable EGPRS if configured and egprs_ms_class present
Enable the TBF to use EGPRS if the bts->egprs_enabled config variable
has been set via the VTY "egprs" command and if the MS has signaled a
EGPRS multislot class.

Tell the MS to use EGPRS if the condition above holds.

Note that this will cause the MS to use EGPRS RLC block formats for
further messages which are not yet understood by the PCU.

Sponsored-by: On-Waves ehf
2015-12-15 15:19:42 +01:00
Jacob Erlbeck
86b6f05d19 edge: Support EGPRS multislot class handling in tbf_alloc
Add an egprs_ms_class argument to the allocation functions and
set/pass it where necessary.

Sponsored-by: On-Waves ehf
2015-12-15 15:17:51 +01:00
Jacob Erlbeck
5643f35fb4 edge: Add m_egprs_enabled and related methods to TBF
Add the following methods to gprs_rlcmac_tbf:
  - is_egprs_enabled
  - enable_egprs
  - disable_egprs

Also show the value of the flag in name() by displaying "EGPRS" if
it is set.

Sponsored-by: On-Waves ehf
2015-12-15 15:17:51 +01:00
Jacob Erlbeck
76d767cbe8 edge: Support EGPRS in packet uplink assignment message
Currently the Encoding::write_packet_uplink_assignment method only
supports the GPRS variant of the message.

This commit adds the missing EGPRS variant to the encoder.

Sponsored-by: On-Waves ehf
2015-11-30 12:20:36 +01:00
Jacob Erlbeck
953c78987a edge: Add egprs config command
Add a global config flag to enable the use EDGE/EGPRS.

The following VTY commands are added to node config-pcu:

 - egprs              Enables EGPRS
 - no egprs           Disable EGPRS

Note that enabling EGPRS is experimental and will most likely break
packet transmission until a minimal required set of EGPRS
functionality is implemented.

Sponsored-by: On-Waves ehf
2015-11-30 12:20:36 +01:00
Jacob Erlbeck
c3c58046c7 edge: Get EGPRS multislot class
The EGPRS MS class ist contained in the MS_RA_capability information.
Its presence indicates, that the MS is able (and willing) to use
EGPRS.

This commit implements basic support for retrieving, storing, and
showing it in the VTY. The information is stored in the MS object.

Sponsored-by: On-Waves ehf
2015-11-30 12:20:36 +01:00
Jacob Erlbeck
111ebe84c2 Revert "pcu: Improve default config"
This reverts commit acfb883011.

The values are now the default values of the application, so they
do not need to be set in this file.

Sponsored-by: On-Waves ehf
2015-11-30 12:11:48 +01:00
Jacob Erlbeck
eb93f592e5 pcu: Enable dl-tbf-idle-time and idle-ack-delay by default
Currently these are enabled in the default config file. Since CoDel
is enabled by default in main() but should not be used without at
least dl-tbf-idle-time, the current default config may lead to
packet loss and performance problems.

This commit enables both features to provide a good (GPRS) performance
experience even without a configuration.

Sponsored-by: On-Waves ehf
2015-11-30 12:11:39 +01:00
Jacob Erlbeck
f5898a0528 stat: Add global stat group
Add a global stat_item group for measurement values and a
corresponding macro to get and set the values.
Add a stat_item STAT_MS_PRESET to monitor the number of
MS objects in the storage.

Sponsored-by: On-Waves ehf
2015-11-30 12:11:29 +01:00
Jacob Erlbeck
edfd7e3d94 encoder: Whitespace fixes
Sponsored-by: On-Waves ehf
2015-11-27 15:09:10 +01:00
Jacob Erlbeck
acfb883011 pcu: Improve default config
Currently the optional features dl-tbf-idle-time and idle-ack-delay
are not enabled when using the default config. Without the former,
the packet loss is significantly increased since CoDel is enabled by
default, eventually throwing away packets from ongoing paging and TBF
establishment procedures.

This commit changes the default config for satisfactory results even
with a single PDCH.

Sponsored-by: On-Waves ehf
2015-11-27 15:09:10 +01:00
Jacob Erlbeck
42aba81c2f stats: Enable stats subsystem
Sponsored-by: On-Waves ehf
2015-11-17 15:42:01 +01:00
Harald Welte
08e5d604d3 remove obsolete OpenBTS PCU interface support
This OpenBTS socket interface was originally added to enable GPRS
capabilitie with a forked version of OpenBTS, at a time when the public
OpenBTS release didn't yet have any GPRS support.

Meanwhile, the later OpenBTS releases included their own version of
GPRS, without any external PCU/SGSN/GGSN, so this interface is no longer
needed.

This also means that the OsmoBTS socket interface is now the default at
compilation time.  There is no other interface.
2015-11-13 16:07:25 +01:00
Harald Welte
19d1e9270d osmobts_sock.cpp: Add missing space in log statement. 2015-11-12 01:08:19 +01:00
Harald Welte
218482769b print/log OpenBTS / OsmoBTS variant in PCU startup
Otherwise it can be very confusing, as Max had to figure out today...
2015-11-12 01:07:41 +01:00
Harald Welte
d32cbbb130 rename sysmo_sock.cpp to osmobts_sock.cpp
This also renames the --enable-sysmbts option to --enable-osmobts

This socket interface was nevery sysmoBTS specific, but it is a generic
socket interface to any OsmoBTS supported layer1/hardware.  So it was a
mis-nomer so far.
2015-11-12 01:01:35 +01:00
Holger Hans Peter Freyther
8df447dc77 stats: Include the header file for the new class identifier 2015-11-07 21:04:40 +01:00
Holger Hans Peter Freyther
b8a5426cf0 stats: Attempt to compile fix the new rate_ctr
We wanted to support gcc-4.2 and this didn't allow us to use
the C99 initializers inside C++ code. Attempt to initialize
the class_id correctly.
2015-11-07 21:01:23 +01:00
Jacob Erlbeck
2db0f08e08 bssgp: Use measured leak rate for flow control
The leak rate sent to the SGSN does not reflect the current CS level,
lost frames, and control message overhead. So the SGSN cannot do
proper queue control under non-optimal conditions.

This commit computes the leak rate for the last flow control interval
by computing the maximum theoretical leak rate and basically
substracting control blocks, nacked blocks, and reduced block sizes
due to CS downgrade. By using this approach, the value will by more
stable on low load, where the value will tend to be near the value
derived from the configuration. On full load the transmitted value is
completely derived from the measurements.

Note that the MS default values are no adapted to the adapted BVC
leak rate, since a single MS which has a lower link quality would
otherwise be reducing the rate of another MS with good radio
conditions, which would not make much sense if they did not share any
PDCH.

Sponsored-by: On-Waves ehf
2015-09-11 11:52:02 +02:00
Jacob Erlbeck
7c8d39a67b poll: Count failed procedures
When a timeout has occured several times, the procedures handled by
poll_timeout are aborted. This happens when the number of repetitions
exceed N3105. Currently only the timeouts themselves are counted.

This commits adds counters that are incremented if a procedure has
really failed.

New counter:
- rlc.ass.failed:   Count failing UL and DL assigments via PACCH
- rlc.ack.failed:   Count failing DL Ack/Nack requests

Sponsored-by: On-Waves ehf
2015-09-07 14:10:35 +02:00
Jacob Erlbeck
c8cbfc2c98 bts: Start a DL TBF if needed after establishment of an UL TBF
Currently an existing DL TBF can get lost in the process of
establishing an UL TBF via RACH. This can lead to stalled connections
until the network sends more LLC frames.

This commit adds a check for a non-empty LLC queue after the UL TBF
has been established to rcv_control_ack (GPRS_RLCMAC_UL_ASS_WAIT_ACK
path) to eventually establish a new DL TBF on the UL TBF's PACCH.

Sponsored-by: On-Waves ehf
2015-09-01 12:01:20 +02:00
Jacob Erlbeck
ae0a799f44 bts: Release DL TBF instead of killing in rcv_resource_request
Currently an existing DL TBF is freed immediately, when a resource
request is received. This makes sense since the MS might have dropped
it when switching to the PDCH signaled via the AGCH for the SBA. But
if the TBF still is assumed to exist on the MS side, there might be
TFI collisions if the old TBF object is not kept to block its TFI
for some time.

This commit changes rcv_resource_request to call release() instead of
tbf_free() on the DL TBF object (if it exists).

Sponsored-by: On-Waves ehf
2015-09-01 12:00:31 +02:00
Jacob Erlbeck
91ff7d1864 tbf: Refactor reuse_tbf into releasing and DL TBF establishment
Currently reuse_tbf (partly) resets the old DL TBF and uses its PACCH
to establish a new DL TBF. The method can not be used with UL TBFs.

This commit replaces the reuse_tbf method into a
gprs_rlcmac_dl_tbf:release method which triggers the TBF's timer
based deletion (so that the TFI is still reserved for some time) and
a gprs_rlcmac_tbf::establish_dl_tbf_on_pacch which can establish DL
TBFs on existing PACCHs of either DL or UL TBFs.

Sponsored-by: On-Waves ehf
2015-09-01 11:49:04 +02:00
Jacob Erlbeck
9659d59307 tbf: Keep the old MS object alive in extract_tlli
Currently when a second MS object has been created for an MS, because
the TLLI was not known yet, the will be detected in
gprs_rlcmac_tbf::extract_tlli and the two objects will be merged by
update_ms. But when the dl_tbf is moved from the old to the new
(second) MS object, the old MS object can get idle and be removed
before the object are merged. This can cause LLC frame loss when the
MS object is deleted immediately after getting idle (no timeout
configured).

This commit adds a guard to keep the MS object until extract_tlli has
been executed.

Sponsored-by: On-Waves ehf
2015-09-01 11:48:26 +02:00
Jacob Erlbeck
cf6ae9d12f Revert "tbf: Do not kill DL TBF on Packet Resource Request"
This reverts commit e91bd3babd.

That commit seems to cause hanging DL TBFs when there was a RACH
based UL TBF establishment while it that TBF is active. This could be
caused by the use of a different PDCH for the SBA.

Conflicts:
	tests/tbf/TbfTest.cpp
	tests/tbf/TbfTest.err
2015-09-01 11:48:25 +02:00
Jacob Erlbeck
af75ce8e15 l1: Use the FN of all data_ind/ra_ind DSP messages
Currently all of these messages are discarded if they are assumend to
be caused by noise. But even in these cases, the FN and TN values
which are added by the DSP are valid. So these can be used to update
the current_frame value.

The osmo-bts sets the fBFILevel of a physical channel to -200dB if it
is used for PDTCH or PACCH which is the case for all PDCH. This way
a data_ind or ra_ind message is already send at least once per block
period (4 frames) per PDCH. These messages are passed to either
handle_ph_data_ind or handle_ph_ra_ind even if they contain garbage
data.

The ra_ind messages are sometimes sent a few frames earlier than
data_ind messages using the same frame.

This commit adds calls to update the current_frame value based on all
of these messages before they are discarded. The FN taken from ra_ind
are passed with an increased max_delay (5) to compensate for early
ra_ind messages.

Sponsored-by: On-Waves ehf
2015-08-28 12:23:07 +02:00
Jacob Erlbeck
be4a08b58a poll: Count unexpected block FN values
Currently a log entry is written if FN_data_ind - FN_time_ind <= -13.

This commit adds a counter 'rlc.late-block' that is incremented in
these cases.

Sponsored-by: On-Waves ehf
2015-08-28 12:23:07 +02:00
Jacob Erlbeck
60f77033ad poll: Use the data_ind FN as time source for current frame
The FN of the data_ind taken from the DSP are monotonic, so latency
does not affect the detection of poll timeouts if these FN are used.
If the FN is larger than a poll_fn value, it can safely be assumed
that the poll response will not arrive later on.

Currently a max_delay of 60 frames is used, which has the drawback
that additional ~250ms will pass until a lost ACK is detected.

Using the data_ind's FN alone breaks the poll timeout detection if
there are no other MS sending data blocks.

This commit adds BTS::set_current_block_frame_number that is called
with the FN taken from data_ind messages. The max_delay is set to 0
which removes the additional delay, when this FN is used to detect
poll timeouts. So the average additional delay decreases with the
number of data_ind per time. The current_frame is updated unless it
seems to have been updated already (assumed if 0 < cur_fn - block_fn
< 500). Thus the time_ind has still priority to update the
current_frame value.

Sponsored-by: On-Waves ehf
2015-08-28 12:23:07 +02:00
Jacob Erlbeck
e77d49f2a2 poll: Set the max_delay to 60 frames
Currently the max_delay parameter is set to 13, since that is
slightly above maximum number of frames that a time_ind can preceed a
block's data_ind of the same frame. This assumes that these messages
are not reordered after thay have been obtained from the DSP. In the
current implementation, the GPRS data_ind can directly be taken from
the DSP by the PCU while the time_ind messages are provided via the
BTS. So the messages are queued differently in that case, resulting
in a additional delay of the data_ind with respect to the time_ind.
The propability for this raises with a increased CPU load of the PCU.

If this happens, a poll timeout is detected by mistake and the poll
is either retried or cleared.

This commit increases the tolerance to 60 frames, since
values for FN_data_ind - FN_time_ind of up to 50 frames have been
observed under heavy PCU load.

Sponsored-by: On-Waves ehf
2015-08-28 12:23:07 +02:00
Jacob Erlbeck
ac49d0943a poll: Add a max_delay parameter to PollController::expireTimedout
Currently the maximum additional delay is hard coded to 13. This
value depends on the source of the frame number value.

This commit adds the max_delay parameter to make it caller dependant.

Sponsored-by: On-Waves ehf
2015-08-28 12:23:07 +02:00
Jacob Erlbeck
16d29c7da4 tbf: Add logging for polling
This commit adds the relevant frame number to the "poll timeout"
logging message. In addition, logging is added to the places where
poll_fn gets set.

The goal is to track down the source for frequent "poll timeout"
messages.

Sponsored-by: On-Waves ehf
2015-08-28 12:23:07 +02:00
Jacob Erlbeck
b6b3c7eb27 tbf: Use explicit initialisations in constructor (Coverity)
Currently when allocating tbf_alloc_ul_tbf or tbf_alloc_dl_tbf
objects, the allocated memory area is pre-initialised by talloc_zero
before the C++ constructors are called. This is not recognised by
Coverity, since there is no talloc model file yet. Thus Coverity
complains about missing initialisers.

On the other hand, it is still planned to convert the TBF classes
into real C++ ones. So instead of silencing Coverity directly, this
is an opportunity to do it the C++ way.

This commit adds initialisers and initialisation code for all
members that relied on talloc_zero. The corresponding calls to
talloc_zero are replaced by calls to talloc to give ASAN/valgrind
a chance to detect future initialisation errors. Some initialisation
code is also moved from setup_tbf to the constructors, notably the
initialisation of the bts pointer.

Fixes: Coverity CID 1320604, 1320605, 1320606

Sponsored-by: On-Waves ehf
2015-08-28 12:07:14 +02:00
Jacob Erlbeck
1c95bd383e openbts: Remove unused declaration of fl1h in udp_read_cb
Fixes:
openbts_sock.cpp:94:22: warning: unused variable 'fl1h'

Sponsored-by: On-Waves ehf
2015-08-27 11:47:35 +02:00
Jacob Erlbeck
159d4de370 ms/vty: Show LLC queue octets and packets in both views
Currently the per IMSI/TLLI view only shows the number of packets and
the 'all' view does not show any LLC related information at all. A
constant LLC queue length is often an indication for a stalled TCP
connection where the RLC layer has stopped to send downlink data
messages.

This commit adds the number of packets to the 'all' view and the
number of octets to the IMSI/TLLI views.

Sponsored-by: On-Waves ehf
2015-08-27 10:33:10 +02:00
Jacob Erlbeck
c62216b4fc ms/vty: Show old TBFs
This commit extends the 'show ms imsi|tlli' command to show the old
TBFs, too.

Sponsored-by: On-Waves ehf
2015-08-24 12:23:50 +02:00
Jacob Erlbeck
6835cead8c ms: Store references to replaced TBFs in the MS object
Currently when calling GprsMs::attach_tbf and a TBF of the same
direction already exists, the old TBF gets detached from the MS
object.

Therefore that TBF object loses access to that MS object including
for instance TLLI and IMSI.

This leads to failing DL TBF reuses, since the downlink assigment
cannot be sent on the PACCH later on because that must be sent on the
old DL TBF which ms() is NULL and the new DL TBF cannot be retrieved.

This commit fixes this bug by changing the GprsMs implementation to
keep a list of replaced (old) TBFs. TBFs are only removed when they
are being detached explicitely (see tbf_free and set_ms).

Addresses:
tbf.cpp:741 We have a schedule for downlink assignment at uplink
TBF(TFI=1 TLLI=0xf35a680e DIR=UL STATE=RELEASING), but there is no
downlink TBF

Sponsored-by: On-Waves ehf
2015-08-24 12:23:50 +02:00
Jacob Erlbeck
6e013a136a bssgp: Only call bssgp_tx_llc_discarded if the bctx exists
While this does not happen in real use, and unset btcx can lead to
segfaults in test cases. The other code outside of gprs_bssgp_pcu.cpp
does not depend on bctx being non-NULL:

Sponsored-by: On-Waves ehf
2015-08-24 12:23:50 +02:00
Jacob Erlbeck
af387e2199 llist: Add missing const qualifier in llist cast method
The missing const qualifier prevents the llist_empty() C++ wrapper
function from being compiled successfully when it is used.

Sponsored-by: On-Waves ehf
2015-08-24 12:23:50 +02:00
Jacob Erlbeck
444bc82081 tbf: Use C++/talloc magic to support TBF constructors/destructors
The TBF object are currently created by using talloc_zero/talloc_free
directly from plain functions. Therefore C++ constructors and destructors
are not called. So the only initialisation that is done is setting
every member to 0. Non POD members do not have their constructors
called either, which makes it impossible to use the current LListHead
class for real members when the LListHead::m_back member has to be set.

This commit changes the TBF allocation functions to call the
corresponding C++ constructor after the call to talloc_zero and to
register the C++ destructor with the talloc context, so that is is
called before talloc_free actually frees the memory.

With this change, non-POD members and custom
constructors/desctructors can be used with gprs_rlcmac_tbf,
gprs_rlcmac_dl_tbf, and gprs_rlcmac_ul_tbf.

Note that this change is only a single step of the plan to turn the
TBF classes into real C++ classes.

Sponsored-by: On-Waves ehf
2015-08-24 12:23:50 +02:00
Jacob Erlbeck
23c4b3f158 tbf/test: Add test_tbf_dl_reuse
This tests the usage of an existing TBF that is no longer in FLOW
state to request a new DL TBF via the old TBF's PACCH.

The test triggers a bug that breaks the association between both TBF
objects, resulting in packet loss and transmission stalling.

Sponsored-by: On-Waves ehf
2015-08-24 12:23:50 +02:00
Jacob Erlbeck
af45473cd5 tbf/test: Do RLC based ack instead of just faking
Currently the assignment is completed by manipulating the state of
the TBF objects directly by setting the state fields to fixed values.
This way, the PCU's code that is responsible to update the state
accordingly is not tested.

This commit changes this to simulate RLC Control Acknowledgement
messages instead.

Sponsored-by: On-Waves ehf
2015-08-24 12:19:18 +02:00
Jacob Erlbeck
cef20ae67a tbf/test: Rename send_rlc_block to request_dl_rlc_block
This function basically request the generation of the next downlink
RLC block. Since this will no really send somthing to the PCU, the
current name can be misleading.

This commit just renames the function.

Sponsored-by: On-Waves ehf
2015-08-24 12:04:41 +02:00
Jacob Erlbeck
ee31090b2e tbf/test: Simplify RLC block number handling
The block number can always be deduced from the frame number. The
current test code handles the block number explicitely, which makes
the code more complex and has also led to block number errors cause
by not wrapping the numbers (valid block numbers range from 0 to 11).

This commit changes send_rlc_block to always compute the block number
based on the frame number. It also turns the block_nr into an
optionaly output-only parameter.

Sponsored-by: On-Waves ehf
2015-08-24 11:55:17 +02:00
Jacob Erlbeck
64921d217b tbf/test: Add send_rlc_block function with a TBF as parameter
The current implementation takes a lot of parameters (bts, trx_no,
...) that can also be taken from a TBF object.

This commit adds an alternative variant with just takes a TBF, the fn
(in/out), and the block number (in/out).

Sponsored-by: On-Waves ehf
2015-08-24 11:50:27 +02:00
Jacob Erlbeck
56f99d19c3 tbf/test: Move UL MAC block encoding into a separate function
This commits adds send_ul_mac_block() to encode and send a
RlcMacUplink_t to the PCU.

Sponsored-by: On-Waves ehf
2015-08-21 19:02:18 +02:00
Jacob Erlbeck
e0b21f41c2 tbf: Move pending LLC frames when merging MS objects
Currently the pending LLC packets are lost in some cases when MS
objects are merged, for instance after a RACH when there were 2 MS
object for the same MS (they get merged, when the TLLI is known for
both objects).

This patch modifies GprsMs::merge_old_ms to move all pending LLC
packets (if there are any) to the current MS object.

Sponsored-by: On-Waves ehf
2015-08-21 19:02:18 +02:00
Jacob Erlbeck
257b630216 llc: Add move_and_merge method to llc_queue
This methods takes all LLC frames from the old LLC queue and moves
them into the current. If both queues are ordered chronologically
(recv_time), the resulting queue is also ordered.

Sponsored-by: On-Waves ehf
2015-08-21 19:02:18 +02:00
Jacob Erlbeck
e91bd3babd tbf: Do not kill DL TBF on Packet Resource Request
Currently all active TBF of an MS are killed if a Packet Resource
Request is received from the MS. In general this happens after a RACH
request. This does not happen after a resource request that has been
included into a Downlink Ack/Nack.

Sometimes an UL TBF is requested by an MS via RACH while a DL TBF is
running for instance to send a TCP Ack. This can happen, if a former
request via PACCH did not work.

This commit removes the killing of the DL TBF from
gprs_rlcmac_pdch::rcv_resource_request().

Sponsored-by: On-Waves ehf
2015-08-21 19:02:18 +02:00
Jacob Erlbeck
b139598b1c tbf/test: Add tests for RACH while DL TBFs are active
This adds tests for
- RA update with RACH for the RAUpdateComplete message
- RACH for UL while DL is active (LLC queue not empty)

Sponsored-by: On-Waves ehf
2015-08-21 19:02:18 +02:00
Jacob Erlbeck
076f5c794d tbf/test: Fix existing tests
This commit fixes several issues:
- Set MS class in request
- Set IMSI in establish_ul_tbf_two_phase
- Fake assigment acknowledgement in establish_ul_tbf_two_phase
- Fix TFI bit offset to 1 (was 2)

Sponsored-by: On-Waves ehf
2015-08-21 19:02:18 +02:00
Jacob Erlbeck
537b149828 tbf: Fix typos in log messages concerning UL/DL
The TBF in create_dl_ass can be of any direction. The text in
rcv_resource_request uses DL instead of UL.

Sponsored-by: On-Waves ehf
2015-08-21 18:59:14 +02:00
Jacob Erlbeck
4a6fe534ce tbf/test: Move UL TBF establishment into separate functions
Currently the functions test_tbf_single_phase and test_tbf_two_phase
do the test logging, BTS intialisation, and the complete message
sequencing on their own. Therefore they cannot be used to test more
complex sequences like TBF reestablishment.

This commit moves the code that does the actual messaging into own
functions. The frame number handling is generalised which also fixes
a block number wrapping error on the way.

Sponsored-by: On-Waves ehf
2015-08-21 16:56:56 +02:00
Jacob Erlbeck
2b349b5d33 ms: Move MS information merging to GprsMS
Currently the merging of the meta information (MS class, IMSI) takes
place in gprs_rlcmac_tbf::merge_and_clear_ms(). This makes it
difficult to merge the internal state and does not directly relate to
TBFs anyway.

This commit moves this into a new method GprsMs::merge_old_ms.

Sponsored-by: On-Waves ehf
2015-08-18 11:55:03 +02:00
Jacob Erlbeck
ebebad1c92 ns: Reconnect NSVC after timeout
Currently the signal S_NS_ALIVE_EXP emitted by the NS layer if the
alive check has timed out too often is ignored. This prevents the PCU
from reconnecting to the SGSN if it has not been accessible for some
time.

This commit modifies nsvc_signal_cb to reset the NSCV if
S_NS_ALIVE_EXP is sent, so that the PCU continues to send NS RESET
message if that happened.

Sponsored-by: On-Waves ehf
2015-08-17 16:29:34 +02:00
Jacob Erlbeck
56af6d55ed ns: Add logging support
Currently there is not support for Network Service (NS) logging.

This commit adds the missing definitions and sets the default level
to INFO. Further configuration can now be done with the 'logging
level ns' VTY command.

Sponsored-by: On-Waves ehf
2015-08-17 16:24:11 +02:00
Jacob Erlbeck
f76fedeed5 vty: Change API to have node installation be done by int
This commit fixes the go_parent_cb API according to libosmocore's
commit of the same name.

Fixes:
pcu_vty.c:799:2: warning: initialization from incompatible pointer
type [enabled by default]
  .go_parent_cb = pcu_vty_go_parent,

Sponsored-by: On-Waves ehf
2015-08-17 16:23:27 +02:00
Jacob Erlbeck
fea17f8b8c ms: Do not retrieve MS with IMSI 000 from the storage
The IMSI '000' is used as default value for an incoming BSSGP
message's IMSI IE. This can lead to the retrieval of the wrong MS
object from the storage.

This commit changes the get_ms method to skip the IMSI search if such
an IMSI is passed as selector.

Note that changing the default value in the BSSGP code does not help
here.

Sponsored-by: On-Waves ehf
2015-08-17 16:23:27 +02:00
Jacob Erlbeck
af9a39d954 tbf: Use update_ms instead of confirm_tlli in handle()
The confirm_tlli method does not handle TLLI clashes in the MS
storage.

This commit changes gprs_rlcmac_dl_tbf::handle() to use update_ms
instead.

Sponsored-by: On-Waves ehf
2015-08-17 16:23:27 +02:00
Jacob Erlbeck
28c40b1757 tbf: Clean old MS objects if they have the same TLLI
Currently if an MS retries to access the PCU by using RACH and if
there is already an entry for that MS, a duplicated MS object
referring to the same TLLI is created. This is caused by blindly
setting the TLLI without querying the MS storage to avoid
inconsitencies.

This leads to several entries in the MS storage that are assigned to
the same TLLI. If that happens, 'show ms all' can display multiple
entries with the same TLLI (note that an MS object can belong to
several TLLIs, so there might be an intersection that is not visible
in the list) or 'show tbf all' can show entries with MS_CLASS == 0 in
some cases.

This commit changes update_ms() to merge and clean up old entries
that belong to the given TLLI if they exist. Some data (like the MS
class) is copied to the new MS object.

Note that TBF belonging to the old MS object are deleted immediately
if they have not registered a timer.

Sponsored-by: On-Waves ehf
2015-08-17 16:23:01 +02:00
Jacob Erlbeck
3449a61032 pcu: Update example config file
This commits sets the initial CS to 2 to allow a successful
connection setup if the radio link has a low quality. The slot
allocation algorithm is changed to 'dynamic', which is the binary's
current default anyway.

Sponsored-by: On-Waves ehf
2015-08-17 16:16:51 +02:00
Jacob Erlbeck
1c3b8998bc ms: Set default CoDel interval to 4s
The current default interval is 2s which seems to be too short when
the DL TBF has to be established. This may cause freezing or really
slow TCP connections.

This commit increases the default value to 4s. When the
dl-tbf-idle-time is set, DL TBF are established less frequent, so
smaller values (like 2s or below) can be used to improve the average
latency when the load is high.

Sponsored-by: On-Waves ehf
2015-08-14 16:35:34 +02:00
Jacob Erlbeck
ac28905082 tbf: Handle TLLI change on DL
When doing an RA Update the network can request to change the TLLI.
In this case, there can be 2 MS objects with different TLLI for a
single real MS. The first is associated with the old TLLI and the
IMSI, while the second is associated with the new TLLI and no IMSI if
it had been created for the uplink TBF. When the first message with
the new TLLI and the IMSI arrives from the network, the PCU is able
to detect this.

Currently this is not handled properly. The TBFs of the old MS object
are not cleaned up properly, keeping the old MS from being deleted.

This patch modifies gprs_rlcmac_dl_tbf::handle to check for this and
if neccessary to move an existing DL TBF and to clean up the old MS
object to ensure its deletion.

Sponsored-by: On-Waves ehf
2015-08-14 16:35:29 +02:00
Jacob Erlbeck
04e72d34f5 tbf: Always start T3193 when changing state to GPRS_RLCMAC_WAIT_RELEASE
Currently when receiving a PACKET DL ACK/NACK message with the Final
Ack Indicator bit set, the TBF's state is set to
GPRS_RLCMAC_WAIT_RELEASE but T3193 is only started when the LLC queue is
empty. Otherwise the reuse_tbf() method is called to establish a new
DL TBF. In that case, the timer is not started. This will leave the
current TBF without a timer so it is potentially not released later
on.

This is recognisable by sticky entries in the output of the
'show tbf all' command and possibly allocation failures if there are
too many of them.

This commit changes the code to always start T3193 to make sure, that
a timer is always active when the the state is set to
GPRS_RLCMAC_WAIT_RELEASE.

Note that TS 44.060, 9.3.2.6 requests to release the 'old' TBF
immediately in some cases, which is not implemented by this change.
This will lead to a longer reservation period of the TFI only, which
is safer than reassigning it too early.

Sponsored-by: On-Waves ehf
2015-08-13 19:51:50 +02:00
121 changed files with 236915 additions and 6971 deletions

27
.gitignore vendored
View File

@@ -5,6 +5,7 @@
Makefile.in
Makefile
.deps
src/cscope*
aclocal.m4
autom4te.cache
@@ -13,10 +14,11 @@ config.guess
config.sub
config.status
configure
compile
depcomp
install-sh
missing
libtool
*libtool
ltmain.sh
core
@@ -32,13 +34,22 @@ src/osmo-pcu-remote
.dirstamp
tests/atconfig
tests/package.m4
tests/alloc/AllocTest
tests/rlcmac/RLCMACTest
tests/tbf/TbfTest
tests/types/TypesTest
tests/ms/MsTest
tests/llist/LListTest
tests/codel/codel_test
tests/*/*Test
tests/*/*_test
tests/emu/pcu_emu
tests/testsuite
tests/testsuite.log
# 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/
osmo-pcu.pc

3
.gitreview Normal file
View File

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

View File

@@ -1,5 +1,9 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
SUBDIRS = src examples tests
EXTRA_DIST = osmoappdesc.py
SUBDIRS = include src examples tests
EXTRA_DIST = osmoappdesc.py README.md
@RELMAKE@
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = osmo-pcu.pc

20
README
View File

@@ -1,20 +0,0 @@
This is an implementation of Packet Control Unit (PCU) according to TS 04.60
The PCU is part of BSS, so it connects directly to SGSN.
== Current limitations ==
* No PFC support
* No fixed allocation support
* No extended dynamic allocation support
* No unacknowledged mode operation
* No PCCCH/PBCCH support
* Only single slot assignment on uplink direction
* No half-duplex class support (only semi-duplex)
* No handover support
* No measurement support
* No TA loop
* No power loop
* No CS loop
* No EGPRS

84
README.md Normal file
View File

@@ -0,0 +1,84 @@
osmo-pcu - Osmocom Packet Control Unit
======================================
This repository contains a C/C++-language implementation of a GPRS
Packet Control Unit, as specified by ETSI/3GPP. It is part of the
[Osmocom](https://osmocom.org/) Open Source Mobile Communications
project.
The Packet Control Unit is terminating the Layer 2 (RLC/MAC) of the GPRS
radio interface and adapting it to the Gb Interface (BSSGP+NS Protocol)
towards the SGSN.
The PCU interfaces with the physical layer of the radio interface.
OsmoPCU is typically used co-located with the BTS, specifically
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki).
For legacy BTSs that run proprietary sotware without an interface to
OsmoPCU, you may also co-locate it with the BSC, specifically
[OsmoBSC](https://osmocom.org/projects/openbsc/wiki/Osmo-bsc)
Homepage
--------
The official homepage of the project is
https://osmocom.org/projects/osmopcu/wiki/OsmoPCU
GIT Repository
--------------
You can clone from the official osmo-pcu.git repository using
git clone git://git.osmocom.org/osmo-pcu.git
There is a cgit interface at http://git.osmocom.org/osmo-pcu/
Documentation
-------------
We provide a
[user manual](http://ftp.osmocom.org/docs/latest/osmopcu-usermanual.pdf)
as well as a
[vty reference manual](http://ftp.osmocom.org/docs/latest/osmopcu-vty-reference.pdf)
Please note that a lot of the PCU configuration actually happens inside
the BSC, which passes this configuration via A-bis OML to the BTS, which
then in turn passes it via the PCU socket into OsmoPCU.
Mailing List
------------
Discussions related to osmo-pcu are happening on the
osmocom-net-gprs@lists.osmocom.org mailing list, please see
https://lists.osmocom.org/mailman/listinfo/osmocom-net-gprs for
subscription options and the list archive.
Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Contributing
------------
Our coding standards are described at
https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards
We us a gerrit based patch submission/review process for managing
contributions. Please see
https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
more details
The current patch queue for osmo-pcu can be seen at
https://gerrit.osmocom.org/#/q/project:osmo-pcu+status:open
Current limitations
-------------------
* No PFC support
* No fixed allocation support (was removed from 3GPP Rel >= 5 anyway)
* No extended dynamic allocation support
* No unacknowledged mode operation
* Only single slot assignment on uplink direction
* No half-duplex class support (only semi-duplex)
* No TA loop
* No power loop

View File

@@ -3,12 +3,19 @@ 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)
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
dnl include release helper
RELMAKE='-include osmo-release.mk'
AC_SUBST([RELMAKE])
dnl checks for programs
AC_PROG_MAKE_SET
AC_PROG_CC
@@ -16,32 +23,120 @@ 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
dnl Checks for typedefs, structures and compiler characteristics
dnl checks for libraries
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.9)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.5.1.4)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING([--enable-sanitize], [Compile with address sanitizer enabled], )],
[sanitize=$enableval], [sanitize="no"])
if test x"$sanitize" = x"yes"
then
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined"
fi
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
AC_ARG_ENABLE(sysmocom-bts,
AC_HELP_STRING([--enable-sysmocom-bts],
[enable code for sysmocom femto-bts [default=no]]),
[enable_sysmocom_bts="$enableval"],[enable_sysmocom_bts="no"])
AC_MSG_RESULT([$enable_sysmocom_bts])
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
AC_ARG_ENABLE(werror,
[AS_HELP_STRING(
[--enable-werror],
[Turn all compiler warnings into errors, with exceptions:
a) deprecation (allow upstream to mark deprecation without breaking builds);
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
]
)],
[werror=$enableval], [werror="no"])
if test x"$werror" = x"yes"
then
WERROR_FLAGS="-Werror"
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
CFLAGS="$CFLAGS $WERROR_FLAGS"
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
fi
AC_ARG_ENABLE(profile,
[AS_HELP_STRING([--enable-profile], [Compile with profiling support enabled], )],
[profile=$enableval], [profile="no"])
if test x"$profile" = x"yes"
then
CFLAGS="$CFLAGS -pg"
CPPFLAGS="$CPPFLAGS -pg"
fi
dnl checks for libraries
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.11.0)
AC_MSG_CHECKING([whether to enable direct DSP access for PDCH of sysmocom-bts])
AC_ARG_ENABLE(sysmocom-dsp,
AC_HELP_STRING([--enable-sysmocom-dsp],
[enable code for sysmocom DSP [default=no]]),
[enable_sysmocom_dsp="$enableval"],[enable_sysmocom_dsp="no"])
AC_MSG_RESULT([$enable_sysmocom_dsp])
AC_HELP_STRING([--enable-sysmocom-dsp],
[enable code for direct sysmocom DSP access[default=no]]),
[enable_sysmocom_dsp="$enableval"], [enable_sysmocom_dsp="unset"])
AC_ARG_WITH([sysmobts],
[AS_HELP_STRING([--with-sysmobts=INCLUDE_DIR],
[Location of the sysmobts API header files, implies --enable-sysmocom-dsp])],
[sysmobts_incdir="$withval"], [sysmobts_incdir=""])
if test "x$sysmobts_incdir" != "x"; then
# --with-sysmobts was passed, imply enable_sysmocom_dsp
if test "x$enable_sysmocom_dsp" = "xno"; then
AC_MSG_RESULT([error])
AC_MSG_ERROR([--with-sysmobts does not work with --disable-sysmocom-dsp])
fi
enable_sysmocom_dsp="yes"
# 'readlink' should make an absolute path, but must not return empty if the path does not exist,
# so we can still report on it below.
sysmobts_incdir="$(readlink -fm "$sysmobts_incdir")"
AC_SUBST([SYSMOBTS_INCDIR], $sysmobts_incdir)
AC_MSG_RESULT([yes, using -I$SYSMOBTS_INCDIR])
else
AC_SUBST([SYSMOBTS_INCDIR], "")
AC_MSG_RESULT([$enable_sysmocom_dsp])
fi
AM_CONDITIONAL(ENABLE_SYSMODSP, test "x$enable_sysmocom_dsp" = "xyes")
if test "x$enable_sysmocom_dsp" = "xyes"; then
oldCPPFLAGS="$CPPFLAGS"
_sysmobts_include=""
_sysmobts_include_msg=""
if test -n "$SYSMOBTS_INCDIR"; then
_sysmobts_include="-I$SYSMOBTS_INCDIR"
_sysmobts_include_msg=" in -I$SYSMOBTS_INCDIR"
fi
CPPFLAGS="$CPPFLAGS $_sysmobts_include -I$srcdir/include $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([sysmocom/femtobts/superfemto.h],[],
[AC_MSG_ERROR([sysmocom/femtobts/superfemto.h can not be found$_sysmobts_include_msg, see --with-sysmobts])],
[#include <sysmocom/femtobts/superfemto.h>])
CPPFLAGS="$oldCPPFLAGS"
fi
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_cflags="-I$withval"],[litecell15_cflags=""])
AC_SUBST([LITECELL15_CFLAGS], $litecell15_cflags)
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 $LITECELL15_CFLAGS -I$srcdir/include $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([nrw/litecell15/litecell15.h],[],
[AC_MSG_ERROR([nrw/litecell15/litecell15.h can not be found using $litecell15_cflags])],
[#include <nrw/litecell15/litecell15.h>])
CPPFLAGS="$oldCPPFLAGS"
fi
AC_ARG_ENABLE([vty_tests],
AC_HELP_STRING([--enable-vty-tests],
@@ -58,7 +153,15 @@ 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_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
AC_OUTPUT(
osmo-pcu.pc
include/Makefile
src/Makefile
examples/Makefile
tests/Makefile

84
contrib/jenkins.sh Executable file
View File

@@ -0,0 +1,84 @@
#!/bin/sh
# jenkins build helper script for osmo-pcu. This is how we build on jenkins.osmocom.org
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
exit 2
fi
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
osmo-clean-workspace.sh
mkdir "$deps" || true
# Collect configure options for osmo-pcu
PCU_CONFIG=""
if [ "$with_dsp" = sysmo ]; then
PCU_CONFIG="$PCU_CONFIG --enable-sysmocom-dsp --with-sysmobts=$inst/include/"
# For direct sysmo DSP access, provide the SysmoBTS Layer 1 API
cd "$deps"
osmo-layer1-headers.sh sysmo
mkdir -p "$inst/include/sysmocom/femtobts"
ln -s $deps/layer1-headers/include/* "$inst/include/sysmocom/femtobts/"
cd "$base"
elif [ "$with_dsp" = lc15 ]; then
PCU_CONFIG="$PCU_CONFIG --enable-lc15bts-phy --with-litecell15=$deps/layer1-headers/inc"
cd "$deps"
osmo-layer1-headers.sh lc15 "$FIRMWARE_VERSION"
cd "$base"
elif [ -z "$with_dsp" -o "$with_dsp" = none ]; then
echo "Direct DSP access disabled, sanitizer enabled"
PCU_CONFIG="$PCU_CONFIG --enable-sanitize"
else
echo 'Invalid $with_dsp value:' $with_dsp
exit 1
fi
if [ "$with_vty" = "True" ]; then
PCU_CONFIG="$PCU_CONFIG --enable-vty-tests"
elif [ -z "$with_vty" -o "$with_vty" = "False" ]; then
echo "VTY tests disabled"
else
echo 'Invalid $with_vty value:' $with_vty
exit 1
fi
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
# Build deps
osmo-build-dep.sh libosmocore "" --disable-doxygen
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
osmo-clean-workspace.sh

17
contrib/osmo-pcu.service Normal file
View File

@@ -0,0 +1,17 @@
[Unit]
Description=sysmocom sysmoPCU
[Service]
Type=simple
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
Restart=always
RestartSec=2
RestartPreventExitStatus=1
# The msg queues must be read fast enough
CPUSchedulingPolicy=rr
CPUSchedulingPriority=1
[Install]
WantedBy=multi-user.target
Alias=sysmopcu.service

View File

@@ -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
@@ -14,3 +14,4 @@ CPUSchedulingPriority=1
[Install]
WantedBy=multi-user.target
Alias=osmo-pcu.service

168
debian/changelog vendored Normal file
View File

@@ -0,0 +1,168 @@
osmo-pcu (0.5.0) unstable; urgency=medium
[ Neels Hofmeyr ]
* jenkins: use osmo-clean-workspace.sh before and after build
* vty: skip installing cmds now always installed by default
* implement support for 3-digit MNC with leading zeros
* configure: add --enable-werror
* mslot_class: find_free_tfi(): use uint32_t to shift 1 << 31
* mslot_class: two more: use uint32_t to shift 1 << 31
* Revert "Use Timing Advance Index in UL assignments"
* Revert "Rewrite Packet Uplink Assignment"
* Revert "Rewrite Packet Downlink Assignment"
* configure: fix --enable-sysmocom-dsp and --with-sysmobts flags
* configure: properly quote CFLAGS in lc15 check
* Revert "Rewrite EGPRS Packet Uplink Assignment"
* use osmo_init_logging2() with proper talloc ctx
[ Minh-Quang Nguyen ]
* PCU: Fix TA adjustment
* PCU: display TA information in TBF stats
[ Max ]
* Remove unused parameter
* Move multislot table to separate file
* Replace '.' in counter names with ':'
* Fix compiler warning
* TBF: log timer override
* TBF: fix compiler warning in test
* TBF: expand timer logging
* vty: print class and TBFs for each MS
* DL window: constify resend_needed() function
* TBF: move EGPRS enablement into (U|D)L-TBF
* TBF-DL: fix misleading idle time check
* TBF: remove unused variable
* Remove unused includes and forward declarations
* Fix tests after rate_ctr change
* Introduce LOGTBF* for consistent logging
* TBF: implement independent T31xx timers
* TBF: add N3101 counter
* Fix warnings
* Add function to get max supported MS class
* Add --enable-sanitize configure option
* Enable sanitize for CI test
* Add tests for pcu_lsb()
* Add optional profiling support
* TBF: unify timer handling
* TBF: log timer invocation source
* TBF: bail out for unknown timers
* Fix llc_queue_size() type
* TBF-DL: mark rcvd_dl_ack() parameters as boolean
* window: move encoding into functions
* cosmetic: clarify coding scheme and puncturing
* Make TBF state private
* TBF: cleanup state flag handling
* Clarify RACH-related interfaces
* TBF-UL: add simpler test helper
* Avoid code duplication in TBF test
* TBF: move window parameters to UL/DL level
* TBF-DL: move priority computation into function
* TBF: unify EGPRS window calculation
* Don't access TBF internals in vty functions
* Fix jenkins.sh to match jenkins job axis filter
* Allocate global context for TypesTest
* Fix sanitizer build
* Rewrite EGPRS Packet Uplink Assignment
* Rewrite Packet Downlink Assignment
* Rewrite Packet Uplink Assignment
* Use Timing Advance Index in UL assignments
* Allow specifying sysmocom headers explicitly
* TBF: log source of state transitions
* jenkins.sh: Disable building doxygen for deps
* Set V_N and V_B to known initial state
* TBF: add dedicated log categories
* TBF: make UL/DL state internal
* TBF: make UL ack state internal
* TBF: make poll state internal
* TBF: adjust test log levels
* Add tests for find_multi_slots()
* AllocTest: adjust test_alloc_b()
* AllocTest: expand test output
* AllocTest: remove assumption on max MS class
* Add multislot classes from latest spec
* cosmetic: fix whitespace issue with include files
* TBF: decrease L1 logging verbosity in test
* TBF: override send function via linker option
* Simplify TS alloc: adjust allocator signatures
* Simplify TS alloc: fix allocation calls
* Simplify TS alloc: avoid TS reassignment
* Simplify TS alloc: use defines for constants
* Simplify TS alloc: adjust function signatures
* TS alloc: print suggested TRX on allocation errors
* Simplify TS alloc: internalize TRX check
* TBF: decrease logging verbosity for traffic
* TBF: add helpers for assignment type handling
* TBF: show assignment kind in vty
* vty: drop unused function
* RACH: improve single block detection
* TBF: move common test code into functions
* emu: use libosmocore definitions
* Use explicit type for pcu_lsb()
* Move paging generation into PDCH
* Move include guard to the top
* Update header includes
* Simplify TS alloc: split off RX mask computation
* Simplify TS alloc: separate capacity computation
* Simplify TS alloc: split allocation
* Simplify TS alloc: split USF/UL allocation
* Move PDCH-related functions into separate files
* Simplify TS alloc: don't use PDCH for free TFI
* Simplify TS alloc: constify max dl slot func
* TBF: make network counters internal
* Simplify TS alloc: move slot assignment
* Simplify TS alloc: move slot check into functions
[ Pau Espin Pedrol ]
* Print error cause of pcu socket connect failure
* gprs_bssgp_pcu.cpp: Comment unused function parse_ra_cap
[ Stefan Sperling ]
* Make osmo-pcu wait for BTS to become available at start-up time.
* improve documentation of Encoding::write_paging_request()
[ Alexander Couzens ]
* pcuif_proto.h: fix whitespaces and indention
* pcuif_proto: add version 8 features
[ Philipp Maier ]
* cosmetic: remove runaway semicolon
* pcu_l1_if: add frame number to log output
* tbf: add frame number to log output
-- Pau Espin Pedrol <pespin@sysmocom.de> Thu, 03 May 2018 16:20:00 +0200
osmo-pcu (0.4.0) unstable; urgency=medium
[ Holger Hans Peter Freyther ]
* Initial release.
[ Max ]
* Use value string check from osmo-ci
* cosmetic: tighten direct-phy related code
* Support receiving SI13 from BTS
* Move gsmtap and accounting into separate function
* cosmetic: convert explicit warnings to fixme/todo
* Assert valid CS
* TBF-DL: extend index check for RLC block copy
* TS alloc: properly count UL slots
* cosmetic: reformat multislot classes table
[ Philipp Maier ]
* gb: allow only packets from a specific SGSN
[ Harald Welte ]
* tests: Don't use private version of log_info but global gprs_log_info
* Call osmo_init_logging() before static BTS constructor
* Forward GPRS SUSPEND REQ from BTS to SGSN using BSSGP
* Debian: Cosmetic changes to control file; add better Description
* Debian: print test results in case of failure + clean-up autotest
* Debian: migrate from DEB_BUILD_HARDENING to DEB_BUILD_MAINT_OPTIONS
* Debian: upgrade to debhelper 9 / Standards 3.9.8
-- Harald Welte <laforge@gnumonks.org> Sun, 29 Oct 2017 12:03:05 +0100
osmo-pcu (0.3.0) UNRELEASED; urgency=medium
* Initial release.
-- Holger Hans Peter Freyther <holger@moiji-mobile.com> Fri, 01 Apr 2016 18:59:00 +0200

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
9

37
debian/control vendored Normal file
View File

@@ -0,0 +1,37 @@
Source: osmo-pcu
Section: net
Priority: optional
Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
Build-Depends: debhelper (>= 9),
dh-autoreconf,
dh-systemd (>= 1.5),
autotools-dev,
pkg-config,
libosmocore-dev (>= 0.10.1)
Standards-Version: 3.9.8
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: Osmocom GPRS/EDGE Packet Control Unit (PCU)
The GPRS Packet Control Unit is co-located with the GSM BTS or GSM BSC
in order to provide packet-switched services for 2G (2.5G, 2.75G)
networks. OsmoPCU is the Osmcoom implementation of this network
element. It interfaces to osmo-bts via the PCU socket of OsmoBTS
and via Gb (NS-over-IP) interface with the SGSN such as OsmoSGSN.
Package: osmo-pcu-dbg
Architecture: any
Section: debug
Priority: extra
Depends: osmo-pcu (= ${binary:Version}),
${misc:Depends}
Description: Debug symbols for the Osmocom GPRS/EDGE Packet Control Unit (PCU)
The GPRS Packet Control Unit is co-located with the GSM BTS or GSM BSC
in order to provide packet-switched services for 2G (2.5G, 2.75G)
networks. OsmoPCU is the Osmcoom implementation of this network
element. It interfaces to osmo-bts via the PCU socket of OsmoBTS
and via Gb (NS-over-IP) interface with the SGSN such as OsmoSGSN.

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

@@ -0,0 +1,4 @@
etc/osmocom/osmo-pcu.cfg
usr/bin/osmo-pcu
usr/include/osmocom/pcu/pcuif_proto.h
usr/lib/*/pkgconfig/osmo-pcu.pc

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

@@ -0,0 +1,15 @@
[Unit]
Description=Osmocom osmo-pcu
[Service]
Type=simple
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg
Restart=always
RestartSec=2
# Read quickly enough
CPUSchedulingPolicy=rr
CPUSchedulingPriority=1
[Install]
WantedBy=multi-user.target

28
debian/rules vendored Executable file
View File

@@ -0,0 +1,28 @@
#!/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_MAINT_OPTIONS = hardening=+all
%:
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
override_dh_clean:
dh_clean
$(RM) tests/package.m4
$(RM) test/testsuite
# Print test results in case of a failure
override_dh_auto_test:
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)

1
debian/source/format vendored Normal file
View File

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

View File

@@ -1,6 +1,6 @@
pcu
flow-control-interval 10
cs 4
alloc-algorithm b
cs 2
alloc-algorithm dynamic
alpha 0
gamma 0

2
include/Makefile.am Normal file
View File

@@ -0,0 +1,2 @@
nobase_include_HEADERS = \
osmocom/pcu/pcuif_proto.h

View File

@@ -1,18 +1,25 @@
#ifndef _PCUIF_PROTO_H
#define _PCUIF_PROTO_H
#define PCU_IF_VERSION 0x05
#include <osmocom/gsm/l1sap.h>
#define PCU_SOCK_DEFAULT "/tmp/pcu_bts"
#define PCU_IF_VERSION 0x09
#define TXT_MAX_LEN 128
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
#define PCU_IF_MSG_DATA_CNF_DT 0x11 /* confirm (with direct tlli) */
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
#define PCU_IF_MSG_TXT_IND 0x70 /* Text indication for BTS */
/* sapi */
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
@@ -22,6 +29,7 @@
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */
#define PCU_IF_SAPI_AGCH_DT 0x08 /* assignment on AGCH but with additional TLLI */
/* flags */
#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */
@@ -40,6 +48,16 @@
#define PCU_IF_FLAG_MCS8 (1 << 27)
#define PCU_IF_FLAG_MCS9 (1 << 28)
enum gsm_pcu_if_text_type {
PCU_VERSION,
PCU_OML_ALERT,
};
struct gsm_pcu_if_txt_ind {
uint8_t type; /* gsm_pcu_if_text_type */
char text[TXT_MAX_LEN]; /* Text to be transmitted to BTS */
} __attribute__ ((packed));
struct gsm_pcu_if_data {
uint8_t sapi;
uint8_t len;
@@ -50,6 +68,24 @@ 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));
/* data confirmation with direct tlli (instead of raw mac block with tlli) */
struct gsm_pcu_if_data_cnf_dt {
uint8_t sapi;
uint32_t tlli;
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
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 +100,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 {
@@ -84,12 +122,14 @@ struct gsm_pcu_if_info_ind {
struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
uint8_t bsic;
/* RAI */
uint16_t mcc, mnc, lac, rac;
uint16_t mcc, mnc;
uint8_t mnc_3_digits;
uint16_t lac, rac;
/* NSE */
uint16_t nsei;
uint8_t nse_timer[7];
uint8_t cell_timer[11];
/* cell */
/* cell */
uint16_t cell_id;
uint16_t repeat_time;
uint8_t repeat_count;
@@ -140,9 +180,11 @@ struct gsm_pcu_if {
union {
struct gsm_pcu_if_data data_req;
struct gsm_pcu_if_data data_cnf;
struct gsm_pcu_if_data_cnf_dt data_cnf_dt;
struct gsm_pcu_if_data data_ind;
struct gsm_pcu_if_rts_req rts_req;
struct gsm_pcu_if_rach_ind rach_ind;
struct gsm_pcu_if_txt_ind txt_ind;
struct gsm_pcu_if_info_ind info_ind;
struct gsm_pcu_if_act_req act_req;
struct gsm_pcu_if_time_ind time_ind;

10
osmo-pcu.pc.in Normal file
View File

@@ -0,0 +1,10 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@/
Name: OsmoPCU
Description: Osmocom PCU implementation
Requires:
Version: @VERSION@
Cflags: -I${includedir}

View File

@@ -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
@@ -41,40 +46,31 @@ libgprs_la_SOURCES = \
gprs_ms.cpp \
gprs_ms_storage.cpp \
gsm_timer.cpp \
bitvector.cpp \
pcu_l1_if.cpp \
pcu_vty.c \
pcu_vty_functions.cpp \
mslot_class.c \
tbf.cpp \
tbf_ul.cpp \
tbf_dl.cpp \
bts.cpp \
pdch.cpp \
poll_controller.cpp \
encoding.cpp \
sba.cpp \
decoding.cpp \
llc.cpp \
rlc.cpp \
gprs_codel.c
if ENABLE_SYSMOBTS
libgprs_la_SOURCES += \
sysmo_sock.cpp
else
libgprs_la_SOURCES += \
openbts_sock.cpp
endif
osmobts_sock.cpp \
gprs_codel.c \
gprs_coding_scheme.cpp \
egprs_rlc_compression.cpp
bin_PROGRAMS = \
osmo-pcu
noinst_PROGRAMS =
if ENABLE_SYSMODSP
noinst_PROGRAMS += \
osmo-pcu-remote
endif
noinst_HEADERS = \
gprs_debug.h \
csn1.h \
@@ -83,16 +79,14 @@ 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 \
mslot_class.h \
tbf.h \
bts.h \
pdch.h \
poll_controller.h \
encoding.h \
sba.h \
@@ -101,19 +95,69 @@ noinst_HEADERS = \
llc.h \
pcu_utils.h \
cxx_linuxlist.h \
gprs_codel.h
gprs_codel.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 -I$(SYSMOBTS_INCDIR)
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 += $(LITECELL15_CFLAGS) -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 = \
@@ -123,13 +167,4 @@ osmo_pcu_LDADD = \
$(LIBOSMOGSM_LIBS) \
$(COMMON_LA)
if ENABLE_SYSMODSP
osmo_pcu_remote_LDADD = \
libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COMMON_LA)
endif
#MOSTLYCLEANFILES += testSource testDestination

View File

@@ -1,119 +0,0 @@
/* bitvector.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*! \addtogroup bitvector
* @{
*/
/*! \file bitvector.cpp
* \brief Additional functions for Osmocom bit vector abstraction.
*/
#include <bitvector.h>
extern "C" {
#include <osmocom/core/talloc.h>
}
void *bv_tall_ctx;
struct bitvec *bitvec_alloc(unsigned size)
{
struct bitvec *bv = talloc_zero(bv_tall_ctx, struct bitvec);
bv->data_len = size;
bv->cur_bit = 0;
bv->data = talloc_zero_array(bv_tall_ctx, uint8_t, size);
return bv;
}
void bitvec_free(struct bitvec *bv)
{
talloc_free(bv->data);
talloc_free(bv);
}
unsigned int bitvec_pack(struct bitvec *bv, uint8_t *buffer)
{
unsigned int i = 0;
for (i = 0; i < bv->data_len; i++)
{
buffer[i] = bv->data[i];
}
return i;
}
unsigned int bitvec_unpack(struct bitvec *bv, uint8_t *buffer)
{
unsigned int i = 0;
for (i = 0; i < bv->data_len; i++)
{
bv->data[i] = buffer[i];
}
return i;
}
int bitvec_unhex(struct bitvec *bv, const char* src)
{
unsigned val;
unsigned write_index = 0;
unsigned digits = bv->data_len*2;
for (unsigned i=0; i<digits; i++) {
if (sscanf(src+i, "%1x", &val) < 1) {
return 1;
}
bitvec_write_field(bv, write_index,val, 4);
}
return 0;
}
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len)
{
unsigned int i;
uint64_t ui = 0;
bv->cur_bit = read_index;
for (i = 0; i < len; i++) {
int bit = bitvec_get_bit_pos((const struct bitvec *)bv, bv->cur_bit);
if (bit < 0)
return bit;
if (bit)
ui |= ((uint64_t)1 << (len - i - 1));
bv->cur_bit++;
}
read_index += len;
return ui;
}
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;
}

View File

@@ -1,45 +0,0 @@
/* bitvector.h
*
* Copyright (C) 2012 Ivan Klyuchnikov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef BITVECTOR_H
#define BITVECTOR_H
/*! \defgroup bitvector Bit vectors
* @{
*/
/*! \file bitvector.h
* \brief Additional functions for Osmocom bit vector abstraction.
*/
extern "C" {
#include <osmocom/core/bitvec.h>
}
struct bitvec *bitvec_alloc(unsigned size);
void bitvec_free(struct bitvec *bv);
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);
/*! }@ */
#endif // BITVECTOR_H

File diff suppressed because it is too large Load Diff

420
src/bts.h
View File

@@ -25,99 +25,47 @@
extern "C" {
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <mslot_class.h>
}
#include <gsm_rlcmac.h>
#include "poll_controller.h"
#include "sba.h"
#include "tbf.h"
#include "gprs_ms_storage.h"
#include "gprs_coding_scheme.h"
#include <cxx_linuxlist.h>
#include <pdch.h>
#endif
#include <stdint.h>
#define LLC_CODEL_DISABLE 0
#define LLC_CODEL_USE_DEFAULT (-1)
#define MAX_GPRS_CS 9
/* see bts->gsmtap_categ_mask */
enum pcu_gsmtap_category {
PCU_GSMTAP_C_DL_UNKNOWN = 0, /* unknown or undecodable downlink blocks */
PCU_GSMTAP_C_DL_DUMMY = 1, /* downlink dummy blocks */
PCU_GSMTAP_C_DL_CTRL = 2, /* downlink control blocks */
PCU_GSMTAP_C_DL_DATA_GPRS = 3, /* downlink GPRS data blocks */
PCU_GSMTAP_C_DL_DATA_EGPRS = 4, /* downlink EGPRS data blocks */
PCU_GSMTAP_C_DL_PTCCH = 5, /* downlink PTCCH blocks */
PCU_GSMTAP_C_UL_UNKNOWN = 15, /* unknown or undecodable uplink blocks */
PCU_GSMTAP_C_UL_DUMMY = 16, /* uplink dummy blocks */
PCU_GSMTAP_C_UL_CTRL = 17, /* uplink control blocks */
PCU_GSMTAP_C_UL_DATA_GPRS = 18, /* uplink GPRS data blocks */
PCU_GSMTAP_C_UL_DATA_EGPRS = 19, /* uplink EGPRS data blocks */
};
struct BTS;
struct GprsMs;
/*
* PDCH instance
*/
struct gprs_rlcmac_pdch {
#ifdef __cplusplus
struct gprs_rlcmac_paging *dequeue_paging();
struct msgb *packet_paging_request();
void add_paging(struct gprs_rlcmac_paging *pag);
void free_resources();
bool is_enabled() const;
void enable();
void disable();
/* dispatching of messages */
int rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
struct pcu_l1_meas *meas);
gprs_rlcmac_bts *bts_data() const;
BTS *bts() const;
uint8_t trx_no() const;
struct gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi);
struct gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi);
void attach_tbf(gprs_rlcmac_tbf *tbf);
void detach_tbf(gprs_rlcmac_tbf *tbf);
unsigned num_tbfs(enum gprs_rlcmac_tbf_direction dir) const;
void reserve(enum gprs_rlcmac_tbf_direction dir);
void unreserve(enum gprs_rlcmac_tbf_direction dir);
unsigned num_reserved(enum gprs_rlcmac_tbf_direction dir) const;
uint8_t assigned_usf() const;
uint32_t assigned_tfi(enum gprs_rlcmac_tbf_direction dir) const;
#endif
uint8_t m_is_enabled; /* TS is enabled */
uint8_t tsc; /* TSC of this slot */
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
uint8_t next_ctrl_prio; /* next kind of ctrl message to schedule */
struct llist_head paging_list; /* list of paging messages */
uint32_t last_rts_fn; /* store last frame number of RTS */
/* back pointers */
struct gprs_rlcmac_trx *trx;
uint8_t ts_no;
#ifdef __cplusplus
private:
int rcv_data_block_acknowledged(uint8_t *data, uint8_t len,
struct pcu_l1_meas *meas);
int rcv_control_block(bitvec *rlc_block, uint32_t fn);
void rcv_control_ack(Packet_Control_Acknowledgement_t *, uint32_t fn);
void rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *, uint32_t fn);
void rcv_resource_request(Packet_Resource_Request_t *t, uint32_t fn);
void rcv_measurement_report(Packet_Measurement_Report_t *t, uint32_t fn);
gprs_rlcmac_tbf *tbf_from_list_by_tfi(struct llist_head *tbf_list, uint8_t tfi,
enum gprs_rlcmac_tbf_direction dir);
gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi,
enum gprs_rlcmac_tbf_direction dir);
#endif
uint8_t m_num_tbfs[2];
uint8_t m_num_reserved[2];
uint8_t m_assigned_usf; /* bit set */
uint32_t m_assigned_tfi[2]; /* bit set */
struct gprs_rlcmac_tbf *m_tbfs[2][32];
};
struct gprs_rlcmac_trx {
void *fl1h;
uint16_t arfcn;
@@ -133,11 +81,20 @@ struct gprs_rlcmac_trx {
#endif
};
#ifdef __cplusplus
extern "C" {
#endif
void bts_update_tbf_ta(const char *p, uint32_t fn, uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach);
#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.
*/
struct gprs_rlcmac_bts {
bool active;
uint8_t bsic;
uint8_t fc_interval;
uint16_t fc_bucket_time;
@@ -150,7 +107,9 @@ struct gprs_rlcmac_bts {
uint8_t cs3;
uint8_t cs4;
uint8_t initial_cs_dl, initial_cs_ul;
uint8_t initial_mcs_dl, initial_mcs_ul;
uint8_t max_cs_dl, max_cs_ul;
uint8_t max_mcs_dl, max_mcs_ul;
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
uint32_t llc_discard_csec;
@@ -164,27 +123,30 @@ struct gprs_rlcmac_bts {
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_categ_mask;
struct gprs_rlcmac_trx trx[8];
int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_tbf);
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
bool single, int8_t use_tbf);
uint8_t force_two_phase;
uint8_t alpha, gamma;
uint8_t egprs_enabled;
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
uint8_t si13[GSM_MACBLOCK_LEN];
bool si13_is_set;
/* 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];
struct {int16_t low; int16_t high; } mcs_lqual_ranges[MAX_GPRS_CS];
uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
/* TBF handling, make private or move into TBFController */
/* list of uplink TBFs */
struct llist_head ul_tbfs;
/* list of downlink TBFs */
struct llist_head dl_tbfs;
uint16_t ws_base;
uint16_t ws_pdch; /* increase WS by this value per PDCH */
/* State for dynamic algorithm selection */
int multislot_disabled;
@@ -194,6 +156,9 @@ struct gprs_rlcmac_bts {
* period.
*/
struct BTS *bts;
/* Path to be used for the pcu-bts socket */
char *pcu_sock_path;
};
#ifdef __cplusplus
@@ -207,19 +172,32 @@ public:
enum {
CTR_TBF_DL_ALLOCATED,
CTR_TBF_DL_FREED,
CTR_TBF_DL_ABORTED,
CTR_TBF_UL_ALLOCATED,
CTR_TBF_UL_FREED,
CTR_TBF_UL_ABORTED,
CTR_TBF_REUSED,
CTR_TBF_ALLOC_ALGO_A,
CTR_TBF_ALLOC_ALGO_B,
CTR_TBF_FAILED_EGPRS_ONLY,
CTR_RLC_SENT,
CTR_RLC_RESENT,
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,
@@ -227,7 +205,60 @@ 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 {
STAT_MS_PRESENT,
};
enum {
@@ -244,6 +275,7 @@ public:
/** TODO: change the number to unsigned */
void set_current_frame_number(int frame_number);
void set_current_block_frame_number(int frame_number, unsigned max_delay);
int current_frame_number() const;
/** add paging to paging queue(s) */
@@ -254,36 +286,56 @@ public:
gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
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 tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx) const;
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
void trigger_dl_ass(gprs_rlcmac_dl_tbf *tbf, gprs_rlcmac_tbf *old_tbf);
uint32_t rfn_to_fn(int32_t rfn);
int rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, bool is_11bit,
enum ph_burst_type burst_type);
void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
GprsMsStorage &ms_store();
GprsMs *ms_by_tlli(uint32_t tlli, uint32_t old_tlli = 0);
GprsMs *ms_alloc(uint8_t ms_class);
GprsMs *ms_by_imsi(const char *imsi);
GprsMs *ms_alloc(uint8_t ms_class, uint8_t egprs_ms_class = 0);
void send_gsmtap(enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
uint8_t ts_no, uint8_t channel, uint32_t fn,
const uint8_t *data, unsigned int len);
/*
* Statistics
*/
void tbf_dl_created();
void tbf_dl_freed();
void tbf_dl_aborted();
void tbf_ul_created();
void tbf_ul_freed();
void tbf_ul_aborted();
void tbf_reused();
void tbf_alloc_algo_a();
void tbf_alloc_algo_b();
void tbf_failed_egprs_only();
void rlc_sent();
void rlc_resent();
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();
@@ -291,23 +343,84 @@ 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();
/*
* Below for C interface for the VTY
*/
struct rate_ctr_group *rate_counters() const;
struct osmo_stat_item_group *stat_items() const;
LListHead<gprs_rlcmac_tbf>& ul_tbfs();
LListHead<gprs_rlcmac_tbf>& dl_tbfs();
private:
int m_cur_fn;
int m_cur_blk_fn;
struct gprs_rlcmac_bts m_bts;
PollController m_pollController;
SBAController m_sba;
struct rate_ctr_group *m_ratectrs;
struct osmo_stat_item_group *m_statg;
GprsMsStorage m_ms_store;
private:
/* list of uplink TBFs */
LListHead<gprs_rlcmac_tbf> m_ul_tbfs;
/* list of downlink TBFs */
LListHead<gprs_rlcmac_tbf> m_dl_tbfs;
/* disable copying to avoid slicing */
BTS(const BTS&);
BTS& operator=(const BTS&);
@@ -333,31 +446,19 @@ inline GprsMs *BTS::ms_by_tlli(uint32_t tlli, uint32_t old_tlli)
return ms_store().get_ms(tlli, old_tlli);
}
inline BTS *gprs_rlcmac_pdch::bts() const
inline GprsMs *BTS::ms_by_imsi(const char *imsi)
{
return trx->bts;
return ms_store().get_ms(0, 0, imsi);
}
inline unsigned gprs_rlcmac_pdch::num_tbfs(enum gprs_rlcmac_tbf_direction dir) const
inline LListHead<gprs_rlcmac_tbf>& BTS::ul_tbfs()
{
return m_num_tbfs[dir];
return m_ul_tbfs;
}
inline unsigned gprs_rlcmac_pdch::num_reserved(
enum gprs_rlcmac_tbf_direction dir) const
inline LListHead<gprs_rlcmac_tbf>& BTS::dl_tbfs()
{
return gprs_rlcmac_pdch::m_num_reserved[dir];
}
inline uint8_t gprs_rlcmac_pdch::assigned_usf() const
{
return m_assigned_usf;
}
inline uint32_t gprs_rlcmac_pdch::assigned_tfi(
enum gprs_rlcmac_tbf_direction dir) const
{
return m_assigned_tfi[dir];
return m_dl_tbfs;
}
inline struct rate_ctr_group *BTS::rate_counters() const
@@ -365,6 +466,16 @@ inline struct rate_ctr_group *BTS::rate_counters() const
return m_ratectrs;
}
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]); \
@@ -372,19 +483,32 @@ inline struct rate_ctr_group *BTS::rate_counters() const
CREATE_COUNT_INLINE(tbf_dl_created, CTR_TBF_DL_ALLOCATED)
CREATE_COUNT_INLINE(tbf_dl_freed, CTR_TBF_DL_FREED)
CREATE_COUNT_INLINE(tbf_dl_aborted, CTR_TBF_DL_ABORTED)
CREATE_COUNT_INLINE(tbf_ul_created, CTR_TBF_UL_ALLOCATED)
CREATE_COUNT_INLINE(tbf_ul_freed, CTR_TBF_UL_FREED)
CREATE_COUNT_INLINE(tbf_ul_aborted, CTR_TBF_UL_ABORTED)
CREATE_COUNT_INLINE(tbf_reused, CTR_TBF_REUSED)
CREATE_COUNT_INLINE(tbf_alloc_algo_a, CTR_TBF_ALLOC_ALGO_A)
CREATE_COUNT_INLINE(tbf_alloc_algo_b, CTR_TBF_ALLOC_ALGO_B)
CREATE_COUNT_INLINE(tbf_failed_egprs_only, CTR_TBF_FAILED_EGPRS_ONLY)
CREATE_COUNT_INLINE(rlc_sent, CTR_RLC_SENT)
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)
@@ -392,20 +516,71 @@ 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
#define CREATE_STAT_INLINE(func_name, func_name_get, stat_name) \
inline void BTS::func_name(int32_t val) {\
osmo_stat_item_set(m_statg->items[stat_name], val); \
} \
inline int32_t BTS::func_name_get() {\
return osmo_stat_item_get_last(m_statg->items[stat_name]); \
}
inline gprs_rlcmac_bts *gprs_rlcmac_pdch::bts_data() const
{
return trx->bts->bts_data();
}
CREATE_STAT_INLINE(ms_present, ms_present_get, STAT_MS_PRESENT);
#undef CREATE_STAT_INLINE
inline uint8_t gprs_rlcmac_pdch::trx_no() const
{
return trx->trx_no;
}
#endif
#ifdef __cplusplus
@@ -413,11 +588,8 @@ extern "C" {
#endif
struct gprs_rlcmac_bts *bts_main_data();
struct rate_ctr_group *bts_main_data_stats();
struct osmo_stat_item_group *bts_main_data_stat_items();
#ifdef __cplusplus
}
inline bool gprs_rlcmac_pdch::is_enabled() const
{
return m_is_enabled;
}
#endif

View File

@@ -64,15 +64,15 @@ get_masked_bits8( bitvec *vector, unsigned& readIndex, gint bit_offset, const g
readIndex -= relative_bit_offset;
if (bit_shift >= 0)
{
result = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) >> bit_shift;
result = (0x2B ^ ((guint8)bitvec_read_field(vector, &readIndex, 8))) >> bit_shift;
readIndex-= bit_shift;
result &= maskBits[no_of_bits];
}
else
{
guint8 hight_part = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) & maskBits[8 - relative_bit_offset];
guint8 hight_part = (0x2B ^ ((guint8)bitvec_read_field(vector, &readIndex, 8))) & maskBits[8 - relative_bit_offset];
hight_part = (guint8) (hight_part << (-bit_shift));
result = (0x2B ^ ((guint8)bitvec_read_field(vector, readIndex, 8))) >> (8 + bit_shift);
result = (0x2B ^ ((guint8)bitvec_read_field(vector, &readIndex, 8))) >> (8 + bit_shift);
readIndex = readIndex - (8 - (-bit_shift));
result |= hight_part;
}
@@ -133,7 +133,7 @@ ProcessError( unsigned readIndex, const char* sz, gint16 err, const CSN_DESCR* p
static gboolean
existNextElement(bitvec *vector, unsigned& readIndex, guint8 Tag)
{
guint8 res = bitvec_read_field(vector, readIndex, 1);
guint8 res = bitvec_read_field(vector, &readIndex, 1);
if (Tag == STANDARD_TAG)
{
return (res > 0);
@@ -167,7 +167,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
if (remaining_bits_len > 0)
{
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = bitvec_read_field(vector, readIndex, 1);
*pui8 = bitvec_read_field(vector, &readIndex, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
/* end add the bit value to protocol tree */
}
@@ -202,21 +202,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
{
if (no_of_bits <= 8)
{
guint8 ui8 = bitvec_read_field(vector, readIndex, no_of_bits);
guint8 ui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
}
else if (no_of_bits <= 16)
{
guint16 ui16 = bitvec_read_field(vector, readIndex, no_of_bits);
guint16 ui16 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui16 = pui16DATA(data, pDescr->offset);
*pui16 = ui16;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
}
else if (no_of_bits <= 32)
{
guint32 ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
guint32 ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui32 = pui32DATA(data, pDescr->offset);
*pui32 = ui32;
LOGPC(DCSN1, LOGL_NOTICE, "%s = 0x%08x | ", pDescr->sz , *pui32);
@@ -264,21 +264,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
{
if (no_of_bits <= 8)
{
guint8 ui8 = bitvec_read_field(vector, readIndex, no_of_bits);
guint8 ui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8 + (guint8)pDescr->descr.value;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
}
else if (no_of_bits <= 16)
{
guint16 ui16 = bitvec_read_field(vector, readIndex, no_of_bits);
guint16 ui16 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui16 = pui16DATA(data, pDescr->offset);
*pui16 = ui16 + (guint16)pDescr->descr.value;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
}
else if (no_of_bits <= 32)
{
guint32 ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
guint32 ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui32 = pui32DATA(data, pDescr->offset);
*pui32 = ui32 + (guint16)pDescr->descr.value;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
@@ -308,7 +308,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
remaining_bits_len -= no_of_bits;
if (no_of_bits <= 8)
{
guint8 ui8 = get_masked_bits8(vector, readIndex, bit_offset, no_of_bits);
guint8 ui8 = get_masked_bits8(vector, readIndex, bit_offset, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
@@ -347,7 +347,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
pui8 = pui8DATA(data, pDescr->offset);
do
{
*pui8 = bitvec_read_field(vector, readIndex, no_of_bits);
*pui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
bit_offset += no_of_bits;
@@ -401,7 +401,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
LOGPC(DCSN1, LOGL_NOTICE, "%s | ", pDescr->sz);
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
if (Status >= 0)
{
pui8 += nSize;
@@ -430,7 +430,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
{
for(unsigned ib = 0; ib < 4; ib++)
{
guint8 ui8 = bitvec_read_field(vector, readIndex, 8);
guint8 ui8 = bitvec_read_field(vector, &readIndex, 8);
pui8 = pui8DATA(data, pDescr->offset+ib);
*pui8 = ui8;
LOGPC(DCSN1, LOGL_NOTICE, "%s[%u] = %u | ", pDescr->sz , ib, (unsigned)*pui8);
@@ -440,7 +440,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
{
for(unsigned ib = 0; ib < 8; ib++)
{
guint8 ui8 = bitvec_read_field(vector, readIndex, 8);
guint8 ui8 = bitvec_read_field(vector, &readIndex, 8);
pui8 = pui8DATA(data, pDescr->offset+ib);
*pui8 = ui8;
LOGPC(DCSN1, LOGL_NOTICE, "%s[%u] = %u | ", pDescr->sz , ib, (unsigned)*pui8);
@@ -467,7 +467,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
csnStream_t arT = *ar;
LOGPC(DCSN1, LOGL_NOTICE, " : %s | ", pDescr->sz);
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
LOGPC(DCSN1, LOGL_NOTICE, ": End %s | ", pDescr->sz);
if (Status >= 0)
{
@@ -493,7 +493,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
while (count > 0)
{
guint8 no_of_bits = pChoice->bits;
guint8 value = bitvec_read_field(vector, readIndex, no_of_bits);
guint8 value = bitvec_read_field(vector, &readIndex, no_of_bits);
if (value == pChoice->value)
{
CSN_DESCR descr[2];
@@ -510,7 +510,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
remaining_bits_len -= no_of_bits;
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamDecoder(&arT, descr, vector, readIndex, data);
Status = csnStreamDecoder(&arT, descr, vector, readIndex, data);
if (Status >= 0)
{
@@ -541,7 +541,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
guint8 length_len = pDescr->i;
gint16 Status = -1;
guint8 length = bitvec_read_field(vector, readIndex, length_len);
guint8 length = bitvec_read_field(vector, &readIndex, length_len);
LOGPC(DCSN1, LOGL_NOTICE, "%s length = %d | ", pDescr->sz , (int)length);
bit_offset += length_len;
@@ -549,7 +549,8 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
csnStreamInit(&arT, bit_offset, length);
arT.direction = 1;
Status = serialize(&arT, vector, readIndex, pvDATA(data, pDescr->offset));
LOGPC(DCSN1, LOGL_NOTICE, "ptr = %p | offset = %d | ", (void *)data, (int)pDescr->offset);
Status = serialize(&arT, vector, readIndex, pvDATA(data, pDescr->offset));
if (Status >= 0)
{
@@ -594,7 +595,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
}
else
{
index |= bitvec_read_field(vector, readIndex, 1);
index |= bitvec_read_field(vector, &readIndex, 1);
}
remaining_bits_len--;
bit_offset++;
@@ -617,7 +618,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
{
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = 0x00;
if (bitvec_read_field(vector, readIndex, 1) > 0)
if (bitvec_read_field(vector, &readIndex, 1) > 0)
{
*pui8 = 0x01;
}
@@ -643,21 +644,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
if (no_of_bits <= 8)
{
guint8 ui8 = bitvec_read_field(vector, readIndex, no_of_bits);
guint8 ui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
}
else if (no_of_bits <= 16)
{
guint16 ui16 = bitvec_read_field(vector, readIndex, no_of_bits);
guint16 ui16 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui16 = pui16DATA(data, pDescr->offset);
*pui16 = ui16;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
}
else if (no_of_bits <= 32)
{
guint32 ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
guint32 ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui32 = pui32DATA(data, pDescr->offset);
*pui32 = ui32;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
@@ -685,21 +686,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
{
if (no_of_bits <= 8)
{
guint8 ui8 = bitvec_read_field(vector, readIndex, no_of_bits);
guint8 ui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8 + (guint8)pDescr->descr.value;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
}
else if (no_of_bits <= 16)
{
guint16 ui16 = bitvec_read_field(vector, readIndex, no_of_bits);
guint16 ui16 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui16 = pui16DATA(data, pDescr->offset);
*pui16 = ui16 + (guint16)pDescr->descr.value;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
}
else if (no_of_bits <= 32)
{
guint32 ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
guint32 ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui32 = pui32DATA(data, pDescr->offset);
*pui32 = ui32 + (guint16)pDescr->descr.value;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
@@ -727,7 +728,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
{
if (no_of_bits <= 8)
{
guint8 ui8 = get_masked_bits8(vector, readIndex, bit_offset, no_of_bits);
guint8 ui8 = get_masked_bits8(vector, readIndex, bit_offset, no_of_bits);
pui8 = pui8DATA(data, pDescr->offset);
*pui8 = ui8;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
@@ -766,7 +767,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
while (nCount > 0)
{
*pui8 = bitvec_read_field(vector, readIndex, no_of_bits);
*pui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
bit_offset += no_of_bits;
@@ -779,7 +780,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
while (nCount > 0)
{
*pui16 = bitvec_read_field(vector, readIndex, no_of_bits);
*pui16 = bitvec_read_field(vector, &readIndex, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , *pui16);
pui16++;
bit_offset += no_of_bits;
@@ -829,7 +830,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
{
LOGPC(DCSN1, LOGL_NOTICE, "%s | ", pDescr->sz);
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
if (Status >= 0)
{
pui8 += nSize;
@@ -855,13 +856,13 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
if (no_of_bits <= 32)
{
guint32 ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
guint32 ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui32 = pui32DATA(data, pDescr->offset);
*pui32 = ui32;
}
else if (no_of_bits <= 64)
{
guint64 ui64 = bitvec_read_field(vector, readIndex, no_of_bits);
guint64 ui64 = bitvec_read_field(vector, &readIndex, no_of_bits);
pui64 = pui64DATA(data, pDescr->offset);
*pui64 = ui64;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %lu | ", pDescr->sz , *pui64);
@@ -887,7 +888,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
csnStream_t arT = *ar;
LOGPC(DCSN1, LOGL_NOTICE, " : %s | ", pDescr->sz);
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pvDATA(data, pDescr->offset));
LOGPC(DCSN1, LOGL_NOTICE, " : End %s | ", pDescr->sz);
if (Status >= 0)
{
@@ -922,11 +923,11 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
if (CSN_EXIST_LH == pDescr->type)
{
fExist = get_masked_bits8(vector, readIndex, bit_offset, 1);
fExist = get_masked_bits8(vector, readIndex, bit_offset, 1);
}
else
{
fExist = bitvec_read_field(vector, readIndex, 1);
fExist = bitvec_read_field(vector, &readIndex, 1);
}
*pui8 = fExist;
@@ -965,7 +966,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
/* the "regular" M_NEXT_EXIST description element */
fExist = 0x00;
if (bitvec_read_field(vector, readIndex, 1))
if (bitvec_read_field(vector, &readIndex, 1))
{
fExist = 0x01;
}
@@ -1058,7 +1059,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
if (nB1 > 0)
{ /* take care of the first byte - it will be right aligned */
*pui8 = bitvec_read_field(vector, readIndex, nB1);
*pui8 = bitvec_read_field(vector, &readIndex, nB1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
no_of_bits -= nB1;
@@ -1068,7 +1069,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
/* remaining no_of_bits is a multiple of 8 or 0 */
while (no_of_bits > 0)
{
*pui8 = bitvec_read_field(vector, readIndex, 8);
*pui8 = bitvec_read_field(vector, &readIndex, 8);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
no_of_bits -= 8;
@@ -1109,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);
*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;
}
}
}
@@ -1144,13 +1144,13 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
guint8 bits_to_handle = remaining_bits_len%8;
if (bits_to_handle > 0)
{
LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, readIndex, bits_to_handle));
LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, &readIndex, bits_to_handle));
remaining_bits_len -= bits_to_handle;
bit_offset += bits_to_handle;
}
else if (bits_to_handle == 0)
{
LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, readIndex, 8));
LOGPC(DCSN1, LOGL_NOTICE, "%" PRIu64 "|", bitvec_read_field(vector, &readIndex, 8));
remaining_bits_len -= 8;
bit_offset += 8;
}
@@ -1191,7 +1191,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
while (count > 0)
{
readIndex -= 8;
*pui8 = bitvec_read_field(vector, readIndex, 8);
*pui8 = bitvec_read_field(vector, &readIndex, 8);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
bit_offset += 8;
@@ -1217,14 +1217,14 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
pui8 = pui8DATA(data, pDescr->offset);
while (existNextElement(vector, readIndex, Tag))
while (existNextElement(vector, readIndex, Tag))
{ /* tag control shows existence of next list elements */
LOGPC(DCSN1, LOGL_NOTICE, "%s = Exist | ", pDescr->sz);
bit_offset++;
remaining_bits_len--;
/* extract and store no_of_bits long element from bitstream */
*pui8 = bitvec_read_field(vector, readIndex, no_of_bits);
*pui8 = bitvec_read_field(vector, &readIndex, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
remaining_bits_len -= no_of_bits;
@@ -1238,7 +1238,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
bit_offset += no_of_bits;
}
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)bitvec_read_field(vector, readIndex, 1));
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)bitvec_read_field(vector, &readIndex, 1));
/* existNextElement() returned FALSE, 1 bit consumed */
bit_offset++;
@@ -1258,7 +1258,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
guint8 ElementCount = 0;
pui8 = pui8DATA(data, pDescr->offset);
while (existNextElement(vector, readIndex, Tag))
while (existNextElement(vector, readIndex, Tag))
{ /* tag control shows existence of next list elements */
LOGPC(DCSN1, LOGL_NOTICE, "%s = Exist | ", pDescr->sz);
/* existNextElement() returned TRUE, 1 bit consumed */
@@ -1270,7 +1270,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
csnStream_t arT = *ar;
gint16 Status;
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
if (Status >= 0)
{ /* successful completion */
@@ -1290,7 +1290,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
}
}
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)bitvec_read_field(vector, readIndex, 1));
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)bitvec_read_field(vector, &readIndex, 1));
/* existNextElement() returned FALSE, 1 bit consumed */
bit_offset++;
@@ -1331,7 +1331,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
LOGPC(DCSN1, LOGL_NOTICE, "%s { | ", pDescr->sz);
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
Status = csnStreamDecoder(&arT, (const CSN_DESCR*)pDescr->descr.ptr, vector, readIndex, pui8);
if (Status >= 0)
{ /* successful completion */
@@ -1351,7 +1351,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
/* control of next element's tag */
LOGPC(DCSN1, LOGL_NOTICE, "%s } | ", pDescr->sz);
EndOfList = !(existNextElement(vector, readIndex, Tag));
EndOfList = !(existNextElement(vector, readIndex, Tag));
bit_offset++;
remaining_bits_len--; /* 1 bit consumed (tag) */
@@ -1372,7 +1372,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
if (no_of_bits <= 32)
{
ui32 = bitvec_read_field(vector, readIndex, no_of_bits);
ui32 = bitvec_read_field(vector, &readIndex, no_of_bits);
}
else
{
@@ -1449,7 +1449,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
if (remaining_bits_len > 0)
{
pui8 = pui8DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui8, 1);
bitvec_write_field(vector, &writeIndex, *pui8, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
/* end add the bit value to protocol tree */
}
@@ -1483,19 +1483,19 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
if (no_of_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
}
else if (no_of_bits <= 16)
{
pui16 = pui16DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui16, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui16, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
}
else if (no_of_bits <= 32)
{
pui32 = pui32DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui32, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui32, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
}
else
@@ -1528,19 +1528,19 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
if (no_of_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui8 - (guint8)pDescr->descr.value, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui8 - (guint8)pDescr->descr.value, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(*pui8 - (guint8)pDescr->descr.value));
}
else if (no_of_bits <= 16)
{
pui16 = pui16DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui16 - (guint16)pDescr->descr.value, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui16 - (guint16)pDescr->descr.value, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , (unsigned short)(*pui16 - (guint16)pDescr->descr.value));
}
else if (no_of_bits <= 32)
{
pui32 = pui32DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui32 - (guint16)pDescr->descr.value, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui32 - (guint16)pDescr->descr.value, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , (unsigned int)(*pui32 - (guint16)pDescr->descr.value));
}
else
@@ -1569,12 +1569,12 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
if (no_of_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
// TODO : Change get_masked_bits8()
writeIndex -= no_of_bits;
guint8 ui8 = get_masked_bits8(vector, writeIndex, bit_offset, no_of_bits);
writeIndex -= no_of_bits;
bitvec_write_field(vector, writeIndex, ui8, no_of_bits);
bitvec_write_field(vector, &writeIndex, ui8, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
}
@@ -1612,7 +1612,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
pui8 = pui8DATA(data, pDescr->offset);
do
{
bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
bit_offset += no_of_bits;
@@ -1697,7 +1697,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
for(unsigned ib = 0; ib < 4; ib++)
{
pui8 = pui8DATA(data, pDescr->offset+ib);
bitvec_write_field(vector, writeIndex, *pui8, 8);
bitvec_write_field(vector, &writeIndex, *pui8, 8);
LOGPC(DCSN1, LOGL_NOTICE, "%s[%u] = %u | ", pDescr->sz , ib, (unsigned)*pui8);
}
}
@@ -1706,7 +1706,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
for(unsigned ib = 0; ib < 8; ib++)
{
pui8 = pui8DATA(data, pDescr->offset+ib);
bitvec_write_field(vector, writeIndex, *pui8, 8);
bitvec_write_field(vector, &writeIndex, *pui8, 8);
LOGPC(DCSN1, LOGL_NOTICE, "%s[%u] = %u | ", pDescr->sz , ib, (unsigned)*pui8);
}
}
@@ -1761,7 +1761,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
guint8 no_of_bits = pChoice->bits;
guint8 value = pChoice->value;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pChoice->descr.sz , (unsigned)value);
bitvec_write_field(vector, writeIndex, value, no_of_bits);
bitvec_write_field(vector, &writeIndex, value, no_of_bits);
CSN_DESCR descr[2];
gint16 Status;
@@ -1807,7 +1807,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
csnStreamInit(&arT, bit_offset, remaining_bits_len);
Status = serialize(&arT, vector, writeIndex, pvDATA(data, pDescr->offset));
bitvec_write_field(vector, lengthIndex, writeIndex-lengthIndex-length_len, length_len);
bitvec_write_field(vector, &lengthIndex, writeIndex-lengthIndex-length_len, length_len);
LOGPC(DCSN1, LOGL_NOTICE, "%s length = %u | ", pDescr->sz , (unsigned)(writeIndex-lengthIndex));
if (Status >= 0)
@@ -1846,7 +1846,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
/* Assign UnionType */
pui8 = pui8DATA(data, pDescr->offset);
//read index from data and write to vector
bitvec_write_field(vector, writeIndex, *pui8, Bits);
bitvec_write_field(vector, &writeIndex, *pui8, Bits);
//decode index
writeIndex -= Bits;
@@ -1861,7 +1861,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
}
else
{
index |= bitvec_read_field(vector, writeIndex, 1);
index |= bitvec_read_field(vector, &writeIndex, 1);
}
remaining_bits_len--;
@@ -1870,7 +1870,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
}
writeIndex -= Bits;
bitvec_write_field(vector, writeIndex, index, Bits);
bitvec_write_field(vector, &writeIndex, index, Bits);
/* script index to continue on, limited in case we do not have a power of 2 */
@@ -1883,7 +1883,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
case CSN_BIT:
{
pui8 = pui8DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui8, 1);
bitvec_write_field(vector, &writeIndex, *pui8, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
remaining_bits_len -= 1;
bit_offset++;
@@ -1907,19 +1907,19 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
if (no_of_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
}
else if (no_of_bits <= 16)
{
pui16 = pui16DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui16, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui16, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui16);
}
else if (no_of_bits <= 32)
{
pui32 = pui32DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui32, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui32, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
}
else
@@ -1946,19 +1946,19 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
if (no_of_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui8 - (guint8)pDescr->descr.value, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui8 - (guint8)pDescr->descr.value, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(*pui8 - (guint8)pDescr->descr.value));
}
else if (no_of_bits <= 16)
{
pui16 = pui16DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui16 - (guint16)pDescr->descr.value, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui16 - (guint16)pDescr->descr.value, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , (unsigned short)(*pui16 - (guint16)pDescr->descr.value));
}
else if (no_of_bits <= 32)
{
pui32 = pui32DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui32 - (guint16)pDescr->descr.value, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui32 - (guint16)pDescr->descr.value, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , (unsigned int)(*pui32 - (guint16)pDescr->descr.value));
}
else
@@ -1987,12 +1987,12 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
if (no_of_bits <= 8)
{
pui8 = pui8DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
// TODO : Change get_masked_bits8()
writeIndex -= no_of_bits;
guint8 ui8 = get_masked_bits8(vector, writeIndex, bit_offset, no_of_bits);
writeIndex -= no_of_bits;
bitvec_write_field(vector, writeIndex, ui8, no_of_bits);
bitvec_write_field(vector, &writeIndex, ui8, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
}
@@ -2030,7 +2030,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
pui8 = pui8DATA(data, pDescr->offset);
do
{
bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
bit_offset += no_of_bits;
@@ -2112,13 +2112,13 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
if (no_of_bits <= 32)
{
pui32 = pui32DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui32, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui32, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %d | ", pDescr->sz , *pui32);
}
else if (no_of_bits <= 64)
{
pui64 = pui64DATA(data, pDescr->offset);
bitvec_write_field(vector, writeIndex, *pui64, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui64, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %lu | ", pDescr->sz , *pui64);
}
else
@@ -2176,7 +2176,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
unsigned exist = 0;
pui8 = pui8DATA(data, pDescr->offset);
exist = *pui8;
bitvec_write_field(vector, writeIndex, *pui8, 1);
bitvec_write_field(vector, &writeIndex, *pui8, 1);
writeIndex--;
if (CSN_EXIST_LH == pDescr->type)
{
@@ -2184,10 +2184,10 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
}
else
{
fExist = bitvec_read_field(vector, writeIndex, 1);
fExist = bitvec_read_field(vector, &writeIndex, 1);
}
writeIndex--;
bitvec_write_field(vector, writeIndex, fExist, 1);
bitvec_write_field(vector, &writeIndex, fExist, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz, (unsigned)fExist);
pDescr++;
remaining_bits_len -= 1;
@@ -2217,7 +2217,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
break;
}
bitvec_write_field(vector, writeIndex, *pui8, 1);
bitvec_write_field(vector, &writeIndex, *pui8, 1);
fExist = *pui8;
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
remaining_bits_len -= 1;
@@ -2257,11 +2257,11 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
}
/* the "regular" M_NEXT_EXIST_LH description element */
bitvec_write_field(vector, writeIndex, *pui8, 1);
bitvec_write_field(vector, &writeIndex, *pui8, 1);
writeIndex--;
fExist = get_masked_bits8(vector,writeIndex, bit_offset, 1);
writeIndex--;
bitvec_write_field(vector, writeIndex, fExist, 1);
bitvec_write_field(vector, &writeIndex, fExist, 1);
pui8++;
remaining_bits_len -= 1;
@@ -2310,7 +2310,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
if (nB1 > 0)
{ /* take care of the first byte - it will be right aligned */
bitvec_write_field(vector, writeIndex, *pui8, nB1);
bitvec_write_field(vector, &writeIndex, *pui8, nB1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
no_of_bits -= nB1;
@@ -2320,7 +2320,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
/* remaining no_of_bits is a multiple of 8 or 0 */
while (no_of_bits > 0)
{
bitvec_write_field(vector, writeIndex, *pui8, 8);
bitvec_write_field(vector, &writeIndex, *pui8, 8);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
no_of_bits -= 8;
@@ -2366,14 +2366,14 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
while (no_of_bits > 0)
{
bitvec_write_field(vector, writeIndex, *pui8, 8);
bitvec_write_field(vector, &writeIndex, *pui8, 8);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
no_of_bits -= 8;
}
if (nB1 > 0)
{
bitvec_write_field(vector, writeIndex, *pui8, nB1);
bitvec_write_field(vector, &writeIndex, *pui8, nB1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
no_of_bits -= nB1;
@@ -2399,15 +2399,20 @@ 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));
bitvec_write_field(vector, writeIndex, fl, 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;
bit_offset += bits_to_handle;
}
else if (bits_to_handle == 0)
{
bitvec_write_field(vector, writeIndex, filler, 8);
bitvec_write_field(vector, &writeIndex, filler, 8);
LOGPC(DCSN1, LOGL_NOTICE, "%u|", filler);
remaining_bits_len -= 8;
bit_offset += 8;
@@ -2448,7 +2453,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
while (count > 0)
{
bitvec_write_field(vector, writeIndex, *pui8, 8);
bitvec_write_field(vector, &writeIndex, *pui8, 8);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
bit_offset += 8;
@@ -2475,13 +2480,13 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
ElementCount = *pui8DATA(data, (gint16)pDescr->descr.value);
while (ElementCount > 0)
{ /* tag control shows existence of next list elements */
bitvec_write_field(vector, writeIndex, Tag, 1);
bitvec_write_field(vector, &writeIndex, Tag, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)Tag);
bit_offset++;
remaining_bits_len--;
/* extract and store no_of_bits long element from bitstream */
bitvec_write_field(vector, writeIndex, *pui8, no_of_bits);
bitvec_write_field(vector, &writeIndex, *pui8, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
remaining_bits_len -= no_of_bits;
@@ -2495,9 +2500,10 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
bit_offset += no_of_bits;
}
bitvec_write_field(vector, writeIndex, !Tag, 1);
bitvec_write_field(vector, &writeIndex, !Tag, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(!Tag));
bit_offset++;
remaining_bits_len--;
pDescr++;
break;
@@ -2516,7 +2522,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
while (ElementCount > 0)
{ /* tag control shows existence of next list elements */
bitvec_write_field(vector, writeIndex, Tag, 1);
bitvec_write_field(vector, &writeIndex, Tag, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)Tag);
bit_offset++;
@@ -2547,7 +2553,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
}
}
bitvec_write_field(vector, writeIndex, !Tag, 1);
bitvec_write_field(vector, &writeIndex, !Tag, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(!Tag));
bit_offset++;
@@ -2585,7 +2591,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
{ /* get data element */
if (ElementCount != ElementNum)
{
bitvec_write_field(vector, writeIndex, Tag, 1);
bitvec_write_field(vector, &writeIndex, Tag, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)Tag);
bit_offset++;
remaining_bits_len--;
@@ -2612,7 +2618,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
}
}
bitvec_write_field(vector, writeIndex, !Tag, 1);
bitvec_write_field(vector, &writeIndex, !Tag, 1);
bit_offset++;
remaining_bits_len--;
Tag = STANDARD_TAG; /* in case it was set to "reversed" */
@@ -2623,7 +2629,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
case CSN_FIXED:
{ /* Verify the fixed bits */
guint8 no_of_bits = (guint8) pDescr->i;
bitvec_write_field(vector, writeIndex, pDescr->offset, no_of_bits);
bitvec_write_field(vector, &writeIndex, pDescr->offset, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)pDescr->offset);
remaining_bits_len -= no_of_bits;
bit_offset += no_of_bits;

View File

@@ -25,12 +25,15 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <bitvector.h>
#include <iostream>
#include <cstdlib>
#ifndef _PACKET_CSN1_H_
#define _PACKET_CSN1_H_
extern "C" {
#include <osmocom/core/bitvec.h>
}
#include <iostream>
#include <cstdlib>
#define MIN(a,b) (((a)<(b))?(a):(b))
//#define max(a,b) (((a)>(b))?(a):(b))

View File

@@ -45,7 +45,7 @@ struct LListHead {
return *static_cast<llist_head *>(static_cast<void *>(this));
}
const llist_head &llist() const {
return *static_cast<llist_head *>(static_cast<void *>(this));
return *static_cast<const llist_head *>(static_cast<const void *>(this));
}
private:

View File

@@ -20,52 +20,275 @@
#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>
#include <errno.h>
#include <string.h>
int Decoding::tlli_from_ul_data(const uint8_t *data, uint8_t len,
uint32_t *tlli)
#define LENGTH_TO_END 255
/*!
* \returns num extensions fields (num frames == offset) on success,
* -errno otherwise.
*/
static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
unsigned int *offs,
bool is_last_block,
Decoding::RlcData *chunks, unsigned int chunks_size)
{
struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
struct rlc_li_field *li;
const struct rlc_li_field_egprs *li;
uint8_t e;
uint32_t _tlli;
unsigned int num_chunks = 0;
if (!rh->ti)
return -EINVAL;
data += 3;
len -= 3;
e = rh->e;
/* if E is not set (LI follows) */
e = 0;
while (!e) {
if (!len) {
if (*offs > data_len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more data\n");
return -EINVAL;
}
/* get new E */
li = (struct rlc_li_field *)data;
if (li->e == 0) /* if LI==0, E is interpreted as '1' */
e = 1;
else
e = li->e;
data++;
len--;
}
if (len < 4) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of frame "
"border\n");
return -EINVAL;
}
memcpy(&_tlli, data, 4);
*tlli = ntohl(_tlli);
return 0;
/* get new E */
li = (struct rlc_li_field_egprs *)&data[*offs];
e = li->e;
*offs += 1;
if (!chunks)
continue;
if (num_chunks == chunks_size) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more chunks possible\n");
return -ENOSPC;
}
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 == 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 */
chunks[num_chunks].length = li->li;
chunks[num_chunks].is_complete = true;
} else {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI contains "
"invalid extension octet: LI=%d, E=%d, count=%d\n",
li->li, li->e, num_chunks);
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) {
/* There is space after the last chunk, add a final one */
if (num_chunks == chunks_size) {
LOGP(DRLCMACUL, LOGL_NOTICE,
"UL DATA LI possibly extended, "
"but no more chunks possible\n");
return -ENOSPC;
}
chunks[num_chunks].length = LENGTH_TO_END;
chunks[num_chunks].is_complete = is_last_block;
num_chunks += 1;
}
}
return num_chunks;
}
static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len,
unsigned int *offs,
bool is_last_block,
Decoding::RlcData *chunks, unsigned int chunks_size)
{
const struct rlc_li_field *li;
uint8_t m, e;
unsigned int num_chunks = 0;
e = 0;
while (!e) {
if (*offs > data_len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more data\n");
return -EINVAL;
}
/* get new E */
li = (const struct rlc_li_field *)&data[*offs];
e = li->e;
m = li->m;
*offs += 1;
if (li->li == 0) {
/* TS 44.060, 10.4.14, par 6 */
e = 1;
m = 0;
}
/* TS 44.060, table 10.4.13.1 */
if (m == 0 && e == 0) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA "
"ignored, because M='0' and E='0'.\n");
return 0;
}
if (!chunks)
continue;
if (num_chunks == chunks_size) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more chunks possible\n");
return -ENOSPC;
}
if (li->li == 0)
/* e is 1 here */
chunks[num_chunks].length = LENGTH_TO_END;
else
chunks[num_chunks].length = li->li;
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) {
if (num_chunks == chunks_size) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
"but no more chunks possible\n");
return -ENOSPC;
}
/* TS 44.060, 10.4.13.1, row 4 */
chunks[num_chunks].length = LENGTH_TO_END;
chunks[num_chunks].is_complete = is_last_block;
num_chunks += 1;
}
}
return num_chunks;
}
int Decoding::rlc_data_from_ul_data(
const struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
const uint8_t *data, RlcData *chunks, unsigned int chunks_size,
uint32_t *tlli)
{
uint8_t e;
unsigned int data_len = rdbi->data_len;
int num_chunks = 0, i;
unsigned int offs = 0;
bool is_last_block = (rdbi->cv == 0);
if (!chunks)
chunks_size = 0;
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;
num_chunks += 1;
} else if (chunks) {
LOGP(DRLCMACUL, LOGL_NOTICE, "No extension, "
"but no more chunks possible\n");
return -ENOSPC;
}
} else if (cs.isEgprs()) {
/* if E is not set (LI follows), EGPRS */
num_chunks = parse_extensions_egprs(data, data_len, &offs,
is_last_block,
chunks, chunks_size);
} else {
/* if E is not set (LI follows), GPRS */
num_chunks = parse_extensions_gprs(data, data_len, &offs,
is_last_block,
chunks, chunks_size);
}
if (num_chunks < 0)
return num_chunks;
/* TLLI */
if (rdbi->ti) {
uint32_t tlli_enc;
if (offs + 4 > data_len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of block "
"border\n");
return -EINVAL;
}
memcpy(&tlli_enc, data + offs, sizeof(tlli_enc));
if (cs.isGprs())
/* The TLLI is encoded in big endian for GPRS (see
* TS 44.060, figure 10.2.2.1, note) */
*tlli = be32toh(tlli_enc);
else
/* The TLLI is encoded in little endian for EGPRS (see
* TS 44.060, figure 10.3a.2.1, note 2) */
*tlli = le32toh(tlli_enc);
offs += sizeof(tlli_enc);
} else {
*tlli = 0;
}
/* PFI */
if (rdbi->pi) {
LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
"please disable in SYSTEM INFORMATION\n");
return -ENOTSUP;
/* TODO: Skip all extensions with E=0 (see TS 44.060, 10.4.11 */
}
if (chunks_size == 0)
return num_chunks;
/* LLC */
for (i = 0; i < num_chunks; i++) {
chunks[i].offset = offs;
if (chunks[i].length == LENGTH_TO_END) {
if (offs == data_len) {
/* There is no place for an additional chunk,
* so drop it (this may happen with EGPRS since
* there is no M flag. */
num_chunks -= 1;
break;
}
chunks[i].length = data_len - offs;
}
offs += chunks[i].length;
if (offs > data_len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA out of block "
"border, chunk idx: %d, size: %d\n",
i, chunks[i].length);
return -EINVAL;
}
}
return num_chunks;
}
uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
@@ -83,6 +306,21 @@ uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
return 0;
}
uint8_t Decoding::get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
{
int i;
for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
continue;
if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_EGPRS_multislot_class)
continue;
return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.EGPRS_multislot_class;
}
return 0;
}
/**
* show_rbb needs to be an array with 65 elements
* The index of the array is the bit position in the rbb
@@ -99,3 +337,463 @@ void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb)
show_rbb[64] = '\0';
}
void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
{
unsigned int i;
for (i = 0; i < rbb->cur_bit; i++) {
uint8_t bit;
bit = bitvec_get_bit_pos(rbb, i);
show_rbb[i] = bit == 1 ? 'R' : 'I';
}
show_rbb[i] = '\0';
}
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
const uint8_t *data, GprsCodingScheme cs)
{
unsigned int cur_bit = 0;
switch(cs.headerTypeData()) {
case GprsCodingScheme::HEADER_GPRS_DATA :
cur_bit = rlc_parse_ul_data_header_gprs(rlc, data, cs);
break;
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;
default:
LOGP(DRLCMACDL, LOGL_ERROR,
"Decoding of uplink %s data blocks not yet supported.\n",
cs.name());
return -ENOTSUP;
};
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.
*
* Note that the bitstream is encoded in LSB first order, so the two octets
* 654321xx xxxxxx87 contain the octet 87654321 starting at bit position 3
* (LSB has bit position 1). This is a different order than the one used by
* CSN.1.
*
* \param data_block_idx The block index, 0..1 for header type 1, 0 otherwise
* \param src A pointer to the start of the RLC block (incl. the header)
* \param buffer A data area of a least the size of the RLC block
* \returns the number of bytes copied
*/
unsigned int Decoding::rlc_copy_to_aligned_buffer(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
const uint8_t *src, uint8_t *buffer)
{
unsigned int hdr_bytes;
unsigned int extra_bits;
unsigned int i;
uint8_t c, last_c;
uint8_t *dst;
const struct gprs_rlc_data_block_info *rdbi;
OSMO_ASSERT(data_block_idx < rlc->num_data_blocks);
rdbi = &rlc->block_info[data_block_idx];
hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
if (extra_bits == 0) {
/* It is aligned already */
memmove(buffer, src + hdr_bytes, rdbi->data_len);
return rdbi->data_len;
}
dst = buffer;
src = src + hdr_bytes;
last_c = *(src++);
for (i = 0; i < rdbi->data_len; i++) {
c = src[i];
*(dst++) = (last_c >> extra_bits) | (c << (8 - extra_bits));
last_c = c;
}
return rdbi->data_len;
}
/**
* \brief Get a pointer to byte aligned RLC data.
*
* Since the RLC data may not be byte aligned to the RLC block data such that a
* single RLC data byte is spread over two RLC block bytes, this function
* eventually uses the provided buffer as data storage.
*
* \param src A pointer to the start of the RLC block (incl. the header)
* \param buffer A data area of a least the size of the RLC block
* \returns A pointer to the RLC data start within src if it is aligned, and
* buffer otherwise.
*/
const uint8_t *Decoding::rlc_get_data_aligned(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
const uint8_t *src, uint8_t *buffer)
{
unsigned int hdr_bytes;
unsigned int extra_bits;
OSMO_ASSERT(data_block_idx < ARRAY_SIZE(rlc->data_offs_bits));
hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3;
extra_bits = (rlc->data_offs_bits[data_block_idx] & 7);
if (extra_bits == 0)
/* It is aligned already, return a pointer that refers to the
* original data. */
return src + hdr_bytes;
Decoding::rlc_copy_to_aligned_buffer(rlc, data_block_idx, src, buffer);
return buffer;
}
static int handle_final_ack(bitvec *bits, int *bsn_begin, int *bsn_end,
gprs_rlc_dl_window *window)
{
int num_blocks, i;
num_blocks = window->mod_sns(window->v_s() - window->v_a());
for (i = 0; i < num_blocks; i++)
bitvec_set_bit(bits, ONE);
*bsn_begin = window->v_a();
*bsn_end = window->mod_sns(*bsn_begin + num_blocks);
return num_blocks;
}
int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc,
bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
{
int urbb_len = desc->URBB_LENGTH;
int crbb_len = 0;
int num_blocks = 0;
struct bitvec urbb;
int i;
bool have_bitmap;
int implicitly_acked_blocks;
int ssn = desc->STARTING_SEQUENCE_NUMBER;
int rc;
if (desc->FINAL_ACK_INDICATION)
return handle_final_ack(bits, bsn_begin, bsn_end, window);
if (desc->Exist_CRBB)
crbb_len = desc->CRBB_LENGTH;
have_bitmap = (urbb_len + crbb_len) > 0;
/*
* bow & bitmap present:
* V(A)-> [ 11111...11111 0 SSN-> BBBBB...BBBBB ] (SSN+Nbits) .... V(S)
* bow & not bitmap present:
* V(A)-> [ 11111...11111 ] . SSN .... V(S)
* not bow & bitmap present:
* V(A)-> ... [ 0 SSN-> BBBBB...BBBBB ](SSN+N) .... V(S)
* not bow & not bitmap present:
* V(A)-> ... [] . SSN .... V(S)
*/
if (desc->BEGINNING_OF_WINDOW) {
implicitly_acked_blocks = window->mod_sns(ssn - 1 - window->v_a());
for (i = 0; i < implicitly_acked_blocks; i++)
bitvec_set_bit(bits, ONE);
num_blocks += implicitly_acked_blocks;
}
if (!have_bitmap)
goto aborted;
/* next bit refers to V(Q) and thus is always zero (and not
* transmitted) */
bitvec_set_bit(bits, ZERO);
num_blocks += 1;
if (crbb_len > 0) {
int old_len = bits->cur_bit;
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(
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 */
goto aborted;
}
LOGP(DRLCMACDL, LOGL_DEBUG,
"CRBB len: %d, decoded len: %d, cc: %d, crbb: '%s'\n",
desc->CRBB_LENGTH, bits->cur_bit - old_len,
desc->CRBB_STARTING_COLOR_CODE,
osmo_hexdump(
desc->CRBB, (desc->CRBB_LENGTH + 7)/8)
);
num_blocks += (bits->cur_bit - old_len);
}
urbb.cur_bit = 0;
urbb.data = (uint8_t *)desc->URBB;
urbb.data_len = sizeof(desc->URBB);
for (i = urbb_len; i > 0; i--) {
/*
* Set bit at the appropriate position (see 3GPP TS
* 44.060 12.3.1)
*/
int is_ack = bitvec_get_bit_pos(&urbb, i-1);
bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
}
num_blocks += urbb_len;
aborted:
*bsn_begin = window->v_a();
*bsn_end = window->mod_sns(*bsn_begin + num_blocks);
return num_blocks;
}
int Decoding::decode_gprs_acknack_bits(const Ack_Nack_Description_t *desc,
bitvec *bits, int *bsn_begin, int *bsn_end, gprs_rlc_dl_window *window)
{
int urbb_len = RLC_GPRS_WS;
int num_blocks;
struct bitvec urbb;
if (desc->FINAL_ACK_INDICATION)
return handle_final_ack(bits, bsn_begin, bsn_end, window);
*bsn_begin = window->v_a();
*bsn_end = desc->STARTING_SEQUENCE_NUMBER;
num_blocks = window->mod_sns(*bsn_end - *bsn_begin);
if (num_blocks < 0 || num_blocks > urbb_len) {
*bsn_end = *bsn_begin;
LOGP(DRLCMACUL, LOGL_NOTICE,
"Invalid GPRS Ack/Nack window %d:%d (length %d)\n",
*bsn_begin, *bsn_end, num_blocks);
return -EINVAL;
}
urbb.cur_bit = 0;
urbb.data = (uint8_t *)desc->RECEIVED_BLOCK_BITMAP;
urbb.data_len = sizeof(desc->RECEIVED_BLOCK_BITMAP);
/*
* TS 44.060, 12.3:
* BSN = (SSN - bit_number) modulo 128, for bit_number = 1 to 64.
* The BSN values represented range from (SSN - 1) mod 128 to (SSN - 64) mod 128.
*
* We are only interested in the range from V(A) to SSN-1 which is
* num_blocks large. The RBB is laid out as
* [SSN-1] [SSN-2] ... [V(A)] ... [SSN-64]
* so we want to start with [V(A)] and go backwards until we reach
* [SSN-1] to get the needed BSNs in an increasing order. Note that
* the bit numbers are counted from the end of the buffer.
*/
for (int i = num_blocks; i > 0; i--) {
int is_ack = bitvec_get_bit_pos(&urbb, urbb_len - i);
bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO);
}
return num_blocks;
}

View File

@@ -20,14 +20,62 @@
#pragma once
#include <gsm_rlcmac.h>
#include "rlc.h"
#include <stdint.h>
struct bitvec;
class Decoding {
public:
static int tlli_from_ul_data(const uint8_t *data, uint8_t len,
uint32_t *tlli);
/* represents (parts) LLC PDUs within one RLC Data block */
struct RlcData {
uint8_t offset;
uint8_t length;
bool is_complete; /* if this PDU ends in this block */
};
static int rlc_data_from_ul_data(
const struct gprs_rlc_data_block_info *rdbi,
GprsCodingScheme cs, const uint8_t *data, RlcData *chunks,
unsigned int chunks_size, uint32_t *tlli);
static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
static uint8_t get_egprs_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
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(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
const uint8_t *src, uint8_t *buffer);
static const uint8_t *rlc_get_data_aligned(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
const uint8_t *src, uint8_t *buffer);
static int decode_egprs_acknack_bits(
const EGPRS_AckNack_Desc_t *desc,
struct bitvec *bits, int *bsn_begin, int *bsn_end,
struct gprs_rlc_dl_window *window);
static int decode_gprs_acknack_bits(
const Ack_Nack_Description_t *desc,
bitvec *bits, int *bsn_begin, int *bsn_end,
gprs_rlc_dl_window *window);
};

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -22,10 +22,16 @@
#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;
struct bitvec;
struct gprs_llc;
struct gprs_rlc_data_block_info;
/**
* I help with encoding data into CSN1 messages.
@@ -36,28 +42,45 @@ struct bitvec;
class Encoding {
public:
static int write_immediate_assignment(
struct gprs_rlcmac_bts *bts,
bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma,
int8_t ta_idx);
struct gprs_rlcmac_tbf *tbf,
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,
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,
bitvec * dest, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t alpha,
uint8_t gamma, int8_t ta_idx);
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t rrbp,
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
int8_t use_egprs);
static void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts);
static void write_packet_downlink_assignment(RlcMacDownlink_t * block,
bool old_tfi_is_valid, uint8_t old_tfi, uint8_t old_downlink,
struct gprs_rlcmac_dl_tbf *tbf, uint8_t poll, uint8_t rrbp,
uint8_t alpha, uint8_t gamma,
int8_t ta_idx, uint8_t ta_ts, bool use_egprs);
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
static void write_packet_uplink_ack(struct gprs_rlcmac_bts *bts, RlcMacDownlink_t * block, struct gprs_rlcmac_ul_tbf *tbf,
uint8_t final);
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,
uint8_t rrbp);
static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len);
@@ -65,4 +88,23 @@ public:
uint8_t *identity, uint8_t chan_needed);
static unsigned write_packet_paging_request(bitvec * dest);
static int rlc_write_dl_data_header(
const struct gprs_rlc_data_info *rlc,
uint8_t *data);
static unsigned int rlc_copy_from_aligned_buffer(
const struct gprs_rlc_data_info *rlc,
unsigned int data_block_idx,
uint8_t *dst, const uint8_t *buffer);
enum AppendResult {
AR_NEED_MORE_BLOCKS,
AR_COMPLETED_SPACE_LEFT,
AR_COMPLETED_BLOCK_FILLED,
};
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, int *count_payload);
};

View File

@@ -21,8 +21,11 @@
#include <gprs_rlcmac.h>
#include <gprs_bssgp_pcu.h>
#include <pcu_l1_if.h>
#include <gprs_debug.h>
#include <bts.h>
#include <tbf.h>
#include <gprs_coding_scheme.h>
#include <pdch.h>
#define BSSGP_TIMER_T1 30 /* Guards the (un)blocking procedures */
#define BSSGP_TIMER_T2 30 /* Guards the reset procedure */
@@ -41,6 +44,7 @@ static struct gprs_bssgp_pcu the_pcu = { 0, };
extern void *tall_pcu_ctx;
extern uint16_t spoof_mcc, spoof_mnc;
extern bool spoof_mnc_3_digits;
static void bvc_timeout(void *_priv);
@@ -73,45 +77,33 @@ static int parse_imsi(struct tlv_parsed *tp, char *imsi)
return 0;
}
static int parse_ra_cap_ms_class(struct tlv_parsed *tp)
#if 0
static int parse_ra_cap(struct tlv_parsed *tp, MS_Radio_Access_capability_t *rac)
{
bitvec *block;
unsigned rp = 0;
uint8_t ms_class = 0;
uint8_t cap_len;
uint8_t *cap;
memset(rac, 0, sizeof(*rac));
if (!TLVP_PRESENT(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP))
return ms_class;
return -EINVAL;
cap_len = TLVP_LEN(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
cap = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
block = bitvec_alloc(cap_len);
LOGP(DBSSGP, LOGL_DEBUG, "Got BSSGP RA Capability of size %d\n", cap_len);
block = bitvec_alloc(cap_len, tall_pcu_ctx);
bitvec_unpack(block, cap);
bitvec_read_field(block, rp, 4); // Access Technology Type
bitvec_read_field(block, rp, 7); // Length of Access Capabilities
bitvec_read_field(block, rp, 3); // RF Power Capability
if (bitvec_read_field(block, rp, 1)) // A5 Bits Present
bitvec_read_field(block, rp, 7); // A5 Bits
bitvec_read_field(block, rp, 1); // ES IND
bitvec_read_field(block, rp, 1); // PS
bitvec_read_field(block, rp, 1); // VGCS
bitvec_read_field(block, rp, 1); // VBS
if (bitvec_read_field(block, rp, 1)) { // Multislot Cap Present
if (bitvec_read_field(block, rp, 1)) // HSCSD Present
bitvec_read_field(block, rp, 5); // Class
if (bitvec_read_field(block, rp, 1)) { // GPRS Present
ms_class = bitvec_read_field(block, rp, 5); // Class
bitvec_read_field(block, rp, 1); // Ext.
}
if (bitvec_read_field(block, rp, 1)) // SMS Present
bitvec_read_field(block, rp, 4); // SMS Value
}
/* TS 24.008, 10.5.5.12a */
decode_gsm_ra_cap(block, rac);
bitvec_free(block);
return ms_class;
return 0;
}
#endif
static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
{
@@ -122,6 +114,11 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
uint8_t *data;
uint16_t len;
char imsi[16] = "000";
uint8_t ms_class = 0;
uint8_t egprs_ms_class = 0;
#if 0
MS_Radio_Access_capability_t rac;
#endif
budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
tlli = ntohl(budh->tlli);
@@ -146,17 +143,25 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
* will listen to all paging blocks. */
parse_imsi(tp, imsi);
#if 0 /* Do not rely on this IE. TODO: make this configurable */
/* parse ms radio access capability */
uint8_t ms_class = parse_ra_cap_ms_class(tp);
if (parse_ra_cap(tp, &rac) >= 0) {
/* Get the EGPRS class from the RA capability */
ms_class = Decoding::get_ms_class_by_capability(&rac);
egprs_ms_class =
Decoding::get_egprs_ms_class_by_capability(&rac);
LOGP(DBSSGP, LOGL_DEBUG, "Got downlink MS class %d/%d\n",
ms_class, egprs_ms_class);
}
#endif
/* get lifetime */
uint16_t delay_csec = 0xffff;
if (TLVP_PRESENT(tp, BSSGP_IE_PDU_LIFETIME))
{
uint8_t lt_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME);
uint16_t *lt = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_PDU_LIFETIME);
if (lt_len == 2)
delay_csec = ntohs(*lt);
delay_csec = tlvp_val16be(tp, BSSGP_IE_PDU_LIFETIME);
else
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
"PDU_LIFETIME IE\n");
@@ -168,9 +173,8 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
{
uint8_t tlli_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME);
uint16_t *e_tlli_old = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_TLLI);
if (tlli_len == 2)
tlli_old = ntohs(*e_tlli_old);
tlli_old = tlvp_val16be(tp, BSSGP_IE_TLLI);
else
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
"TLLI (old) IE\n");
@@ -179,7 +183,7 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
return gprs_rlcmac_dl_tbf::handle(the_pcu.bts, tlli, tlli_old, imsi,
ms_class, delay_csec, data, len);
ms_class, egprs_ms_class, delay_csec, data, len);
}
int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
@@ -208,7 +212,8 @@ int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
static int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
{
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
uint8_t pdu_type = bgph->pdu_type;
enum bssgp_pdu_type pdu_type = (enum bssgp_pdu_type) bgph->pdu_type;
int bvci = bctx ? bctx->bvci : -1;
unsigned rc = 0;
if (!bctx)
@@ -225,29 +230,52 @@ static int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct
}
switch (pdu_type) {
case BSSGP_PDUT_STATUS:
/* already handled in libosmogb */
OSMO_ASSERT(0);
break;
case BSSGP_PDUT_DL_UNITDATA:
LOGP(DBSSGP, LOGL_DEBUG, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
LOGP(DBSSGP, LOGL_DEBUG, "Rx BSSGP BVCI=%d (PTP) DL_UNITDATA\n", bvci);
if (the_pcu.on_dl_unit_data)
the_pcu.on_dl_unit_data(&the_pcu, msg, tp);
gprs_bssgp_pcu_rx_dl_ud(msg, tp);
break;
case BSSGP_PDUT_PAGING_PS:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
break;
case BSSGP_PDUT_PAGING_CS:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
break;
case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RA_CAPA_UPDATE_ACK\n");
break;
case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_BVC_ACK\n");
break;
case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_MS_ACK\n");
LOGP(DBSSGP, LOGL_DEBUG, "Rx BSSGP BVCI=%d (PTP) %s\n",
bvci, bssgp_pdu_str(pdu_type));
break;
case BSSGP_PDUT_PAGING_PS:
case BSSGP_PDUT_PAGING_CS:
case BSSGP_PDUT_RA_CAPABILITY:
case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
LOGP(DBSSGP, LOGL_INFO, "Rx BSSGP BVCI=%d (PTP) PDU type %s not implemented\n",
bvci, bssgp_pdu_str(pdu_type));
break;
/* See TS 08.18 5.4.1 */
case BSSGP_PDUT_SUSPEND:
case BSSGP_PDUT_SUSPEND_ACK:
case BSSGP_PDUT_SUSPEND_NACK:
case BSSGP_PDUT_RESUME:
case BSSGP_PDUT_RESUME_ACK:
case BSSGP_PDUT_RESUME_NACK:
case BSSGP_PDUT_FLUSH_LL:
case BSSGP_PDUT_FLUSH_LL_ACK:
case BSSGP_PDUT_LLC_DISCARD:
case BSSGP_PDUT_BVC_BLOCK:
case BSSGP_PDUT_BVC_BLOCK_ACK:
case BSSGP_PDUT_BVC_UNBLOCK:
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
case BSSGP_PDUT_BVC_RESET:
case BSSGP_PDUT_BVC_RESET_ACK:
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%u (PTP) PDU type %s unexpected at PTP\n",
bctx->bvci, bssgp_pdu_str(pdu_type));
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
default:
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%u (PTP) PDU type %s unknown\n",
bctx->bvci, bssgp_pdu_str(pdu_type));
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
}
@@ -258,22 +286,22 @@ static int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct
static int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx)
{
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
enum bssgp_pdu_type pdu_type = (enum bssgp_pdu_type) bgph->pdu_type;
int rc = 0;
int bvci = bctx ? bctx->bvci : -1;
switch (bgph->pdu_type) {
switch (pdu_type) {
case BSSGP_PDUT_STATUS:
/* Some exception has occurred */
DEBUGP(DBSSGP, "BSSGP BVCI=%d Rx BVC STATUS\n", bvci);
/* FIXME: send NM_STATUS.ind to NM */
/* already handled in libosmogb */
OSMO_ASSERT(0);
break;
case BSSGP_PDUT_SUSPEND_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
break;
case BSSGP_PDUT_SUSPEND_NACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_NACK\n");
case BSSGP_PDUT_RESUME_ACK:
case BSSGP_PDUT_BVC_BLOCK_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "Rx BSSGP BVCI=%d (SIGN) %s\n",
bvci, bssgp_pdu_str(pdu_type));
break;
case BSSGP_PDUT_BVC_RESET_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%d (SIGN) BVC_RESET_ACK\n", bvci);
if (!the_pcu.bvc_sig_reset)
the_pcu.bvc_sig_reset = 1;
else
@@ -281,37 +309,49 @@ static int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struc
bvc_timeout(NULL);
break;
case BSSGP_PDUT_PAGING_PS:
LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_PAGING_PS\n");
gprs_bssgp_pcu_rx_paging_ps(msg, tp);
break;
case BSSGP_PDUT_PAGING_CS:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
break;
case BSSGP_PDUT_RESUME_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_ACK\n");
break;
case BSSGP_PDUT_RESUME_NACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_NACK\n");
break;
case BSSGP_PDUT_FLUSH_LL:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLUSH_LL\n");
break;
case BSSGP_PDUT_BVC_BLOCK_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
break;
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%d (SIGN) BVC_UNBLOCK_ACK\n", bvci);
the_pcu.bvc_unblocked = 1;
if (the_pcu.on_unblock_ack)
the_pcu.on_unblock_ack(&the_pcu);
bvc_timeout(NULL);
break;
case BSSGP_PDUT_SUSPEND_NACK:
case BSSGP_PDUT_RESUME_NACK:
case BSSGP_PDUT_PAGING_CS:
case BSSGP_PDUT_FLUSH_LL:
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
LOGP(DBSSGP, LOGL_INFO, "Rx BSSGP BVCI=%d (SIGN) PDU type %s not implemented\n",
bvci, bssgp_pdu_str(pdu_type));
break;
/* See TS 08.18 5.4.1 */
case BSSGP_PDUT_UL_UNITDATA:
case BSSGP_PDUT_DL_UNITDATA:
case BSSGP_PDUT_RA_CAPABILITY:
case BSSGP_PDUT_PTM_UNITDATA:
case BSSGP_PDUT_RA_CAPA_UDPATE:
case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
case BSSGP_PDUT_RADIO_STATUS:
case BSSGP_PDUT_FLOW_CONTROL_BVC:
case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
case BSSGP_PDUT_FLOW_CONTROL_MS:
case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
case BSSGP_PDUT_CREATE_BSS_PFC:
case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
case BSSGP_PDUT_MODIFY_BSS_PFC:
case BSSGP_PDUT_MODIFY_BSS_PFC_ACK:
case BSSGP_PDUT_DELETE_BSS_PFC:
case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%d (SIGN) PDU type %s unexpected at SIGN\n",
bvci, bssgp_pdu_str(pdu_type));
break;
default:
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%d Rx PDU type 0x%02x unknown\n",
bvci, bgph->pdu_type);
LOGP(DBSSGP, LOGL_NOTICE, "Rx BSSGP BVCI=%d (SIGN) PDU type %s unknown\n",
bvci, bssgp_pdu_str(pdu_type));
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
break;
}
@@ -323,15 +363,16 @@ static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
struct tlv_parsed tp;
uint8_t pdu_type = bgph->pdu_type;
uint16_t ns_bvci = msgb_bvci(msg);
enum bssgp_pdu_type pdu_type = (enum bssgp_pdu_type) bgph->pdu_type;
enum gprs_bssgp_cause cause = BSSGP_CAUSE_OML_INTERV;
uint16_t ns_bvci = msgb_bvci(msg), nsei = msgb_nsei(msg);
int data_len;
int rc = 0;
struct bssgp_bvc_ctx *bctx;
if (pdu_type == BSSGP_PDUT_STATUS)
/* Pass the message to the generic BSSGP parser, which handles
* STATUS message in either direction. */
* STATUS and RESET messages in either direction. */
return bssgp_rcvmsg(msg);
/* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
@@ -348,6 +389,23 @@ static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
rc = bssgp_tlv_parse(&tp, budh->data, data_len);
}
if (pdu_type == BSSGP_PDUT_BVC_RESET) {
rc = bssgp_rcvmsg(msg);
if (ns_bvci != BVCI_SIGNALLING)
return rc;
if (TLVP_PRES_LEN(&tp, BSSGP_IE_CAUSE, 1))
cause = (enum gprs_bssgp_cause)*TLVP_VAL(&tp, BSSGP_IE_CAUSE);
else
LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u BVC RESET without cause?!\n", nsei);
rc = bssgp_tx_bvc_ptp_reset(nsei, cause);
if (rc < 0)
LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u BVC PTP reset procedure failed: %d\n", nsei, rc);
return rc;
}
/* look-up or create the BTS context for this BVC */
bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
@@ -356,15 +414,14 @@ static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
&& pdu_type != BSSGP_PDUT_BVC_UNBLOCK_ACK
&& pdu_type != BSSGP_PDUT_PAGING_PS)
{
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
"type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
pdu_type);
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU type %s for unknown BVCI\n",
nsei, ns_bvci, bssgp_pdu_str(pdu_type));
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
}
if (bctx)
{
log_set_context(BSC_CTX_BVC, bctx);
log_set_context(LOG_CTX_GB_BVC, bctx);
rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN], msgb_bssgp_len(msg));
}
@@ -503,6 +560,11 @@ static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
}
break;
case S_NS_ALIVE_EXP:
LOGP(DPCU, LOGL_NOTICE, "Tns alive expired too often, "
"re-starting RESET procedure\n");
gprs_ns_reconnect(nssd->nsvc);
break;
}
return 0;
@@ -527,19 +589,12 @@ static unsigned count_pdch(const struct gprs_rlcmac_bts *bts)
return num_pdch;
}
static uint32_t gprs_bssgp_max_leak_rate(unsigned cs, int num_pdch)
static uint32_t gprs_bssgp_max_leak_rate(GprsCodingScheme cs, int num_pdch)
{
static const uint32_t max_lr_per_ts[4] = {
20 * (1000 / 20), /* CS-1: 20 byte payload per 20ms */
30 * (1000 / 20), /* CS-2: 30 byte payload per 20ms */
36 * (1000 / 20), /* CS-3: 36 byte payload per 20ms */
50 * (1000 / 20), /* CS-4: 50 byte payload per 20ms */
};
int bytes_per_rlc_block = cs.maxDataBlockBytes() * cs.numDataBlocks();
if (cs > ARRAY_SIZE(max_lr_per_ts))
cs = 1;
return max_lr_per_ts[cs-1] * num_pdch;
/* n byte payload per 20ms */
return bytes_per_rlc_block * (1000 / 20) * num_pdch;
}
static uint32_t compute_bucket_size(struct gprs_rlcmac_bts *bts,
@@ -586,6 +641,71 @@ static uint32_t get_and_reset_avg_queue_delay(void)
return avg_delay_ms;
}
static int get_and_reset_measured_leak_rate(int *usage_by_1000, unsigned num_pdch)
{
int rate; /* byte per second */
if (the_pcu.queue_frames_sent == 0)
return -1;
if (the_pcu.queue_frames_recv == 0)
return -1;
*usage_by_1000 = the_pcu.queue_frames_recv * 1000 /
the_pcu.queue_frames_sent;
/* 20ms/num_pdch is the average RLC block duration, so the rate is
* calculated as:
* rate = bytes_recv / (block_dur * block_count) */
rate = the_pcu.queue_bytes_recv * 1000 * num_pdch /
(20 * the_pcu.queue_frames_recv);
the_pcu.queue_frames_sent = 0;
the_pcu.queue_bytes_recv = 0;
the_pcu.queue_frames_recv = 0;
return rate;
}
static GprsCodingScheme max_coding_scheme_dl(struct gprs_rlcmac_bts *bts)
{
int num;
if (bts->egprs_enabled) {
if (!bts->cs_adj_enabled) {
if (bts->initial_mcs_dl)
num = bts->initial_mcs_dl;
else
num = 1;
} else if (bts->max_mcs_dl) {
num = bts->max_mcs_dl;
} else {
num = 9;
}
return GprsCodingScheme::getEgprsByNum(num);
}
if (!bts->cs_adj_enabled) {
if (bts->initial_cs_dl)
num = bts->initial_cs_dl;
else if (bts->cs4)
num = 4;
else if (bts->cs3)
num = 3;
else if (bts->cs2)
num = 2;
else
num = 1;
} else if (bts->max_cs_dl) {
num = bts->max_cs_dl;
} else {
num = 4;
}
return GprsCodingScheme::getGprsByNum(num);
}
int gprs_bssgp_tx_fc_bvc(void)
{
struct gprs_rlcmac_bts *bts;
@@ -595,7 +715,7 @@ int gprs_bssgp_tx_fc_bvc(void)
uint32_t ms_leak_rate; /* oct/s */
uint32_t avg_delay_ms;
int num_pdch = -1;
int max_cs_dl;
GprsCodingScheme max_cs_dl;
if (!the_pcu.bctx) {
LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
@@ -603,27 +723,33 @@ int gprs_bssgp_tx_fc_bvc(void)
}
bts = bts_main_data();
if (bts->cs_adj_enabled) {
max_cs_dl = bts->max_cs_dl;
if (!max_cs_dl) {
if (bts->cs4)
max_cs_dl = 4;
else if (bts->cs3)
max_cs_dl = 3;
else if (bts->cs2)
max_cs_dl = 2;
else
max_cs_dl = 1;
}
} else {
max_cs_dl = bts->initial_cs_dl;
}
max_cs_dl = max_coding_scheme_dl(bts);
bucket_size = bts->fc_bvc_bucket_size;
leak_rate = bts->fc_bvc_leak_rate;
ms_bucket_size = bts->fc_ms_bucket_size;
ms_leak_rate = bts->fc_ms_leak_rate;
if (leak_rate == 0) {
int meas_rate;
int usage; /* in 0..1000 */
if (num_pdch < 0)
num_pdch = count_pdch(bts);
meas_rate = get_and_reset_measured_leak_rate(&usage, num_pdch);
if (meas_rate > 0) {
leak_rate = gprs_bssgp_max_leak_rate(max_cs_dl, num_pdch);
leak_rate =
(meas_rate * usage + leak_rate * (1000 - usage)) /
1000;
LOGP(DBSSGP, LOGL_DEBUG,
"Estimated BVC leak rate = %d "
"(measured %d, usage %d%%)\n",
leak_rate, meas_rate, usage/10);
}
}
if (leak_rate == 0) {
if (num_pdch < 0)
num_pdch = count_pdch(bts);
@@ -631,8 +757,8 @@ int gprs_bssgp_tx_fc_bvc(void)
leak_rate = gprs_bssgp_max_leak_rate(max_cs_dl, num_pdch);
LOGP(DBSSGP, LOGL_DEBUG,
"Computed BVC leak rate = %d, num_pdch = %d, cs = %d\n",
leak_rate, num_pdch, max_cs_dl);
"Computed BVC leak rate = %d, num_pdch = %d, cs = %s\n",
leak_rate, num_pdch, max_cs_dl.name());
};
if (ms_leak_rate == 0) {
@@ -654,8 +780,9 @@ int gprs_bssgp_tx_fc_bvc(void)
* should be derived from the max number of PDCH TS per TRX.
*/
LOGP(DBSSGP, LOGL_DEBUG,
"Computed MS default leak rate = %d, ms_num_pdch = %d, cs = %d\n",
ms_leak_rate, ms_num_pdch, max_cs_dl);
"Computed MS default leak rate = %d, ms_num_pdch = %d, "
"cs = %s\n",
ms_leak_rate, ms_num_pdch, max_cs_dl.name());
};
/* TODO: Force leak_rate to 0 on buffer bloat */
@@ -723,20 +850,41 @@ static void bvc_timeout(void *_priv)
osmo_timer_schedule(&the_pcu.bvc_timer, the_pcu.bts->fc_interval, 0);
}
int gprs_ns_reconnect(struct gprs_nsvc *nsvc)
{
struct gprs_nsvc *nsvc2;
if (!bssgp_nsi) {
LOGP(DBSSGP, LOGL_ERROR, "NS instance does not exist\n");
return -EINVAL;
}
if (nsvc != the_pcu.nsvc) {
LOGP(DBSSGP, LOGL_ERROR, "NSVC is invalid\n");
return -EBADF;
}
nsvc2 = gprs_ns_nsip_connect(bssgp_nsi, &nsvc->ip.bts_addr,
nsvc->nsei, nsvc->nsvci);
if (!nsvc2) {
LOGP(DBSSGP, LOGL_ERROR, "Failed to reconnect NSVC\n");
return -EIO;
}
return 0;
}
/* create BSSGP/NS layer instances */
struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts,
uint16_t local_port, uint32_t sgsn_ip,
uint16_t sgsn_port, uint16_t nsei, uint16_t nsvci, uint16_t bvci,
uint16_t mcc, uint16_t mnc, uint16_t lac, uint16_t rac,
uint16_t mcc, uint16_t mnc, bool mnc_3_digits, uint16_t lac, uint16_t rac,
uint16_t cell_id)
{
struct sockaddr_in dest;
int rc;
mcc = ((mcc & 0xf00) >> 8) * 100 + ((mcc & 0x0f0) >> 4) * 10 + (mcc & 0x00f);
mnc = ((mnc & 0xf00) >> 8) * 100 + ((mnc & 0x0f0) >> 4) * 10 + (mnc & 0x00f);
cell_id = ntohs(cell_id);
/* if already created... return the current address */
if (the_pcu.bctx)
return &the_pcu;
@@ -749,6 +897,8 @@ struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts
return NULL;
}
gprs_ns_vty_init(bssgp_nsi);
bssgp_nsi->nsip.remote_port = sgsn_port;
bssgp_nsi->nsip.remote_ip = sgsn_ip;
bssgp_nsi->nsip.local_port = local_port;
rc = gprs_ns_nsip_listen(bssgp_nsi);
if (rc < 0) {
@@ -779,7 +929,13 @@ struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts
return NULL;
}
the_pcu.bctx->ra_id.mcc = spoof_mcc ? : mcc;
the_pcu.bctx->ra_id.mnc = spoof_mnc ? : mnc;
if (spoof_mnc) {
the_pcu.bctx->ra_id.mnc = spoof_mnc;
the_pcu.bctx->ra_id.mnc_3_digits = spoof_mnc_3_digits;
} else {
the_pcu.bctx->ra_id.mnc = mnc;
the_pcu.bctx->ra_id.mnc_3_digits = mnc_3_digits;
}
the_pcu.bctx->ra_id.lac = lac;
the_pcu.bctx->ra_id.rac = rac;
the_pcu.bctx->cell_id = cell_id;
@@ -794,28 +950,33 @@ struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts
void gprs_bssgp_destroy(void)
{
if (!bssgp_nsi)
struct gprs_ns_inst *nsi = bssgp_nsi;
if (!nsi)
return;
bssgp_nsi = NULL;
osmo_timer_del(&the_pcu.bvc_timer);
osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
the_pcu.nsvc = NULL;
/* FIXME: move this to libgb: btsctx_free() */
llist_del(&the_pcu.bctx->list);
talloc_free(the_pcu.bctx);
the_pcu.bctx = NULL;
/* FIXME: blocking... */
the_pcu.nsvc_unblocked = 0;
the_pcu.bvc_sig_reset = 0;
the_pcu.bvc_reset = 0;
the_pcu.bvc_unblocked = 0;
gprs_ns_destroy(bssgp_nsi);
bssgp_nsi = NULL;
gprs_ns_destroy(nsi);
/* FIXME: move this to libgb: btsctx_free() */
llist_del(&the_pcu.bctx->list);
#warning "This causes ASAN to complain. It is not critical for normal operation but should be fixed nevertheless"
#if 0
talloc_free(the_pcu.bctx);
#endif
the_pcu.bctx = NULL;
}
struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void)
@@ -823,6 +984,17 @@ struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void)
return the_pcu.bctx;
}
void gprs_bssgp_update_frames_sent()
{
the_pcu.queue_frames_sent += 1;
}
void gprs_bssgp_update_bytes_received(unsigned bytes_recv, unsigned frames_recv)
{
the_pcu.queue_bytes_recv += bytes_recv;
the_pcu.queue_frames_recv += frames_recv;
}
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
const struct timeval *tv_now)
{

View File

@@ -42,8 +42,6 @@ struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
#define NS_HDR_LEN 4
#define IE_LLC_PDU 14
struct gprs_rlcmac_bts;
struct gprs_bssgp_pcu {
struct gprs_nsvc *nsvc;
struct bssgp_bvc_ctx *bctx;
@@ -62,6 +60,9 @@ struct gprs_bssgp_pcu {
struct timeval queue_delay_sum;
unsigned queue_delay_count;
uint8_t fc_tag;
unsigned queue_frames_sent;
unsigned queue_bytes_recv;
unsigned queue_frames_recv;
/** callbacks below */
@@ -76,14 +77,17 @@ struct gprs_bssgp_pcu {
struct gprs_bssgp_pcu *gprs_bssgp_create_and_connect(struct gprs_rlcmac_bts *bts,
uint16_t local_port,
uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc,
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, bool mnc_3_digits,
uint16_t lac, uint16_t rac, uint16_t cell_id);
void gprs_bssgp_destroy(void);
int gprs_ns_reconnect(struct gprs_nsvc *nsvc);
struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void);
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
const struct timeval *tv_now);
void gprs_bssgp_update_frames_sent();
void gprs_bssgp_update_bytes_received(unsigned bytes_recv, unsigned frames_recv);
#endif // GPRS_BSSGP_PCU_H

318
src/gprs_coding_scheme.cpp Normal file
View File

@@ -0,0 +1,318 @@
/* gprs_coding_scheme.cpp
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "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 {
uint8_t bytes;
uint8_t ext_bits;
uint8_t data_header_bits;
} uplink, downlink;
uint8_t data_bytes;
uint8_t optional_padding_bits;
const char *name;
GprsCodingScheme::HeaderType data_hdr;
GprsCodingScheme::Family family;
} mcs_info[GprsCodingScheme::NUM_SCHEMES] = {
{{0, 0}, {0, 0}, 0, 0, "UNKNOWN",
GprsCodingScheme::HEADER_INVALID, GprsCodingScheme::FAMILY_INVALID},
{{23, 0}, {23, 0}, 20, 0, "CS-1",
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
{{33, 7}, {33, 7}, 30, 0, "CS-2",
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
{{39, 3}, {39, 3}, 36, 0, "CS-3",
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
{{53, 7}, {53, 7}, 50, 0, "CS-4",
GprsCodingScheme::HEADER_GPRS_DATA, GprsCodingScheme::FAMILY_INVALID},
{{26, 1}, {26, 1}, 22, 0, "MCS-1",
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_C},
{{32, 1}, {32, 1}, 28, 0, "MCS-2",
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_B},
{{41, 1}, {41, 1}, 37, 48, "MCS-3",
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_A},
{{48, 1}, {48, 1}, 44, 0, "MCS-4",
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3, GprsCodingScheme::FAMILY_C},
{{60, 7}, {59, 6}, 56, 0, "MCS-5",
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2, GprsCodingScheme::FAMILY_B},
{{78, 7}, {77, 6}, 74, 48, "MCS-6",
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2, GprsCodingScheme::FAMILY_A},
{{118, 2}, {117, 4}, 56, 0, "MCS-7",
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_B},
{{142, 2}, {141, 4}, 68, 0, "MCS-8",
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_A},
{{154, 2}, {153, 4}, 74, 0, "MCS-9",
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1, GprsCodingScheme::FAMILY_A},
};
static struct {
struct {
uint8_t data_header_bits;
} uplink, downlink;
uint8_t data_block_header_bits;
uint8_t num_blocks;
const char *name;
} hdr_type_info[GprsCodingScheme::NUM_HEADER_TYPES] = {
{{0}, {0}, 0, 0, "INVALID"},
{{1*8 + 0}, {1*8 + 0}, 0, 0, "CONTROL"},
{{3*8 + 0}, {3*8 + 0}, 0, 1, "GPRS_DATA"},
{{5*8 + 6}, {5*8 + 0}, 2, 2, "EGPRS_DATA_TYPE1"},
{{4*8 + 5}, {3*8 + 4}, 2, 1, "EGPRS_DATA_TYPE2"},
{{3*8 + 7}, {3*8 + 7}, 2, 1, "EGPRS_DATA_TYPE3"},
};
GprsCodingScheme GprsCodingScheme::getBySizeUL(unsigned size)
{
switch (size) {
case 23: return GprsCodingScheme(CS1);
case 27: return GprsCodingScheme(MCS1);
case 33: return GprsCodingScheme(MCS2);
case 34: return GprsCodingScheme(CS2);
case 40: return GprsCodingScheme(CS3);
case 42: return GprsCodingScheme(MCS3);
case 49: return GprsCodingScheme(MCS4);
case 54: return GprsCodingScheme(CS4);
case 61: return GprsCodingScheme(MCS5);
case 79: return GprsCodingScheme(MCS6);
case 119: return GprsCodingScheme(MCS7);
case 143: return GprsCodingScheme(MCS8);
case 155: return GprsCodingScheme(MCS9);
}
return GprsCodingScheme(UNKNOWN);
}
uint8_t GprsCodingScheme::sizeUL() const
{
return mcs_info[m_scheme].uplink.bytes + (spareBitsUL() ? 1 : 0);
}
uint8_t GprsCodingScheme::usedSizeUL() const
{
if (mcs_info[m_scheme].data_hdr == HEADER_GPRS_DATA)
return mcs_info[m_scheme].uplink.bytes;
else
return sizeUL();
}
uint8_t GprsCodingScheme::maxBytesUL() const
{
return mcs_info[m_scheme].uplink.bytes;
}
uint8_t GprsCodingScheme::spareBitsUL() const
{
return mcs_info[m_scheme].uplink.ext_bits;
}
uint8_t GprsCodingScheme::sizeDL() const
{
return mcs_info[m_scheme].downlink.bytes + (spareBitsDL() ? 1 : 0);
}
uint8_t GprsCodingScheme::usedSizeDL() const
{
if (mcs_info[m_scheme].data_hdr == HEADER_GPRS_DATA)
return mcs_info[m_scheme].downlink.bytes;
else
return sizeDL();
}
uint8_t GprsCodingScheme::maxBytesDL() const
{
return mcs_info[m_scheme].downlink.bytes;
}
uint8_t GprsCodingScheme::spareBitsDL() const
{
return mcs_info[m_scheme].downlink.ext_bits;
}
uint8_t GprsCodingScheme::maxDataBlockBytes() const
{
return mcs_info[m_scheme].data_bytes;
}
uint8_t GprsCodingScheme::optionalPaddingBits() const
{
return mcs_info[m_scheme].optional_padding_bits;
}
uint8_t GprsCodingScheme::numDataBlocks() const
{
return hdr_type_info[headerTypeData()].num_blocks;
}
uint8_t GprsCodingScheme::numDataHeaderBitsUL() const
{
return hdr_type_info[headerTypeData()].uplink.data_header_bits;
}
uint8_t GprsCodingScheme::numDataHeaderBitsDL() const
{
return hdr_type_info[headerTypeData()].downlink.data_header_bits;
}
uint8_t GprsCodingScheme::numDataBlockHeaderBits() const
{
return hdr_type_info[headerTypeData()].data_block_header_bits;
}
const char *GprsCodingScheme::name() const
{
return mcs_info[m_scheme].name;
}
GprsCodingScheme::HeaderType GprsCodingScheme::headerTypeData() const
{
return mcs_info[m_scheme].data_hdr;
}
GprsCodingScheme::Family GprsCodingScheme::family() const
{
return mcs_info[m_scheme].family;
}
void GprsCodingScheme::inc(Mode mode)
{
if (!isCompatible(mode))
/* This should not happen. TODO: Use assert? */
return;
Scheme new_cs(Scheme(m_scheme + 1));
if (!GprsCodingScheme(new_cs).isCompatible(mode))
/* Clipping, do not change the value */
return;
m_scheme = new_cs;
}
void GprsCodingScheme::dec(Mode mode)
{
if (!isCompatible(mode))
/* This should not happen. TODO: Use assert? */
return;
Scheme new_cs(Scheme(m_scheme - 1));
if (!GprsCodingScheme(new_cs).isCompatible(mode))
/* Clipping, do not change the value */
return;
m_scheme = new_cs;
}
void GprsCodingScheme::inc()
{
if (isGprs() && m_scheme == CS4)
return;
if (isEgprs() && m_scheme == MCS9)
return;
if (!isValid())
return;
m_scheme = Scheme(m_scheme + 1);
}
void GprsCodingScheme::dec()
{
if (isGprs() && m_scheme == CS1)
return;
if (isEgprs() && m_scheme == MCS1)
return;
if (!isValid())
return;
m_scheme = Scheme(m_scheme - 1);
}
const char *GprsCodingScheme::modeName(Mode mode)
{
switch (mode) {
case GPRS: return "GPRS";
case EGPRS_GMSK: return "EGPRS_GMSK-only";
case EGPRS: return "EGPRS";
default: return "???";
}
}
bool GprsCodingScheme::isFamilyCompatible(GprsCodingScheme o) const
{
if (*this == o)
return true;
if (family() == FAMILY_INVALID)
return false;
return family() == o.family();
}
bool GprsCodingScheme::isCombinable(GprsCodingScheme o) const
{
return numDataBlocks() == o.numDataBlocks();
}
void GprsCodingScheme::decToSingleBlock(bool *needStuffing)
{
switch (m_scheme) {
case MCS7: *needStuffing = false; m_scheme = MCS5; break;
case MCS8: *needStuffing = true; m_scheme = MCS6; break;
case MCS9: *needStuffing = false; m_scheme = MCS6; break;
default: *needStuffing = false; break;
}
}

247
src/gprs_coding_scheme.h Normal file
View File

@@ -0,0 +1,247 @@
/* gprs_coding_scheme.h
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
extern "C" {
#include <osmocom/core/utils.h>
}
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,
MCS1, MCS2, MCS3, MCS4,
MCS5, MCS6, MCS7, MCS8, MCS9,
NUM_SCHEMES
};
enum Mode {
GPRS,
EGPRS_GMSK,
EGPRS,
};
enum HeaderType {
HEADER_INVALID,
HEADER_GPRS_CONTROL,
HEADER_GPRS_DATA,
HEADER_EGPRS_DATA_TYPE_1,
HEADER_EGPRS_DATA_TYPE_2,
HEADER_EGPRS_DATA_TYPE_3,
NUM_HEADER_TYPES
};
enum Family {
FAMILY_INVALID,
FAMILY_A,
FAMILY_B,
FAMILY_C,
};
GprsCodingScheme(Scheme s = UNKNOWN);
operator bool() const {return m_scheme != UNKNOWN;}
operator Scheme() const {return m_scheme;}
uint8_t 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;}
bool isGprs() const {return CS1 <= m_scheme && m_scheme <= CS4;}
bool isEgprs() const {return m_scheme >= MCS1;}
bool isEgprsGmsk() const {return isEgprs() && m_scheme <= MCS4;}
bool isCompatible(Mode mode) const;
bool isCompatible(GprsCodingScheme o) const;
bool isFamilyCompatible(GprsCodingScheme o) const;
bool isCombinable(GprsCodingScheme o) const;
void inc(Mode mode);
void dec(Mode mode);
void inc();
void dec();
void decToSingleBlock(bool *needStuffing);
uint8_t sizeUL() const;
uint8_t sizeDL() const;
uint8_t usedSizeUL() const;
uint8_t usedSizeDL() const;
uint8_t maxBytesUL() const;
uint8_t maxBytesDL() const;
uint8_t spareBitsUL() const;
uint8_t spareBitsDL() const;
uint8_t maxDataBlockBytes() const;
uint8_t numDataBlocks() const;
uint8_t numDataHeaderBitsUL() const;
uint8_t numDataHeaderBitsDL() const;
uint8_t numDataBlockHeaderBits() const;
uint8_t optionalPaddingBits() const;
const char *name() const;
HeaderType headerTypeData() const;
HeaderType headerTypeControl() const;
Family family() const;
static GprsCodingScheme getBySizeUL(unsigned size);
static GprsCodingScheme getGprsByNum(unsigned num);
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 */
enum Scheme m_scheme;
};
inline uint8_t GprsCodingScheme::to_num() const
{
if (isGprs())
return (m_scheme - CS1) + 1;
if (isEgprs())
return (m_scheme - MCS1) + 1;
return 0;
}
inline bool GprsCodingScheme::isCompatible(Mode mode) const
{
switch (mode) {
case GPRS: return isGprs();
case EGPRS_GMSK: return isEgprsGmsk();
case EGPRS: return isEgprs();
}
return false;
}
inline bool GprsCodingScheme::isCompatible(GprsCodingScheme o) const
{
return (isGprs() && o.isGprs()) || (isEgprs() && o.isEgprs());
}
inline GprsCodingScheme::HeaderType GprsCodingScheme::headerTypeControl() const
{
return HEADER_GPRS_CONTROL;
}
inline GprsCodingScheme::GprsCodingScheme(Scheme s)
: m_scheme(s)
{
if (!isValid())
m_scheme = UNKNOWN;
}
inline GprsCodingScheme& GprsCodingScheme::operator =(Scheme s)
{
m_scheme = s;
if (!isValid())
m_scheme = UNKNOWN;
return *this;
}
inline GprsCodingScheme& GprsCodingScheme::operator =(GprsCodingScheme o)
{
m_scheme = o.m_scheme;
return *this;
}
inline GprsCodingScheme GprsCodingScheme::getGprsByNum(unsigned num)
{
if (num < 1 || num > 4)
return GprsCodingScheme();
return GprsCodingScheme(Scheme(CS1 + (num - 1)));
}
inline GprsCodingScheme GprsCodingScheme::getEgprsByNum(unsigned num)
{
if (num < 1 || num > 9)
return GprsCodingScheme();
return GprsCodingScheme(Scheme(MCS1 + (num - 1)));
}
/* The coding schemes form a partial ordering */
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);
}
inline bool operator <(GprsCodingScheme a, GprsCodingScheme b)
{
return a.isCompatible(b) &&
GprsCodingScheme::Scheme(a) < GprsCodingScheme::Scheme(b);
}
inline bool operator >(GprsCodingScheme a, GprsCodingScheme b)
{
return b < a;
}
inline bool operator <=(GprsCodingScheme a, GprsCodingScheme b)
{
return a == b || a < b;
}
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)
{
OSMO_ASSERT(mcs.to_num() > 0);
OSMO_ASSERT(demanded_mcs.to_num() > 0);
return egprs_mcs_retx_tbl[arq_type][mcs.to_num() - 1]
[demanded_mcs.to_num() - 1];
}

View File

@@ -16,16 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <gprs_debug.h>
@@ -41,31 +32,28 @@ static const struct log_info_cat default_categories[] = {
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_NOTICE, 1},
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_NOTICE, 1},
{"DRLCMACMEAS", "\033[1;31m", "GPRS RLC/MAC layer Measurements (RLCMAC)", LOGL_INFO, 1},
{"DTBF","\033[1;34m", "Temporary Block Flow (TBF)", LOGL_INFO , 1},
{"DTBFDL","\033[1;34m", "Temporary Block Flow (TBF) Downlink", LOGL_INFO , 1},
{"DTBFUL","\033[1;34m", "Temporary Block Flow (TBF) Uplink", 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},
};
enum {
_FLT_ALL = LOG_FILTER_ALL, /* libosmocore */
FLT_IMSI = 1,
FLT_NSVC = 2,
FLT_BVC = 3,
};
static int filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
const struct gprs_nsvc *nsvc = (const struct gprs_nsvc*)ctx->ctx[BSC_CTX_NSVC];
const struct gprs_nsvc *bvc = (const struct gprs_nsvc*)ctx->ctx[BSC_CTX_BVC];
const struct gprs_nsvc *nsvc = (const struct gprs_nsvc*)ctx->ctx[LOG_CTX_GB_NSVC];
const struct gprs_nsvc *bvc = (const struct gprs_nsvc*)ctx->ctx[LOG_CTX_GB_BVC];
/* Filter on the NS Virtual Connection */
if ((tar->filter_map & (1 << FLT_NSVC)) != 0
&& nsvc && (nsvc == tar->filter_data[FLT_NSVC]))
if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0
&& nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC]))
return 1;
/* Filter on the BVC */
if ((tar->filter_map & (1 << FLT_BVC)) != 0
&& bvc && (bvc == tar->filter_data[FLT_BVC]))
if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0
&& bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC]))
return 1;
return 0;

View File

@@ -16,19 +16,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef GPRS_DEBUG_H
#define GPRS_DEBUG_H
#include <stdio.h>
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#ifdef __cplusplus
};
#endif
/* Debug Areas of the code */
enum {
DCSN1,
@@ -39,38 +37,13 @@ enum {
DRLCMACUL,
DRLCMACSCHED,
DRLCMACMEAS,
DTBF,
DTBFDL,
DTBFUL,
DNS,
DBSSGP,
DPCU,
aDebug_LastEntry
};
/* context */
#define BSC_CTX_SUBSCR 1
#define BSC_CTX_NSVC 4
#define BSC_CTX_BVC 5
/* target */
enum {
//DEBUG_FILTER_ALL = 1 << 0,
LOG_FILTER_IMSI = 1 << 1,
LOG_FILTER_NSVC = 1 << 2,
LOG_FILTER_BVC = 1 << 3,
};
/* we don't need a header dependency for this... */
struct gprs_nsvc;
struct bssgp_bvc_ctx;
void log_set_imsi_filter(struct log_target *target, const char *imsi);
void log_set_nsvc_filter(struct log_target *target,
struct gprs_nsvc *nsvc);
void log_set_bvc_filter(struct log_target *target,
struct bssgp_bvc_ctx *bctx);
extern const struct log_info gprs_log_info;
#endif // GPRS_DEBUG_H

View File

@@ -20,20 +20,23 @@
#include "gprs_ms.h"
#include <gprs_coding_scheme.h>
#include "bts.h"
#include "tbf.h"
#include "gprs_debug.h"
#include "gprs_codel.h"
#include "pcu_utils.h"
#include <time.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/core/logging.h>
}
#define GPRS_CODEL_SLOW_INTERVAL_MS 2000
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
extern void *tall_pcu_ctx;
@@ -94,10 +97,9 @@ 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_current_cs_ul(1),
m_current_cs_dl(1),
m_egprs_ms_class(0),
m_is_idle(true),
m_ref(0),
m_list(this),
@@ -106,7 +108,9 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
m_reserved_dl_slots(0),
m_reserved_ul_slots(0),
m_current_trx(NULL),
m_codel_state(NULL)
m_codel_state(NULL),
m_mode(GprsCodingScheme::GPRS),
m_dl_ctrl_msg(0)
{
int codel_interval = LLC_CODEL_USE_DEFAULT;
@@ -116,17 +120,11 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
memset(&m_timer, 0, sizeof(m_timer));
m_timer.cb = GprsMs::timeout;
m_llc_queue.init();
if (m_bts) {
m_current_cs_ul = m_bts->bts_data()->initial_cs_ul;
if (m_current_cs_ul < 1)
m_current_cs_ul = 1;
m_current_cs_dl = m_bts->bts_data()->initial_cs_dl;
if (m_current_cs_dl < 1)
m_current_cs_dl = 1;
set_mode(m_mode);
if (m_bts)
codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
}
if (codel_interval) {
if (codel_interval == LLC_CODEL_USE_DEFAULT)
@@ -140,6 +138,8 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
GprsMs::~GprsMs()
{
LListHead<gprs_rlcmac_tbf> *pos, *tmp;
LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
set_reserved_slots(NULL, 0, 0);
@@ -156,6 +156,10 @@ GprsMs::~GprsMs()
m_dl_tbf->set_ms(NULL);
m_dl_tbf = NULL;
}
llist_for_each_safe(pos, tmp, &m_old_tbfs)
pos->entry()->set_ms(NULL);
m_llc_queue.clear(m_bts);
}
@@ -208,12 +212,53 @@ void GprsMs::stop_timer()
unref();
}
void GprsMs::set_mode(GprsCodingScheme::Mode mode)
{
m_mode = mode;
if (!m_bts)
return;
switch (m_mode) {
case GprsCodingScheme::GPRS:
if (!m_current_cs_ul.isGprs()) {
m_current_cs_ul = GprsCodingScheme::getGprsByNum(
m_bts->bts_data()->initial_cs_ul);
if (!m_current_cs_ul.isValid())
m_current_cs_ul = GprsCodingScheme::CS1;
}
if (!m_current_cs_dl.isGprs()) {
m_current_cs_dl = GprsCodingScheme::getGprsByNum(
m_bts->bts_data()->initial_cs_dl);
if (!m_current_cs_dl.isValid())
m_current_cs_dl = GprsCodingScheme::CS1;
}
break;
case GprsCodingScheme::EGPRS_GMSK:
case GprsCodingScheme::EGPRS:
if (!m_current_cs_ul.isEgprs()) {
m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
m_bts->bts_data()->initial_mcs_ul);
if (!m_current_cs_ul.isValid())
m_current_cs_ul = GprsCodingScheme::MCS1;
}
if (!m_current_cs_dl.isEgprs()) {
m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
m_bts->bts_data()->initial_mcs_dl);
if (!m_current_cs_dl.isValid())
m_current_cs_dl = GprsCodingScheme::MCS1;
}
break;
}
}
void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
{
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
attach_dl_tbf(static_cast<gprs_rlcmac_dl_tbf *>(tbf));
attach_dl_tbf(as_dl_tbf(tbf));
else
attach_ul_tbf(static_cast<gprs_rlcmac_ul_tbf *>(tbf));
attach_ul_tbf(as_ul_tbf(tbf));
}
void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
@@ -227,7 +272,7 @@ void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
Guard guard(this);
if (m_ul_tbf)
detach_tbf(m_ul_tbf);
llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
m_ul_tbf = tbf;
@@ -246,7 +291,7 @@ void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
Guard guard(this);
if (m_dl_tbf)
detach_tbf(m_dl_tbf);
llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
m_dl_tbf = tbf;
@@ -256,12 +301,26 @@ void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
{
if (m_ul_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf))
if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
m_ul_tbf = NULL;
else if (m_dl_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf))
} else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
m_dl_tbf = NULL;
else
return;
} else {
bool found = false;
LListHead<gprs_rlcmac_tbf> *pos, *tmp;
llist_for_each_safe(pos, tmp, &m_old_tbfs) {
if (pos->entry() == tbf) {
llist_del(pos);
found = true;
break;
}
}
/* Protect against recursive calls via set_ms() */
if (!found)
return;
}
LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
tlli(), tbf->name());
@@ -297,6 +356,36 @@ void GprsMs::update_status()
}
}
void GprsMs::reset()
{
LOGP(DRLCMAC, LOGL_INFO,
"Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
tlli(), imsi());
stop_timer();
m_tlli = 0;
m_new_dl_tlli = 0;
m_new_ul_tlli = 0;
m_imsi[0] = '\0';
}
void GprsMs::merge_old_ms(GprsMs *old_ms)
{
if (old_ms == this)
return;
if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
set_imsi(old_ms->imsi());
if (!ms_class() && old_ms->ms_class())
set_ms_class(old_ms->ms_class());
m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
old_ms->reset();
}
void GprsMs::set_tlli(uint32_t tlli)
{
if (tlli == m_tlli || tlli == m_new_ul_tlli)
@@ -378,11 +467,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_)
@@ -397,13 +490,25 @@ void GprsMs::set_ms_class(uint8_t ms_class_)
m_ms_class = ms_class_;
}
void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
{
if (ms_class_ == m_egprs_ms_class)
return;
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
tlli(), m_egprs_ms_class, ms_class_);
m_egprs_ms_class = ms_class_;
}
void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
{
struct gprs_rlcmac_bts *bts_data;
int64_t now;
uint8_t max_cs_dl = 4;
GprsCodingScheme max_cs_dl = this->max_cs_dl();
OSMO_ASSERT(m_bts != NULL);
OSMO_ASSERT(max_cs_dl);
bts_data = m_bts->bts_data();
if (error_rate < 0)
@@ -411,32 +516,30 @@ void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
now = now_msec();
if (bts_data->max_cs_dl)
max_cs_dl = bts_data->max_cs_dl;
/* TODO: Check for TBF direction */
/* TODO: Support different CS values for UL and DL */
m_nack_rate_dl = error_rate;
if (error_rate > bts_data->cs_adj_upper_limit) {
if (m_current_cs_dl > 1) {
m_current_cs_dl -= 1;
if (m_current_cs_dl.to_num() > 1) {
m_current_cs_dl.dec(mode());
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): High error rate %d%%, "
"reducing CS level to %d\n",
imsi(), error_rate, m_current_cs_dl);
"reducing CS level to %s\n",
imsi(), error_rate, m_current_cs_dl.name());
m_last_cs_not_low = now;
}
} else if (error_rate < bts_data->cs_adj_lower_limit) {
if (m_current_cs_dl < max_cs_dl) {
if (now - m_last_cs_not_low > 1000) {
m_current_cs_dl += 1;
m_current_cs_dl.inc(mode());
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): Low error rate %d%%, "
"increasing DL CS level to %d\n",
imsi(), error_rate, m_current_cs_dl);
"increasing DL CS level to %s\n",
imsi(), error_rate,
m_current_cs_dl.name());
m_last_cs_not_low = now;
} else {
LOGP(DRLCMACDL, LOGL_DEBUG,
@@ -453,46 +556,131 @@ void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
}
}
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
GprsCodingScheme GprsMs::max_cs_ul() const
{
struct gprs_rlcmac_bts *bts_data;
uint8_t max_cs_ul = 4;
unsigned i;
OSMO_ASSERT(m_bts != NULL);
bts_data = m_bts->bts_data();
if (bts_data->max_cs_ul)
max_cs_ul = bts_data->max_cs_ul;
if (m_current_cs_ul.isGprs()) {
if (!bts_data->max_cs_ul)
return GprsCodingScheme(GprsCodingScheme::CS4);
if (meas->have_link_qual) {
int old_link_qual = meas->link_qual;
int low = bts_data->cs_lqual_ranges[current_cs_ul()-1].low;
int high = bts_data->cs_lqual_ranges[current_cs_ul()-1].high;
uint8_t new_cs_ul = m_current_cs_ul;
if (m_l1_meas.have_link_qual)
old_link_qual = m_l1_meas.link_qual;
if (meas->link_qual < low && old_link_qual < low)
new_cs_ul = m_current_cs_ul - 1;
else if (meas->link_qual > high && old_link_qual > high &&
m_current_cs_ul < max_cs_ul)
new_cs_ul = m_current_cs_ul + 1;
if (m_current_cs_ul != new_cs_ul) {
LOGP(DRLCMACDL, LOGL_INFO,
"MS (IMSI %s): "
"Link quality %ddB (%ddB) left window [%d, %d], "
"modifying uplink CS level: %d -> %d\n",
imsi(), meas->link_qual, old_link_qual,
low, high,
m_current_cs_ul, new_cs_ul);
m_current_cs_ul = new_cs_ul;
}
return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
}
if (!m_current_cs_ul.isEgprs())
return GprsCodingScheme(); /* UNKNOWN */
if (bts_data->max_mcs_ul)
return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
else if (bts_data->max_cs_ul)
return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
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;
OSMO_ASSERT(m_bts != NULL);
bts_data = m_bts->bts_data();
if (m_current_cs_dl.isGprs()) {
if (!bts_data->max_cs_dl)
return GprsCodingScheme(GprsCodingScheme::CS4);
return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
}
if (!m_current_cs_dl.isEgprs())
return GprsCodingScheme(); /* UNKNOWN */
if (bts_data->max_mcs_dl)
return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
else if (bts_data->max_cs_dl)
return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
return GprsCodingScheme(GprsCodingScheme::MCS4);
}
void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
{
struct gprs_rlcmac_bts *bts_data;
GprsCodingScheme max_cs_ul = this->max_cs_ul();
int old_link_qual;
int low;
int high;
GprsCodingScheme new_cs_ul = m_current_cs_ul;
unsigned current_cs_num = m_current_cs_ul.to_num();
bts_data = m_bts->bts_data();
if (!max_cs_ul) {
LOGP(DRLCMACMEAS, LOGL_ERROR,
"max_cs_ul cannot be derived (current UL CS: %s)\n",
m_current_cs_ul.name());
return;
}
OSMO_ASSERT(current_cs_num > 0);
if (!m_current_cs_ul)
return;
if (!meas->have_link_qual)
return;
old_link_qual = meas->link_qual;
if (m_current_cs_ul.isGprs()) {
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
} else if (m_current_cs_ul.isEgprs()) {
if (current_cs_num > MAX_GPRS_CS)
current_cs_num = MAX_GPRS_CS;
low = bts_data->mcs_lqual_ranges[current_cs_num-1].low;
high = bts_data->mcs_lqual_ranges[current_cs_num-1].high;
} else {
return;
}
if (m_l1_meas.have_link_qual)
old_link_qual = m_l1_meas.link_qual;
if (meas->link_qual < low && old_link_qual < low)
new_cs_ul.dec(mode());
else if (meas->link_qual > high && old_link_qual > high &&
m_current_cs_ul < max_cs_ul)
new_cs_ul.inc(mode());
if (m_current_cs_ul != new_cs_ul) {
LOGP(DRLCMACMEAS, LOGL_INFO,
"MS (IMSI %s): "
"Link quality %ddB (%ddB) left window [%d, %d], "
"modifying uplink CS level: %s -> %s\n",
imsi(), meas->link_qual, old_link_qual,
low, high,
m_current_cs_ul.name(), new_cs_ul.name());
m_current_cs_ul = new_cs_ul;
}
}
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
{
unsigned i;
update_cs_ul(meas);
if (meas->have_rssi)
m_l1_meas.set_rssi(meas->rssi);
if (meas->have_bto)
@@ -519,9 +707,9 @@ void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
}
}
uint8_t GprsMs::current_cs_dl() const
GprsCodingScheme GprsMs::current_cs_dl() const
{
uint8_t cs = m_current_cs_dl;
GprsCodingScheme cs = m_current_cs_dl;
size_t unencoded_octets;
if (!m_bts)
@@ -531,7 +719,7 @@ uint8_t GprsMs::current_cs_dl() const
/* If the DL TBF is active, add number of unencoded chunk octets */
if (m_dl_tbf)
unencoded_octets = m_dl_tbf->m_llc.chunk_size();
unencoded_octets += m_dl_tbf->m_llc.chunk_size();
/* There are many unencoded octets, don't reduce */
if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
@@ -542,11 +730,11 @@ uint8_t GprsMs::current_cs_dl() const
return cs;
/* The throughput would probably be better if the CS level was reduced */
cs -= 1;
cs.dec(mode());
/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
if (cs == 2)
cs -= 1;
if (cs == GprsCodingScheme(GprsCodingScheme::CS2))
cs.dec(mode());
return cs;
}
@@ -588,6 +776,31 @@ uint8_t GprsMs::ul_slots() const
return slots;
}
uint8_t GprsMs::current_pacch_slots() const
{
uint8_t slots = 0;
bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
if (!is_dl_active && !is_ul_active)
return 0;
/* see TS 44.060, 8.1.1.2.2 */
if (is_dl_active && !is_ul_active)
slots = m_dl_tbf->dl_slots();
else if (!is_dl_active && is_ul_active)
slots = m_ul_tbf->ul_slots();
else
slots = m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
/* Assume a multislot class 1 device */
/* TODO: For class 2 devices, this could be removed */
slots = pcu_lsb(slots);
return slots;
}
void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
uint8_t ul_slots, uint8_t dl_slots)
{

View File

@@ -20,18 +20,18 @@
#pragma once
struct gprs_rlcmac_tbf;
struct gprs_rlcmac_dl_tbf;
struct gprs_rlcmac_ul_tbf;
struct gprs_codel;
#include <gprs_coding_scheme.h>
#include "cxx_linuxlist.h"
#include "llc.h"
#include "tbf.h"
#include "pcu_l1_if.h"
#include <gprs_coding_scheme.h>
extern "C" {
#include <osmocom/core/timer.h>
#include <osmocom/core/linuxlist.h>
}
#include <stdint.h>
@@ -63,6 +63,8 @@ public:
void set_callback(Callback *cb) {m_cb = cb;}
void merge_old_ms(GprsMs *old_ms);
gprs_rlcmac_ul_tbf *ul_tbf() const {return m_ul_tbf;}
gprs_rlcmac_dl_tbf *dl_tbf() const {return m_dl_tbf;}
gprs_rlcmac_tbf *tbf(enum gprs_rlcmac_tbf_direction dir) const;
@@ -71,22 +73,32 @@ public:
bool confirm_tlli(uint32_t tlli);
bool check_tlli(uint32_t tlli);
void reset();
GprsCodingScheme::Mode mode() const;
void set_mode(GprsCodingScheme::Mode mode);
const char *imsi() const;
void set_imsi(const char *imsi);
uint8_t ta() const;
void set_ta(uint8_t ta);
uint8_t ms_class() const;
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);
uint8_t current_cs_ul() const;
uint8_t current_cs_dl() const;
GprsCodingScheme current_cs_ul() const;
GprsCodingScheme current_cs_dl() const;
GprsCodingScheme max_cs_ul() const;
GprsCodingScheme max_cs_dl() const;
int first_common_ts() const;
uint8_t dl_slots() const;
uint8_t ul_slots() const;
uint8_t reserved_dl_slots() const;
uint8_t reserved_ul_slots() const;
uint8_t current_pacch_slots() const;
gprs_rlcmac_trx *current_trx() const;
void set_reserved_slots(gprs_rlcmac_trx *trx,
uint8_t ul_slots, uint8_t dl_slots);
@@ -105,17 +117,21 @@ public:
void update_error_rate(gprs_rlcmac_tbf *tbf, int percent);
bool is_idle() const {return !m_ul_tbf && !m_dl_tbf && !m_ref;}
bool is_idle() const;
bool need_dl_tbf() const;
void* operator new(size_t num);
void operator delete(void* p);
LListHead<GprsMs>& list() {return this->m_list;}
const LListHead<GprsMs>& list() const {return this->m_list;}
const LListHead<gprs_rlcmac_tbf>& old_tbfs() const {return m_old_tbfs;}
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_);
@@ -126,12 +142,15 @@ protected:
void unref();
void start_timer();
void stop_timer();
void update_cs_ul(const pcu_l1_meas*);
private:
BTS *m_bts;
Callback * m_cb;
gprs_rlcmac_ul_tbf *m_ul_tbf;
gprs_rlcmac_dl_tbf *m_dl_tbf;
LListHead<gprs_rlcmac_tbf> m_old_tbfs;
uint32_t m_tlli;
uint32_t m_new_ul_tlli;
uint32_t m_new_dl_tlli;
@@ -140,9 +159,10 @@ private:
char m_imsi[16];
uint8_t m_ta;
uint8_t m_ms_class;
uint8_t m_egprs_ms_class;
/* current coding scheme */
uint8_t m_current_cs_ul;
uint8_t m_current_cs_dl;
GprsCodingScheme m_current_cs_ul;
GprsCodingScheme m_current_cs_dl;
gprs_llc_queue m_llc_queue;
@@ -161,8 +181,24 @@ private:
gprs_rlcmac_trx *m_current_trx;
struct gprs_codel *m_codel_state;
GprsCodingScheme::Mode m_mode;
unsigned m_dl_ctrl_msg;
};
inline bool GprsMs::is_idle() const
{
return !m_ul_tbf && !m_dl_tbf && !m_ref && llist_empty(&m_old_tbfs);
}
inline bool GprsMs::need_dl_tbf() const
{
if (dl_tbf() != NULL && dl_tbf()->state_is_not(GPRS_RLCMAC_WAIT_RELEASE))
return false;
return llc_queue()->size() > 0;
}
inline uint32_t GprsMs::tlli() const
{
return m_new_ul_tlli ? m_new_ul_tlli :
@@ -191,11 +227,21 @@ inline uint8_t GprsMs::ms_class() const
return m_ms_class;
}
inline uint8_t GprsMs::current_cs_ul() const
inline uint8_t GprsMs::egprs_ms_class() const
{
return m_egprs_ms_class;
}
inline GprsCodingScheme GprsMs::current_cs_ul() const
{
return m_current_cs_ul;
}
inline GprsCodingScheme::Mode GprsMs::mode() const
{
return m_mode;
}
inline void GprsMs::set_timeout(unsigned secs)
{
m_delay = secs;
@@ -221,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;

View File

@@ -22,7 +22,13 @@
#include "gprs_ms_storage.h"
#include "tbf.h"
#include "gprs_debug.h"
#include "bts.h"
extern "C" {
#include <osmocom/core/linuxlist.h>
}
#define GPRS_UNDEFINED_IMSI "000"
GprsMsStorage::GprsMsStorage(BTS *bts) :
m_bts(bts)
@@ -30,6 +36,11 @@ GprsMsStorage::GprsMsStorage(BTS *bts) :
}
GprsMsStorage::~GprsMsStorage()
{
cleanup();
}
void GprsMsStorage::cleanup()
{
LListHead<GprsMs> *pos, *tmp;
@@ -43,6 +54,8 @@ GprsMsStorage::~GprsMsStorage()
void GprsMsStorage::ms_idle(class GprsMs *ms)
{
llist_del(&ms->list());
if (m_bts)
m_bts->ms_present(m_bts->ms_present_get() - 1);
if (ms->is_idle())
delete ms;
}
@@ -69,7 +82,7 @@ GprsMs *GprsMsStorage::get_ms(uint32_t tlli, uint32_t old_tlli, const char *imsi
/* not found by TLLI */
if (imsi && imsi[0]) {
if (imsi && imsi[0] && strcmp(imsi, GPRS_UNDEFINED_IMSI) != 0) {
llist_for_each(pos, &m_list) {
ms = pos->entry();
if (strcmp(imsi, ms->imsi()) == 0)
@@ -88,6 +101,8 @@ GprsMs *GprsMsStorage::create_ms()
ms->set_callback(this);
llist_add(&ms->list(), &m_list);
if (m_bts)
m_bts->ms_present(m_bts->ms_present_get() + 1);
return ms;
}

View File

@@ -33,6 +33,8 @@ public:
GprsMsStorage(BTS *bts);
~GprsMsStorage();
void cleanup();
virtual void ms_idle(class GprsMs *);
virtual void ms_active(class GprsMs *);

View File

@@ -19,13 +19,12 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <gprs_bssgp_pcu.h>
#include <pcu_l1_if.h>
#include <gprs_rlcmac.h>
#include <bts.h>
#include <encoding.h>
#include <tbf.h>
#include <gprs_debug.h>
extern void *tall_pcu_ctx;
@@ -33,7 +32,7 @@ int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
const char *imsi)
{
LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH)\n");
bitvec *paging_request = bitvec_alloc(23);
bitvec *paging_request = bitvec_alloc(23, tall_pcu_ctx);
bitvec_unhex(paging_request, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
int plen = Encoding::write_paging_request(paging_request, ptmsi, ptmsi_len);
pcu_l1if_tx_pch(paging_request, plen, (char *)imsi);

View File

@@ -21,14 +21,16 @@
#ifndef GPRS_RLCMAC_H
#define GPRS_RLCMAC_H
#include <stdbool.h>
#ifdef __cplusplus
#include <bitvector.h>
#include <gsm_rlcmac.h>
#include <gsm_timer.h>
extern "C" {
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/bitvec.h>
}
#endif
@@ -41,7 +43,6 @@ extern "C" {
struct gprs_rlcmac_tbf;
struct gprs_rlcmac_bts;
struct BTS;
struct GprsMs;
#ifdef __cplusplus
@@ -63,6 +64,9 @@ struct gprs_rlcmac_cs {
uint8_t block_payload;
};
/* TS allocation internal functions */
int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *ul_slots, uint8_t *dl_slots);
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
uint16_t lost);
@@ -90,28 +94,22 @@ 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,
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts,
uint8_t ms_class = 0);
extern "C" {
#endif
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_trx);
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx);
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_trx);
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx);
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_trx);
int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx);
#ifdef __cplusplus
}
#endif

View File

@@ -28,7 +28,7 @@
/*
* downlink measurement
*/
#warning "TODO: trigger the measurement report from the pollcontroller and use it for flow control"
/* TODO: trigger the measurement report from the pollcontroller and use it for flow control */
/* received Measurement Report */
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr)
@@ -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);

View File

@@ -22,10 +22,18 @@
#include <pcu_l1_if.h>
#include <bts.h>
#include <tbf.h>
#include <gprs_debug.h>
#include <gprs_ms.h>
#include <rlc.h>
#include <sba.h>
#include <pdch.h>
#include "pcu_utils.h"
static uint32_t sched_poll(struct gprs_rlcmac_bts *bts,
extern "C" {
#include <osmocom/core/gsmtap.h>
}
static uint32_t sched_poll(BTS *bts,
uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
struct gprs_rlcmac_tbf **poll_tbf,
struct gprs_rlcmac_tbf **ul_ass_tbf,
@@ -34,41 +42,46 @@ static uint32_t sched_poll(struct gprs_rlcmac_bts *bts,
{
struct gprs_rlcmac_ul_tbf *ul_tbf;
struct gprs_rlcmac_dl_tbf *dl_tbf;
struct llist_pods *lpods;
LListHead<gprs_rlcmac_tbf> *pos;
uint32_t poll_fn;
/* check special TBF for events */
poll_fn = fn + 4;
if ((block_nr % 3) == 2)
poll_fn ++;
poll_fn = poll_fn % 2715648;
llist_pods_for_each_entry(ul_tbf, &bts->ul_tbfs, list, lpods) {
poll_fn = poll_fn % GSM_MAX_FN;
llist_for_each(pos, &bts->ul_tbfs()) {
ul_tbf = as_ul_tbf(pos->entry());
OSMO_ASSERT(ul_tbf);
/* this trx, this ts */
if (ul_tbf->trx->trx_no != trx || ul_tbf->control_ts != ts)
if (ul_tbf->trx->trx_no != trx || !ul_tbf->is_control_ts(ts))
continue;
/* polling for next uplink block */
if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& ul_tbf->poll_fn == poll_fn)
if (ul_tbf->poll_scheduled() && ul_tbf->poll_fn == poll_fn)
*poll_tbf = ul_tbf;
if (ul_tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
if (ul_tbf->ul_ack_state_is(GPRS_RLCMAC_UL_ACK_SEND_ACK))
*ul_ack_tbf = ul_tbf;
if (ul_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
if (ul_tbf->dl_ass_state_is(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_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
|| ul_tbf->ul_ass_state_is(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?"
/* FIXME: Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all
states? */
}
llist_pods_for_each_entry(dl_tbf, &bts->dl_tbfs, list, lpods) {
llist_for_each(pos, &bts->dl_tbfs()) {
dl_tbf = as_dl_tbf(pos->entry());
OSMO_ASSERT(dl_tbf);
/* this trx, this ts */
if (dl_tbf->trx->trx_no != trx || dl_tbf->control_ts != ts)
if (dl_tbf->trx->trx_no != trx || !dl_tbf->is_control_ts(ts))
continue;
/* polling for next uplink block */
if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& dl_tbf->poll_fn == poll_fn)
if (dl_tbf->poll_scheduled() && dl_tbf->poll_fn == poll_fn)
*poll_tbf = dl_tbf;
if (dl_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
if (dl_tbf->dl_ass_state_is(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_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)
|| dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))
*ul_ass_tbf = dl_tbf;
}
@@ -126,34 +139,67 @@ static struct msgb *sched_select_ctrl_msg(
if (!tbf)
continue;
if (tbf == ul_ass_tbf)
msg = ul_ass_tbf->create_ul_ass(fn);
else if (tbf == dl_ass_tbf)
msg = dl_ass_tbf->create_dl_ass(fn);
/*
* Assignments for the same direction have lower precedence,
* 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->ul_ass_state_is(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_is(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)
msg = ul_ack_tbf->create_ul_ack(fn);
else
abort();
msg = ul_ack_tbf->create_ul_ack(fn, ts);
if (!msg) {
tbf = NULL;
continue;
}
pdch->next_ctrl_prio += i + 1;
pdch->next_ctrl_prio += 1;
pdch->next_ctrl_prio %= 3;
break;
}
if (!msg) {
/*
* If one of these is left, the response (CONTROL ACK) from the
* MS will kill the current TBF, only one of them can be
* non-NULL
*/
if (dl_ass_tbf) {
tbf = dl_ass_tbf;
msg = dl_ass_tbf->create_dl_ass(fn, ts);
} else if (ul_ass_tbf) {
tbf = ul_ass_tbf;
msg = ul_ass_tbf->create_ul_ass(fn, ts);
}
}
/* any message */
if (msg) {
if (!tbf) {
LOGP(DRLCMACSCHED, LOGL_ERROR,
"Control message to be scheduled, but no TBF (TRX=%d, TS=%d)\n", trx, ts);
msgb_free(msg);
return NULL;
}
tbf->rotate_in_list();
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 */
/* schedule PACKET PAGING REQUEST, if any are pending */
msg = pdch->packet_paging_request();
if (msg) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
@@ -164,27 +210,41 @@ static struct msgb *sched_select_ctrl_msg(
return NULL;
}
static inline enum tbf_dl_prio tbf_compute_priority(const struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_dl_tbf *tbf,
uint8_t ts, uint32_t fn, int age)
{
const gprs_rlc_dl_window *w = tbf->window();
int age_thresh1 = msecs_to_frames(200),
age_thresh2 = msecs_to_frames(OSMO_MIN(BTS::TIMER_T3190_MSEC/2, bts->dl_tbf_idle_msec));
if (tbf->is_control_ts(ts) && tbf->need_control_ts())
return DL_PRIO_CONTROL;
if (tbf->is_control_ts(ts) && age > age_thresh2 && age_thresh2 > 0)
return DL_PRIO_HIGH_AGE;
if ((tbf->state_is(GPRS_RLCMAC_FLOW) && tbf->have_data()) || w->resend_needed() >= 0)
return DL_PRIO_NEW_DATA;
if (tbf->is_control_ts(ts) && age > age_thresh1 && tbf->keep_open(fn))
return DL_PRIO_LOW_AGE;
if (!w->window_empty())
return DL_PRIO_SENT_DATA;
return DL_PRIO_NONE;
}
static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
{
struct msgb *msg = NULL;
struct gprs_rlcmac_dl_tbf *tbf, *prio_tbf = NULL;
enum {
DL_PRIO_NONE,
DL_PRIO_SENT_DATA, /* the data has been sent and not (yet) nacked */
DL_PRIO_LOW_AGE, /* the age has reached the first threshold */
DL_PRIO_NEW_DATA, /* the data has not been sent yet or nacked */
DL_PRIO_HIGH_AGE, /* the age has reached the second threshold */
DL_PRIO_CONTROL, /* a control block needs to be sent */
} prio, max_prio = DL_PRIO_NONE;
enum tbf_dl_prio prio, max_prio = DL_PRIO_NONE;
uint8_t i, tfi, prio_tfi;
int age;
const int age_thresh1 = msecs_to_frames(200);
const int high_prio_msecs =
OSMO_MIN(BTS::TIMER_T3190_MSEC/2, bts->dl_tbf_idle_msec);
const int age_thresh2 = msecs_to_frames(high_prio_msecs);
/* select downlink resource */
for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
@@ -208,20 +268,8 @@ static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
age = tbf->frames_since_last_poll(fn);
/* compute priority */
if (tbf->is_control_ts(ts) && tbf->need_control_ts())
prio = DL_PRIO_CONTROL;
else if (tbf->is_control_ts(ts) &&
age > age_thresh2 && age_thresh2 > 0)
prio = DL_PRIO_HIGH_AGE;
else if ((tbf->state_is(GPRS_RLCMAC_FLOW) && tbf->have_data()) ||
tbf->m_window.resend_needed() >= 0)
prio = DL_PRIO_NEW_DATA;
else if (tbf->is_control_ts(ts) &&
age > age_thresh1 && tbf->keep_open(fn))
prio = DL_PRIO_LOW_AGE;
else if (!tbf->m_window.window_empty())
prio = DL_PRIO_SENT_DATA;
else
prio = tbf_compute_priority(bts, tbf, ts, fn, age);
if (prio == DL_PRIO_NONE)
continue;
/* get the TBF with the highest priority */
@@ -265,8 +313,36 @@ static struct msgb *sched_dummy(void)
return msg;
}
static inline void tap_n_acc(const struct msgb *msg, const struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
uint32_t fn, enum pcu_gsmtap_category cat)
{
if (!msg)
return;
switch(cat) {
case PCU_GSMTAP_C_DL_CTRL:
bts->bts->rlc_sent_control();
bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_CTRL, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
msg->len);
break;
case PCU_GSMTAP_C_DL_DATA_GPRS:
bts->bts->rlc_sent();
/* FIXME: distinguish between GPRS and EGPRS */
bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_DATA_GPRS, false, trx, ts, GSMTAP_CHANNEL_PDTCH, fn, msg->data,
msg->len);
break;
case PCU_GSMTAP_C_DL_DUMMY:
bts->bts->rlc_sent_dummy();
bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_DUMMY, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
msg->len);
break;
default:
break;
}
}
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;
@@ -277,8 +353,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];
@@ -292,7 +366,7 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
/* store last frame number of RTS */
pdch->last_rts_fn = fn;
poll_fn = sched_poll(bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
poll_fn = sched_poll(bts->bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
&dl_ass_tbf, &ul_ack_tbf);
/* check uplink resource for polling */
if (poll_tbf)
@@ -316,24 +390,35 @@ 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);
tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_CTRL);
/* Prio 2: select data message for downlink */
if (!msg)
if (!msg) {
msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_DATA_GPRS);
}
/* Prio 3: send dummy contol message */
if (!msg)
if (!msg) {
/* increase counter */
msg = sched_dummy();
tap_n_acc(msg, bts, trx, ts, fn, PCU_GSMTAP_C_DL_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);
msg->data[0] = (msg->data[0] & 0xf8) | usf;
/* Used to measure the leak rate, count all blocks */
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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -4799,7 +4799,7 @@ void decode_gsm_rlcmac_uplink(bitvec * vector, RlcMacUplink_t * data)
{
csnStream_t ar;
unsigned readIndex = 0;
guint8 payload_type = bitvec_read_field(vector, readIndex, 2);
guint8 payload_type = bitvec_read_field(vector, &readIndex, 2);
if (payload_type == PAYLOAD_TYPE_DATA)
{
@@ -4814,7 +4814,7 @@ void decode_gsm_rlcmac_uplink(bitvec * vector, RlcMacUplink_t * data)
data->NrOfBits = 23 * 8;
csnStreamInit(&ar, 0, data->NrOfBits);
readIndex += 6;
data->u.MESSAGE_TYPE = bitvec_read_field(vector, readIndex, 6);
data->u.MESSAGE_TYPE = bitvec_read_field(vector, &readIndex, 6);
readIndex = 0;
switch (data->u.MESSAGE_TYPE)
{
@@ -4907,10 +4907,10 @@ void decode_gsm_rlcmac_downlink(bitvec * vector, RlcMacDownlink_t * data)
gint bit_offset = 0;
gint bit_length;
unsigned readIndex = 0;
data->PAYLOAD_TYPE = bitvec_read_field(vector, readIndex, 2);
data->RRBP = bitvec_read_field(vector, readIndex, 2);
data->SP = bitvec_read_field(vector, readIndex, 1);
data->USF = bitvec_read_field(vector, readIndex, 3);
data->PAYLOAD_TYPE = bitvec_read_field(vector, &readIndex, 2);
data->RRBP = bitvec_read_field(vector, &readIndex, 2);
data->SP = bitvec_read_field(vector, &readIndex, 1);
data->USF = bitvec_read_field(vector, &readIndex, 3);
if (data->PAYLOAD_TYPE == PAYLOAD_TYPE_DATA)
{
@@ -4929,27 +4929,27 @@ void decode_gsm_rlcmac_downlink(bitvec * vector, RlcMacDownlink_t * data)
bit_offset = 8;
if (data->PAYLOAD_TYPE == PAYLOAD_TYPE_CTRL_OPT_OCTET)
{
data->RBSN = bitvec_read_field(vector, readIndex, 1);
data->RTI = bitvec_read_field(vector, readIndex, 5);
data->FS = bitvec_read_field(vector, readIndex, 1);
data->AC = bitvec_read_field(vector, readIndex, 1);
data->RBSN = bitvec_read_field(vector, &readIndex, 1);
data->RTI = bitvec_read_field(vector, &readIndex, 5);
data->FS = bitvec_read_field(vector, &readIndex, 1);
data->AC = bitvec_read_field(vector, &readIndex, 1);
bit_offset += 8;
if (data->AC == 1)
{
data->PR = bitvec_read_field(vector, readIndex, 2);
data->TFI = bitvec_read_field(vector, readIndex, 5);
data->D = bitvec_read_field(vector, readIndex, 1);
data->PR = bitvec_read_field(vector, &readIndex, 2);
data->TFI = bitvec_read_field(vector, &readIndex, 5);
data->D = bitvec_read_field(vector, &readIndex, 1);
bit_offset += 8;
}
if ((data->RBSN == 1) && (data->FS == 0))
{
data->RBSNe = bitvec_read_field(vector, readIndex, 3);
data->FSe = bitvec_read_field(vector, readIndex, 1);
data->spare = bitvec_read_field(vector, readIndex, 4);
data->RBSNe = bitvec_read_field(vector, &readIndex, 3);
data->FSe = bitvec_read_field(vector, &readIndex, 1);
data->spare = bitvec_read_field(vector, &readIndex, 4);
bit_offset += 8;
}
}
data->u.MESSAGE_TYPE = bitvec_read_field(vector, readIndex, 6);
data->u.MESSAGE_TYPE = bitvec_read_field(vector, &readIndex, 6);
}
/* Initialize the contexts */
@@ -5206,30 +5206,30 @@ void encode_gsm_rlcmac_downlink(bitvec * vector, RlcMacDownlink_t * data)
else
{
/* First print the message type and create a tree item */
bitvec_write_field(vector, writeIndex, data->PAYLOAD_TYPE, 2);
bitvec_write_field(vector, writeIndex, data->RRBP, 2);
bitvec_write_field(vector, writeIndex, data->SP, 1);
bitvec_write_field(vector, writeIndex, data->USF, 3);
bitvec_write_field(vector, &writeIndex, data->PAYLOAD_TYPE, 2);
bitvec_write_field(vector, &writeIndex, data->RRBP, 2);
bitvec_write_field(vector, &writeIndex, data->SP, 1);
bitvec_write_field(vector, &writeIndex, data->USF, 3);
bit_offset = 8;
if (data->PAYLOAD_TYPE == PAYLOAD_TYPE_CTRL_OPT_OCTET)
{
bitvec_write_field(vector, writeIndex, data->RBSN, 1);
bitvec_write_field(vector, writeIndex, data->RTI, 5);
bitvec_write_field(vector, writeIndex, data->FS, 1);
bitvec_write_field(vector, writeIndex, data->AC, 1);
bitvec_write_field(vector, &writeIndex, data->RBSN, 1);
bitvec_write_field(vector, &writeIndex, data->RTI, 5);
bitvec_write_field(vector, &writeIndex, data->FS, 1);
bitvec_write_field(vector, &writeIndex, data->AC, 1);
bit_offset += 8;
if (data->AC == 1)
{
bitvec_write_field(vector, writeIndex, data->PR, 2);
bitvec_write_field(vector, writeIndex, data->TFI, 5);
bitvec_write_field(vector, writeIndex, data->D, 1);
bitvec_write_field(vector, &writeIndex, data->PR, 2);
bitvec_write_field(vector, &writeIndex, data->TFI, 5);
bitvec_write_field(vector, &writeIndex, data->D, 1);
bit_offset += 8;
}
if ((data->RBSN == 1) && (data->FS == 0))
{
bitvec_write_field(vector, writeIndex, data->RBSNe, 3);
bitvec_write_field(vector, writeIndex, data->FSe, 1);
bitvec_write_field(vector, writeIndex, data->spare, 4);
bitvec_write_field(vector, &writeIndex, data->RBSNe, 3);
bitvec_write_field(vector, &writeIndex, data->FSe, 1);
bitvec_write_field(vector, &writeIndex, data->spare, 4);
bit_offset += 8;
}
}
@@ -5378,32 +5378,32 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
{
unsigned readIndex = 0;
//unsigned dataLen = 0;
guint8 payload_type = bitvec_read_field(vector, readIndex, 2);
guint8 payload_type = bitvec_read_field(vector, &readIndex, 2);
if (payload_type == PAYLOAD_TYPE_DATA)
{
readIndex = 0;
// MAC header
data->PAYLOAD_TYPE = bitvec_read_field(vector, readIndex, 2);
data->CV = bitvec_read_field(vector, readIndex, 4);
data->SI = bitvec_read_field(vector, readIndex, 1);
data->R = bitvec_read_field(vector, readIndex, 1);
data->PAYLOAD_TYPE = bitvec_read_field(vector, &readIndex, 2);
data->CV = bitvec_read_field(vector, &readIndex, 4);
data->SI = bitvec_read_field(vector, &readIndex, 1);
data->R = bitvec_read_field(vector, &readIndex, 1);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "PAYLOAD_TYPE = %u ", (unsigned)(data->PAYLOAD_TYPE));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "CV = %u ", (unsigned)(data->CV));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "SI = %u ", (unsigned)(data->SI));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "R = %u ", (unsigned)(data->R));
// Octet 1
data->spare = bitvec_read_field(vector, readIndex, 1);
data->PI = bitvec_read_field(vector, readIndex, 1);
data->TFI = bitvec_read_field(vector, readIndex, 5);
data->TI = bitvec_read_field(vector, readIndex, 1);
data->spare = bitvec_read_field(vector, &readIndex, 1);
data->PI = bitvec_read_field(vector, &readIndex, 1);
data->TFI = bitvec_read_field(vector, &readIndex, 5);
data->TI = bitvec_read_field(vector, &readIndex, 1);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "spare = %u ", (unsigned)(data->spare));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "PI = %u ", (unsigned)(data->PI));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "TFI = %u ", (unsigned)(data->TFI));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "TI = %u ", (unsigned)(data->TI));
// Octet 2
data->BSN = bitvec_read_field(vector, readIndex, 7);
data->E_1 = bitvec_read_field(vector, readIndex, 1);
data->BSN = bitvec_read_field(vector, &readIndex, 7);
data->E_1 = bitvec_read_field(vector, &readIndex, 1);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "BSN = %u ", (unsigned)(data->BSN));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "E_1 = %u ", (unsigned)(data->E_1));
@@ -5414,9 +5414,9 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
unsigned i = 0;
do
{
data->LENGTH_INDICATOR[i] = bitvec_read_field(vector, readIndex, 6);
data->M[i] = bitvec_read_field(vector, readIndex, 1);
data->E[i] = bitvec_read_field(vector, readIndex, 1);
data->LENGTH_INDICATOR[i] = bitvec_read_field(vector, &readIndex, 6);
data->M[i] = bitvec_read_field(vector, &readIndex, 1);
data->E[i] = bitvec_read_field(vector, &readIndex, 1);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "LENGTH_INDICATOR[%u] = %u ", i, (unsigned)(data->LENGTH_INDICATOR[i]));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "M[%u] = %u ", i, (unsigned)(data->M[i]));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "E[%u] = %u ", i, (unsigned)(data->E[i]));
@@ -5425,12 +5425,12 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
}
if(data->TI == 1) // TLLI field is present
{
data->TLLI = bitvec_read_field(vector, readIndex, 32);
data->TLLI = bitvec_read_field(vector, &readIndex, 32);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "TLLI = %08x ", data->TLLI);
if (data->PI == 1) // PFI is present if TI field indicates presence of TLLI
{
data->PFI = bitvec_read_field(vector, readIndex, 7);
data->E_2 = bitvec_read_field(vector, readIndex, 1);
data->PFI = bitvec_read_field(vector, &readIndex, 7);
data->E_2 = bitvec_read_field(vector, &readIndex, 1);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "PFI = %u ", (unsigned)(data->PFI));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "E_2 = %u ", (unsigned)(data->E_2));
}
@@ -5440,7 +5440,7 @@ void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * da
assert(dataLen <= 20);
for (unsigned i = 0; i < dataLen; i++)
{
data->RLC_DATA[i] = bitvec_read_field(vector, readIndex, 8);
data->RLC_DATA[i] = bitvec_read_field(vector, &readIndex, 8);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "%02x", (unsigned)(data->RLC_DATA[i]));
}
LOGPC(DRLCMACDATA, LOGL_NOTICE, "\n");
@@ -5459,26 +5459,26 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
if (data->PAYLOAD_TYPE == PAYLOAD_TYPE_DATA)
{
// MAC header
bitvec_write_field(vector, writeIndex, data->PAYLOAD_TYPE, 2);
bitvec_write_field(vector, writeIndex, data->RRBP, 2);
bitvec_write_field(vector, writeIndex, data->SP, 1);
bitvec_write_field(vector, writeIndex, data->USF, 3);
bitvec_write_field(vector, &writeIndex, data->PAYLOAD_TYPE, 2);
bitvec_write_field(vector, &writeIndex, data->RRBP, 2);
bitvec_write_field(vector, &writeIndex, data->SP, 1);
bitvec_write_field(vector, &writeIndex, data->USF, 3);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "PAYLOAD_TYPE = %u ", (unsigned)(data->PAYLOAD_TYPE));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "RRBP = %u ", (unsigned)(data->RRBP));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "SP = %u ", (unsigned)(data->SP));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "USF = %u ", (unsigned)(data->USF));
// Octet 1
bitvec_write_field(vector, writeIndex, data->PR, 2);
bitvec_write_field(vector, writeIndex, data->TFI, 5);
bitvec_write_field(vector, writeIndex, data->FBI, 1);
bitvec_write_field(vector, &writeIndex, data->PR, 2);
bitvec_write_field(vector, &writeIndex, data->TFI, 5);
bitvec_write_field(vector, &writeIndex, data->FBI, 1);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "PR = %u ", (unsigned)(data->PR));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "TFI = %u ", (unsigned)(data->TFI));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "FBI = %u ", (unsigned)(data->FBI));
// Octet 2
bitvec_write_field(vector, writeIndex, data->BSN, 7);
bitvec_write_field(vector, writeIndex, data->E_1, 1);
bitvec_write_field(vector, &writeIndex, data->BSN, 7);
bitvec_write_field(vector, &writeIndex, data->E_1, 1);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "BSN = %u ", (unsigned)(data->BSN));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "E_1 = %u ", (unsigned)(data->E_1));
@@ -5488,9 +5488,9 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
unsigned i = 0;
do
{
bitvec_write_field(vector, writeIndex, data->LENGTH_INDICATOR[i], 6);
bitvec_write_field(vector, writeIndex, data->M[i], 1);
bitvec_write_field(vector, writeIndex, data->E[i], 1);
bitvec_write_field(vector, &writeIndex, data->LENGTH_INDICATOR[i], 6);
bitvec_write_field(vector, &writeIndex, data->M[i], 1);
bitvec_write_field(vector, &writeIndex, data->E[i], 1);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "LENGTH_INDICATOR[%u] = %u ", i, (unsigned)(data->LENGTH_INDICATOR[i]));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "M[%u] = %u ", i, (unsigned)(data->M[i]));
LOGPC(DRLCMACDATA, LOGL_NOTICE, "E[%u] = %u ", i, (unsigned)(data->E[i]));
@@ -5503,9 +5503,17 @@ void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t
assert(dataNumOctets <= 20);
for (unsigned i = 0; i < dataNumOctets; i++)
{
bitvec_write_field(vector, writeIndex, data->RLC_DATA[i], 8);
bitvec_write_field(vector, &writeIndex, data->RLC_DATA[i], 8);
LOGPC(DRLCMACDATA, LOGL_NOTICE, "%02x", (unsigned)(data->RLC_DATA[i]));
}
LOGPC(DRLCMACDATA, LOGL_NOTICE, "\n");
}
}
void decode_gsm_ra_cap(bitvec * vector, MS_Radio_Access_capability_t *data)
{
csnStream_t ar;
unsigned readIndex = 0;
csnStreamInit(&ar, 0, 8 * vector->data_len);
/*ret =*/ csnStreamDecoder(&ar, CSNDESCR(MS_Radio_Access_capability_t), vector, readIndex, data);
}

View File

@@ -29,11 +29,12 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __PACKET_GSM_RLCMAC_H__
#define __PACKET_GSM_RLCMAC_H__
#include "csn1.h"
#include <iostream>
#include <cstdlib>
#ifndef __PACKET_GSM_RLCMAC_H__
#define __PACKET_GSM_RLCMAC_H__
#ifndef PRE_PACKED
#define PRE_PACKED
@@ -476,7 +477,7 @@ typedef struct
typedef struct
{
guint8 LENGTH;
/* guint8 LENGTH; */
EGPRS_AckNack_Desc_t Desc;
} EGPRS_AckNack_w_len_t;
@@ -5136,4 +5137,5 @@ typedef struct
void encode_gsm_rlcmac_uplink(bitvec * vector, RlcMacUplink_t * data);
void decode_gsm_rlcmac_uplink_data(bitvec * vector, RlcMacUplinkDataBlock_t * data);
void encode_gsm_rlcmac_downlink_data(bitvec * vector, RlcMacDownlinkDataBlock_t * data);
void decode_gsm_ra_cap(bitvec * vector, MS_Radio_Access_capability_t * data);
#endif /* __PACKET_GSM_RLCMAC_H__ */

View File

@@ -43,7 +43,7 @@ static struct rb_root timer_root = RB_ROOT;
* all time functions schedule based on the BTS they
* are scheduled on.
*/
static int get_current_fn()
int get_current_fn()
{
return BTS::main_bts()->current_frame_number();
}

View File

@@ -79,6 +79,12 @@ void osmo_gsm_timers_prepare(void);
int osmo_gsm_timers_update(void);
int osmo_gsm_timers_check(void);
/*
* Get Current Frame Number
*/
int get_current_fn();
/*! }@ */
#endif // GSM_TIMER_H

View File

@@ -19,7 +19,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <tbf.h>
#include <bts.h>
#include <stdio.h>
@@ -134,6 +133,59 @@ void gprs_llc_queue::clear(BTS *bts)
m_queue_octets = 0;
}
void gprs_llc_queue::move_and_merge(gprs_llc_queue *o)
{
struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
struct llist_head new_queue;
size_t queue_size = 0;
size_t queue_octets = 0;
INIT_LLIST_HEAD(&new_queue);
while (1) {
if (msg1 == NULL)
msg1 = msgb_dequeue(&m_queue);
if (msg2 == NULL)
msg2 = msgb_dequeue(&o->m_queue);
if (msg1 == NULL && msg2 == NULL)
break;
if (msg1 == NULL) {
msg = msg2;
msg2 = NULL;
} else if (msg2 == NULL) {
msg = msg1;
msg1 = NULL;
} else {
const MetaInfo *mi1 = (MetaInfo *)&msg1->cb[0];
const MetaInfo *mi2 = (MetaInfo *)&msg2->cb[0];
if (timercmp(&mi2->recv_time, &mi1->recv_time, >)) {
msg = msg1;
msg1 = NULL;
} else {
msg = msg2;
msg2 = NULL;
}
}
msgb_enqueue(&new_queue, msg);
queue_size += 1;
queue_octets += msgb_length(msg);
}
OSMO_ASSERT(llist_empty(&m_queue));
OSMO_ASSERT(llist_empty(&o->m_queue));
o->m_queue_size = 0;
o->m_queue_octets = 0;
llist_splice_init(&new_queue, &m_queue);
m_queue_size = queue_size;
m_queue_octets = queue_octets;
}
#define ALPHA 0.5f
struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)

View File

@@ -29,8 +29,6 @@ extern "C" {
#define LLC_MAX_LEN 1543
struct BTS;
struct timeval;
struct msgb;
/**
* I represent the LLC data to a MS
@@ -80,6 +78,7 @@ struct gprs_llc_queue {
void enqueue(struct msgb *llc_msg, const MetaInfo *info = 0);
struct msgb *dequeue(const MetaInfo **info = 0);
void clear(BTS *bts);
void move_and_merge(gprs_llc_queue *o);
size_t size() const;
size_t octets() const;
@@ -126,10 +125,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;
}

297
src/mslot_class.c Normal file
View File

@@ -0,0 +1,297 @@
/* mslot_class.c
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
* Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <mslot_class.h>
#include <gprs_debug.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <errno.h>
/* 3GPP TS 45.002 Annex B Table B.1 */
struct gprs_ms_multislot_class {
uint8_t rx, tx, sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */
uint8_t ta, tb, ra, rb; /* Minimum Number of Slots */
uint8_t type; /* Type of Mobile */
};
static const struct gprs_ms_multislot_class gprs_ms_multislot_class[] = {
/* M-S Class | Max # of slots | Min # of slots | Type */
/* | Rx Tx Sum | Tta Ttb Tra Trb | */
/* N/A */ { MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
/* 1 */ { 1, 1, 2, 3, 2, 4, 2, 1 },
/* 2 */ { 2, 1, 3, 3, 2, 3, 1, 1 },
/* 3 */ { 2, 2, 3, 3, 2, 3, 1, 1 },
/* 4 */ { 3, 1, 4, 3, 1, 3, 1, 1 },
/* 5 */ { 2, 2, 4, 3, 1, 3, 1, 1 },
/* 6 */ { 3, 2, 4, 3, 1, 3, 1, 1 },
/* 7 */ { 3, 3, 4, 3, 1, 3, 1, 1 },
/* 8 */ { 4, 1, 5, 3, 1, 2, 1, 1 },
/* 9 */ { 3, 2, 5, 3, 1, 2, 1, 1 },
/* 10 */ { 4, 2, 5, 3, 1, 2, 1, 1 },
/* 11 */ { 4, 3, 5, 3, 1, 2, 1, 1 },
/* 12 */ { 4, 4, 5, 2, 1, 2, 1, 1 },
/* 13 */ { 3, 3, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
/* 14 */ { 4, 4, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
/* 15 */ { 5, 5, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
/* 16 */ { 6, 6, MS_NA, MS_NA, MS_A, 2, MS_A, 2 },
/* 17 */ { 7, 7, MS_NA, MS_NA, MS_A, 1, 0, 2 },
/* 18 */ { 8, 8, MS_NA, MS_NA, 0, 0, 0, 2 },
/* 19 */ { 6, 2, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 20 */ { 6, 3, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 21 */ { 6, 4, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 22 */ { 6, 4, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* 23 */ { 6, 6, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* 24 */ { 8, 2, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 25 */ { 8, 3, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 26 */ { 8, 4, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 27 */ { 8, 4, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* 28 */ { 8, 6, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* 29 */ { 8, 8, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* 30 */ { 5, 1, 6, 2, 1, 1, 1, 1 },
/* 31 */ { 5, 2, 6, 2, 1, 1, 1, 1 },
/* 32 */ { 5, 3, 6, 2, 1, 1, 1, 1 },
/* 33 */ { 5, 4, 6, 2, 1, 1, 1, 1 },
/* 34 */ { 5, 5, 6, 2, 1, 1, 1, 1 },
/* 35 */ { 5, 1, 6, 2, 1, MS_TO, 1, 1 },
/* 36 */ { 5, 2, 6, 2, 1, MS_TO, 1, 1 },
/* 37 */ { 5, 3, 6, 2, 1, MS_TO, 1, 1 },
/* 38 */ { 5, 4, 6, 2, 1, MS_TO, 1, 1 },
/* 39 */ { 5, 5, 6, 2, 1, MS_TO, 1, 1 },
/* 40 */ { 6, 1, 7, 1, 1, 1, MS_TO, 1 },
/* 41 */ { 6, 2, 7, 1, 1, 1, MS_TO, 1 },
/* 42 */ { 6, 3, 7, 1, 1, 1, MS_TO, 1 },
/* 43 */ { 6, 4, 7, 1, 1, 1, MS_TO, 1 },
/* 44 */ { 6, 5, 7, 1, 1, 1, MS_TO, 1 },
/* 45 */ { 6, 6, 7, 1, 1, 1, MS_TO, 1 },
};
static inline const struct gprs_ms_multislot_class *get_mslot_table(uint8_t ms_cl)
{
uint8_t index = ms_cl ? ms_cl : DEFAULT_MSLOT_CLASS;
if (ms_cl >= ARRAY_SIZE(gprs_ms_multislot_class))
index = 0;
return &gprs_ms_multislot_class[index];
}
uint8_t mslot_class_max()
{
return ARRAY_SIZE(gprs_ms_multislot_class);
}
uint8_t mslot_class_get_ta(uint8_t ms_cl)
{
return get_mslot_table(ms_cl)->ta;
}
/* TODO: Set it to 1 if FH is implemented and enabled
* MS_A and MS_B are 0 iff FH is disabled and there is no Tx/Rx change.
* This is never the case with the current implementation, so 1 will always be used. */
uint8_t mslot_class_get_tb(uint8_t ms_cl)
{
const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
switch (t->tb) {
case MS_A:
return 0;
case MS_B:
return 1;
default:
return t->tb;
}
}
uint8_t mslot_class_get_ra(uint8_t ms_cl, uint8_t ta)
{
const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
switch (t->ra) {
case MS_TO:
return ta + 1;
default:
return t->ra;
}
}
uint8_t mslot_class_get_rb(uint8_t ms_cl, uint8_t ta)
{
const struct gprs_ms_multislot_class *t = get_mslot_table(ms_cl);
switch (t->rb) {
case MS_A:
return 0;
case MS_C:
return 1;
case MS_TO:
return ta;
default:
return t->rb;
}
}
uint8_t mslot_class_get_tx(uint8_t ms_cl)
{
return get_mslot_table(ms_cl)->tx;
}
uint8_t mslot_class_get_rx(uint8_t ms_cl)
{
return get_mslot_table(ms_cl)->rx;
}
uint8_t mslot_class_get_sum(uint8_t ms_cl)
{
return get_mslot_table(ms_cl)->sum;
}
uint8_t mslot_class_get_type(uint8_t ms_cl)
{
return get_mslot_table(ms_cl)->type;
}
/*! Fill in RX mask table for a given MS Class
*
* \param[in] ms_cl MS Class pointer
* \param[in] num_tx Number of TX slots to consider
* \param[out] rx_mask RX mask table
*/
void mslot_fill_rx_mask(uint8_t mslot_class, uint8_t num_tx, uint8_t *rx_mask)
{
static const char *digit[10] = { "0","1","2","3","4","5","6","7","8","9" };
uint8_t Tx = mslot_class_get_tx(mslot_class), /* Max number of Tx slots */
Sum = mslot_class_get_sum(mslot_class), /* Max number of Tx + Rx slots */
Type = mslot_class_get_type(mslot_class), /* Type of Mobile */
Tta = mslot_class_get_ta(mslot_class), /* Minimum number of slots */
Ttb = mslot_class_get_tb(mslot_class),
/* FIXME: use actual TA offset for computation - make sure to adjust "1 + MS_TO" accordingly
see also "Offset required" bit in 3GPP TS 24.008 §10.5.1.7 */
Tra = mslot_class_get_ra(mslot_class, 0),
Trb = mslot_class_get_rb(mslot_class, 0);
if (num_tx == 1) /* it's enough to log this once per TX slot set iteration */
LOGP(DRLCMAC, LOGL_DEBUG,
"Rx=%d Tx=%d Sum Rx+Tx=%s, Tta=%s Ttb=%d, Tra=%d Trb=%d, Type=%d\n",
mslot_class_get_rx(mslot_class), Tx,
(Sum == MS_NA) ? "N/A" : digit[Sum],
(Tta == MS_NA) ? "N/A" : digit[Tta], Ttb, Tra, Trb, Type);
if (Type == 1) {
rx_mask[MASK_TT] = (0x100 >> OSMO_MAX(Ttb, Tta)) - 1;
rx_mask[MASK_TT] &= ~((1 << (Trb + num_tx)) - 1);
rx_mask[MASK_TR] = (0x100 >> Ttb) - 1;
rx_mask[MASK_TR] &= ~((1 << (OSMO_MAX(Trb, Tra) + num_tx)) - 1);
} else {
/* Class type 2 MS have independant RX and TX */
rx_mask[MASK_TT] = 0xff;
rx_mask[MASK_TR] = 0xff;
}
rx_mask[MASK_TT] = (rx_mask[MASK_TT] << 3) | (rx_mask[MASK_TT] >> 5);
rx_mask[MASK_TR] = (rx_mask[MASK_TR] << 3) | (rx_mask[MASK_TR] >> 5);
}
/* look for USF, don't use USF=7 */
int8_t find_free_usf(uint8_t usf_map)
{
uint8_t usf;
if (usf_map == (1 << 7) - 1)
return -1;
for (usf = 0; usf < 7; usf++) {
if (!(usf_map & (1 << usf)))
return usf;
}
return -1;
}
/* look for USF, don't use USF=7 */
int8_t find_free_tfi(uint32_t tfi_map)
{
int8_t tfi;
if (tfi_map == NO_FREE_TFI)
return -1;
for (tfi = 0; tfi < 32; tfi++) {
if (!(tfi_map & (((uint32_t)1) << tfi)))
return tfi;
}
return -1;
}
void masked_override_with(char *buf, uint8_t mask, char set_char)
{
int i;
for (i = 0; mask; i++, mask >>= 1)
if (mask & 1)
buf[i] = set_char;
}
void ts_format(char *buf, uint8_t dl_mask, uint8_t ul_mask)
{
snprintf(buf, 9, OSMO_BIT_SPEC, OSMO_BIT_PRINT_EX(dl_mask, 'D'));
masked_override_with(buf, ul_mask, 'U');
masked_override_with(buf, ul_mask & dl_mask, 'C');
}
uint16_t mslot_wrap_window(uint16_t win)
{
return (win | win >> 8) & 0xFF;
}
bool mslot_test_and_set_bit(uint32_t *bits, size_t elem)
{
bool was_set = bits[elem/32] & (((uint32_t)1) << (elem % 32));
bits[elem/32] |= (((uint32_t)1) << (elem % 32));
return was_set;
}
/*! Filter out bad slots
*
* \param[in] mask TS selection mask
* \param[in] ul_slots set of UL timeslots
* \param[in] dl_slots set of DL timeslots
* \param[in] rx_valid_win Mask for valid RX window value
* \returns negative error code or RX window on success
*/
int16_t mslot_filter_bad(uint8_t mask, uint8_t ul_slots, uint8_t dl_slots, uint16_t rx_valid_win)
{
uint8_t rx_good;
uint16_t rx_bad = (uint16_t)(0xFF & ~mask) << ul_slots;
/* TODO: CHECK this calculation -> separate function for unit testing */
rx_bad = (rx_bad | (rx_bad >> 8)) & 0xFF;
rx_good = dl_slots & ~rx_bad;
if (!rx_good)
return -1;
return rx_good & rx_valid_win;
}

62
src/mslot_class.h Normal file
View File

@@ -0,0 +1,62 @@
/* mslot_class.h
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
* Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/* 3GPP TS 05.02 Annex B.1 */
#define MS_NA 255 /* N/A */
#define MS_A 254 /* 1 with hopping, 0 without */
#define MS_B 253 /* 1 with hopping, 0 without (change Rx to Tx)*/
#define MS_C 252 /* 1 with hopping, 0 without (change Tx to Rx)*/
#define MS_TO 251 /* 31 symbol periods (this can be provided by a TA offset, i.e. a minimum TA value) */
#define DEFAULT_MSLOT_CLASS 12
#define NO_FREE_TFI 0xffffffff
enum { MASK_TT = 0, MASK_TR = 1 };
/* multislot class selection routines */
uint8_t mslot_class_get_ta(uint8_t ms_cl);
uint8_t mslot_class_get_tb(uint8_t ms_cl);
uint8_t mslot_class_get_ra(uint8_t ms_cl, uint8_t ta);
uint8_t mslot_class_get_rb(uint8_t ms_cl, uint8_t ta);
uint8_t mslot_class_get_tx(uint8_t ms_cl);
uint8_t mslot_class_get_rx(uint8_t ms_cl);
uint8_t mslot_class_get_sum(uint8_t ms_cl);
uint8_t mslot_class_get_type(uint8_t ms_cl);
uint8_t mslot_class_max();
/* multislot allocation helper routines */
void mslot_fill_rx_mask(uint8_t mslot_class, uint8_t num_tx, uint8_t *rx_mask);
int8_t find_free_usf(uint8_t usf_map);
int8_t find_free_tfi(uint32_t tfi_map);
void masked_override_with(char *buf, uint8_t mask, char set_char);
void ts_format(char *buf, uint8_t dl_mask, uint8_t ul_mask);
uint16_t mslot_wrap_window(uint16_t win);
bool mslot_test_and_set_bit(uint32_t *bits, size_t elem);
int16_t mslot_filter_bad(uint8_t mask, uint8_t ul_slots, uint8_t dl_slots, uint16_t rx_valid_win);

View File

@@ -1,191 +0,0 @@
/* openbts_sock.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <errno.h>
#include <string.h>
#include <gprs_rlcmac.h>
#include <gprs_bssgp_pcu.h>
#include <pcu_l1_if.h>
#include <gprs_debug.h>
#include <bitvector.h>
#include <sys/socket.h>
#include <arpa/inet.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h>
#include <pcuif_proto.h>
}
extern void *tall_pcu_ctx;
struct femtol1_hdl {
struct gsm_time gsm_time;
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
uint32_t dsp_trace_f;
uint16_t clk_cal;
struct llist_head wlc_list;
void *priv; /* user reference */
struct osmo_timer_list alive_timer;
unsigned int alive_prim_cnt;
struct osmo_fd read_ofd; /* osmo file descriptors */
struct osmo_wqueue write_q;
struct {
uint16_t arfcn;
uint8_t tn;
uint8_t tsc;
uint16_t ta;
} channel_info;
};
struct l1fwd_hdl {
struct sockaddr_storage remote_sa;
socklen_t remote_sa_len;
struct osmo_wqueue udp_wq;
struct femtol1_hdl *fl1h;
};
struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
// TODO: We should move this parameters to config file.
#define PCU_L1_IF_PORT 5944
/* OpenBTS socket functions */
int pcu_sock_send(struct msgb *msg)
{
if (osmo_wqueue_enqueue(&l1fh->udp_wq, msg) != 0) {
LOGP(DPCU, LOGL_ERROR, "PCU write queue full. Dropping message.\n");
msgb_free(msg);
}
return 0;
}
/* data has arrived on the udp socket */
static int udp_read_cb(struct osmo_fd *ofd)
{
struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
struct femtol1_hdl *fl1h = l1fh->fl1h;
int rc;
if (!msg)
return -ENOMEM;
msg->l1h = msg->data;
l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
if (rc < 0) {
perror("read from udp");
msgb_free(msg);
return rc;
} else if (rc == 0) {
perror("len=0 read from udp");
msgb_free(msg);
return rc;
}
msgb_put(msg, rc);
struct gsm_pcu_if *pcu_prim = (gsm_pcu_if *)(msg->l1h);
rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
msgb_free(msg);
return rc;
}
/* callback when we can write to the UDP socket */
static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
{
int rc;
struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
//LOGP(DPCU, LOGL_ERROR, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_length(msg));
rc = sendto(ofd->fd, msgb_data(msg), msgb_length(msg), 0,
(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
if (rc < 0) {
LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
strerror(errno));
return rc;
} else if (rc < (int)msgb_length(msg)) {
LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
"%u < %u\n", rc, msgb_length(msg));
return -EIO;
}
return 0;
}
int pcu_l1if_open()
{
struct femtol1_hdl *fl1h;
int rc;
/* allocate new femtol1_handle */
fl1h = talloc_zero(tall_pcu_ctx, struct femtol1_hdl);
INIT_LLIST_HEAD(&fl1h->wlc_list);
l1fh->fl1h = fl1h;
fl1h->priv = l1fh;
struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
osmo_wqueue_init(queue, 10);
queue->bfd.when |= BSC_FD_READ;
queue->bfd.data = l1fh;
queue->bfd.priv_nr = 0;
/* Open UDP */
struct osmo_wqueue *wq = &l1fh->udp_wq;
osmo_wqueue_init(wq, 10);
wq->write_cb = udp_write_cb;
wq->read_cb = udp_read_cb;
wq->bfd.when |= BSC_FD_READ;
wq->bfd.data = l1fh;
wq->bfd.priv_nr = 0;
rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
OSMO_SOCK_F_BIND);
if (rc < 0) {
perror("sock_init");
exit(1);
}
return 0;
}
void pcu_l1if_close(void)
{
gprs_bssgp_destroy();
/* FIXME: cleanup l1if */
talloc_free(l1fh->fl1h);
exit(0);
}

View File

@@ -0,0 +1,213 @@
/* Interface handler for Nuran Wireless Litecell 1.5 L1 (real hardware) */
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
* based on:
* femto_l1_hw.c
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <assert.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/select.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm_utils.h>
#include <nrw/litecell15/litecell15.h>
#include <nrw/litecell15/gsml1prim.h>
#include <nrw/litecell15/gsml1const.h>
#include <nrw/litecell15/gsml1types.h>
#include "gprs_debug.h"
#include "lc15bts.h"
#include "lc15_l1_if.h"
#define DEV_SYS_DSP2ARM_NAME "/dev/msgq/litecell15_dsp2arm_trx"
#define DEV_SYS_ARM2DSP_NAME "/dev/msgq/litecell15_arm2dsp_trx"
#define DEV_L1_DSP2ARM_NAME "/dev/msgq/gsml1_sig_dsp2arm_trx"
#define DEV_L1_ARM2DSP_NAME "/dev/msgq/gsml1_sig_arm2dsp_trx"
#define DEV_TCH_DSP2ARM_NAME "/dev/msgq/gsml1_tch_dsp2arm_trx"
#define DEV_TCH_ARM2DSP_NAME "/dev/msgq/gsml1_tch_arm2dsp_trx"
#define DEV_PDTCH_DSP2ARM_NAME "/dev/msgq/gsml1_pdtch_dsp2arm_trx"
#define DEV_PDTCH_ARM2DSP_NAME "/dev/msgq/gsml1_pdtch_arm2dsp_trx"
static const char *rd_devnames[] = {
[MQ_SYS_READ] = DEV_SYS_DSP2ARM_NAME,
[MQ_L1_READ] = DEV_L1_DSP2ARM_NAME,
[MQ_TCH_READ] = DEV_TCH_DSP2ARM_NAME,
[MQ_PDTCH_READ] = DEV_PDTCH_DSP2ARM_NAME,
};
static const char *wr_devnames[] = {
[MQ_SYS_WRITE] = DEV_SYS_ARM2DSP_NAME,
[MQ_L1_WRITE] = DEV_L1_ARM2DSP_NAME,
[MQ_TCH_WRITE] = DEV_TCH_ARM2DSP_NAME,
[MQ_PDTCH_WRITE]= DEV_PDTCH_ARM2DSP_NAME,
};
/* callback when there's something to read from the l1 msg_queue */
static int l1if_fd_cb(struct osmo_fd *ofd, unsigned int what)
{
//struct msgb *msg = l1p_msgb_alloc();
struct msgb *msg = msgb_alloc_headroom(sizeof(Litecell15_Prim_t) + 128,
128, "1l_fd");
struct lc15l1_hdl *fl1h = ofd->data;
int rc;
msg->l1h = msg->data;
rc = read(ofd->fd, msg->l1h, msgb_tailroom(msg));
if (rc < 0) {
if (rc != -1)
LOGP(DL1IF, LOGL_ERROR, "error reading from L1 msg_queue: %s\n",
strerror(errno));
msgb_free(msg);
return rc;
}
msgb_put(msg, rc);
switch (ofd->priv_nr) {
case MQ_SYS_WRITE:
if (rc != sizeof(Litecell15_Prim_t))
LOGP(DL1IF, LOGL_NOTICE, "%u != "
"sizeof(Litecell15_Prim_t)\n", rc);
return l1if_handle_sysprim(fl1h, msg);
case MQ_L1_WRITE:
case MQ_TCH_WRITE:
case MQ_PDTCH_WRITE:
if (rc != sizeof(GsmL1_Prim_t))
LOGP(DL1IF, LOGL_NOTICE, "%u != "
"sizeof(GsmL1_Prim_t)\n", rc);
return l1if_handle_l1prim(ofd->priv_nr, fl1h, msg);
default:
/* The compiler can't know that priv_nr is an enum. Assist. */
LOGP(DL1IF, LOGL_FATAL, "writing on a wrong queue: %d\n",
ofd->priv_nr);
exit(0);
break;
}
};
/* callback when we can write to one of the l1 msg_queue devices */
static int l1fd_write_cb(struct osmo_fd *ofd, struct msgb *msg)
{
int rc;
rc = write(ofd->fd, msg->l1h, msgb_l1len(msg));
if (rc < 0) {
LOGP(DL1IF, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
strerror(errno));
return rc;
} else if (rc < msg->len) {
LOGP(DL1IF, LOGL_ERROR, "short write to L1 msg_queue: "
"%u < %u\n", rc, msg->len);
return -EIO;
}
return 0;
}
int l1if_transport_open(int q, struct lc15l1_hdl *hdl)
{
int rc;
char buf[PATH_MAX];
/* Step 1: Open all msg_queue file descriptors */
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
struct osmo_wqueue *wq = &hdl->write_q[q];
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
snprintf(buf, sizeof(buf)-1, "%s%d", rd_devnames[q], hdl->hw_info.trx_nr);
buf[sizeof(buf)-1] = '\0';
rc = open(buf, O_RDONLY);
if (rc < 0) {
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
buf, strerror(errno));
return rc;
}
read_ofd->fd = rc;
read_ofd->priv_nr = q;
read_ofd->data = hdl;
read_ofd->cb = l1if_fd_cb;
read_ofd->when = BSC_FD_READ;
rc = osmo_fd_register(read_ofd);
if (rc < 0) {
close(read_ofd->fd);
read_ofd->fd = -1;
return rc;
}
snprintf(buf, sizeof(buf)-1, "%s%d", wr_devnames[q], hdl->hw_info.trx_nr);
buf[sizeof(buf)-1] = '\0';
rc = open(buf, O_WRONLY);
if (rc < 0) {
LOGP(DL1IF, LOGL_FATAL, "unable to open msg_queue %s: %s\n",
buf, strerror(errno));
goto out_read;
}
osmo_wqueue_init(wq, 10);
wq->write_cb = l1fd_write_cb;
write_ofd->fd = rc;
write_ofd->priv_nr = q;
write_ofd->data = hdl;
write_ofd->when = BSC_FD_WRITE;
rc = osmo_fd_register(write_ofd);
if (rc < 0) {
close(write_ofd->fd);
write_ofd->fd = -1;
goto out_read;
}
return 0;
out_read:
close(hdl->read_ofd[q].fd);
osmo_fd_unregister(&hdl->read_ofd[q]);
return rc;
}
int l1if_transport_close(int q, struct lc15l1_hdl *hdl)
{
struct osmo_fd *read_ofd = &hdl->read_ofd[q];
struct osmo_fd *write_ofd = &hdl->write_q[q].bfd;
osmo_fd_unregister(read_ofd);
close(read_ofd->fd);
read_ofd->fd = -1;
osmo_fd_unregister(write_ofd);
close(write_ofd->fd);
write_ofd->fd = -1;
return 0;
}

View File

@@ -0,0 +1,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 <osmocom/gsm/protocol/gsm_04_08.h>
#include <lc15_l1_if.h>
#include <gprs_debug.h>
#include <pcu_l1_if.h>
#include <pdch.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;
get_meas(&meas, &data_ind->measParam);
bts_update_tbf_ta("PH-DATA", data_ind->u32Fn, fl1h->trx_no,
data_ind->u8Tn, sign_qta2ta(meas.bto), false);
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 */
rc = 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
rc = -1;
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;
}
if (rc < 0) {
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);
}
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), true);
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);
/* 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;
}

View File

@@ -0,0 +1,104 @@
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
* based on:
* femto_l1_if.h
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _LC15_L1_IF_H
#define _LC15_L1_IF_H
#include <osmocom/core/select.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/gsm/gsm_utils.h>
#include "lc15bts.h"
enum {
MQ_SYS_READ,
MQ_L1_READ,
MQ_TCH_READ,
MQ_PDTCH_READ,
_NUM_MQ_READ
};
enum {
MQ_SYS_WRITE,
MQ_L1_WRITE,
MQ_TCH_WRITE,
MQ_PDTCH_WRITE,
_NUM_MQ_WRITE
};
struct lc15l1_hdl {
struct gsm_time gsm_time;
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
uint32_t dsp_trace_f;
struct llist_head wlc_list;
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_sapi_mask;
uint8_t trx_no;
struct osmo_timer_list alive_timer;
unsigned int alive_prim_cnt;
struct osmo_fd read_ofd[_NUM_MQ_READ]; /* osmo file descriptors */
struct osmo_wqueue write_q[_NUM_MQ_WRITE];
struct {
int trx_nr; /* <1-2> */
} hw_info;
};
#define msgb_l1prim(msg) ((GsmL1_Prim_t *)(msg)->l1h)
#define msgb_sysprim(msg) ((Litecell15_Prim_t *)(msg)->l1h)
typedef int l1if_compl_cb(struct msgb *l1_msg, void *data);
/* send a request primitive to the L1 and schedule completion call-back */
int l1if_req_compl(struct lc15l1_hdl *fl1h, struct msgb *msg,
int is_system_prim, l1if_compl_cb *cb, void *data);
int l1if_reset(struct lc15l1_hdl *hdl);
int l1if_activate_rf(struct lc15l1_hdl *hdl, int on);
int l1if_set_trace_flags(struct lc15l1_hdl *hdl, uint32_t flags);
int l1if_set_txpower(struct lc15l1_hdl *fl1h, float tx_power);
struct msgb *l1p_msgb_alloc(void);
struct msgb *sysp_msgb_alloc(void);
uint32_t l1if_lchan_to_hLayer2(struct gsm_lchan *lchan);
struct gsm_lchan *l1if_hLayer2_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer2);
int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg);
int l1if_handle_l1prim(int wq, struct lc15l1_hdl *fl1h, struct msgb *msg);
/* tch.c */
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
/*
* The implementation of these functions is selected by either compiling and
* linking sysmo_l1_hw.c or sysmo_l1_fwd.c
*/
int l1if_transport_open(int q, struct lc15l1_hdl *hdl);
int l1if_transport_close(int q, struct lc15l1_hdl *hdl);
#endif /* _SYSMO_L1_IF_H */

View File

@@ -0,0 +1,332 @@
/* NuRAN Wireless Litecell 1.5 L1 API related definitions */
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
* based on:
* sysmobts.c
* (C) 2011 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <nrw/litecell15/litecell15.h>
#include <nrw/litecell15/gsml1const.h>
#include <nrw/litecell15/gsml1dbg.h>
#include "lc15bts.h"
enum l1prim_type lc15bts_get_l1prim_type(GsmL1_PrimId_t id)
{
switch (id) {
case GsmL1_PrimId_MphInitReq: return L1P_T_REQ;
case GsmL1_PrimId_MphCloseReq: return L1P_T_REQ;
case GsmL1_PrimId_MphConnectReq: return L1P_T_REQ;
case GsmL1_PrimId_MphDisconnectReq: return L1P_T_REQ;
case GsmL1_PrimId_MphActivateReq: return L1P_T_REQ;
case GsmL1_PrimId_MphDeactivateReq: return L1P_T_REQ;
case GsmL1_PrimId_MphConfigReq: return L1P_T_REQ;
case GsmL1_PrimId_MphMeasureReq: return L1P_T_REQ;
case GsmL1_PrimId_MphInitCnf: return L1P_T_CONF;
case GsmL1_PrimId_MphCloseCnf: return L1P_T_CONF;
case GsmL1_PrimId_MphConnectCnf: return L1P_T_CONF;
case GsmL1_PrimId_MphDisconnectCnf: return L1P_T_CONF;
case GsmL1_PrimId_MphActivateCnf: return L1P_T_CONF;
case GsmL1_PrimId_MphDeactivateCnf: return L1P_T_CONF;
case GsmL1_PrimId_MphConfigCnf: return L1P_T_CONF;
case GsmL1_PrimId_MphMeasureCnf: return L1P_T_CONF;
case GsmL1_PrimId_PhEmptyFrameReq: return L1P_T_REQ;
case GsmL1_PrimId_PhDataReq: return L1P_T_REQ;
case GsmL1_PrimId_MphTimeInd: return L1P_T_IND;
case GsmL1_PrimId_MphSyncInd: return L1P_T_IND;
case GsmL1_PrimId_PhConnectInd: return L1P_T_IND;
case GsmL1_PrimId_PhReadyToSendInd: return L1P_T_IND;
case GsmL1_PrimId_PhDataInd: return L1P_T_IND;
case GsmL1_PrimId_PhRaInd: return L1P_T_IND;
default: return L1P_T_INVALID;
}
}
const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1] = {
{ GsmL1_PrimId_MphInitReq, "MPH-INIT.req" },
{ GsmL1_PrimId_MphCloseReq, "MPH-CLOSE.req" },
{ GsmL1_PrimId_MphConnectReq, "MPH-CONNECT.req" },
{ GsmL1_PrimId_MphDisconnectReq,"MPH-DISCONNECT.req" },
{ GsmL1_PrimId_MphActivateReq, "MPH-ACTIVATE.req" },
{ GsmL1_PrimId_MphDeactivateReq,"MPH-DEACTIVATE.req" },
{ GsmL1_PrimId_MphConfigReq, "MPH-CONFIG.req" },
{ GsmL1_PrimId_MphMeasureReq, "MPH-MEASURE.req" },
{ GsmL1_PrimId_MphInitCnf, "MPH-INIT.conf" },
{ GsmL1_PrimId_MphCloseCnf, "MPH-CLOSE.conf" },
{ GsmL1_PrimId_MphConnectCnf, "MPH-CONNECT.conf" },
{ GsmL1_PrimId_MphDisconnectCnf,"MPH-DISCONNECT.conf" },
{ GsmL1_PrimId_MphActivateCnf, "MPH-ACTIVATE.conf" },
{ GsmL1_PrimId_MphDeactivateCnf,"MPH-DEACTIVATE.conf" },
{ GsmL1_PrimId_MphConfigCnf, "MPH-CONFIG.conf" },
{ GsmL1_PrimId_MphMeasureCnf, "MPH-MEASURE.conf" },
{ GsmL1_PrimId_MphTimeInd, "MPH-TIME.ind" },
{ GsmL1_PrimId_MphSyncInd, "MPH-SYNC.ind" },
{ GsmL1_PrimId_PhEmptyFrameReq, "PH-EMPTY_FRAME.req" },
{ GsmL1_PrimId_PhDataReq, "PH-DATA.req" },
{ GsmL1_PrimId_PhConnectInd, "PH-CONNECT.ind" },
{ GsmL1_PrimId_PhReadyToSendInd,"PH-READY_TO_SEND.ind" },
{ GsmL1_PrimId_PhDataInd, "PH-DATA.ind" },
{ GsmL1_PrimId_PhRaInd, "PH-RA.ind" },
{ 0, NULL }
};
GsmL1_PrimId_t lc15bts_get_l1prim_conf(GsmL1_PrimId_t id)
{
switch (id) {
case GsmL1_PrimId_MphInitReq: return GsmL1_PrimId_MphInitCnf;
case GsmL1_PrimId_MphCloseReq: return GsmL1_PrimId_MphCloseCnf;
case GsmL1_PrimId_MphConnectReq: return GsmL1_PrimId_MphConnectCnf;
case GsmL1_PrimId_MphDisconnectReq: return GsmL1_PrimId_MphDisconnectCnf;
case GsmL1_PrimId_MphActivateReq: return GsmL1_PrimId_MphActivateCnf;
case GsmL1_PrimId_MphDeactivateReq: return GsmL1_PrimId_MphDeactivateCnf;
case GsmL1_PrimId_MphConfigReq: return GsmL1_PrimId_MphConfigCnf;
case GsmL1_PrimId_MphMeasureReq: return GsmL1_PrimId_MphMeasureCnf;
default: return -1; // Weak
}
}
enum l1prim_type lc15bts_get_sysprim_type(Litecell15_PrimId_t id)
{
switch (id) {
case Litecell15_PrimId_SystemInfoReq: return L1P_T_REQ;
case Litecell15_PrimId_SystemInfoCnf: return L1P_T_CONF;
case Litecell15_PrimId_SystemFailureInd: return L1P_T_IND;
case Litecell15_PrimId_ActivateRfReq: return L1P_T_REQ;
case Litecell15_PrimId_ActivateRfCnf: return L1P_T_CONF;
case Litecell15_PrimId_DeactivateRfReq: return L1P_T_REQ;
case Litecell15_PrimId_DeactivateRfCnf: return L1P_T_CONF;
case Litecell15_PrimId_SetTraceFlagsReq: return L1P_T_REQ;
case Litecell15_PrimId_Layer1ResetReq: return L1P_T_REQ;
case Litecell15_PrimId_Layer1ResetCnf: return L1P_T_CONF;
case Litecell15_PrimId_SetCalibTblReq: return L1P_T_REQ;
case Litecell15_PrimId_SetCalibTblCnf: return L1P_T_CONF;
case Litecell15_PrimId_MuteRfReq: return L1P_T_REQ;
case Litecell15_PrimId_MuteRfCnf: return L1P_T_CONF;
case Litecell15_PrimId_SetRxAttenReq: return L1P_T_REQ;
case Litecell15_PrimId_SetRxAttenCnf: return L1P_T_CONF;
default: return L1P_T_INVALID;
}
}
const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1] = {
{ Litecell15_PrimId_SystemInfoReq, "SYSTEM-INFO.req" },
{ Litecell15_PrimId_SystemInfoCnf, "SYSTEM-INFO.conf" },
{ Litecell15_PrimId_SystemFailureInd, "SYSTEM-FAILURE.ind" },
{ Litecell15_PrimId_ActivateRfReq, "ACTIVATE-RF.req" },
{ Litecell15_PrimId_ActivateRfCnf, "ACTIVATE-RF.conf" },
{ Litecell15_PrimId_DeactivateRfReq, "DEACTIVATE-RF.req" },
{ Litecell15_PrimId_DeactivateRfCnf, "DEACTIVATE-RF.conf" },
{ Litecell15_PrimId_SetTraceFlagsReq, "SET-TRACE-FLAGS.req" },
{ Litecell15_PrimId_Layer1ResetReq, "LAYER1-RESET.req" },
{ Litecell15_PrimId_Layer1ResetCnf, "LAYER1-RESET.conf" },
{ Litecell15_PrimId_SetCalibTblReq, "SET-CALIB.req" },
{ Litecell15_PrimId_SetCalibTblCnf, "SET-CALIB.cnf" },
{ Litecell15_PrimId_MuteRfReq, "MUTE-RF.req" },
{ Litecell15_PrimId_MuteRfCnf, "MUTE-RF.cnf" },
{ Litecell15_PrimId_SetRxAttenReq, "SET-RX-ATTEN.req" },
{ Litecell15_PrimId_SetRxAttenCnf, "SET-RX-ATTEN-CNF.cnf" },
{ 0, NULL }
};
Litecell15_PrimId_t lc15bts_get_sysprim_conf(Litecell15_PrimId_t id)
{
switch (id) {
case Litecell15_PrimId_SystemInfoReq: return Litecell15_PrimId_SystemInfoCnf;
case Litecell15_PrimId_ActivateRfReq: return Litecell15_PrimId_ActivateRfCnf;
case Litecell15_PrimId_DeactivateRfReq: return Litecell15_PrimId_DeactivateRfCnf;
case Litecell15_PrimId_Layer1ResetReq: return Litecell15_PrimId_Layer1ResetCnf;
case Litecell15_PrimId_SetCalibTblReq: return Litecell15_PrimId_SetCalibTblCnf;
case Litecell15_PrimId_MuteRfReq: return Litecell15_PrimId_MuteRfCnf;
case Litecell15_PrimId_SetRxAttenReq: return Litecell15_PrimId_SetRxAttenCnf;
default: return -1; // Weak
}
}
const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1] = {
{ GsmL1_Sapi_Idle, "IDLE" },
{ GsmL1_Sapi_Fcch, "FCCH" },
{ GsmL1_Sapi_Sch, "SCH" },
{ GsmL1_Sapi_Sacch, "SACCH" },
{ GsmL1_Sapi_Sdcch, "SDCCH" },
{ GsmL1_Sapi_Bcch, "BCCH" },
{ GsmL1_Sapi_Pch, "PCH" },
{ GsmL1_Sapi_Agch, "AGCH" },
{ GsmL1_Sapi_Cbch, "CBCH" },
{ GsmL1_Sapi_Rach, "RACH" },
{ GsmL1_Sapi_TchF, "TCH/F" },
{ GsmL1_Sapi_FacchF, "FACCH/F" },
{ GsmL1_Sapi_TchH, "TCH/H" },
{ GsmL1_Sapi_FacchH, "FACCH/H" },
{ GsmL1_Sapi_Nch, "NCH" },
{ GsmL1_Sapi_Pdtch, "PDTCH" },
{ GsmL1_Sapi_Pacch, "PACCH" },
{ GsmL1_Sapi_Pbcch, "PBCCH" },
{ GsmL1_Sapi_Pagch, "PAGCH" },
{ GsmL1_Sapi_Ppch, "PPCH" },
{ GsmL1_Sapi_Pnch, "PNCH" },
{ GsmL1_Sapi_Ptcch, "PTCCH" },
{ GsmL1_Sapi_Prach, "PRACH" },
{ 0, NULL }
};
const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1] = {
{ GsmL1_Status_Success, "Success" },
{ GsmL1_Status_Generic, "Generic error" },
{ GsmL1_Status_NoMemory, "Not enough memory" },
{ GsmL1_Status_Timeout, "Timeout" },
{ GsmL1_Status_InvalidParam, "Invalid parameter" },
{ GsmL1_Status_Busy, "Resource busy" },
{ GsmL1_Status_NoRessource, "No more resources" },
{ GsmL1_Status_Uninitialized, "Trying to use uninitialized resource" },
{ GsmL1_Status_NullInterface, "Trying to call a NULL interface" },
{ GsmL1_Status_NullFctnPtr, "Trying to call a NULL function ptr" },
{ GsmL1_Status_BadCrc, "Bad CRC" },
{ GsmL1_Status_BadUsf, "Bad USF" },
{ GsmL1_Status_InvalidCPS, "Invalid CPS field" },
{ GsmL1_Status_UnexpectedBurst, "Unexpected burst" },
{ GsmL1_Status_UnavailCodec, "AMR codec is unavailable" },
{ GsmL1_Status_CriticalError, "Critical error" },
{ GsmL1_Status_OverheatError, "Overheat error" },
{ GsmL1_Status_DeviceError, "Device error" },
{ GsmL1_Status_FacchError, "FACCH / TCH order error" },
{ GsmL1_Status_AlreadyDeactivated, "Lchan already deactivated" },
{ GsmL1_Status_TxBurstFifoOvrn, "FIFO overrun" },
{ GsmL1_Status_TxBurstFifoUndr, "FIFO underrun" },
{ GsmL1_Status_NotSynchronized, "Not synchronized" },
{ GsmL1_Status_Unsupported, "Unsupported feature" },
{ GsmL1_Status_ClockError, "System clock error" },
{ 0, NULL }
};
const struct value_string lc15bts_tracef_names[29] = {
{ DBG_DEBUG, "DEBUG" },
{ DBG_L1WARNING, "L1_WARNING" },
{ DBG_ERROR, "ERROR" },
{ DBG_L1RXMSG, "L1_RX_MSG" },
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE" },
{ DBG_L1TXMSG, "L1_TX_MSG" },
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE" },
{ DBG_MPHCNF, "MPH_CNF" },
{ DBG_MPHIND, "MPH_IND" },
{ DBG_MPHREQ, "MPH_REQ" },
{ DBG_PHIND, "PH_IND" },
{ DBG_PHREQ, "PH_REQ" },
{ DBG_PHYRF, "PHY_RF" },
{ DBG_PHYRFMSGBYTE, "PHY_MSG_BYTE" },
{ DBG_MODE, "MODE" },
{ DBG_TDMAINFO, "TDMA_INFO" },
{ DBG_BADCRC, "BAD_CRC" },
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
{ DBG_DEVICEMSG, "DEVICE_MSG" },
{ DBG_RACHINFO, "RACH_INFO" },
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
{ DBG_MEMORY, "MEMORY" },
{ DBG_PROFILING, "PROFILING" },
{ DBG_TESTCOMMENT, "TEST_COMMENT" },
{ DBG_TEST, "TEST" },
{ DBG_STATUS, "STATUS" },
{ 0, NULL }
};
const struct value_string lc15bts_tracef_docs[29] = {
{ DBG_DEBUG, "Debug Region" },
{ DBG_L1WARNING, "L1 Warning Region" },
{ DBG_ERROR, "Error Region" },
{ DBG_L1RXMSG, "L1_RX_MSG Region" },
{ DBG_L1RXMSGBYTE, "L1_RX_MSG_BYTE Region" },
{ DBG_L1TXMSG, "L1_TX_MSG Region" },
{ DBG_L1TXMSGBYTE, "L1_TX_MSG_BYTE Region" },
{ DBG_MPHCNF, "MphConfirmation Region" },
{ DBG_MPHIND, "MphIndication Region" },
{ DBG_MPHREQ, "MphRequest Region" },
{ DBG_PHIND, "PhIndication Region" },
{ DBG_PHREQ, "PhRequest Region" },
{ DBG_PHYRF, "PhyRF Region" },
{ DBG_PHYRFMSGBYTE, "PhyRF Message Region" },
{ DBG_MODE, "Mode Region" },
{ DBG_TDMAINFO, "TDMA Info Region" },
{ DBG_BADCRC, "Bad CRC Region" },
{ DBG_PHINDBYTE, "PH_IND_BYTE" },
{ DBG_PHREQBYTE, "PH_REQ_BYTE" },
{ DBG_DEVICEMSG, "Device Message Region" },
{ DBG_RACHINFO, "RACH Info" },
{ DBG_LOGCHINFO, "LOG_CH_INFO" },
{ DBG_MEMORY, "Memory Region" },
{ DBG_PROFILING, "Profiling Region" },
{ DBG_TESTCOMMENT, "Test Comments" },
{ DBG_TEST, "Test Region" },
{ DBG_STATUS, "Status Region" },
{ 0, NULL }
};
const struct value_string lc15bts_tch_pl_names[] = {
{ GsmL1_TchPlType_NA, "N/A" },
{ GsmL1_TchPlType_Fr, "FR" },
{ GsmL1_TchPlType_Hr, "HR" },
{ GsmL1_TchPlType_Efr, "EFR" },
{ GsmL1_TchPlType_Amr, "AMR(IF2)" },
{ GsmL1_TchPlType_Amr_SidBad, "AMR(SID BAD)" },
{ GsmL1_TchPlType_Amr_Onset, "AMR(ONSET)" },
{ GsmL1_TchPlType_Amr_Ratscch, "AMR(RATSCCH)" },
{ GsmL1_TchPlType_Amr_SidUpdateInH, "AMR(SID_UPDATE INH)" },
{ GsmL1_TchPlType_Amr_SidFirstP1, "AMR(SID_FIRST P1)" },
{ GsmL1_TchPlType_Amr_SidFirstP2, "AMR(SID_FIRST P2)" },
{ GsmL1_TchPlType_Amr_SidFirstInH, "AMR(SID_FIRST INH)" },
{ GsmL1_TchPlType_Amr_RatscchMarker, "AMR(RATSCCH MARK)" },
{ GsmL1_TchPlType_Amr_RatscchData, "AMR(RATSCCH DATA)" },
{ 0, NULL }
};
const struct value_string lc15bts_dir_names[] = {
{ GsmL1_Dir_TxDownlink, "TxDL" },
{ GsmL1_Dir_TxUplink, "TxUL" },
{ GsmL1_Dir_RxUplink, "RxUL" },
{ GsmL1_Dir_RxDownlink, "RxDL" },
{ GsmL1_Dir_TxDownlink|GsmL1_Dir_RxUplink, "BOTH" },
{ 0, NULL }
};
const struct value_string lc15bts_chcomb_names[] = {
{ GsmL1_LogChComb_0, "dummy" },
{ GsmL1_LogChComb_I, "tch_f" },
{ GsmL1_LogChComb_II, "tch_h" },
{ GsmL1_LogChComb_IV, "ccch" },
{ GsmL1_LogChComb_V, "ccch_sdcch4" },
{ GsmL1_LogChComb_VII, "sdcch8" },
{ GsmL1_LogChComb_XIII, "pdtch" },
{ 0, NULL }
};
const uint8_t pdch_msu_size[_NUM_PDCH_CS] = {
[PDCH_CS_1] = 23,
[PDCH_CS_2] = 34,
[PDCH_CS_3] = 40,
[PDCH_CS_4] = 54,
[PDCH_MCS_1] = 27,
[PDCH_MCS_2] = 33,
[PDCH_MCS_3] = 42,
[PDCH_MCS_4] = 49,
[PDCH_MCS_5] = 60,
[PDCH_MCS_6] = 78,
[PDCH_MCS_7] = 118,
[PDCH_MCS_8] = 142,
[PDCH_MCS_9] = 154
};

View File

@@ -0,0 +1,64 @@
#ifndef LC15BTS_H
#define LC15BTS_H
#include <stdlib.h>
#include <osmocom/core/utils.h>
#include <nrw/litecell15/litecell15.h>
#include <nrw/litecell15/gsml1const.h>
/*
* Depending on the firmware version either GsmL1_Prim_t or Litecell15_Prim_t
* is the bigger struct. For earlier firmware versions the GsmL1_Prim_t was the
* bigger struct.
*/
#define LC15BTS_PRIM_SIZE \
(OSMO_MAX(sizeof(Litecell15_Prim_t), sizeof(GsmL1_Prim_t)) + 128)
enum l1prim_type {
L1P_T_INVALID, /* this must be 0 to detect uninitialized elements */
L1P_T_REQ,
L1P_T_CONF,
L1P_T_IND,
};
enum l1prim_type lc15bts_get_l1prim_type(GsmL1_PrimId_t id);
const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1];
GsmL1_PrimId_t lc15bts_get_l1prim_conf(GsmL1_PrimId_t id);
enum l1prim_type lc15bts_get_sysprim_type(Litecell15_PrimId_t id);
const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1];
Litecell15_PrimId_t lc15bts_get_sysprim_conf(Litecell15_PrimId_t id);
const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1];
const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1];
const struct value_string lc15bts_tracef_names[29];
const struct value_string lc15bts_tracef_docs[29];
const struct value_string lc15bts_tch_pl_names[15];
const struct value_string lc15bts_clksrc_names[10];
const struct value_string lc15bts_dir_names[6];
enum pdch_cs {
PDCH_CS_1,
PDCH_CS_2,
PDCH_CS_3,
PDCH_CS_4,
PDCH_MCS_1,
PDCH_MCS_2,
PDCH_MCS_3,
PDCH_MCS_4,
PDCH_MCS_5,
PDCH_MCS_6,
PDCH_MCS_7,
PDCH_MCS_8,
PDCH_MCS_9,
_NUM_PDCH_CS
};
const uint8_t pdch_msu_size[_NUM_PDCH_CS];
#endif /* LC15BTS_H */

View File

@@ -10,9 +10,13 @@
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <sysmo_l1_if.h>
#include <gprs_debug.h>
#include <pcu_l1_if.h>
#include <pdch.h>
#include <bts.h>
extern void *tall_pcu_ctx;
@@ -109,23 +113,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 +142,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:
@@ -190,6 +177,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size));
pcu_rx_block_time(data_ind->u16Arfcn, data_ind->u32Fn, data_ind->u8Tn);
/*
* TODO: Add proper bad frame handling here. This could be used
* to switch the used CS. Avoid a crash with the PCU right now
@@ -198,12 +187,9 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
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, sign_qta2ta(meas.bto), false);
switch (data_ind->sapi) {
case GsmL1_Sapi_Pdtch:
@@ -213,7 +199,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,
@@ -221,13 +207,23 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
break;
case GsmL1_Sapi_Ptcch:
// FIXME
rc = -1;
break;
default:
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
get_value_string(femtobts_l1sapi_names, data_ind->sapi));
rc = -1;
break;
}
if (rc < 0) {
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);
//send_gsmtap(PCU_GSMTAP_C_UL_UNKNOWN, true, 0, date_ind->u8Tn, GSMTAP_CHANNEL_PACCH, data_ind->u32Fn, data_ind->msgUnitParam.u8Buffer+1, data_ind->msgUnitParam.u8Size-1);
}
return rc;
}
@@ -235,31 +231,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), true);
return 0;
}
@@ -338,12 +317,6 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
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");
@@ -353,7 +326,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)
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
{
struct femtol1_hdl *fl1h;
int rc;
@@ -363,7 +336,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1)
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;
@@ -374,9 +347,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1)
return NULL;
}
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
if (fl1h->gsmtap)
gsmtap_source_add_sink(fl1h->gsmtap);
fl1h->gsmtap = gsmtap;
return fl1h;
}

View File

@@ -38,7 +38,7 @@ struct femtol1_hdl {
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_sapi_mask;
void *priv; /* user reference */
uint8_t trx_no;
struct osmo_timer_list alive_timer;
unsigned int alive_prim_cnt;

View File

@@ -1,4 +1,4 @@
/* sysmo_sock.cpp
/* osmobts_sock.cpp
*
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
*
@@ -29,15 +29,18 @@ extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
}
#include <gprs_rlcmac.h>
#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>
#include <pdch.h>
extern void *tall_pcu_ctx;
@@ -46,7 +49,7 @@ int l1if_close_pdch(void *obj);
}
/*
* SYSMO-PCU socket functions
* osmo-bts PCU socket functions
*/
struct pcu_sock_state {
@@ -55,7 +58,23 @@ struct pcu_sock_state {
struct llist_head upqueue; /* queue for sending messages */
} *pcu_sock_state = NULL;
static void pcu_sock_timeout(void *_priv);
static void pcu_sock_timeout(void *_priv)
{
pcu_l1if_open();
}
static void pcu_tx_txt_retry(void *_priv)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
struct pcu_sock_state *state = pcu_sock_state;
if (bts->active)
return;
LOGP(DL1IF, LOGL_INFO, "Sending version %s to BTS.\n", PACKAGE_VERSION);
pcu_tx_txt_ind(PCU_VERSION, "%s", PACKAGE_VERSION);
osmo_timer_schedule(&state->timer, 5, 0);
}
int pcu_sock_send(struct msgb *msg)
{
@@ -100,7 +119,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;
@@ -108,7 +127,8 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost)
#endif
for (ts = 0; ts < 8; ts++)
bts->trx[trx].pdch[ts].disable();
#warning "NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c for the reset."
/* FIXME: NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c
for the reset. */
gprs_rlcmac_tbf::free_all(&bts->trx[trx]);
}
@@ -223,6 +243,9 @@ int pcu_l1if_open(void)
struct sockaddr_un local;
unsigned int namelen;
int rc;
struct gprs_rlcmac_bts *bts = bts_main_data();
LOGP(DL1IF, LOGL_INFO, "Opening OsmoPCU L1 interface to OsmoBTS\n");
state = pcu_sock_state;
if (!state) {
@@ -236,13 +259,13 @@ int pcu_l1if_open(void)
bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (bfd->fd < 0) {
LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU socket.\n");
talloc_free(state);
return -1;
}
local.sun_family = AF_UNIX;
strncpy(local.sun_path, "/tmp/pcu_bts", sizeof(local.sun_path));
strncpy(local.sun_path, bts->pcu_sock_path, sizeof(local.sun_path));
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
/* we use the same magic that X11 uses in Xtranssock.c for
@@ -258,12 +281,13 @@ int pcu_l1if_open(void)
#endif
rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
if (rc != 0) {
LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
"socket, delaying... '%s'\n", local.sun_path);
LOGP(DL1IF, LOGL_ERROR, "Failed to connect to the osmo-bts"
" PCU socket (%s), delaying... '%s'\n",
strerror(errno), local.sun_path);
pcu_sock_state = state;
close(bfd->fd);
bfd->fd = -1;
state->timer.cb = pcu_sock_timeout;
osmo_timer_setup(&state->timer, pcu_sock_timeout, NULL);
osmo_timer_schedule(&state->timer, 5, 0);
return 0;
}
@@ -280,10 +304,18 @@ int pcu_l1if_open(void)
return rc;
}
LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
LOGP(DL1IF, LOGL_NOTICE, "osmo-bts PCU socket %s has been connected\n",
local.sun_path);
pcu_sock_state = state;
LOGP(DL1IF, LOGL_INFO, "Sending version %s to BTS.\n", PACKAGE_VERSION);
pcu_tx_txt_ind(PCU_VERSION, "%s", PACKAGE_VERSION);
/* Schedule a timer so we keep trying until the BTS becomes active. */
osmo_timer_setup(&state->timer, pcu_tx_txt_retry, NULL);
osmo_timer_schedule(&state->timer, 5, 0);
return 0;
}
@@ -303,8 +335,3 @@ void pcu_l1if_close(void)
talloc_free(state);
pcu_sock_state = NULL;
}
static void pcu_sock_timeout(void *_priv)
{
pcu_l1if_open();
}

View File

@@ -26,23 +26,32 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
}
#include <gprs_rlcmac.h>
#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>
#include <pdch.h>
// FIXME: move this, when changed from c++ to c.
extern "C" {
void *l1if_open_pdch(void *priv, uint32_t hlayer1);
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);
@@ -70,6 +79,42 @@ struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
return msg;
}
const struct value_string gsm_pcu_if_text_type_names[] = {
OSMO_VALUE_STRING(PCU_VERSION),
OSMO_VALUE_STRING(PCU_OML_ALERT),
{ 0, NULL }
};
int pcu_tx_txt_ind(enum gsm_pcu_if_text_type t, const char *fmt, ...)
{
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_txt_ind *txt;
va_list ap;
char *rep;
struct msgb *msg = pcu_msgb_alloc(PCU_IF_MSG_TXT_IND, 0);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
txt = &pcu_prim->u.txt_ind;
txt->type = t;
va_start(ap, fmt);
rep = talloc_vasprintf(tall_pcu_ctx, fmt, ap);
va_end(ap);
if (!rep)
return -ENOMEM;
osmo_strlcpy(txt->text, rep, TXT_MAX_LEN);
talloc_free(rep);
LOGP(DL1IF, LOGL_INFO, "Sending %s TXT as %s to BTS\n", txt->text,
get_value_string(gsm_pcu_if_text_type_names, t));
return pcu_sock_send(msg);
}
static int pcu_tx_act_req(uint8_t trx, uint8_t ts, uint8_t activate)
{
struct msgb *msg;
@@ -98,9 +143,10 @@ static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
struct gsm_pcu_if_data *data_req;
int current_fn = get_current_fn();
LOGP(DL1IF, LOGL_DEBUG, "Sending data request: trx=%d ts=%d sapi=%d "
"arfcn=%d fn=%d block=%d data=%s\n", trx, ts, sapi, arfcn, fn,
"arfcn=%d fn=%d cur_fn=%d block=%d data=%s\n", trx, ts, sapi, arfcn, fn, current_fn,
block_nr, osmo_hexdump(data, len));
msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_REQ, 0);
@@ -124,15 +170,17 @@ static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
#ifdef ENABLE_SYSMODSP
#ifdef ENABLE_DIRECT_PHY
struct gprs_rlcmac_bts *bts = bts_main_data();
if (bts->trx[trx].fl1h)
if (bts->trx[trx].fl1h) {
l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
msg->data, msg->len);
else
msgb_free(msg);
return;
}
#endif
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
msg->data, msg->len);
msgb_free(msg);
}
@@ -140,15 +188,19 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
#ifdef ENABLE_SYSMODSP
struct gprs_rlcmac_bts *bts = bts_main_data();
if (bts->trx[trx].fl1h)
if (bts->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_PTCCH))
gsmtap_send(bts->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PTCCH, 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);
else
msgb_free(msg);
return;
}
#endif
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
msg->data, msg->len);
msgb_free(msg);
}
@@ -180,6 +232,17 @@ void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi)
pcu_tx_data_req(0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, 23+3);
}
extern "C" void pcu_rx_block_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
{
BTS::main_bts()->set_current_block_frame_number(fn, 0);
}
extern "C" void pcu_rx_ra_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
{
/* access bursts may arrive some bursts earlier */
BTS::main_bts()->set_current_block_frame_number(fn, 5);
}
extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx_no, uint8_t ts_no, uint8_t *data,
uint8_t len, uint32_t fn, struct pcu_l1_meas *meas)
{
@@ -189,38 +252,78 @@ extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx_no, uint8_t ts_no, uint8_t *dat
return pdch->rcv_block(data, len, fn, meas);
}
static int pcu_rx_data_ind_bcch(uint8_t *data, uint8_t len)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
if (len == 0) {
bts->si13_is_set = false;
LOGP(DL1IF, LOGL_INFO, "Received PCU data indication with empty SI13: cache cleaned\n");
return 0;
}
if (len != GSM_MACBLOCK_LEN) {
LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with SI13 with unexpected length %u\n", len);
return -EINVAL;
}
memcpy(bts->si13, data, GSM_MACBLOCK_LEN);
bts->si13_is_set = true;
return 0;
}
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
{
int rc = 0;
struct gprs_rlcmac_bts *bts = bts_main_data();
int rc;
int current_fn = get_current_fn();
pcu_l1_meas meas;
meas.set_rssi(data_ind->rssi);
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,
"fn=%d cur_fn=%d block=%d data=%s\n", data_ind->sapi,
data_ind->arfcn, data_ind->fn, current_fn, data_ind->block_nr,
osmo_hexdump(data_ind->data, data_ind->len));
switch (data_ind->sapi) {
case PCU_IF_SAPI_PDTCH:
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
rc = pcu_rx_data_ind_pdtch(data_ind->trx_nr, data_ind->ts_nr,
data_ind->data, data_ind->len, data_ind->fn,
&meas);
break;
case PCU_IF_SAPI_BCCH:
rc = pcu_rx_data_ind_bcch(data_ind->data, data_ind->len);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
"unsupported sapi %d\n", data_ind->sapi);
rc = -EINVAL;
}
if (rc < 0 && (bts->gsmtap_categ_mask & (1 <<PCU_GSMTAP_C_UL_UNKNOWN))) {
gsmtap_send(bts->gsmtap, data_ind->arfcn | GSMTAP_ARFCN_F_UPLINK, data_ind->ts_nr,
GSMTAP_CHANNEL_PACCH, 0, data_ind->fn, 0, 0, data_ind->data, data_ind->len);
}
return rc;
}
static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
{
int rc = 0;
int current_fn = get_current_fn();
LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d fn=%d\n",
data_cnf->sapi, data_cnf->fn);
LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d fn=%d cur_fn=%d\n",
data_cnf->sapi, data_cnf->fn, current_fn);
switch (data_cnf->sapi) {
case PCU_IF_SAPI_PCH:
@@ -237,25 +340,26 @@ 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)
{
int rc = 0;
int current_fn = get_current_fn();
LOGP(DL1IF, LOGL_DEBUG, "RTS request received: trx=%d ts=%d sapi=%d "
"arfcn=%d fn=%d block=%d\n", rts_req->trx_nr, rts_req->ts_nr,
rts_req->sapi, rts_req->arfcn, rts_req->fn, rts_req->block_nr);
"arfcn=%d fn=%d cur_fn=%d block=%d\n", rts_req->trx_nr, rts_req->ts_nr,
rts_req->sapi, rts_req->arfcn, rts_req->fn, current_fn, rts_req->block_nr);
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 */
@@ -278,16 +382,18 @@ static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
{
int rc = 0;
int current_fn = get_current_fn();
LOGP(DL1IF, LOGL_INFO, "RACH request received: sapi=%d "
"qta=%d, ra=%d, fn=%d\n", rach_ind->sapi, rach_ind->qta,
rach_ind->ra, rach_ind->fn);
"qta=%d, ra=%d, fn=%d, cur_fn=%d, is_11bit=%d\n", rach_ind->sapi, rach_ind->qta,
rach_ind->ra, rach_ind->fn, current_fn, rach_ind->is_11bit);
switch (rach_ind->sapi) {
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 "
@@ -305,8 +411,9 @@ 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;
uint16_t cell_id = ntohs(info_ind->cell_id);
if (info_ind->version != PCU_IF_VERSION) {
fprintf(stderr, "PCU interface version number of BTS (%d) is "
@@ -319,22 +426,25 @@ static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) {
LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
if (!bts->active)
return -EAGAIN;
bssgp_failed:
bts->active = false;
/* 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();
exit(0);
}
LOGP(DL1IF, LOGL_INFO, "BTS available\n");
LOGP(DL1IF, LOGL_DEBUG, " mcc=%x\n", info_ind->mcc);
LOGP(DL1IF, LOGL_DEBUG, " mnc=%x\n", info_ind->mnc);
LOGP(DL1IF, LOGL_DEBUG, " mcc=%03u\n", info_ind->mcc);
LOGP(DL1IF, LOGL_DEBUG, " mnc=%0*u\n", info_ind->mnc_3_digits, info_ind->mnc);
LOGP(DL1IF, LOGL_DEBUG, " lac=%d\n", info_ind->lac);
LOGP(DL1IF, LOGL_DEBUG, " rac=%d\n", info_ind->rac);
LOGP(DL1IF, LOGL_DEBUG, " cell_id=%d\n", ntohs(info_ind->cell_id));
LOGP(DL1IF, LOGL_DEBUG, " cell_id=%d\n", cell_id);
LOGP(DL1IF, LOGL_DEBUG, " bsic=%d\n", info_ind->bsic);
LOGP(DL1IF, LOGL_DEBUG, " nsei=%d\n", info_ind->nsei);
LOGP(DL1IF, LOGL_DEBUG, " nse_timer=%d %d %d %d %d %d %d\n",
@@ -384,8 +494,8 @@ bssgp_failed:
pcu = gprs_bssgp_create_and_connect(bts, info_ind->local_port[0],
info_ind->remote_ip[0], info_ind->remote_port[0],
info_ind->nsei, info_ind->nsvci[0], info_ind->bvci,
info_ind->mcc, info_ind->mnc, info_ind->lac, info_ind->rac,
info_ind->cell_id);
info_ind->mcc, info_ind->mnc, info_ind->mnc_3_digits, info_ind->lac, info_ind->rac,
cell_id);
if (!pcu) {
LOGP(DL1IF, LOGL_NOTICE, "SGSN not available\n");
goto bssgp_failed;
@@ -415,17 +525,18 @@ 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,
info_ind->trx[trx].hlayer1);
trx,
info_ind->trx[trx].hlayer1,
bts->gsmtap);
if (!bts->trx[trx].fl1h) {
LOGP(DL1IF, LOGL_FATAL, "Failed to open direct "
"DSP access for PDCH.\n");
@@ -439,12 +550,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(
@@ -466,6 +577,7 @@ bssgp_failed:
}
}
bts->active = true;
return rc;
}
@@ -476,9 +588,8 @@ static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
/* omit frame numbers not starting at a MAC block */
if (fn13 != 0 && fn13 != 4 && fn13 != 8)
return 0;
#warning uncomment
// LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
// time_ind->fn % 52);
LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n", time_ind->fn % 52);
BTS::main_bts()->set_current_frame_number(time_ind->fn);
return 0;

View File

@@ -29,10 +29,48 @@ extern "C" {
#include <osmocom/core/timer.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/pcu/pcuif_proto.h>
#ifdef __cplusplus
}
#endif
static inline uint8_t qta2ta(int16_t qta)
{
if (qta < 0)
return 0;
if (qta > 252)
qta = 252;
return qta >> 2;
}
static inline int8_t sign_qta2ta(int16_t qta)
{
int8_t ta_adj = 0;
if (qta < -252)
qta = -252;
if (qta > 252)
qta = 252;
/* 1-bit TA adjustment if TA error reported by L1 is outside +/- 2 qbits */
if (qta > 2)
ta_adj = 1;
if (qta < -2)
ta_adj = -1;
return (qta >> 2) + ta_adj;
}
static inline uint8_t ta_limit(int16_t ta)
{
if (ta < 0)
ta = 0;
if (ta > 63)
ta = 63;
return ta;
}
/*
* L1 Measurement values
*/
@@ -48,7 +86,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 +139,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
};
@@ -114,6 +160,8 @@ void pcu_l1if_tx_agch(bitvec * block, int len);
void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi);
int pcu_tx_txt_ind(enum gsm_pcu_if_text_type t, const char *fmt, ...);
int pcu_l1if_open(void);
void pcu_l1if_close(void);
@@ -122,15 +170,18 @@ int pcu_sock_send(struct msgb *msg);
#endif
#ifdef __cplusplus
extern "C"
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);
#ifdef __cplusplus
extern "C"
#endif
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
uint8_t len, uint32_t fn, struct pcu_l1_meas *meas);
void pcu_rx_block_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no);
void pcu_rx_ra_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no);
#ifdef __cplusplus
}
#endif
#endif // PCU_L1_IF_H

View File

@@ -17,32 +17,50 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <gprs_bssgp_pcu.h>
#include <arpa/inet.h>
#include <pcu_l1_if.h>
#include <gprs_rlcmac.h>
#include <gsm_timer.h>
#include <gprs_debug.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <sched.h>
#include <bts.h>
#include <gprs_coding_scheme.h>
#include <osmocom/pcu/pcuif_proto.h>
extern "C" {
#include "pcu_vty.h"
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/ports.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/select.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
}
extern struct gprs_nsvc *nsvc;
uint16_t spoof_mcc = 0, spoof_mnc = 0;
bool spoof_mnc_3_digits = false;
static int config_given = 0;
static char *config_file = strdup("osmo-pcu.cfg");
extern struct vty_app_info pcu_vty_info;
void *tall_pcu_ctx;
void *tall_pcu_ctx = NULL;
extern void *bv_tall_ctx;
static int quit = 0;
static int rt_prio = -1;
static const char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
static void print_help()
{
@@ -54,8 +72,10 @@ static void print_help()
"provided by BTS\n"
" -n --mnc MNC use given MNC instead of value "
"provided by BTS\n"
" -V --version print version\n"
" -r --realtime PRIO Use SCHED_RR with the specified "
"priority\n"
" -i --gsmtap-ip The destination IP used for GSMTAP.\n"
);
}
@@ -72,10 +92,11 @@ static void handle_options(int argc, char **argv)
{ "version", 0, 0, 'V' },
{ "realtime", 1, 0, 'r' },
{ "exit", 0, 0, 'e' },
{ "gsmtap-ip", 1, 0, 'i' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hc:m:n:Vr:e",
c = getopt_long(argc, argv, "hc:m:n:Vr:e:i:",
long_options, &option_idx);
if (c == -1)
break;
@@ -94,12 +115,18 @@ static void handle_options(int argc, char **argv)
spoof_mcc = atoi(optarg);
break;
case 'n':
spoof_mnc = atoi(optarg);
if (osmo_mnc_from_str(optarg, &spoof_mnc, &spoof_mnc_3_digits)) {
fprintf(stderr, "Error decoding MNC '%s'\n", optarg);
exit(1);
}
break;
case 'V':
print_version(1);
exit(0);
break;
case 'i':
gsmtap_addr = optarg;
break;
case 'r':
rt_prio = atoi(optarg);
break;
@@ -154,14 +181,18 @@ int main(int argc, char *argv[])
struct gprs_rlcmac_bts *bts;
int rc;
tall_pcu_ctx = talloc_named_const(NULL, 1, "Osmo-PCU context");
if (!tall_pcu_ctx)
return -ENOMEM;
bv_tall_ctx = tall_pcu_ctx;
/* tall_pcu_ctx may already have been initialized in bts.cpp during early_init(). */
if (!tall_pcu_ctx) {
tall_pcu_ctx = talloc_named_const(NULL, 1, "Osmo-PCU context");
if (!tall_pcu_ctx)
return -ENOMEM;
osmo_init_logging2(tall_pcu_ctx, &gprs_log_info);
}
bts = bts_main_data();
bts->fc_interval = 1;
bts->initial_cs_dl = bts->initial_cs_ul = 1;
bts->initial_mcs_dl = bts->initial_mcs_ul = 1;
bts->cs1 = 1;
bts->t3142 = 20;
bts->t3169 = 5;
@@ -172,12 +203,15 @@ int main(int argc, char *argv[])
bts->n3103 = 4;
bts->n3105 = 8;
bts->alpha = 0; /* a = 0.0 */
bts->si13_is_set = false;
bts->ms_idle_sec = 60; /* slightly above T3314 (default 44s, 24.008, 11.2.2) */
bts->cs_adj_enabled = 1;
bts->cs_adj_upper_limit = 33; /* Decrease CS if the error rate is above */
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 = 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;
@@ -187,13 +221,51 @@ int main(int argc, char *argv[])
bts->cs_lqual_ranges[2].high = 13;
bts->cs_lqual_ranges[3].low = 12;
bts->cs_lqual_ranges[3].high = 256;
/* MCS-1 to MCS-9 */
/* Default thresholds are referenced from literature */
/* Fig. 2.3, Chapter 2, Optimizing Wireless Communication Systems, Springer (2009) */
bts->mcs_lqual_ranges[0].low = -256;
bts->mcs_lqual_ranges[0].high = 6;
bts->mcs_lqual_ranges[1].low = 5;
bts->mcs_lqual_ranges[1].high = 8;
bts->mcs_lqual_ranges[2].low = 7;
bts->mcs_lqual_ranges[2].high = 13;
bts->mcs_lqual_ranges[3].low = 12;
bts->mcs_lqual_ranges[3].high = 15;
bts->mcs_lqual_ranges[4].low = 14;
bts->mcs_lqual_ranges[4].high = 17;
bts->mcs_lqual_ranges[5].low = 16;
bts->mcs_lqual_ranges[5].high = 18;
bts->mcs_lqual_ranges[6].low = 17;
bts->mcs_lqual_ranges[6].high = 20;
bts->mcs_lqual_ranges[7].low = 19;
bts->mcs_lqual_ranges[7].high = 24;
bts->mcs_lqual_ranges[8].low = 23;
bts->mcs_lqual_ranges[8].high = 256;
bts->cs_downgrade_threshold = 200;
/* TODO: increase them when CRBB decoding is implemented */
bts->ws_base = 64;
bts->ws_pdch = 0;
bts->llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
bts->dl_tbf_idle_msec = 2000;
bts->llc_idle_ack_csec = 10;
msgb_set_talloc_ctx(tall_pcu_ctx);
/*
* By default resegmentation is supported in DL
* can also be configured through VTY
*/
bts->dl_arq_type = EGPRS_ARQ1;
osmo_init_logging(&gprs_log_info);
bts->pcu_sock_path = talloc_strdup(tall_pcu_ctx, PCU_SOCK_DEFAULT);
msgb_talloc_ctx_init(tall_pcu_ctx, 0);
osmo_stats_init(tall_pcu_ctx);
rate_ctr_init(tall_pcu_ctx);
gprs_ns_set_log_ss(DNS);
bssgp_set_log_ss(DBSSGP);
vty_init(&pcu_vty_info);
@@ -206,6 +278,13 @@ int main(int argc, char *argv[])
exit(0);
}
bts->gsmtap = gsmtap_source_init(gsmtap_addr, GSMTAP_UDP_PORT, 1);
if (bts->gsmtap)
gsmtap_source_add_sink(bts->gsmtap);
else
fprintf(stderr, "Failed to initialize GSMTAP for %s\n", gsmtap_addr);
rc = vty_read_config_file(config_file, NULL);
if (rc < 0 && config_given) {
fprintf(stderr, "Failed to parse the config file: '%s'\n",
@@ -216,7 +295,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);

View File

@@ -16,11 +16,35 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
inline int msecs_to_frames(int msecs) {
return (msecs * (1024 * 1000 / 4615)) / 1024;
}
inline uint32_t next_fn(uint32_t fn, uint32_t offset)
{
return (fn + offset) % GSM_MAX_FN;
}
inline void csecs_to_timeval(unsigned csecs, struct timeval *tv) {
tv->tv_sec = csecs / 100;
tv->tv_usec = (csecs % 100) * 10000;
}
template <typename T>
inline unsigned int pcu_bitcount(T x)
{
unsigned int count = 0;
for (count = 0; x; count += 1)
x &= x - 1;
return count;
}
inline uint8_t pcu_lsb(uint8_t x)
{
return x & -x;
}

View File

@@ -3,18 +3,87 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/pcu/pcuif_proto.h>
#include "pcu_vty.h"
#include "gprs_rlcmac.h"
#include <pdch.h>
#include "bts.h"
#include "tbf.h"
#include "pcu_vty_functions.h"
enum node_type pcu_vty_go_parent(struct vty *vty)
extern void *tall_pcu_ctx;
static const struct value_string pcu_gsmtap_categ_names[] = {
{ PCU_GSMTAP_C_DL_UNKNOWN, "dl-unknown" },
{ PCU_GSMTAP_C_DL_DUMMY, "dl-dummy" },
{ PCU_GSMTAP_C_DL_CTRL, "dl-ctrl" },
{ PCU_GSMTAP_C_DL_DATA_GPRS, "dl-data-gprs" },
{ PCU_GSMTAP_C_DL_DATA_EGPRS, "dl-data-egprs" },
{ PCU_GSMTAP_C_DL_PTCCH, "dl-ptcch" },
{ PCU_GSMTAP_C_UL_UNKNOWN, "ul-unknown" },
{ PCU_GSMTAP_C_UL_DUMMY, "ul-dummy" },
{ PCU_GSMTAP_C_UL_CTRL, "ul-ctrl" },
{ PCU_GSMTAP_C_UL_DATA_GPRS, "ul-data-gprs" },
{ PCU_GSMTAP_C_UL_DATA_EGPRS, "ul-data-egprs" },
{ 0, NULL }
};
static const struct value_string pcu_gsmtap_categ_help[] = {
{ PCU_GSMTAP_C_DL_UNKNOWN, "Unknown / Unparseable / Erroneous Downlink Blocks" },
{ PCU_GSMTAP_C_DL_DUMMY, "Downlink Dummy Blocks" },
{ PCU_GSMTAP_C_DL_CTRL, "Downlink Control Blocks" },
{ PCU_GSMTAP_C_DL_DATA_GPRS, "Downlink Data Blocks (GPRS)" },
{ PCU_GSMTAP_C_DL_DATA_EGPRS, "Downlink Data Blocks (EGPRS)" },
{ PCU_GSMTAP_C_DL_PTCCH, "Downlink PTCCH Blocks" },
{ PCU_GSMTAP_C_UL_UNKNOWN, "Unknown / Unparseable / Erroneous Downlink Blocks" },
{ PCU_GSMTAP_C_UL_DUMMY, "Uplink Dummy Blocks" },
{ PCU_GSMTAP_C_UL_CTRL, "Uplink Control Blocks" },
{ PCU_GSMTAP_C_UL_DATA_GPRS, "Uplink Data Blocks (GPRS)" },
{ PCU_GSMTAP_C_UL_DATA_EGPRS, "Uplink Data Blocks (EGPRS)" },
{ 0, NULL }
};
DEFUN(cfg_pcu_gsmtap_categ, cfg_pcu_gsmtap_categ_cmd, "HIDDEN", "HIDDEN")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
int categ;
categ = get_string_value(pcu_gsmtap_categ_names, argv[0]);
if (categ < 0)
return CMD_WARNING;
bts->gsmtap_categ_mask |= (1 << categ);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_gsmtap_categ, cfg_pcu_no_gsmtap_categ_cmd, "HIDDEN", "HIDDEN")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
int categ;
categ = get_string_value(pcu_gsmtap_categ_names, argv[0]);
if (categ < 0)
return CMD_WARNING;
bts->gsmtap_categ_mask &= ~(1 << categ);
return CMD_SUCCESS;
}
int pcu_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
#if 0
@@ -51,8 +120,12 @@ static struct cmd_node pcu_node = {
static int config_write_pcu(struct vty *vty)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
unsigned int i;
vty_out(vty, "pcu%s", VTY_NEWLINE);
if (bts->egprs_enabled)
vty_out(vty, " egprs only%s", VTY_NEWLINE);
vty_out(vty, " flow-control-interval %d%s", bts->fc_interval,
VTY_NEWLINE);
if (bts->fc_bvc_bucket_size)
@@ -105,6 +178,50 @@ static int config_write_pcu(struct vty *vty)
bts->cs_lqual_ranges[3].low,
VTY_NEWLINE);
vty_out(vty, " mcs link-quality-ranges mcs1 %d mcs2 %d %d mcs3 %d %d mcs4 %d %d mcs5 %d %d mcs6 %d %d mcs7 %d %d mcs8 %d %d mcs9 %d%s",
bts->mcs_lqual_ranges[0].high,
bts->mcs_lqual_ranges[1].low,
bts->mcs_lqual_ranges[1].high,
bts->mcs_lqual_ranges[2].low,
bts->mcs_lqual_ranges[2].high,
bts->mcs_lqual_ranges[3].low,
bts->mcs_lqual_ranges[3].high,
bts->mcs_lqual_ranges[4].low,
bts->mcs_lqual_ranges[4].high,
bts->mcs_lqual_ranges[5].low,
bts->mcs_lqual_ranges[5].high,
bts->mcs_lqual_ranges[6].low,
bts->mcs_lqual_ranges[6].high,
bts->mcs_lqual_ranges[7].low,
bts->mcs_lqual_ranges[7].high,
bts->mcs_lqual_ranges[8].low,
VTY_NEWLINE);
if (bts->initial_mcs_dl != 1 && bts->initial_mcs_ul != 1) {
if (bts->initial_mcs_ul == bts->initial_mcs_dl)
vty_out(vty, " mcs %d%s", bts->initial_mcs_dl,
VTY_NEWLINE);
else
vty_out(vty, " mcs %d %d%s", bts->initial_mcs_dl,
bts->initial_mcs_ul, VTY_NEWLINE);
}
if (bts->max_mcs_dl && bts->max_mcs_ul) {
if (bts->max_mcs_ul == bts->max_mcs_dl)
vty_out(vty, " mcs max %d%s", bts->max_mcs_dl,
VTY_NEWLINE);
else
vty_out(vty, " mcs max %d %d%s", bts->max_mcs_dl,
bts->max_mcs_ul, VTY_NEWLINE);
}
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)
@@ -137,8 +254,18 @@ static int config_write_pcu(struct vty *vty)
if (bts->dl_tbf_idle_msec)
vty_out(vty, " dl-tbf-idle-time %d%s", bts->dl_tbf_idle_msec,
VTY_NEWLINE);
if (strcmp(bts->pcu_sock_path, PCU_SOCK_DEFAULT))
vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE);
return pcu_vty_config_write_pcu_ext(vty);
for (i = 0; i < 32; i++) {
unsigned int cs = (1 << i);
if (bts->gsmtap_categ_mask & cs) {
vty_out(vty, " gsmtap-category %s%s",
get_value_string(pcu_gsmtap_categ_names, i), VTY_NEWLINE);
}
}
return CMD_SUCCESS;
}
/* per-BTS configuration */
@@ -152,6 +279,32 @@ DEFUN(cfg_pcu,
return CMD_SUCCESS;
}
#define EGPRS_STR "EGPRS configuration\n"
DEFUN(cfg_pcu_egprs,
cfg_pcu_egprs_cmd,
"egprs only",
EGPRS_STR "Use EGPRS and disable plain GPRS\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->egprs_enabled = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_egprs,
cfg_pcu_no_egprs_cmd,
"no egprs",
NO_STR EGPRS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->egprs_enabled = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_interval,
cfg_pcu_fc_interval_cmd,
"flow-control-interval <1-10>",
@@ -323,11 +476,12 @@ DEFUN(cfg_pcu_no_cs,
return CMD_SUCCESS;
}
#define CS_MAX_STR "Set maximum values for adaptive CS selection (overrides BTS config)\n"
DEFUN(cfg_pcu_cs_max,
cfg_pcu_cs_max_cmd,
"cs max <1-4> [<1-4>]",
CS_STR
"Set maximum values for adaptive CS selection (overrides BTS config)\n"
CS_MAX_STR
"Maximum CS value to be used\n"
"Use a different maximum CS value for the uplink")
{
@@ -346,8 +500,7 @@ DEFUN(cfg_pcu_cs_max,
DEFUN(cfg_pcu_no_cs_max,
cfg_pcu_no_cs_max_cmd,
"no cs max",
NO_STR CS_STR
"Set maximum values for adaptive CS selection (overrides BTS config)\n")
NO_STR CS_STR CS_MAX_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
@@ -357,6 +510,112 @@ DEFUN(cfg_pcu_no_cs_max,
return CMD_SUCCESS;
}
#define MCS_STR "Modulation and Coding Scheme configuration (EGPRS)\n"
DEFUN(cfg_pcu_mcs,
cfg_pcu_mcs_cmd,
"mcs <1-9> [<1-9>]",
MCS_STR
"Initial MCS value to be used (default 1)\n"
"Use a different initial MCS value for the uplink")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs = atoi(argv[0]);
bts->initial_mcs_dl = cs;
if (argc > 1)
bts->initial_mcs_ul = atoi(argv[1]);
else
bts->initial_mcs_ul = cs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_mcs,
cfg_pcu_no_mcs_cmd,
"no mcs",
NO_STR MCS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->initial_mcs_dl = 1;
bts->initial_mcs_ul = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_mcs_max,
cfg_pcu_mcs_max_cmd,
"mcs max <1-9> [<1-9>]",
MCS_STR
CS_MAX_STR
"Maximum MCS value to be used\n"
"Use a different maximum MCS value for the uplink")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t mcs = atoi(argv[0]);
bts->max_mcs_dl = mcs;
if (argc > 1)
bts->max_mcs_ul = atoi(argv[1]);
else
bts->max_mcs_ul = mcs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_mcs_max,
cfg_pcu_no_mcs_max_cmd,
"no mcs max",
NO_STR MCS_STR CS_MAX_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->max_mcs_dl = 0;
bts->max_mcs_ul = 0;
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>]",
"Window size configuration (b + N_PDCH * f)\n"
"Base value (b)\n"
"Factor for number of PDCH (f)")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t b = atoi(argv[0]);
bts->ws_base = b;
if (argc > 1) {
uint16_t f = atoi(argv[1]);
bts->ws_pdch = f;
}
return CMD_SUCCESS;
}
#define QUEUE_STR "Packet queue options\n"
#define LIFETIME_STR "Set lifetime limit of LLC frame in centi-seconds " \
"(overrides the value given by SGSN)\n"
@@ -730,6 +989,75 @@ DEFUN(cfg_pcu_cs_lqual_ranges,
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_mcs_lqual_ranges,
cfg_pcu_mcs_lqual_ranges_cmd,
"mcs link-quality-ranges mcs1 <0-35> mcs2 <0-35> <0-35> mcs3 <0-35> <0-35> mcs4 <0-35> <0-35> mcs5 <0-35> <0-35> mcs6 <0-35> <0-35> mcs7 <0-35> <0-35> mcs8 <0-35> <0-35> mcs9 <0-35>",
CS_STR "Set link quality ranges\n"
"Set quality range for MCS-1 (high value only)\n"
"MCS-1 high (dB)\n"
"Set quality range for MCS-2\n"
"MCS-2 high (dB)\n"
"MCS-2 low (dB)\n"
"Set quality range for MCS-3\n"
"MCS-3 high (dB)\n"
"MCS-3 low (dB)\n"
"Set quality range for MCS-4\n"
"MCS-4 high (dB)\n"
"MCS-4 low (dB)\n"
"Set quality range for MCS-5\n"
"MCS-5 high (dB)\n"
"MCS-5 low (dB)\n"
"Set quality range for MCS-6\n"
"MCS-6 low (dB)\n"
"MCS-6 high (dB)\n"
"Set quality range for MCS-7\n"
"MCS-7 low (dB)\n"
"MCS-7 high (dB)\n"
"Set quality range for MCS-8\n"
"MCS-8 low (dB)\n"
"MCS-8 high (dB)\n"
"Set quality range for MCS-9 (low value only)\n"
"MCS-9 low (dB)\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->mcs_lqual_ranges[0].high = atoi(argv[0]);
bts->mcs_lqual_ranges[1].low = atoi(argv[1]);
bts->mcs_lqual_ranges[1].high = atoi(argv[2]);
bts->mcs_lqual_ranges[2].low = atoi(argv[3]);
bts->mcs_lqual_ranges[2].high = atoi(argv[4]);
bts->mcs_lqual_ranges[3].low = atoi(argv[5]);
bts->mcs_lqual_ranges[3].high = atoi(argv[6]);
bts->mcs_lqual_ranges[4].low = atoi(argv[7]);
bts->mcs_lqual_ranges[4].high = atoi(argv[8]);
bts->mcs_lqual_ranges[5].low = atoi(argv[9]);
bts->mcs_lqual_ranges[5].high = atoi(argv[10]);
bts->mcs_lqual_ranges[6].low = atoi(argv[11]);
bts->mcs_lqual_ranges[6].high = atoi(argv[12]);
bts->mcs_lqual_ranges[7].low = atoi(argv[13]);
bts->mcs_lqual_ranges[7].high = atoi(argv[14]);
bts->mcs_lqual_ranges[8].low = atoi(argv[15]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_sock,
cfg_pcu_sock_cmd,
"pcu-socket PATH",
"Configure the osmo-bts PCU socket file/path name\n"
"Path of the socket to connect to\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
if (bts->pcu_sock_path) {
/* FIXME: close the interface? */
talloc_free(bts->pcu_sock_path);
}
bts->pcu_sock_path = talloc_strdup(tall_pcu_ctx, argv[0]);
/* FIXME: re-open the interface? */
return CMD_SUCCESS;
}
DEFUN(show_tbf,
show_tbf_cmd,
@@ -737,19 +1065,7 @@ DEFUN(show_tbf,
SHOW_STR "information about TBFs\n" "All TBFs\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
struct llist_head *tbf;
vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
llist_for_each(tbf, &bts->ul_tbfs) {
tbf_print_vty_info(vty, tbf);
}
vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE);
llist_for_each(tbf, &bts->dl_tbfs) {
tbf_print_vty_info(vty, tbf);
}
return CMD_SUCCESS;
return pcu_vty_show_tbf_all(vty, bts);
}
DEFUN(show_ms_all,
@@ -804,11 +1120,26 @@ int pcu_vty_init(const struct log_info *cat)
{
// install_element_ve(&show_pcu_cmd);
cfg_pcu_gsmtap_categ_cmd.string = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_names,
"gsmtap-category (",
"|",")", VTY_DO_LOWER);
cfg_pcu_gsmtap_categ_cmd.doc = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_help,
"GSMTAP Category\n",
"\n", "", 0);
cfg_pcu_no_gsmtap_categ_cmd.string = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_names,
"no gsmtap-category (",
"|",")", VTY_DO_LOWER);
cfg_pcu_no_gsmtap_categ_cmd.doc = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_help,
NO_STR "GSMTAP Category\n",
"\n", "", 0);
logging_vty_add_cmds(cat);
osmo_stats_vty_add_cmds(cat);
install_node(&pcu_node, config_write_pcu);
install_element(CONFIG_NODE, &cfg_pcu_cmd);
vty_install_default(PCU_NODE);
install_element(PCU_NODE, &cfg_pcu_egprs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_egprs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_two_phase_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_cmd);
@@ -819,6 +1150,13 @@ int pcu_vty_init(const struct log_info *cat)
install_element(PCU_NODE, &cfg_pcu_cs_downgrade_thrsh_cmd);
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_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);
install_element(PCU_NODE, &cfg_pcu_window_size_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_inf_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd);
@@ -848,6 +1186,9 @@ int pcu_vty_init(const struct log_info *cat)
install_element(PCU_NODE, &cfg_pcu_no_dl_tbf_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_ms_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_no_ms_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_gsmtap_categ_cmd);
install_element(PCU_NODE, &cfg_pcu_no_gsmtap_categ_cmd);
install_element(PCU_NODE, &cfg_pcu_sock_cmd);
install_element_ve(&show_bts_stats_cmd);
install_element_ve(&show_tbf_cmd);

View File

@@ -1,6 +1,7 @@
#ifndef _PCU_VTY_H
#define _PCU_VTY_H
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
@@ -8,7 +9,7 @@ enum pcu_vty_node {
PCU_NODE = _LAST_OSMOVTY_NODE + 1,
};
enum node_type pcu_vty_go_parent(struct vty *vty);
int pcu_vty_go_parent(struct vty *vty);
int pcu_vty_is_config_node(struct vty *vty, int node);
int pcu_vty_init(const struct log_info *cat);

View File

@@ -27,15 +27,92 @@
#include "gprs_ms_storage.h"
#include "gprs_ms.h"
#include "cxx_linuxlist.h"
#include <gprs_coding_scheme.h>
#include <llc.h>
#include <pcu_l1_if.h>
#include <rlc.h>
#include <tbf.h>
#include <pdch.h>
extern "C" {
# include <osmocom/vty/command.h>
# include <osmocom/vty/logging.h>
# include <osmocom/vty/misc.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
#include <osmocom/vty/vty.h>
}
int pcu_vty_config_write_pcu_ext(struct vty *vty)
static void tbf_print_vty_info(struct vty *vty, gprs_rlcmac_tbf *tbf)
{
gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
vty_out(vty, "TBF: TFI=%d TLLI=0x%08x (%s) TA=%u DIR=%s IMSI=%s%s", tbf->tfi(),
tbf->tlli(), tbf->is_tlli_valid() ? "valid" : "invalid",
tbf->ta(),
tbf->direction == GPRS_RLCMAC_UL_TBF ? "UL" : "DL",
tbf->imsi(), VTY_NEWLINE);
vty_out(vty, " created=%lu state=%08x [CCCH:%u, PACCH:%u] 1st_TS=%d 1st_cTS=%d ctrl_TS=%d MS_CLASS=%d/%d%s",
tbf->created_ts(), tbf->state_flags,
tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH),
tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH),
tbf->first_ts,
tbf->first_common_ts, tbf->control_ts,
tbf->ms_class(),
tbf->ms() ? tbf->ms()->egprs_ms_class() : -1,
VTY_NEWLINE);
vty_out(vty, " TS_alloc=");
for (int i = 0; i < 8; i++) {
bool is_ctrl = tbf->is_control_ts(i);
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", tbf->current_cs().name());
if (ul_tbf) {
gprs_rlc_ul_window *win = ul_tbf->window();
vty_out(vty, " WS=%u V(Q)=%d V(R)=%d",
ul_tbf->window_size(), 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->window();
vty_out(vty, " WS=%u V(A)=%d V(S)=%d nBSN=%d%s",
dl_tbf->window_size(), 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);
}
int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
{
BTS *bts = bts_data->bts;
LListHead<gprs_rlcmac_tbf> *ms_iter;
vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
llist_for_each(ms_iter, &bts->ul_tbfs())
tbf_print_vty_info(vty, ms_iter->entry());
vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE);
llist_for_each(ms_iter, &bts->dl_tbfs())
tbf_print_vty_info(vty, ms_iter->entry());
return CMD_SUCCESS;
}
@@ -47,9 +124,16 @@ 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=%d, CS-DL=%d, IMSI=%s%s",
vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%s, CS-DL=%s, LLC=%zd, Cl=%d, E-Cl=%d,"
" TBF-UL=%s, TBF-DL=%s, IMSI=%s%s",
ms->tlli(),
ms->ta(), ms->current_cs_ul(), ms->current_cs_dl(),
ms->ta(), ms->current_cs_ul().name(),
ms->current_cs_dl().name(),
ms->llc_queue()->size(),
ms->ms_class(),
ms->egprs_ms_class(),
ms->ul_tbf() ? ms->ul_tbf()->state_name() : "NA",
ms->dl_tbf() ? ms->dl_tbf()->state_name() : "NA",
ms->imsi(),
VTY_NEWLINE);
}
@@ -59,15 +143,28 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
static int show_ms(struct vty *vty, GprsMs *ms)
{
unsigned i;
LListHead<gprs_rlcmac_tbf> *i_tbf;
uint8_t slots;
vty_out(vty, "MS TLLI=%08x, IMSI=%s%s", ms->tlli(), ms->imsi(), VTY_NEWLINE);
vty_out(vty, " Timing advance (TA): %d%s", ms->ta(), VTY_NEWLINE);
vty_out(vty, " Coding scheme uplink: CS-%d%s", ms->current_cs_ul(),
vty_out(vty, " Coding scheme uplink: %s%s", ms->current_cs_ul().name(),
VTY_NEWLINE);
vty_out(vty, " Coding scheme downlink: CS-%d%s", ms->current_cs_dl(),
vty_out(vty, " Coding scheme downlink: %s%s", ms->current_cs_dl().name(),
VTY_NEWLINE);
vty_out(vty, " Mode: %s%s",
GprsCodingScheme::modeName(ms->mode()), VTY_NEWLINE);
vty_out(vty, " MS class: %d%s", ms->ms_class(), VTY_NEWLINE);
vty_out(vty, " LLC queue length: %d%s", ms->llc_queue()->size(),
vty_out(vty, " EGPRS MS class: %d%s", ms->egprs_ms_class(), VTY_NEWLINE);
vty_out(vty, " PACCH: ");
slots = ms->current_pacch_slots();
for (int i = 0; i < 8; i++)
if (slots & (1 << i))
vty_out(vty, "%d ", i);
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " LLC queue length: %zd%s", ms->llc_queue()->size(),
VTY_NEWLINE);
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",
@@ -98,16 +195,30 @@ 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",
i_tbf->entry()->direction == GPRS_RLCMAC_UL_TBF ?
"Uplink TBF:" : "Downlink TBF:",
i_tbf->entry()->tfi(),
i_tbf->entry()->state_name(),
VTY_NEWLINE);
return CMD_SUCCESS;
}

View File

@@ -27,7 +27,7 @@ extern "C" {
struct vty;
struct gprs_rlcmac_bts;
int pcu_vty_config_write_pcu_ext(struct vty *vty);
int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data);
int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data);
int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
uint32_t tlli);

963
src/pdch.cpp Normal file
View File

@@ -0,0 +1,963 @@
/*
* Copyright (C) 2013 by Holger Hans Peter Freyther
* Copyright (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 <bts.h>
#include <pdch.h>
#include <decoding.h>
#include <encoding.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <gprs_coding_scheme.h>
#include <gprs_ms.h>
#include <gprs_ms_storage.h>
#include <pcu_l1_if.h>
#include <rlc.h>
#include <sba.h>
#include <tbf.h>
#include <cxx_linuxlist.h>
#include <gsm_rlcmac.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
}
#include <errno.h>
#include <arpa/inet.h>
extern void *tall_pcu_ctx;
static void get_rx_qual_meas(struct pcu_l1_meas *meas, uint8_t rx_qual_enc)
{
static const int16_t rx_qual_map[] = {
0, /* 0,14 % */
0, /* 0,28 % */
1, /* 0,57 % */
1, /* 1,13 % */
2, /* 2,26 % */
5, /* 4,53 % */
9, /* 9,05 % */
18, /* 18,10 % */
};
meas->set_ms_rx_qual(rx_qual_map[
OSMO_MIN(rx_qual_enc, ARRAY_SIZE(rx_qual_map)-1)
]);
}
static void get_meas(struct pcu_l1_meas *meas,
const Packet_Resource_Request_t *qr)
{
unsigned i;
meas->set_ms_c_value(qr->C_VALUE);
if (qr->Exist_SIGN_VAR)
meas->set_ms_sign_var((qr->SIGN_VAR + 2) / 4); /* SIGN_VAR * 0.25 dB */
for (i = 0; i < OSMO_MIN(ARRAY_SIZE(qr->Slot), ARRAY_SIZE(meas->ts)); i++)
{
if (qr->Slot[i].Exist) {
LOGP(DRLCMAC, LOGL_INFO,
"Packet resource request: i_level[%d] = %d\n",
i, qr->Slot[i].I_LEVEL);
meas->set_ms_i_level(i, -2 * qr->Slot[i].I_LEVEL);
}
}
}
static void get_meas(struct pcu_l1_meas *meas,
const Channel_Quality_Report_t *qr)
{
unsigned i;
get_rx_qual_meas(meas, qr->RXQUAL);
meas->set_ms_c_value(qr->C_VALUE);
meas->set_ms_sign_var((qr->SIGN_VAR + 2) / 4); /* SIGN_VAR * 0.25 dB */
for (i = 0; i < OSMO_MIN(ARRAY_SIZE(qr->Slot), ARRAY_SIZE(meas->ts)); i++)
{
if (qr->Slot[i].Exist) {
LOGP(DRLCMAC, LOGL_DEBUG,
"Channel quality report: i_level[%d] = %d\n",
i, qr->Slot[i].I_LEVEL_TN);
meas->set_ms_i_level(i, -2 * qr->Slot[i].I_LEVEL_TN);
}
}
}
static inline void sched_ul_ass_or_rej(BTS *bts, gprs_rlcmac_bts *bts_data, struct gprs_rlcmac_dl_tbf *tbf)
{
bts->channel_request_description();
/* This call will register the new TBF with the MS on success */
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 or reject */
if (ul_tbf) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack message, so we provide one:\n");
TBF_SET_ASS_STATE_UL(tbf, GPRS_RLCMAC_UL_ASS_SEND_ASS);
} else {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack message, so we packet access reject:\n");
TBF_SET_ASS_STATE_UL(tbf, GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ);
}
}
void gprs_rlcmac_pdch::enable()
{
/* TODO: Check if there are still allocated resources.. */
INIT_LLIST_HEAD(&paging_list);
m_is_enabled = 1;
}
void gprs_rlcmac_pdch::disable()
{
/* TODO.. kick free_resources once we know the TRX/TS we are on */
m_is_enabled = 0;
}
void gprs_rlcmac_pdch::free_resources()
{
struct gprs_rlcmac_paging *pag;
/* we are not enabled. there should be no resources */
if (!is_enabled())
return;
/* kick all TBF on slot */
gprs_rlcmac_tbf::free_all(this);
/* flush all pending paging messages */
while ((pag = dequeue_paging()))
talloc_free(pag);
trx->bts->sba()->free_resources(this);
}
struct gprs_rlcmac_paging *gprs_rlcmac_pdch::dequeue_paging()
{
struct gprs_rlcmac_paging *pag;
if (llist_empty(&paging_list))
return NULL;
pag = llist_entry(paging_list.next, struct gprs_rlcmac_paging, list);
llist_del(&pag->list);
return pag;
}
struct msgb *gprs_rlcmac_pdch::packet_paging_request()
{
struct gprs_rlcmac_paging *pag;
struct msgb *msg;
unsigned wp = 0, len;
/* no paging, no message */
pag = dequeue_paging();
if (!pag)
return NULL;
LOGP(DRLCMAC, LOGL_DEBUG, "Scheduling paging\n");
/* alloc message */
msg = msgb_alloc(23, "pag ctrl block");
if (!msg) {
talloc_free(pag);
return NULL;
}
bitvec *pag_vec = bitvec_alloc(23, tall_pcu_ctx);
if (!pag_vec) {
msgb_free(msg);
talloc_free(pag);
return NULL;
}
wp = Encoding::write_packet_paging_request(pag_vec);
/* loop until message is full */
while (pag) {
/* try to add paging */
if ((pag->identity_lv[1] & 0x07) == 4) {
/* TMSI */
LOGP(DRLCMAC, LOGL_DEBUG, "- TMSI=0x%08x\n",
ntohl(*((uint32_t *)(pag->identity_lv + 1))));
len = 1 + 1 + 1 + 32 + 2 + 1;
if (pag->identity_lv[0] != 5) {
LOGP(DRLCMAC, LOGL_ERROR, "TMSI paging with "
"MI != 5 octets!\n");
goto continue_next;
}
} else {
/* MI */
LOGP(DRLCMAC, LOGL_DEBUG, "- MI=%s\n",
osmo_hexdump(pag->identity_lv + 1,
pag->identity_lv[0]));
len = 1 + 1 + 1 + 4 + (pag->identity_lv[0]<<3) + 2 + 1;
if (pag->identity_lv[0] > 8) {
LOGP(DRLCMAC, LOGL_ERROR, "Paging with "
"MI > 8 octets!\n");
goto continue_next;
}
}
if (wp + len > 184) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Does not fit, so schedule "
"next time\n");
/* put back paging record, because does not fit */
llist_add_tail(&pag->list, &paging_list);
break;
}
Encoding::write_repeated_page_info(pag_vec, wp, pag->identity_lv[0],
pag->identity_lv + 1, pag->chan_needed);
continue_next:
talloc_free(pag);
pag = dequeue_paging();
}
bitvec_pack(pag_vec, msgb_put(msg, 23));
RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Paging Request +++++++++++++++++++++++++\n");
decode_gsm_rlcmac_downlink(pag_vec, mac_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Paging Request -------------------------\n");
bitvec_free(pag_vec);
talloc_free(mac_control_block);
return msg;
}
bool gprs_rlcmac_pdch::add_paging(uint8_t chan_needed, uint8_t *identity_lv)
{
struct gprs_rlcmac_paging *pag = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_paging);
if (!pag)
return false;
pag->chan_needed = chan_needed;
memcpy(pag->identity_lv, identity_lv, identity_lv[0] + 1);
llist_add(&pag->list, &paging_list);
return true;
}
void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet, uint32_t fn)
{
struct gprs_rlcmac_tbf *tbf, *new_tbf;
uint32_t tlli = packet->TLLI;
GprsMs *ms = bts()->ms_by_tlli(tlli);
gprs_rlcmac_ul_tbf *ul_tbf;
tbf = bts()->ul_tbf_by_poll_fn(fn, trx_no(), ts_no);
if (!tbf)
tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
"unknown FN=%u TLLI=0x%08x (TRX %d TS %d)\n",
fn, tlli, trx_no(), ts_no);
if (ms)
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
"unknown TBF corresponds to MS with IMSI %s, TA %d, "
"uTBF (TFI=%d, state=%s), dTBF (TFI=%d, state=%s)\n",
ms->imsi(), ms->ta(),
ms->ul_tbf() ? ms->ul_tbf()->tfi() : 0,
ms->ul_tbf() ? ms->ul_tbf()->state_name() : "None",
ms->dl_tbf() ? ms->dl_tbf()->tfi() : 0,
ms->dl_tbf() ? ms->dl_tbf()->state_name() : "None");
return;
}
/* Reset N3101 counter: */
tbf->n_reset(N3101);
tbf->update_ms(tlli, GPRS_RLCMAC_UL_TBF);
LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] Packet Control Ack\n");
TBF_POLL_SCHED_UNSET(tbf);
/* check if this control ack belongs to packet uplink ack */
ul_tbf = as_ul_tbf(tbf);
if (ul_tbf && ul_tbf->handle_ctrl_ack()) {
LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] END\n");
if (ul_tbf->ctrl_ack_to_toggle())
LOGPTBF(tbf, LOGL_NOTICE, "Recovered uplink ack for UL\n");
tbf_free(tbf);
return;
}
if (tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_WAIT_ACK)) {
LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] DOWNLINK ASSIGNED\n");
/* reset N3105 */
tbf->n_reset(N3105);
TBF_SET_ASS_STATE_DL(tbf, GPRS_RLCMAC_DL_ASS_NONE);
new_tbf = tbf->ms() ? tbf->ms()->dl_tbf() : NULL;
if (!new_tbf) {
LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but DL "
"TBF is gone TLLI=0x%08x\n", tlli);
return;
}
if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE) &&
tbf->direction == new_tbf->direction)
tbf_free(tbf);
if (new_tbf->check_n_clear(GPRS_RLCMAC_FLAG_CCCH)) {
/* We now know that the PACCH really existed */
LOGPTBF(new_tbf, LOGL_INFO,
"The TBF has been confirmed on the PACCH, "
"changed type from CCCH to PACCH\n");
TBF_ASS_TYPE_SET(new_tbf, GPRS_RLCMAC_FLAG_PACCH);
}
TBF_SET_STATE(new_tbf, GPRS_RLCMAC_FLOW);
/* stop pending assignment timer */
new_tbf->t_stop(T0, "control acked (DL-TBF)");
if (new_tbf->check_n_clear(GPRS_RLCMAC_FLAG_TO_DL_ASS))
LOGPTBF(new_tbf, LOGL_NOTICE, "Recovered downlink assignment\n");
tbf_assign_control_ts(new_tbf);
return;
}
if (tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_WAIT_ACK)) {
LOGPTBF(tbf, LOGL_DEBUG, "[DOWNLINK] UPLINK ASSIGNED\n");
/* reset N3105 */
tbf->n_reset(N3105);
TBF_SET_ASS_STATE_UL(tbf, GPRS_RLCMAC_UL_ASS_NONE);
new_tbf = tbf->ms() ? tbf->ms()->ul_tbf() : NULL;
if (!new_tbf) {
LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but UL "
"TBF is gone TLLI=0x%08x\n", tlli);
return;
}
if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE) &&
tbf->direction == new_tbf->direction)
tbf_free(tbf);
TBF_SET_STATE(new_tbf, GPRS_RLCMAC_FLOW);
if (new_tbf->check_n_clear(GPRS_RLCMAC_FLAG_TO_UL_ASS))
LOGPTBF(new_tbf, LOGL_NOTICE, "Recovered uplink assignment for UL\n");
tbf_assign_control_ts(new_tbf);
/* there might be LLC packets waiting in the queue, but the DL
* TBF might have been released while the UL TBF has been
* established */
if (new_tbf->ms()->need_dl_tbf())
new_tbf->establish_dl_tbf_on_pacch();
return;
}
LOGP(DRLCMAC, LOGL_ERROR, "Error: received PACET CONTROL ACK "
"at no request\n");
}
void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_nack, uint32_t fn)
{
int8_t tfi = 0; /* must be signed */
struct gprs_rlcmac_dl_tbf *tbf;
int rc;
struct pcu_l1_meas meas;
int num_blocks;
uint8_t bits_data[RLC_GPRS_WS/8];
bitvec bits;
int bsn_begin, bsn_end;
char show_bits[RLC_GPRS_WS + 1];
tfi = ack_nack->DOWNLINK_TFI;
tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
fn, tfi, trx_no(), ts_no);
return;
}
if (tbf->tfi() != tfi) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
"wrong TFI=%d, ignoring!\n", tfi);
return;
}
/* Reset N3101 counter: */
tbf->n_reset(N3101);
if (tbf->handle_ack_nack())
LOGPTBF(tbf, LOGL_NOTICE, "Recovered downlink ack\n");
LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] Packet Downlink Ack/Nack\n");
bits.data = bits_data;
bits.data_len = sizeof(bits_data);
bits.cur_bit = 0;
num_blocks = Decoding::decode_gprs_acknack_bits(
&ack_nack->Ack_Nack_Description, &bits,
&bsn_begin, &bsn_end, tbf->window());
LOGP(DRLCMAC, LOGL_DEBUG,
"Got GPRS DL ACK bitmap: SSN: %d, BSN %d to %d - 1 (%d blocks), "
"\"%s\"\n",
ack_nack->Ack_Nack_Description.STARTING_SEQUENCE_NUMBER,
bsn_begin, bsn_end, num_blocks,
(Decoding::extract_rbb(&bits, show_bits), show_bits));
rc = tbf->rcvd_dl_ack(
ack_nack->Ack_Nack_Description.FINAL_ACK_INDICATION,
bsn_begin, &bits);
if (rc == 1) {
tbf_free(tbf);
return;
}
/* check for channel request */
if (ack_nack->Exist_Channel_Request_Description)
sched_ul_ass_or_rej(bts(), bts_data(), tbf);
/* get measurements */
if (tbf->ms()) {
get_meas(&meas, &ack_nack->Channel_Quality_Report);
tbf->ms()->update_l1_meas(&meas);
}
}
void gprs_rlcmac_pdch::rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *ack_nack, uint32_t fn)
{
int8_t tfi = 0; /* must be signed */
struct gprs_rlcmac_dl_tbf *tbf;
struct pcu_l1_meas meas;
int rc;
int num_blocks;
uint8_t bits_data[RLC_EGPRS_MAX_WS/8];
char show_bits[RLC_EGPRS_MAX_WS + 1];
bitvec bits;
int bsn_begin, bsn_end;
tfi = ack_nack->DOWNLINK_TFI;
tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "EGPRS PACKET DOWNLINK ACK with "
"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
fn, tfi, trx_no(), ts_no);
return;
}
if (tbf->tfi() != tfi) {
LOGP(DRLCMAC, LOGL_NOTICE, "EGPRS PACKET DOWNLINK ACK with "
"wrong TFI=%d, ignoring!\n", tfi);
return;
}
/* Reset N3101 counter: */
tbf->n_reset(N3101);
if (tbf->handle_ack_nack())
LOGPTBF(tbf, LOGL_NOTICE, "Recovered EGPRS downlink ack\n");
LOGPTBF(tbf, LOGL_DEBUG,
"RX: [PCU <- BTS] EGPRS Packet Downlink Ack/Nack\n");
LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS ACK/NACK: "
"ut: %d, final: %d, bow: %d, eow: %d, ssn: %d, have_crbb: %d, "
"urbb_len:%d, %p, %p, %d, %d, win: %d-%d, urbb: %s\n",
(int)ack_nack->EGPRS_AckNack.UnionType,
(int)ack_nack->EGPRS_AckNack.Desc.FINAL_ACK_INDICATION,
(int)ack_nack->EGPRS_AckNack.Desc.BEGINNING_OF_WINDOW,
(int)ack_nack->EGPRS_AckNack.Desc.END_OF_WINDOW,
(int)ack_nack->EGPRS_AckNack.Desc.STARTING_SEQUENCE_NUMBER,
(int)ack_nack->EGPRS_AckNack.Desc.Exist_CRBB,
(int)ack_nack->EGPRS_AckNack.Desc.URBB_LENGTH,
(void *)&ack_nack->EGPRS_AckNack.UnionType,
(void *)&ack_nack->EGPRS_AckNack.Desc,
(int)offsetof(EGPRS_AckNack_t, Desc),
(int)offsetof(EGPRS_AckNack_w_len_t, Desc),
tbf->window()->v_a(),
tbf->window()->v_s(),
osmo_hexdump((const uint8_t *)&ack_nack->EGPRS_AckNack.Desc.URBB,
sizeof(ack_nack->EGPRS_AckNack.Desc.URBB)));
bits.data = bits_data;
bits.data_len = sizeof(bits_data);
bits.cur_bit = 0;
num_blocks = Decoding::decode_egprs_acknack_bits(
&ack_nack->EGPRS_AckNack.Desc, &bits,
&bsn_begin, &bsn_end, tbf->window());
LOGP(DRLCMAC, LOGL_DEBUG,
"Got EGPRS DL ACK bitmap: SSN: %d, BSN %d to %d - 1 (%d blocks), "
"\"%s\"\n",
ack_nack->EGPRS_AckNack.Desc.STARTING_SEQUENCE_NUMBER,
bsn_begin, bsn_end, num_blocks,
(Decoding::extract_rbb(&bits, show_bits), show_bits)
);
rc = tbf->rcvd_dl_ack(
ack_nack->EGPRS_AckNack.Desc.FINAL_ACK_INDICATION,
bsn_begin, &bits);
if (rc == 1) {
tbf_free(tbf);
return;
}
/* check for channel request */
if (ack_nack->Exist_ChannelRequestDescription)
sched_ul_ass_or_rej(bts(), bts_data(), tbf);
/* get measurements */
if (tbf->ms()) {
/* TODO: Implement Measurements parsing for EGPRS */
/*
get_meas(&meas, &ack_nack->Channel_Quality_Report);
tbf->ms()->update_l1_meas(&meas);
*/
}
}
void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request, uint32_t fn)
{
struct gprs_rlcmac_sba *sba;
if (request->ID.UnionType) {
struct gprs_rlcmac_ul_tbf *ul_tbf = NULL;
struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
uint32_t tlli = request->ID.u.TLLI;
uint8_t ms_class = 0;
uint8_t egprs_ms_class = 0;
uint8_t ta = GSM48_TA_INVALID;
struct pcu_l1_meas meas;
GprsMs *ms = bts()->ms_by_tlli(tlli);
/* Keep the ms, even if it gets idle temporarily */
GprsMs::Guard guard(ms);
if (ms) {
ul_tbf = ms->ul_tbf();
dl_tbf = ms->dl_tbf();
ta = ms->ta();
}
/* We got a RACH so the MS was in packet idle mode and thus
* didn't have any active TBFs */
if (ul_tbf) {
LOGPTBFUL(ul_tbf, LOGL_NOTICE,
"Got RACH from TLLI=0x%08x while TBF still exists. Killing pending UL TBF\n",
tlli);
tbf_free(ul_tbf);
ul_tbf = NULL;
}
if (dl_tbf) {
LOGPTBFUL(dl_tbf, LOGL_NOTICE,
"Got RACH from TLLI=0x%08x while TBF still exists. Release pending DL TBF\n",
tlli);
tbf_free(dl_tbf);
dl_tbf = NULL;
}
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF "
"in packet resource request of single "
"block, so we provide one:\n");
sba = bts()->sba()->find(this, fn);
if (!sba) {
LOGP(DRLCMAC, LOGL_NOTICE, "MS requests UL TBF "
"in packet resource request of single "
"block, but there is no resource request "
"scheduled!\n");
} else {
ta = sba->ta;
bts()->sba()->free_sba(sba);
}
if (request->Exist_MS_Radio_Access_capability) {
ms_class = Decoding::get_ms_class_by_capability(
&request->MS_Radio_Access_capability);
egprs_ms_class =
Decoding::get_egprs_ms_class_by_capability(
&request->MS_Radio_Access_capability);
}
if (!ms_class)
LOGP(DRLCMAC, LOGL_NOTICE, "MS does not give us a class.\n");
if (egprs_ms_class)
LOGP(DRLCMAC, LOGL_NOTICE,
"MS supports EGPRS multislot class %d.\n",
egprs_ms_class);
ul_tbf = tbf_alloc_ul(bts_data(), trx_no(), ms_class,
egprs_ms_class, tlli, ta, ms);
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 */
LOGPTBF(ul_tbf, LOGL_DEBUG, "change control TS %d -> %d until assinment is complete.\n",
ul_tbf->control_ts, ts_no);
ul_tbf->control_ts = ts_no;
/* schedule uplink assignment */
TBF_SET_ASS_STATE_UL(ul_tbf, GPRS_RLCMAC_UL_ASS_SEND_ASS);
/* get capabilities */
if (ul_tbf->ms())
ul_tbf->ms()->set_egprs_ms_class(egprs_ms_class);
/* get measurements */
if (ul_tbf->ms()) {
get_meas(&meas, request);
ul_tbf->ms()->update_l1_meas(&meas);
}
return;
}
if (request->ID.u.Global_TFI.UnionType) {
struct gprs_rlcmac_dl_tbf *dl_tbf;
int8_t tfi = request->ID.u.Global_TFI.u.DOWNLINK_TFI;
dl_tbf = bts()->dl_tbf_by_tfi(tfi, trx_no(), ts_no);
if (!dl_tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown downlink TFI=%d\n", tfi);
return;
}
LOGPTBFDL(dl_tbf, LOGL_ERROR,
"RX: [PCU <- BTS] FIXME: Packet resource request\n");
/* Reset N3101 counter: */
dl_tbf->n_reset(N3101);
} else {
struct gprs_rlcmac_ul_tbf *ul_tbf;
int8_t tfi = request->ID.u.Global_TFI.u.UPLINK_TFI;
ul_tbf = bts()->ul_tbf_by_tfi(tfi, trx_no(), ts_no);
if (!ul_tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TFI=%d\n", tfi);
return;
}
LOGPTBFUL(ul_tbf, LOGL_ERROR,
"RX: [PCU <- BTS] FIXME: Packet resource request\n");
/* Reset N3101 counter: */
ul_tbf->n_reset(N3101);
}
}
void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *report, uint32_t fn)
{
struct gprs_rlcmac_sba *sba;
sba = bts()->sba()->find(this, fn);
if (!sba) {
LOGP(DRLCMAC, LOGL_NOTICE, "MS send measurement "
"in packet resource request of single "
"block, but there is no resource request "
"scheduled! TLLI=0x%08x\n", report->TLLI);
} else {
GprsMs *ms = bts()->ms_store().get_ms(report->TLLI);
if (!ms)
LOGP(DRLCMAC, LOGL_NOTICE, "MS send measurement "
"but TLLI 0x%08x is unknown\n", report->TLLI);
else
ms->set_ta(sba->ta);
bts()->sba()->free_sba(sba);
}
gprs_rlcmac_meas_rep(report);
}
/* Received Uplink RLC control block. */
int gprs_rlcmac_pdch::rcv_control_block(
const uint8_t *data, uint8_t data_len, bitvec *rlc_block, uint32_t fn)
{
RlcMacUplink_t * ul_control_block = (RlcMacUplink_t *)talloc_zero(tall_pcu_ctx, RlcMacUplink_t);
LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ RX : Uplink Control Block +++++++++++++++++++++++++\n");
decode_gsm_rlcmac_uplink(rlc_block, ul_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- RX : Uplink Control Block -------------------------\n");
if (ul_control_block->u.MESSAGE_TYPE == MT_PACKET_UPLINK_DUMMY_CONTROL_BLOCK)
bts()->send_gsmtap(PCU_GSMTAP_C_UL_DUMMY, true, trx_no(), ts_no, GSMTAP_CHANNEL_PACCH, fn, data, data_len);
else
bts()->send_gsmtap(PCU_GSMTAP_C_UL_CTRL, true, trx_no(), ts_no, GSMTAP_CHANNEL_PACCH, fn, data, data_len);
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);
break;
case MT_PACKET_DOWNLINK_ACK_NACK:
rcv_control_dl_ack_nack(&ul_control_block->u.Packet_Downlink_Ack_Nack, fn);
break;
case MT_EGPRS_PACKET_DOWNLINK_ACK_NACK:
rcv_control_egprs_dl_ack_nack(&ul_control_block->u.Egprs_Packet_Downlink_Ack_Nack, fn);
break;
case MT_PACKET_RESOURCE_REQUEST:
rcv_resource_request(&ul_control_block->u.Packet_Resource_Request, fn);
break;
case MT_PACKET_MEASUREMENT_REPORT:
rcv_measurement_report(&ul_control_block->u.Packet_Measurement_Report, fn);
break;
case MT_PACKET_UPLINK_DUMMY_CONTROL_BLOCK:
/* ignoring it. change the SI to not force sending these? */
break;
default:
bts()->decode_error();
LOGP(DRLCMAC, LOGL_NOTICE,
"RX: [PCU <- BTS] unknown control block(%d) received\n",
ul_control_block->u.MESSAGE_TYPE);
}
talloc_free(ul_control_block);
return 1;
}
/* received RLC/MAC block from L1 */
int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
struct pcu_l1_meas *meas)
{
GprsCodingScheme cs = GprsCodingScheme::getBySizeUL(len);
if (!cs) {
bts()->decode_error();
LOGP(DRLCMACUL, LOGL_ERROR, "Dropping data block with invalid"
"length: %d)\n", len);
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());
if (cs.isGprs())
return rcv_block_gprs(data, len, fn, meas, cs);
if (cs.isEgprs())
return rcv_data_block(data, len, fn, meas, cs);
bts()->decode_error();
LOGP(DRLCMACUL, LOGL_ERROR, "Unsupported coding scheme %s\n",
cs.name());
return -EINVAL;
}
/*! \brief process egprs and gprs data blocks */
int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint8_t data_len, uint32_t fn,
struct pcu_l1_meas *meas, GprsCodingScheme cs)
{
int rc;
struct gprs_rlc_data_info rlc_dec;
struct gprs_rlcmac_ul_tbf *tbf;
unsigned len = cs.sizeUL();
/* These are always data blocks, since EGPRS still uses CS-1 for
* control blocks (see 44.060, section 10.3, 1st par.)
*/
if (cs.isEgprs()) {
if (!bts()->bts_data()->egprs_enabled) {
LOGP(DRLCMACUL, LOGL_ERROR,
"Got %s RLC block but EGPRS is not enabled\n",
cs.name());
return -EINVAL;
}
bts()->send_gsmtap(PCU_GSMTAP_C_UL_DATA_EGPRS, true, trx_no(), ts_no, GSMTAP_CHANNEL_PDTCH, fn,
data, data_len);
} else {
bts()->send_gsmtap(PCU_GSMTAP_C_UL_DATA_GPRS, true, trx_no(), ts_no, GSMTAP_CHANNEL_PDTCH, fn,
data, data_len);
}
LOGP(DRLCMACUL, LOGL_DEBUG, " UL data: %s\n", osmo_hexdump(data, len));
rc = Decoding::rlc_parse_ul_data_header(&rlc_dec, data, cs);
if (rc < 0) {
LOGP(DRLCMACUL, LOGL_ERROR,
"Got %s RLC block but header parsing has failed\n",
cs.name());
bts()->decode_error();
return rc;
}
LOGP(DRLCMACUL, LOGL_INFO,
"Got %s RLC block: "
"R=%d, SI=%d, TFI=%d, CPS=%d, RSB=%d, "
"rc=%d\n",
cs.name(),
rlc_dec.r, rlc_dec.si, rlc_dec.tfi, rlc_dec.cps, rlc_dec.rsb,
rc);
/* find TBF inst from given TFI */
tbf = ul_tbf_by_tfi(rlc_dec.tfi);
if (!tbf) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA unknown TFI=%d\n",
rlc_dec.tfi);
return 0;
}
/* Reset N3101 counter: */
tbf->n_reset(N3101);
return tbf->rcv_data_block_acknowledged(&rlc_dec, data, meas);
}
int gprs_rlcmac_pdch::rcv_block_gprs(uint8_t *data, uint8_t data_len, uint32_t fn,
struct pcu_l1_meas *meas, GprsCodingScheme cs)
{
unsigned payload = data[0] >> 6;
bitvec *block;
int rc = 0;
unsigned len = cs.maxBytesUL();
switch (payload) {
case GPRS_RLCMAC_DATA_BLOCK:
rc = rcv_data_block(data, data_len, fn, meas, cs);
break;
case GPRS_RLCMAC_CONTROL_BLOCK:
block = bitvec_alloc(len, tall_pcu_ctx);
if (!block)
return -ENOMEM;
bitvec_unpack(block, data);
rc = rcv_control_block(data, data_len, block, fn);
bitvec_free(block);
break;
case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
LOGP(DRLCMAC, LOGL_NOTICE, "GPRS_RLCMAC_CONTROL_BLOCK_OPT block payload is not supported.\n");
break;
default:
LOGP(DRLCMAC, LOGL_NOTICE, "Unknown RLCMAC block payload(%u).\n", payload);
rc = -EINVAL;
}
return rc;
}
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)
{
gprs_rlcmac_tbf *tbf;
LListHead<gprs_rlcmac_tbf> *pos;
llist_for_each(pos, tbf_list) {
tbf = pos->entry();
if (tbf->tfi() != tfi)
continue;
if (!tbf->pdch[ts_no])
continue;
return tbf;
}
return NULL;
}
gprs_rlcmac_ul_tbf *gprs_rlcmac_pdch::ul_tbf_by_tfi(uint8_t tfi)
{
return as_ul_tbf(tbf_by_tfi(tfi, GPRS_RLCMAC_UL_TBF));
}
gprs_rlcmac_dl_tbf *gprs_rlcmac_pdch::dl_tbf_by_tfi(uint8_t tfi)
{
return as_dl_tbf(tbf_by_tfi(tfi, GPRS_RLCMAC_DL_TBF));
}
/* lookup TBF Entity (by TFI) */
gprs_rlcmac_tbf *gprs_rlcmac_pdch::tbf_by_tfi(uint8_t tfi,
enum gprs_rlcmac_tbf_direction dir)
{
struct gprs_rlcmac_tbf *tbf;
if (tfi >= 32)
return NULL;
tbf = m_tbfs[dir][tfi];
if (!tbf)
return NULL;
if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)) {
return tbf;
}
return NULL;
}
void gprs_rlcmac_pdch::attach_tbf(gprs_rlcmac_tbf *tbf)
{
gprs_rlcmac_ul_tbf *ul_tbf;
if (m_tbfs[tbf->direction][tbf->tfi()])
LOGP(DRLCMAC, LOGL_ERROR, "PDCH(TS %d, TRX %d): "
"%s has not been detached, overwriting it\n",
ts_no, trx_no(),
m_tbfs[tbf->direction][tbf->tfi()]->name());
m_num_tbfs[tbf->direction] += 1;
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
ul_tbf = as_ul_tbf(tbf);
m_assigned_usf |= 1 << ul_tbf->m_usf[ts_no];
}
m_assigned_tfi[tbf->direction] |= 1UL << tbf->tfi();
m_tbfs[tbf->direction][tbf->tfi()] = tbf;
LOGP(DRLCMAC, LOGL_INFO, "PDCH(TS %d, TRX %d): Attaching %s, %d TBFs, "
"USFs = %02x, TFIs = %08x.\n",
ts_no, trx_no(), tbf->name(), m_num_tbfs[tbf->direction],
m_assigned_usf, m_assigned_tfi[tbf->direction]);
}
void gprs_rlcmac_pdch::detach_tbf(gprs_rlcmac_tbf *tbf)
{
gprs_rlcmac_ul_tbf *ul_tbf;
OSMO_ASSERT(m_num_tbfs[tbf->direction] > 0);
m_num_tbfs[tbf->direction] -= 1;
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
ul_tbf = as_ul_tbf(tbf);
m_assigned_usf &= ~(1 << ul_tbf->m_usf[ts_no]);
}
m_assigned_tfi[tbf->direction] &= ~(1UL << tbf->tfi());
m_tbfs[tbf->direction][tbf->tfi()] = NULL;
LOGP(DRLCMAC, LOGL_INFO, "PDCH(TS %d, TRX %d): Detaching %s, %d TBFs, "
"USFs = %02x, TFIs = %08x.\n",
ts_no, trx_no(), tbf->name(), m_num_tbfs[tbf->direction],
m_assigned_usf, m_assigned_tfi[tbf->direction]);
}
void gprs_rlcmac_pdch::reserve(enum gprs_rlcmac_tbf_direction dir)
{
m_num_reserved[dir] += 1;
}
void gprs_rlcmac_pdch::unreserve(enum gprs_rlcmac_tbf_direction dir)
{
OSMO_ASSERT(m_num_reserved[dir] > 0);
m_num_reserved[dir] -= 1;
}
inline BTS *gprs_rlcmac_pdch::bts() const
{
return trx->bts;
}
uint8_t gprs_rlcmac_pdch::trx_no() const
{
return trx->trx_no;
}
inline gprs_rlcmac_bts *gprs_rlcmac_pdch::bts_data() const
{
return trx->bts->bts_data();
}

147
src/pdch.h Normal file
View File

@@ -0,0 +1,147 @@
/* pdch.h
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2013 by Holger Hans Peter Freyther
* Copyright (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#include <osmocom/core/linuxlist.h>
}
#include <gsm_rlcmac.h>
#include <gprs_coding_scheme.h>
#include <bts.h>
#endif
#include <tbf.h>
#include <stdint.h>
/*
* PDCH instance
*/
struct gprs_rlcmac_pdch {
#ifdef __cplusplus
struct gprs_rlcmac_paging *dequeue_paging();
struct msgb *packet_paging_request();
bool add_paging(uint8_t chan_needed, uint8_t *identity_lv);
void free_resources();
bool is_enabled() const;
void enable();
void disable();
/* dispatching of messages */
int rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
struct pcu_l1_meas *meas);
int rcv_block_gprs(uint8_t *data, uint8_t data_len, uint32_t fn,
struct pcu_l1_meas *meas, GprsCodingScheme cs);
int rcv_data_block(uint8_t *data, uint8_t data_len, uint32_t fn,
struct pcu_l1_meas *meas, GprsCodingScheme cs);
gprs_rlcmac_bts *bts_data() const;
BTS *bts() const;
uint8_t trx_no() const;
struct gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi);
struct gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi);
void attach_tbf(gprs_rlcmac_tbf *tbf);
void detach_tbf(gprs_rlcmac_tbf *tbf);
unsigned num_tbfs(enum gprs_rlcmac_tbf_direction dir) const;
void reserve(enum gprs_rlcmac_tbf_direction dir);
void unreserve(enum gprs_rlcmac_tbf_direction dir);
unsigned num_reserved(enum gprs_rlcmac_tbf_direction dir) const;
uint8_t assigned_usf() const;
uint32_t assigned_tfi(enum gprs_rlcmac_tbf_direction dir) const;
#endif
uint8_t m_is_enabled; /* TS is enabled */
uint8_t tsc; /* TSC of this slot */
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
uint8_t next_ctrl_prio; /* next kind of ctrl message to schedule */
struct llist_head paging_list; /* list of paging messages */
uint32_t last_rts_fn; /* store last frame number of RTS */
/* back pointers */
struct gprs_rlcmac_trx *trx;
uint8_t ts_no;
#ifdef __cplusplus
private:
int rcv_control_block(const uint8_t *data, uint8_t data_len, bitvec *rlc_block, uint32_t fn);
void rcv_control_ack(Packet_Control_Acknowledgement_t *, uint32_t fn);
void rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *, uint32_t fn);
void rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *, uint32_t fn);
void rcv_resource_request(Packet_Resource_Request_t *t, uint32_t fn);
void rcv_measurement_report(Packet_Measurement_Report_t *t, uint32_t fn);
gprs_rlcmac_tbf *tbf_from_list_by_tfi(
LListHead<gprs_rlcmac_tbf> *tbf_list, uint8_t tfi,
enum gprs_rlcmac_tbf_direction dir);
gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi,
enum gprs_rlcmac_tbf_direction dir);
#endif
uint8_t m_num_tbfs[2];
uint8_t m_num_reserved[2];
uint8_t m_assigned_usf; /* bit set */
uint32_t m_assigned_tfi[2]; /* bit set */
struct gprs_rlcmac_tbf *m_tbfs[2][32];
};
#ifdef __cplusplus
inline unsigned gprs_rlcmac_pdch::num_tbfs(enum gprs_rlcmac_tbf_direction dir) const
{
return m_num_tbfs[dir];
}
inline unsigned gprs_rlcmac_pdch::num_reserved(
enum gprs_rlcmac_tbf_direction dir) const
{
return gprs_rlcmac_pdch::m_num_reserved[dir];
}
inline uint8_t gprs_rlcmac_pdch::assigned_usf() const
{
return m_assigned_usf;
}
inline uint32_t gprs_rlcmac_pdch::assigned_tfi(
enum gprs_rlcmac_tbf_direction dir) const
{
return m_assigned_tfi[dir];
}
inline bool gprs_rlcmac_pdch::is_enabled() const
{
return m_is_enabled;
}
#endif /* __cplusplus */

View File

@@ -23,46 +23,51 @@
#include <poll_controller.h>
#include <bts.h>
#include <tbf.h>
#include <cxx_linuxlist.h>
#include <sba.h>
extern "C" {
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/gsm_utils.h>
}
PollController::PollController(BTS& bts)
: m_bts(bts)
{}
void PollController::expireTimedout(int frame_number)
static inline bool elapsed_fn_check(unsigned max_delay, int frame_number, uint32_t from)
{
uint32_t elapsed = (frame_number + GSM_MAX_FN - from) % GSM_MAX_FN;
if (elapsed > max_delay && elapsed < 2715400)
return true;
return false;
}
void PollController::expireTimedout(int frame_number, unsigned max_delay)
{
struct gprs_rlcmac_bts *bts = m_bts.bts_data();
struct gprs_rlcmac_dl_tbf *dl_tbf;
struct gprs_rlcmac_ul_tbf *ul_tbf;
struct gprs_rlcmac_sba *sba, *sba2;
struct llist_pods *lpods;
uint32_t elapsed;
LListHead<gprs_rlcmac_tbf> *pos;
/* check for poll timeout
* The UL frame numbers lag 3 behind the DL frames and the data
* indication is only sent after all 4 frames of the block have been
* received. Sometimes there is an idle frame between the end of one
* and start of another frame (every 3 blocks). So the timeout should
* definitely be there if we're more than 8 frames past poll_fn. Let's
* stay on the safe side and say 13 or more. */
llist_pods_for_each_entry(ul_tbf, &bts->ul_tbfs, list, lpods) {
if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number + 2715648 - ul_tbf->poll_fn)
% 2715648;
if (elapsed >= 13 && elapsed < 2715400)
llist_for_each(pos, &m_bts.ul_tbfs()) {
ul_tbf = as_ul_tbf(pos->entry());
if (ul_tbf->poll_scheduled()) {
if (elapsed_fn_check(max_delay, frame_number, ul_tbf->poll_fn))
ul_tbf->poll_timeout();
}
}
llist_pods_for_each_entry(dl_tbf, &bts->dl_tbfs, list, lpods) {
if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number + 2715648 - dl_tbf->poll_fn)
% 2715648;
if (elapsed >= 13 && elapsed < 2715400)
llist_for_each(pos, &m_bts.dl_tbfs()) {
dl_tbf = as_dl_tbf(pos->entry());
if (dl_tbf->poll_scheduled()) {
if (elapsed_fn_check(max_delay, frame_number, dl_tbf->poll_fn))
dl_tbf->poll_timeout();
}
}
llist_for_each_entry_safe(sba, sba2, &m_bts.sba()->m_sbas, list) {
elapsed = (frame_number + 2715648 - sba->fn) % 2715648;
if (elapsed >= 13 && elapsed < 2715400) {
if (elapsed_fn_check(max_delay, frame_number, sba->fn)) {
/* sba will be freed here */
m_bts.sba()->timeout(sba);
}

View File

@@ -21,8 +21,6 @@
#pragma once
struct gprs_rlcmac_bts;
struct BTS;
/**
@@ -34,7 +32,8 @@ class PollController {
public:
PollController(BTS& bts);
void expireTimedout(int frame_number);
/* check for poll timeout */
void expireTimedout(int frame_number, unsigned max_delay);
private:
BTS& m_bts;

View File

@@ -16,12 +16,19 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "tbf.h"
#include "bts.h"
#include "gprs_debug.h"
#include <gprs_coding_scheme.h>
#include <rlc.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
extern "C" {
#include <osmocom/core/utils.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/core/logging.h>
}
@@ -31,6 +38,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;
}
@@ -53,9 +63,9 @@ void gprs_rlc_dl_window::reset()
m_v_b.reset();
}
int gprs_rlc_dl_window::resend_needed()
int gprs_rlc_dl_window::resend_needed() const
{
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn))
return bsn;
}
@@ -67,7 +77,7 @@ int gprs_rlc_dl_window::mark_for_resend()
{
int resend = 0;
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
if (m_v_b.is_unacked(bsn)) {
/* mark to be re-send */
m_v_b.mark_resend(bsn);
@@ -78,12 +88,38 @@ 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;
uint16_t bsn;
for (bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
for (bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
if (!m_v_b.is_acked(bsn))
unacked += 1;
}
@@ -91,19 +127,52 @@ int gprs_rlc_dl_window::count_unacked()
return unacked;
}
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn, uint16_t mod_sns)
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
{
return (ssn - 1 - bitnum) & mod_sns;
return (ssn - 1 - bitnum);
}
void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint8_t ssn,
void gprs_rlc_dl_window::update(BTS *bts, const struct bitvec *rbb,
uint16_t first_bsn, uint16_t *lost,
uint16_t *received)
{
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) */
for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
bool is_ack;
bsn = mod_sns(first_bsn + bitpos);
if (bsn == mod_sns(v_a() - 1))
break;
is_ack = bitvec_get_bit_pos(rbb, bitpos) == 1;
if (is_ack) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
if (!m_v_b.is_acked(bsn))
*received += 1;
m_v_b.mark_acked(bsn);
} else {
LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
m_v_b.mark_nacked(bsn);
bts->rlc_nacked();
*lost += 1;
}
}
}
void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint16_t ssn,
uint16_t *lost, uint16_t *received)
{
/* SSN - 1 is in range V(A)..V(S)-1 */
for (int bitpos = 0; bitpos < ws(); bitpos++) {
uint16_t bsn = bitnum_to_bsn(bitpos, ssn, mod_sns());
uint16_t bsn = mod_sns(bitnum_to_bsn(bitpos, ssn));
if (bsn == ((v_a() - 1) & mod_sns()))
if (bsn == mod_sns(v_a() - 1))
break;
if (show_rbb[ws() - 1 - bitpos] == 'R') {
@@ -126,7 +195,7 @@ int gprs_rlc_dl_window::move_window()
uint16_t bsn;
int moved = 0;
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) {
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
if (m_v_b.is_acked(bsn)) {
m_v_b.mark_invalid(bsn);
moved += 1;
@@ -142,7 +211,7 @@ void gprs_rlc_dl_window::show_state(char *show_v_b)
int i;
uint16_t bsn;
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) {
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
uint16_t index = bsn & mod_sns_half();
switch(m_v_b.get_state(index)) {
case GPRS_RLC_DL_BSN_INVALID:
@@ -170,12 +239,30 @@ void gprs_rlc_v_n::reset()
m_v_n[i] = GPRS_RLC_UL_BSN_INVALID;
}
void gprs_rlc_window::set_sns(uint16_t sns)
{
OSMO_ASSERT(sns >= RLC_GPRS_SNS);
OSMO_ASSERT(sns <= RLC_MAX_SNS);
/* check for 2^n */
OSMO_ASSERT((sns & (-sns)) == sns);
m_sns = sns;
}
void gprs_rlc_window::set_ws(uint16_t ws)
{
LOGP(DRLCMAC, LOGL_INFO, "ws(%d)\n",
ws);
OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
m_ws = ws;
}
/* Update the receive block bitmap */
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';
@@ -186,7 +273,7 @@ void gprs_rlc_ul_window::update_rbb(char *rbb)
void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
{
uint16_t offset_v_r;
offset_v_r = (bsn + 1 - v_r()) & mod_sns();
offset_v_r = mod_sns(bsn + 1 - v_r());
/* Positive offset, so raise. */
if (offset_v_r < (sns() >> 1)) {
while (offset_v_r--) {
@@ -210,7 +297,7 @@ uint16_t gprs_rlc_ul_window::raise_v_q()
if (!m_v_n.is_received(v_q()))
break;
LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
"V(Q) to %d\n", v_q(), (v_q() + 1) & mod_sns());
"V(Q) to %d\n", v_q(), mod_sns(v_q() + 1));
raise_v_q(1);
count += 1;
}
@@ -218,10 +305,251 @@ uint16_t gprs_rlc_ul_window::raise_v_q()
return count;
}
uint16_t gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
void gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
{
m_v_n.mark_received(bsn);
raise_v_r(bsn);
return raise_v_q();
}
bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
{
bool was_valid = m_v_n.is_received(bsn);
m_v_n.mark_missing(bsn);
return was_valid;
}
static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
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;
memset(rlc, 0, sizeof(*rlc));
rlc->cs = cs;
rlc->with_padding = with_padding;
rlc->num_data_blocks = cs.numDataBlocks();
OSMO_ASSERT(rlc->num_data_blocks <= ARRAY_SIZE(rlc->block_info));
for (i = 0; i < rlc->num_data_blocks; i++) {
gprs_rlc_data_block_info_init(&rlc->block_info[i], cs,
with_padding, spb);
rlc->data_offs_bits[i] =
header_bits + padding_bits +
(i+1) * cs.numDataBlockHeaderBits() +
i * 8 * rlc->block_info[0].data_len;
}
}
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding, const unsigned int spb)
{
return gprs_rlc_data_header_init(rlc, cs, with_padding,
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(), 0);
}
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
GprsCodingScheme cs, bool with_padding, const unsigned int spb)
{
unsigned int data_len = cs.maxDataBlockBytes();
if (with_padding)
data_len -= cs.optionalPaddingBits() / 8;
rdbi->data_len = data_len;
rdbi->bsn = 0;
rdbi->ti = 0;
rdbi->e = 1;
rdbi->cv = 15;
rdbi->pi = 0;
rdbi->spb = spb;
}
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs,
enum egprs_puncturing_values punct,
enum egprs_puncturing_values punct2, bool with_padding)
{
/* validate that punct and punct2 are as expected */
switch (GprsCodingScheme::Scheme(cs)) {
case GprsCodingScheme::MCS9:
case GprsCodingScheme::MCS8:
case GprsCodingScheme::MCS7:
if (punct2 == EGPRS_PS_INVALID) {
LOGP(DRLCMACDL, LOGL_ERROR,
"Invalid punct2 value for coding scheme %d: %d\n",
GprsCodingScheme::Scheme(cs), punct2);
return -1;
}
/* fall through */
case GprsCodingScheme::MCS6:
case GprsCodingScheme::MCS5:
case GprsCodingScheme::MCS4:
case GprsCodingScheme::MCS3:
case GprsCodingScheme::MCS2:
case GprsCodingScheme::MCS1:
if (punct == EGPRS_PS_INVALID) {
LOGP(DRLCMACDL, LOGL_ERROR,
"Invalid punct value for coding scheme %d: %d\n",
GprsCodingScheme::Scheme(cs), punct);
return -1;
}
break;
default:
return -1;
}
/* See 3GPP TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1 */
switch (GprsCodingScheme::Scheme(cs)) {
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 % 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 % 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: ;
}
return -1;
}
void gprs_rlc_mcs_cps_decode(unsigned int cps,
GprsCodingScheme cs, int *punct, int *punct2, int *with_padding)
{
*punct2 = -1;
*with_padding = 0;
switch (GprsCodingScheme::Scheme(cs)) {
case GprsCodingScheme::MCS1:
cps -= 0b1011; *punct = cps % 2; break;
case GprsCodingScheme::MCS2:
cps -= 0b1001; *punct = cps % 2; break;
case GprsCodingScheme::MCS3:
cps -= 0b0011; *punct = cps % 3; *with_padding = cps >= 3; break;
case GprsCodingScheme::MCS4:
cps -= 0b0000; *punct = cps % 3; break;
case GprsCodingScheme::MCS5:
cps -= 0b100; *punct = cps % 2; break;
case GprsCodingScheme::MCS6:
cps -= 0b000; *punct = cps % 2; *with_padding = cps >= 2; break;
case GprsCodingScheme::MCS7:
cps -= 0b10100; *punct = cps / 3; *punct2 = cps % 3; break;
case GprsCodingScheme::MCS8:
cps -= 0b01011; *punct = cps / 3; *punct2 = cps % 3; break;
case GprsCodingScheme::MCS9:
cps -= 0b00000; *punct = cps / 4; *punct2 = cps % 3; break;
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;
}
}

310
src/rlc.h
View File

@@ -19,14 +19,23 @@
*/
#pragma once
#include "gprs_coding_scheme.h"
#include <osmocom/core/endian.h>
#include <stdint.h>
#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
#define RLC_MAX_WS 64 /* max window size */
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
#define RLC_GPRS_SNS 128 /* GPRS, must be power of 2 */
#define RLC_GPRS_WS 64 /* max window size */
#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 */
#define RLC_EGPRS_MAX_BSN_DELTA 512
#define RLC_MAX_SNS RLC_EGPRS_SNS
#define RLC_MAX_WS RLC_EGPRS_MAX_WS
#define RLC_MAX_LEN 74 /* MCS-9 data unit */
struct BTS;
struct gprs_rlc_v_n;
/* The state of a BSN in the send/receive window */
enum gprs_rlc_ul_bsn_state {
@@ -45,24 +54,179 @@ 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()
{
return (RLC_MAX_SNS / 2) - 1;
}
struct gprs_rlc_data_block_info {
unsigned int data_len; /* EGPRS: N2, GPRS: N2-2, N-2 */
unsigned int bsn;
unsigned int ti;
unsigned int e;
unsigned int cv; /* FBI == 1 <=> CV == 0 */
unsigned int pi;
unsigned int spb;
};
struct gprs_rlc_data_info {
GprsCodingScheme cs;
unsigned int r;
unsigned int si;
unsigned int tfi;
unsigned int cps;
unsigned int rsb;
unsigned int usf;
unsigned int es_p;
unsigned int rrbp;
unsigned int pr;
uint8_t num_data_blocks; /* this can actually be only 0, 1, 2: enforced in gprs_rlc_data_header_init() */
unsigned int with_padding;
unsigned int data_offs_bits[2];
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;
uint8_t cs;
struct gprs_rlc_data_block_info block_info;
/*
* 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, 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, const unsigned int spb);
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, enum egprs_puncturing_values
punct, enum egprs_puncturing_values punct2, bool 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.
@@ -104,18 +268,28 @@ private:
/**
* TODO: The UL/DL code could/should share a baseclass but
* we are using llist_for_each_entry for the TBF which
* requires everything which creates a requirement for a POD
* type and in < C++11 something that is using even if the
* most simple form of inheritance is not a POD anymore.
* TODO: The UL/DL code could/should share a base class.
*/
struct gprs_rlc_dl_window {
void reset();
class gprs_rlc_window {
public:
gprs_rlc_window();
const uint16_t mod_sns() const;
const uint16_t mod_sns(uint16_t bsn) const;
const uint16_t sns() const;
const uint16_t ws() const;
void set_sns(uint16_t sns);
void set_ws(uint16_t ws);
protected:
uint16_t m_sns;
uint16_t m_ws;
};
struct gprs_rlc_dl_window: public gprs_rlc_window {
void reset();
bool window_stalled() const;
bool window_empty() const;
@@ -125,13 +299,16 @@ struct gprs_rlc_dl_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();
int resend_needed() const;
int mark_for_resend();
void update(BTS *bts, char *show_rbb, uint8_t ssn,
void update(BTS *bts, char *show_rbb, uint16_t ssn,
uint16_t *lost, uint16_t *received);
void update(BTS *bts, const struct bitvec *rbb,
uint16_t first_bsn, uint16_t *lost,
uint16_t *received);
int move_window();
void show_state(char *show_rbb);
int count_unacked();
@@ -140,6 +317,8 @@ struct gprs_rlc_dl_window {
uint16_t m_v_a; /* ack state */
gprs_rlc_v_b m_v_b;
gprs_rlc_dl_window();
};
struct gprs_rlc_v_n {
@@ -157,35 +336,41 @@ private:
gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */
};
struct gprs_rlc_ul_window {
const uint16_t mod_sns() const;
const uint16_t sns() const;
const uint16_t ws() const;
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);
void reset_state();
const uint16_t ssn() const;
bool is_in_window(uint8_t bsn) 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();
void raise_v_q(int);
uint16_t receive_bsn(const uint16_t bsn);
void receive_bsn(const uint16_t bsn);
bool invalidate_bsn(const uint16_t bsn);
uint16_t m_v_r; /* receive state */
uint16_t m_v_q; /* receive window state */
gprs_rlc_v_n m_v_n;
gprs_rlc_ul_window();
};
extern "C" {
/* TS 04.60 10.2.2 */
#if OSMO_IS_LITTLE_ENDIAN
struct rlc_ul_header {
uint8_t r:1,
si:1,
@@ -216,6 +401,14 @@ struct rlc_li_field {
m:1,
li:6;
} __attribute__ ((packed));
struct rlc_li_field_egprs {
uint8_t e:1,
li:7;
} __attribute__ ((packed));
#else
# error "Only little endian headers are supported yet. TODO: add missing structs"
#endif
}
inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const
@@ -283,21 +476,39 @@ inline void gprs_rlc_v_b::mark_invalid(int bsn)
return mark(bsn, GPRS_RLC_DL_BSN_INVALID);
}
inline const uint16_t gprs_rlc_dl_window::sns() const
inline gprs_rlc_window::gprs_rlc_window()
: m_sns(RLC_GPRS_SNS)
, m_ws(RLC_GPRS_WS)
{
return RLC_MAX_SNS;
}
inline const uint16_t gprs_rlc_dl_window::ws() const
inline const uint16_t gprs_rlc_window::sns() const
{
return RLC_MAX_WS;
return m_sns;
}
inline const uint16_t gprs_rlc_dl_window::mod_sns() const
inline const uint16_t gprs_rlc_window::ws() const
{
return m_ws;
}
inline const uint16_t gprs_rlc_window::mod_sns() const
{
return sns() - 1;
}
inline const uint16_t gprs_rlc_window::mod_sns(uint16_t bsn) const
{
return bsn & mod_sns();
}
inline gprs_rlc_dl_window::gprs_rlc_dl_window()
: m_v_s(0)
, m_v_a(0)
{
reset();
}
inline const uint16_t gprs_rlc_dl_window::v_s() const
{
return m_v_s;
@@ -305,7 +516,7 @@ inline const uint16_t gprs_rlc_dl_window::v_s() const
inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
{
return (m_v_s + offset) & mod_sns();
return mod_sns(m_v_s + offset);
}
inline const uint16_t gprs_rlc_dl_window::v_a() const
@@ -315,7 +526,7 @@ inline const uint16_t gprs_rlc_dl_window::v_a() const
inline bool gprs_rlc_dl_window::window_stalled() const
{
return ((m_v_s - m_v_a) & mod_sns()) == ws();
return (mod_sns(m_v_s - m_v_a)) == ws();
}
inline bool gprs_rlc_dl_window::window_empty() const
@@ -333,12 +544,19 @@ 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();
}
inline bool gprs_rlc_ul_window::is_in_window(uint8_t bsn) const
inline gprs_rlc_ul_window::gprs_rlc_ul_window()
: m_v_r(0)
, m_v_q(0)
{
m_v_n.reset();
}
inline bool gprs_rlc_ul_window::is_in_window(uint16_t bsn) const
{
uint16_t offset_v_q;
@@ -349,19 +567,29 @@ inline bool gprs_rlc_ul_window::is_in_window(uint8_t bsn) const
return offset_v_q < ws();
}
inline const uint16_t gprs_rlc_ul_window::sns() const
inline bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
{
return RLC_MAX_SNS;
uint16_t offset_v_r;
/* Offset to the end of the received window */
offset_v_r = (m_v_r - 1 - bsn) & mod_sns();
return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
}
inline const uint16_t gprs_rlc_ul_window::ws() const
inline void gprs_rlc_ul_window::reset_state()
{
return RLC_MAX_WS;
m_v_r = 0;
m_v_q = 0;
}
inline const uint16_t gprs_rlc_ul_window::mod_sns() const
inline const void gprs_rlc_ul_window::set_v_r(int v_r)
{
return sns() - 1;
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
@@ -381,12 +609,12 @@ inline const uint16_t gprs_rlc_ul_window::ssn() const
inline void gprs_rlc_ul_window::raise_v_r_to(int moves)
{
m_v_r = (m_v_r + moves) & mod_sns();
m_v_r = mod_sns(m_v_r + moves);
}
inline void gprs_rlc_ul_window::raise_v_q(int incr)
{
m_v_q = (m_v_q + incr) & mod_sns();
m_v_q = mod_sns(m_v_q + incr);
}
inline void gprs_rlc_v_n::mark_received(int bsn)

View File

@@ -20,12 +20,16 @@
*/
#include <sba.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <bts.h>
#include <pcu_utils.h>
#include <pdch.h>
extern "C" {
#include <osmocom/core/logging.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm_utils.h>
}
#include <errno.h>
@@ -55,6 +59,9 @@ int SBAController::alloc(
if (!sba)
return -ENOMEM;
if (!gsm48_ta_is_valid(ta))
return -EINVAL;
for (trx = 0; trx < 8; trx++) {
for (ts = 7; ts >= 0; ts--) {
pdch = &m_bts.bts_data()->trx[trx].pdch[ts];
@@ -71,7 +78,7 @@ int SBAController::alloc(
return -EINVAL;
}
fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648;
fn = next_fn(pdch->last_rts_fn, AGCH_START_OFFSET);
sba->trx_no = trx;
sba->ts_no = ts;
@@ -106,14 +113,13 @@ gprs_rlcmac_sba *SBAController::find(const gprs_rlcmac_pdch *pdch, uint32_t fn)
uint32_t SBAController::sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr)
{
uint32_t sba_fn;
uint32_t sba_fn = fn + 4;
struct gprs_rlcmac_sba *sba;
/* check special TBF for events */
sba_fn = fn + 4;
if ((block_nr % 3) == 2)
sba_fn ++;
sba_fn = sba_fn % 2715648;
sba_fn++;
sba_fn = sba_fn % GSM_MAX_FN;
sba = find(trx, ts, sba_fn);
if (sba)
return sba_fn;
@@ -123,7 +129,9 @@ uint32_t SBAController::sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t bloc
int SBAController::timeout(struct gprs_rlcmac_sba *sba)
{
LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for SBA\n");
LOGP(DRLCMAC, LOGL_NOTICE,
"Poll timeout for SBA (TRX=%u, TS=%u, FN=%u, TA=%u)\n", sba->trx_no,
sba->ts_no, sba->fn, sba->ta);
m_bts.sba_timedout();
free_sba(sba);
return 0;

View File

@@ -27,8 +27,6 @@ extern "C" {
}
struct BTS;
class PollController;
struct gprs_rlcmac_sba;
struct gprs_rlcmac_pdch;
/*

File diff suppressed because it is too large Load Diff

608
src/tbf.h
View File

@@ -20,25 +20,31 @@
#ifdef __cplusplus
#include "gprs_rlcmac.h"
#include "llc.h"
#include "rlc.h"
#include "cxx_linuxlist.h"
#include <gprs_debug.h>
#include <gprs_coding_scheme.h>
#include <gsm_timer.h>
#include <stdint.h>
extern "C" {
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
}
struct bssgp_bvc_ctx;
struct rlc_ul_header;
struct msgb;
struct pcu_l1_meas;
class GprsMs;
struct gprs_rlcmac_bts;
/*
* TBF instance
*/
#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */
#define Tassign_pacch 2,0 /* timeout for pacch assigment */
#define T_ASS_AGCH_USEC 200000 /* waiting after IMM.ASS confirm */
#define T_ASS_PACCH_SEC 2 /* timeout for pacch assigment */
#define T_REJ_PACCH_USEC 2000 /* timeout for tbf reject for PRR*/
enum gprs_rlcmac_tbf_state {
GPRS_RLCMAC_NULL = 0, /* new created TBF */
@@ -49,34 +55,134 @@ enum gprs_rlcmac_tbf_state {
GPRS_RLCMAC_RELEASING, /* releasing, wait to free TBI/USF */
};
enum gprs_rlcmac_tbf_poll_type {
GPRS_RLCMAC_POLL_UL_ASS,
GPRS_RLCMAC_POLL_DL_ASS,
GPRS_RLCMAC_POLL_UL_ACK,
GPRS_RLCMAC_POLL_DL_ACK,
};
enum gprs_rlcmac_tbf_poll_state {
GPRS_RLCMAC_POLL_NONE = 0,
GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
};
extern const struct value_string gprs_rlcmac_tbf_poll_state_names[];
enum gprs_rlcmac_tbf_dl_ass_state {
GPRS_RLCMAC_DL_ASS_NONE = 0,
GPRS_RLCMAC_DL_ASS_SEND_ASS, /* send downlink assignment on next RTS */
GPRS_RLCMAC_DL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
extern const struct value_string gprs_rlcmac_tbf_dl_ass_state_names[];
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 */
};
extern const struct value_string gprs_rlcmac_tbf_ul_ass_state_names[];
enum gprs_rlcmac_tbf_ul_ack_state {
GPRS_RLCMAC_UL_ACK_NONE = 0,
GPRS_RLCMAC_UL_ACK_SEND_ACK, /* send acknowledge on next RTS */
GPRS_RLCMAC_UL_ACK_WAIT_ACK, /* wait for PACKET CONTROL ACK */
};
extern const struct value_string gprs_rlcmac_tbf_ul_ack_state_names[];
enum gprs_rlcmac_tbf_direction {
GPRS_RLCMAC_DL_TBF,
GPRS_RLCMAC_UL_TBF
};
enum tbf_dl_prio {
DL_PRIO_NONE,
DL_PRIO_SENT_DATA, /* the data has been sent and not (yet) nacked */
DL_PRIO_LOW_AGE, /* the age has reached the first threshold */
DL_PRIO_NEW_DATA, /* the data has not been sent yet or nacked */
DL_PRIO_HIGH_AGE, /* the age has reached the second threshold */
DL_PRIO_CONTROL, /* a control block needs to be sent */
};
enum tbf_rlc_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 LOGPTBF(tbf, level, fmt, args...) LOGP(DTBF, level, "%s " fmt, tbf_name(tbf), ## args)
#define LOGPTBFUL(tbf, level, fmt, args...) LOGP(DTBFUL, level, "%s " fmt, tbf_name(tbf), ## args)
#define LOGPTBFDL(tbf, level, fmt, args...) LOGP(DTBFDL, level, "%s " fmt, tbf_name(tbf), ## args)
enum tbf_timers {
/* internal assign/reject timer */
T0,
/* Wait for reuse of USF and TFI(s) after the MS uplink assignment for this TBF is invalid. */
T3169,
/* Wait for reuse of TFI(s) after sending of the last RLC Data Block on this TBF.
Wait for reuse of TFI(s) after sending the PACKET TBF RELEASE for an MBMS radio bearer. */
T3191,
/* Wait for reuse of TFI(s) after reception of the final PACKET DOWNLINK ACK/NACK from the
MS for this TBF. */
T3193,
/* Wait for reuse of TFI(s) when there is no response from the MS
(radio failure or cell change) for this TBF/MBMS radio bearer. */
T3195,
T_MAX
};
enum tbf_counters { /* TBF counters from 3GPP TS 44.060 §13.4 */
/* counters are reset when: */
N3101, /* received a valid data block from mobile station in a block assigned for this USF */
N3103, /* transmitting the final PACKET UPLINK ACK/NACK message */
N3105, /* after sending a RRBP field in the downlink RLC data block, receives a valid RLC/MAC control message */
N_MAX
};
#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 */
@@ -87,42 +193,47 @@ enum gprs_rlcmac_tbf_direction {
#define GPRS_RLCMAC_FLAG_TO_DL_ASS 7
#define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */
struct llist_pods {
struct llist_head list;
void *back;
};
#define T_START(tbf, t, sec, usec, r, f) tbf->t_start(t, sec, usec, r, f, __FILE__, __LINE__)
#define llist_pods_entry(ptr, type) \
((type *)(container_of(ptr, struct llist_pods, list)->back))
/**
* llist_pods_for_each_entry - like llist_for_each_entry, but uses
* struct llist_pods ->back to access the entry.
* This is necessary for non-PODS classes because container_of is
* not guaranteed to work anymore. */
#define llist_pods_for_each_entry(pos, head, member, lpods) \
for (lpods = llist_entry((head)->next, typeof(struct llist_pods), list), \
pos = ((typeof(pos))lpods->back), \
prefetch(pos->member.list.next); \
&lpods->list != (head); \
lpods = llist_entry(lpods->list.next, typeof(struct llist_pods), list), \
pos = ((typeof(pos))lpods->back),\
prefetch(pos->member.list.next))
#define TBF_SET_STATE(t, st) do { t->set_state(st, __FILE__, __LINE__); } while(0)
#define TBF_SET_ASS_STATE_DL(t, st) do { t->set_ass_state_dl(st, __FILE__, __LINE__); } while(0)
#define TBF_SET_ASS_STATE_UL(t, st) do { t->set_ass_state_ul(st, __FILE__, __LINE__); } while(0)
#define TBF_SET_ACK_STATE(t, st) do { t->set_ack_state(st, __FILE__, __LINE__); } while(0)
#define TBF_POLL_SCHED_SET(t) do { t->poll_sched_set(__FILE__, __LINE__); } while(0)
#define TBF_POLL_SCHED_UNSET(t) do { t->poll_sched_unset(__FILE__, __LINE__); } while(0)
#define TBF_SET_ASS_ON(t, fl, chk) do { t->set_assigned_on(fl, chk, __FILE__, __LINE__); } while(0)
#define TBF_ASS_TYPE_SET(t, kind) do { t->ass_type_mod(kind, false, __FILE__, __LINE__); } while(0)
#define TBF_ASS_TYPE_UNSET(t, kind) do { t->ass_type_mod(kind, true, __FILE__, __LINE__); } while(0)
struct gprs_rlcmac_tbf {
gprs_rlcmac_tbf(BTS *bts_, gprs_rlcmac_tbf_direction dir);
static void free_all(struct gprs_rlcmac_trx *trx);
static void free_all(struct gprs_rlcmac_pdch *pdch);
bool state_is(enum gprs_rlcmac_tbf_state rhs) const;
bool state_is_not(enum gprs_rlcmac_tbf_state rhs) const;
void set_state(enum gprs_rlcmac_tbf_state new_state);
bool dl_ass_state_is(enum gprs_rlcmac_tbf_dl_ass_state rhs) const;
bool ul_ass_state_is(enum gprs_rlcmac_tbf_ul_ass_state rhs) const;
bool ul_ack_state_is(enum gprs_rlcmac_tbf_ul_ack_state rhs) const;
bool poll_scheduled() const;
void set_state(enum gprs_rlcmac_tbf_state new_state, const char *file, int line);
void set_ass_state_dl(enum gprs_rlcmac_tbf_dl_ass_state new_state, const char *file, int line);
void set_ass_state_ul(enum gprs_rlcmac_tbf_ul_ass_state new_state, const char *file, int line);
void set_ack_state(enum gprs_rlcmac_tbf_ul_ack_state new_state, const char *file, int line);
void poll_sched_set(const char *file, int line);
void poll_sched_unset(const char *file, int line);
void check_pending_ass();
bool check_n_clear(uint8_t state_flag);
void set_assigned_on(uint8_t state_flag, bool check_ccch, const char *file, int line);
void ass_type_mod(uint8_t t, bool unset, const char *file, int line);
const char *state_name() const;
const char *name() const;
struct msgb *create_dl_ass(uint32_t fn);
struct msgb *create_ul_ass(uint32_t fn);
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);
@@ -131,11 +242,21 @@ struct gprs_rlcmac_tbf {
int rlcmac_diag();
bool n_inc(enum tbf_counters n);
void n_reset(enum tbf_counters n);
int update();
void handle_timeout();
void stop_timer();
void stop_t3191();
void stop_timers(const char *reason);
bool timers_pending(enum tbf_timers t);
void t_stop(enum tbf_timers t, const char *reason);
void t_start(enum tbf_timers t, uint32_t sec, uint32_t microsec, const char *reason, bool force,
const char *file, unsigned line);
int establish_dl_tbf_on_pacch();
int check_polling(uint32_t fn, uint8_t ts,
uint32_t *poll_fn, unsigned int *rrbp);
void set_polling(uint32_t poll_fn, uint8_t ts, enum gprs_rlcmac_tbf_poll_type t);
void poll_timeout();
/** tlli handling */
@@ -146,6 +267,7 @@ struct gprs_rlcmac_tbf {
void update_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction);
uint8_t tfi() const;
bool is_tfi_assigned() const;
const char *imsi() const;
void assign_imsi(const char *imsi);
@@ -153,18 +275,28 @@ struct gprs_rlcmac_tbf {
void set_ta(uint8_t);
uint8_t ms_class() const;
void set_ms_class(uint8_t);
uint8_t current_cs() const;
gprs_llc_queue *llc_queue();
const gprs_llc_queue *llc_queue() const;
GprsCodingScheme current_cs() const;
size_t llc_queue_size() const;
time_t created_ts() const;
uint8_t dl_slots() const;
uint8_t ul_slots() const;
bool is_control_ts(uint8_t ts) const;
/* EGPRS */
bool is_egprs_enabled() const;
void disable_egprs();
/* attempt to make things a bit more fair */
void rotate_in_list();
struct llist_pods list;
LListHead<gprs_rlcmac_tbf>& ms_list() {return this->m_ms_list;}
const LListHead<gprs_rlcmac_tbf>& ms_list() const {return this->m_ms_list;}
LListHead<gprs_rlcmac_tbf>& list();
const LListHead<gprs_rlcmac_tbf>& list() const;
uint32_t state_flags;
enum gprs_rlcmac_tbf_direction direction;
struct gprs_rlcmac_trx *trx;
@@ -176,34 +308,22 @@ struct gprs_rlcmac_tbf {
gprs_llc m_llc;
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
enum gprs_rlcmac_tbf_poll_state poll_state;
uint32_t poll_fn; /* frame number to poll */
uint8_t poll_ts; /* TS to poll */
gprs_rlc m_rlc;
uint8_t n3105; /* N3105 counter */
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
unsigned int num_T_exp; /* number of consecutive T expirations */
struct osmo_gsm_timer_list gsm_timer;
unsigned int fT; /* fTxxxx number */
unsigned int num_fT_exp; /* number of consecutive fT expirations */
struct {
struct Meas {
struct timeval rssi_tv; /* timestamp for rssi calculation */
int32_t rssi_sum; /* sum of rssi values */
int rssi_num; /* number of rssi values added since rssi_tv */
} meas;
/* these should become protected but only after gprs_rlcmac_data.c
* stops to iterate over all tbf in its current form */
enum gprs_rlcmac_tbf_state state;
Meas();
} meas;
/* Remember if the tbf was in wait_release state when we want to
* schedule a new dl assignment */
@@ -222,10 +342,16 @@ 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;
void enable_egprs();
int set_tlli_from_ul(uint32_t new_tlli);
void merge_and_clear_ms(GprsMs *old_ms);
int extract_tlli(const uint8_t *data, const size_t len);
gprs_llc_queue *llc_queue();
const gprs_llc_queue *llc_queue() const;
static const char *tbf_state_name[6];
@@ -236,34 +362,62 @@ protected:
uint8_t m_ms_class;
private:
enum gprs_rlcmac_tbf_state state;
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
enum gprs_rlcmac_tbf_poll_state poll_state;
LListHead<gprs_rlcmac_tbf> m_list;
LListHead<gprs_rlcmac_tbf> m_ms_list;
bool m_egprs_enabled;
struct osmo_timer_list T[T_MAX];
uint8_t N[N_MAX];
mutable char m_name_buf[60];
};
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts,
int8_t use_trx, uint8_t ms_class,
int8_t use_trx, uint8_t ms_class, uint8_t egprs_ms_class,
uint32_t tlli, uint8_t ta, GprsMs *ms);
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
GprsMs *ms, int8_t use_trx,
uint8_t ms_class, uint8_t single_slot);
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx, uint8_t ms_class,
uint8_t egprs_ms_class, bool single_slot);
struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
GprsMs *ms, int8_t use_trx,
uint8_t ms_class, uint8_t single_slot);
struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx, uint8_t ms_class,
uint8_t egprs_ms_class, bool single_slot);
void tbf_free(struct gprs_rlcmac_tbf *tbf);
int tbf_assign_control_ts(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);
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
unsigned int seconds, unsigned int microseconds);
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
inline bool gprs_rlcmac_tbf::state_is(enum gprs_rlcmac_tbf_state rhs) const
{
return state == rhs;
}
inline bool gprs_rlcmac_tbf::dl_ass_state_is(enum gprs_rlcmac_tbf_dl_ass_state rhs) const
{
return dl_ass_state == rhs;
}
inline bool gprs_rlcmac_tbf::ul_ass_state_is(enum gprs_rlcmac_tbf_ul_ass_state rhs) const
{
return ul_ass_state == rhs;
}
inline bool gprs_rlcmac_tbf::ul_ack_state_is(enum gprs_rlcmac_tbf_ul_ack_state rhs) const
{
return ul_ack_state == rhs;
}
inline bool gprs_rlcmac_tbf::poll_scheduled() const
{
return poll_state == GPRS_RLCMAC_POLL_SCHED;
}
inline bool gprs_rlcmac_tbf::state_is_not(enum gprs_rlcmac_tbf_state rhs) const
{
return state != rhs;
@@ -276,14 +430,147 @@ inline const char *gprs_rlcmac_tbf::state_name() const
return tbf_state_name[state];
}
inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state)
/* Set assignment state and corrsponding flags */
inline void gprs_rlcmac_tbf::set_assigned_on(uint8_t state_flag, bool check_ccch, const char *file, int line)
{
LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n",
set_state(GPRS_RLCMAC_ASSIGN, file, line);
if (check_ccch) {
if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)))
ass_type_mod(state_flag, false, file, line);
} else
state_flags |= (1 << state_flag);
}
inline void gprs_rlcmac_tbf::ass_type_mod(uint8_t t, bool unset, const char *file, int line)
{
const char *ch = "UNKNOWN";
switch (t) {
case GPRS_RLCMAC_FLAG_CCCH:
if (unset) {
if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)))
return;
} else {
if (state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))
LOGPSRC(DTBF, LOGL_ERROR, file, line,
"%s attempted to set ass. type CCCH which is already set.\n",
tbf_name(this));
}
ch = "CCCH";
break;
case GPRS_RLCMAC_FLAG_PACCH:
if (unset) {
if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)))
return;
} else {
if (state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))
LOGPSRC(DTBF, LOGL_ERROR, file, line,
"%s attempted to set ass. type PACCH which is already set.\n",
tbf_name(this));
}
ch = "PACCH";
break;
default:
LOGPSRC(DTBF, LOGL_ERROR, file, line, "%s attempted to %sset unexpected ass. type %d - FIXME!\n",
tbf_name(this), unset ? "un" : "", t);
return;
}
LOGPSRC(DTBF, LOGL_INFO, file, line, "%s %sset ass. type %s [prev CCCH:%u, PACCH:%u]\n",
tbf_name(this), unset ? "un" : "", ch,
state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH),
state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH));
if (unset) {
state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep to flags */
state_flags &= ~(1 << t);
} else
state_flags |= (1 << t);
}
inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state, const char *file, int line)
{
LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes state from %s to %s\n",
tbf_name(this),
tbf_state_name[state], tbf_state_name[new_state]);
state = new_state;
}
inline void gprs_rlcmac_tbf::set_ass_state_dl(enum gprs_rlcmac_tbf_dl_ass_state new_state, const char *file, int line)
{
LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes DL ASS state from %s to %s\n",
tbf_name(this),
get_value_string(gprs_rlcmac_tbf_dl_ass_state_names, dl_ass_state),
get_value_string(gprs_rlcmac_tbf_dl_ass_state_names, new_state));
dl_ass_state = new_state;
}
inline void gprs_rlcmac_tbf::set_ass_state_ul(enum gprs_rlcmac_tbf_ul_ass_state new_state, const char *file, int line)
{
LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes UL ASS state from %s to %s\n",
tbf_name(this),
get_value_string(gprs_rlcmac_tbf_ul_ass_state_names, ul_ass_state),
get_value_string(gprs_rlcmac_tbf_ul_ass_state_names, new_state));
ul_ass_state = new_state;
}
inline void gprs_rlcmac_tbf::set_ack_state(enum gprs_rlcmac_tbf_ul_ack_state new_state, const char *file, int line)
{
LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes UL ACK state from %s to %s\n",
tbf_name(this),
get_value_string(gprs_rlcmac_tbf_ul_ack_state_names, ul_ack_state),
get_value_string(gprs_rlcmac_tbf_ul_ack_state_names, new_state));
ul_ack_state = new_state;
}
inline void gprs_rlcmac_tbf::poll_sched_set(const char *file, int line)
{
LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes poll state from %s to GPRS_RLCMAC_POLL_SCHED\n",
tbf_name(this), get_value_string(gprs_rlcmac_tbf_poll_state_names, poll_state));
poll_state = GPRS_RLCMAC_POLL_SCHED;
}
inline void gprs_rlcmac_tbf::poll_sched_unset(const char *file, int line)
{
LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s changes poll state from %s to GPRS_RLCMAC_POLL_NONE\n",
tbf_name(this), get_value_string(gprs_rlcmac_tbf_poll_state_names, poll_state));
poll_state = GPRS_RLCMAC_POLL_NONE;
}
inline void gprs_rlcmac_tbf::check_pending_ass()
{
if (ul_ass_state != GPRS_RLCMAC_UL_ASS_NONE)
LOGPTBF(this, LOGL_ERROR, "FIXME: Software error: Pending uplink assignment in state %s. "
"This may not happen, because the assignment message never gets transmitted. "
"Please be sure not to free in this state. PLEASE FIX!\n",
get_value_string(gprs_rlcmac_tbf_ul_ass_state_names, ul_ass_state));
if (dl_ass_state != GPRS_RLCMAC_DL_ASS_NONE)
LOGPTBF(this, LOGL_ERROR, "FIXME: Software error: Pending downlink assignment in state %s. "
"This may not happen, because the assignment message never gets transmitted. "
"Please be sure not to free in this state. PLEASE FIX!\n",
get_value_string(gprs_rlcmac_tbf_dl_ass_state_names, dl_ass_state));
}
inline bool gprs_rlcmac_tbf::check_n_clear(uint8_t state_flag)
{
if ((state_flags & (1 << state_flag))) {
state_flags &= ~(1 << state_flag);
return true;
}
return false;
}
inline LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list()
{
return this->m_list;
}
inline const LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list() const
{
return this->m_list;
}
inline GprsMs *gprs_rlcmac_tbf::ms() const
{
return m_ms;
@@ -294,6 +581,16 @@ inline bool gprs_rlcmac_tbf::is_tlli_valid() const
return tlli() != 0;
}
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 ||
(direction == GPRS_RLCMAC_DL_TBF &&
state == GPRS_RLCMAC_ASSIGN &&
(state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)));
}
inline uint8_t gprs_rlcmac_tbf::tfi() const
{
return m_tfi;
@@ -304,31 +601,54 @@ inline time_t gprs_rlcmac_tbf::created_ts() const
return m_created_ts;
}
struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
void cleanup();
inline bool gprs_rlcmac_tbf::is_egprs_enabled() const
{
return m_egprs_enabled;
}
inline void gprs_rlcmac_tbf::enable_egprs()
{
m_egprs_enabled = true;
}
inline void gprs_rlcmac_tbf::disable_egprs()
{
m_egprs_enabled = false;
}
struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
gprs_rlcmac_dl_tbf(BTS *bts);
gprs_rlc_dl_window *window();
void cleanup();
void enable_egprs();
/* dispatch Unitdata.DL messages */
static int handle(struct gprs_rlcmac_bts *bts,
const uint32_t tlli, const uint32_t old_tlli,
const char *imsi, const uint8_t ms_class,
const uint16_t delay_csec, const uint8_t *data, const uint16_t len);
const uint8_t egprs_ms_class, const uint16_t delay_csec,
const uint8_t *data, const uint16_t len);
int append_data(const uint8_t ms_class,
const uint16_t pdu_delay_csec,
const uint8_t *data, const uint16_t len);
int rcvd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb);
int rcvd_dl_ack(bool final, uint8_t ssn, uint8_t *rbb);
int rcvd_dl_ack(bool final_ack, unsigned first_bsn, struct bitvec *rbb);
struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts);
void trigger_ass(struct gprs_rlcmac_tbf *old_tbf);
bool handle_ack_nack();
void request_dl_ack();
bool need_control_ts() const;
bool have_data() const;
int frames_since_last_poll(unsigned fn) const;
int frames_since_last_drain(unsigned fn) const;
bool keep_open(unsigned fn) const;
bool is_control_ts(uint8_t ts) const {
return ts == control_ts;
}
int release();
int abort();
uint16_t window_size() const;
void set_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);
@@ -338,78 +658,178 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
* All states that need reset must be in this struct, so this is why
* variables are in both (dl and ul) structs and not outside union.
*/
gprs_rlc_dl_window m_window;
int32_t m_tx_counter; /* count all transmitted blocks */
uint8_t m_wait_confirm; /* wait for CCCH IMM.ASS cnf */
bool m_dl_ack_requested;
int32_t m_last_dl_poll_fn;
int32_t m_last_dl_drained_fn;
struct {
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 */
uint16_t dl_loss_received; /* sum of received packets */
BandWidth();
} m_bw;
struct rate_ctr_group *m_dl_gprs_ctrs;
struct rate_ctr_group *m_dl_egprs_ctrs;
protected:
struct msgb *create_new_bsn(const uint32_t fn, const uint8_t ts);
struct ana_result {
unsigned received_packets;
unsigned lost_packets;
unsigned received_bytes;
unsigned lost_bytes;
};
int take_next_bsn(uint32_t fn, int previous_bsn,
bool *may_combine);
bool restart_bsn_cycle();
int create_new_bsn(const uint32_t fn, GprsCodingScheme cs);
struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
const int index);
int index, int index2 = -1);
int update_window(const uint8_t ssn, const uint8_t *rbb);
int update_window(unsigned first_bsn, const struct bitvec *rbb);
int maybe_start_new_window();
bool dl_window_stalled() const;
void reuse_tbf();
void start_llc_timer();
int analyse_errors(char *show_rbb, uint8_t ssn);
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;
/* Please note that all variables below 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
* variables are in both (dl and ul) structs and not outside union.
*/
gprs_rlc_dl_window m_window;
};
struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
struct msgb *create_ul_ack(uint32_t fn);
gprs_rlcmac_ul_tbf(BTS *bts);
gprs_rlc_ul_window *window();
struct msgb *create_ul_ack(uint32_t fn, uint8_t ts);
bool ctrl_ack_to_toggle();
bool handle_ctrl_ack();
void enable_egprs();
/* blocks were acked */
int rcv_data_block_acknowledged(const uint8_t *data, size_t len,
struct pcu_l1_meas *meas);
int rcv_data_block_acknowledged(
const struct gprs_rlc_data_info *rlc,
uint8_t *data, struct pcu_l1_meas *meas);
/* TODO: extract LLC class? */
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);
uint16_t window_size() const;
void set_window_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
* variables are in both (dl and ul) structs and not outside union.
*/
gprs_rlc_ul_window m_window;
int32_t m_rx_counter; /* count all received blocks */
uint8_t m_n3103; /* N3103 counter */
uint8_t m_usf[8]; /* list USFs per PDCH (timeslot) */
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 rlc_ul_header *rh);
void maybe_schedule_uplink_acknack(const gprs_rlc_data_info *rlc);
/* Please note that all variables below 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
* variables are in both (dl and ul) structs and not outside union.
*/
gprs_rlc_ul_window m_window;
};
#ifdef __cplusplus
extern "C" {
#endif
void update_tbf_ta(struct gprs_rlcmac_ul_tbf *tbf, int8_t ta_delta);
void set_tbf_ta(struct gprs_rlcmac_ul_tbf *tbf, uint8_t ta);
#ifdef __cplusplus
}
#endif
inline enum gprs_rlcmac_tbf_direction reverse(enum gprs_rlcmac_tbf_direction dir)
{
return (enum gprs_rlcmac_tbf_direction)
((int)GPRS_RLCMAC_UL_TBF - (int)dir + (int)GPRS_RLCMAC_DL_TBF);
}
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
void tbf_print_vty_info(struct vty *vty, struct llist_head *tbf);
#ifdef __cplusplus
inline uint16_t gprs_rlcmac_ul_tbf::window_size() const
{
return m_window.ws();
}
inline uint16_t gprs_rlcmac_dl_tbf::window_size() const
{
return m_window.ws();
}
inline void gprs_rlcmac_ul_tbf::enable_egprs()
{
m_window.set_sns(RLC_EGPRS_SNS);
gprs_rlcmac_tbf::enable_egprs();
}
inline void gprs_rlcmac_dl_tbf::enable_egprs()
{
m_window.set_sns(RLC_EGPRS_SNS);
gprs_rlcmac_tbf::enable_egprs();
}
inline gprs_rlcmac_ul_tbf *as_ul_tbf(gprs_rlcmac_tbf *tbf)
{
if (tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
return static_cast<gprs_rlcmac_ul_tbf *>(tbf);
else
return NULL;
}
inline gprs_rlcmac_dl_tbf *as_dl_tbf(gprs_rlcmac_tbf *tbf)
{
if (tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
return static_cast<gprs_rlcmac_dl_tbf *>(tbf);
else
return NULL;
}
uint16_t egprs_window_size(const struct gprs_rlcmac_bts *bts_data, uint8_t slots);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -28,10 +28,21 @@
#include <gprs_bssgp_pcu.h>
#include <decoding.h>
#include <pcu_l1_if.h>
#include <gprs_coding_scheme.h>
#include <gprs_ms.h>
#include <llc.h>
#include "pcu_utils.h"
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/utils.h>
#include <osmocom/gprs/gprs_bssgp_bss.h>
#include <osmocom/gprs/protocol/gsm_08_18.h>
#include <osmocom/gsm/tlv.h>
}
#include <errno.h>
@@ -42,7 +53,6 @@ extern "C" {
extern void *tall_pcu_ctx;
/*
* Store received block data in LLC message(s) and forward to SGSN
* if complete.
@@ -51,157 +61,41 @@ 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 rlc_ul_header *rh = (const struct rlc_ul_header *) data;
uint8_t e, m;
struct rlc_li_field *li;
uint8_t frame_offset[16], offset = 0, chunk;
int i, frames = 0;
const struct gprs_rlc_data_block_info *rdbi = &_data->block_info;
GprsCodingScheme cs = _data->cs_last;
LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
Decoding::RlcData frames[16], *frame;
int i, num_frames = 0;
uint32_t dummy_tlli;
data += 3;
len -= 3;
e = rh->e; /* if extended */
m = 1; /* more frames, that means: the first frame */
LOGPTBFUL(this, LOGL_DEBUG, "Assembling frames: (len=%d)\n", len);
/* Parse frame offsets from length indicator(s), if any. */
while (1) {
if (frames == (int)sizeof(frame_offset)) {
LOGP(DRLCMACUL, LOGL_ERROR, "%s too many frames in "
"block\n", tbf_name(this));
return -EINVAL;
}
frame_offset[frames++] = offset;
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset "
"%d\n", frames, offset);
if (!len)
break;
/* M == 0 and E == 0 is not allowed in this version. */
if (!m && !e) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA "
"ignored, because M='0' and E='0'.\n",
tbf_name(this));
return 0;
}
/* no more frames in this segment */
if (e) {
break;
}
/* There is a new frame and an LI that delimits it. */
if (m) {
li = (struct rlc_li_field *)data;
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n",
li->li);
/* Special case: LI == 0
* If the last segment would fit precisely into the
* rest of the RLC MAC block, there would be no way
* to delimit that this segment ends and is not
* continued in the next block.
* The special LI (0) is used to force the segment to
* extend into the next block, so it is delimited there.
* This LI must be skipped. Also it is the last LI.
*/
if (li->li == 0) {
data++;
len--;
m = 1; /* M is ignored, we know there is more */
break; /* handle E as '1', so we break! */
}
e = li->e;
m = li->m;
offset += li->li;
data++;
len--;
continue;
}
}
if (!m) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare "
"data\n");
}
LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n",
len);
/* TLLI */
if (rh->ti) {
if (len < 4) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA TLLI out of "
"frame border\n", tbf_name(this));
return -EINVAL;
}
data += 4;
len -= 4;
LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: "
"%d\n", len);
}
/* PFI */
if (rh->pi) {
LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
"please disable in SYSTEM INFORMATION\n");
if (len < 1) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA PFI out of "
"frame border\n", tbf_name(this));
return -EINVAL;
}
data++;
len--;
LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: "
"%d\n", len);
}
/* Now we have:
* - a list of frames offsets: frame_offset[]
* - number of frames: i
* - m == 0: Last frame carries spare data (end of TBF).
*/
/* Check if last offset would exceed frame. */
if (offset > len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA ignored, "
"because LI delimits data that exceeds block size.\n",
tbf_name(this));
return -EINVAL;
}
num_frames = Decoding::rlc_data_from_ul_data(
rdbi, cs, data, &(frames[0]), ARRAY_SIZE(frames),
&dummy_tlli);
/* create LLC frames */
for (i = 0; i < frames; i++) {
/* last frame ? */
if (i == frames - 1) {
/* no more data in last frame */
if (!m)
break;
/* data until end of frame */
chunk = len - frame_offset[i];
} else {
/* data until next frame */
chunk = frame_offset[i + 1] - frame_offset[i];
for (i = 0; i < num_frames; i++) {
frame = frames + i;
if (frame->length) {
bts->rlc_ul_payload_bytes(frame->length);
LOGPTBFUL(this, 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 (!m_llc.fits_in_current_frame(chunk)) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s LLC frame exceeds "
"maximum size %u.\n", tbf_name(this),
m_llc.remaining_space());
chunk = m_llc.remaining_space();
}
m_llc.append_frame(data + frame_offset[i], chunk);
m_llc.consume(chunk);
/* not last frame. */
if (i != frames - 1) {
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();
m_llc.reset();
/* also check if CV==0, because the frame may fill up the
* block precisely, then it is also complete. normally the
* frame would be extended into the next block with a 0-length
* delimiter added to this block. */
} else if (rh->cv == 0) {
/* send frame to SGSN */
LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame "
"that fits precisely in last block: "
"len=%d\n", tbf_name(this), m_llc.frame_length());
LOGPTBFUL(this, LOGL_DEBUG, "complete UL frame len=%d\n", m_llc.frame_length());
snd_ul_ud();
bts->llc_ul_bytes(m_llc.frame_length());
m_llc.reset();
}
}
@@ -209,73 +103,87 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
return 0;
}
bool gprs_rlcmac_ul_tbf::ctrl_ack_to_toggle()
{
if (check_n_clear(GPRS_RLCMAC_FLAG_TO_UL_ACK))
return true; /* GPRS_RLCMAC_FLAG_TO_UL_ACK was set, now cleared */
struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn)
state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ACK);
return false; /* GPRS_RLCMAC_FLAG_TO_UL_ACK was unset, now set */
}
bool gprs_rlcmac_ul_tbf::handle_ctrl_ack()
{
/* check if this control ack belongs to packet uplink ack */
if (ul_ack_state_is(GPRS_RLCMAC_UL_ACK_WAIT_ACK)) {
TBF_SET_ACK_STATE(this, GPRS_RLCMAC_UL_ACK_NONE);
return true;
}
return false;
}
struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn, uint8_t ts)
{
int final = (state_is(GPRS_RLCMAC_FINISHED));
struct msgb *msg;
int rc;
unsigned int rrbp = 0;
uint32_t new_poll_fn = 0;
if (final) {
if (poll_state != GPRS_RLCMAC_POLL_NONE) {
LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
"sheduled for %s, so we must wait for "
"final uplink ack...\n", tbf_name(this));
if (poll_scheduled() && ul_ack_state_is(GPRS_RLCMAC_UL_ACK_WAIT_ACK)) {
LOGPTBFUL(this, LOGL_DEBUG,
"Polling is already scheduled, so we must wait for the final uplink ack...\n");
return NULL;
}
if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) {
LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
"scheduled for single block allocation...\n");
rc = check_polling(fn, ts, &new_poll_fn, &rrbp);
if (rc < 0)
return NULL;
}
}
msg = msgb_alloc(23, "rlcmac_ul_ack");
if (!msg)
return NULL;
bitvec *ack_vec = bitvec_alloc(23);
bitvec *ack_vec = bitvec_alloc(23, tall_pcu_ctx);
if (!ack_vec) {
msgb_free(msg);
return NULL;
}
bitvec_unhex(ack_vec,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
Encoding::write_packet_uplink_ack(bts_data(), mac_control_block, this, final);
encode_gsm_rlcmac_downlink(ack_vec, mac_control_block);
Encoding::write_packet_uplink_ack(bts_data(), ack_vec, this, final, rrbp);
bitvec_pack(ack_vec, msgb_put(msg, 23));
bitvec_free(ack_vec);
talloc_free(mac_control_block);
/* now we must set this flag, so we are allowed to assign downlink
* TBF on PACCH. it is only allowed when TLLI is acknowledged. */
m_contention_resolution_done = 1;
if (final) {
poll_state = GPRS_RLCMAC_POLL_SCHED;
poll_fn = (fn + 13) % 2715648;
set_polling(new_poll_fn, ts, GPRS_RLCMAC_POLL_UL_ACK);
/* waiting for final acknowledge */
ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK;
m_final_ack_sent = 1;
} else
ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
TBF_SET_ACK_STATE(this, GPRS_RLCMAC_UL_ACK_NONE);
return msg;
}
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data,
size_t len, struct pcu_l1_meas *meas)
/*! \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)
{
struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
int rc;
int8_t rssi = meas->have_rssi ? meas->rssi : 0;
const uint16_t mod_sns = m_window.mod_sns();
const uint16_t ws = m_window.ws();
this->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA);
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. "
"V(R)=%d)\n", rh->tfi, this->m_window.v_q(),
LOGPTBFUL(this, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. "
"V(R)=%d)\n", rlc->tfi, this->m_window.v_q(),
this->m_window.v_r());
/* process RSSI */
@@ -285,113 +193,190 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data,
if (ms())
ms()->update_l1_meas(meas);
/* get TLLI */
if (!this->is_tlli_valid()) {
if (!extract_tlli(data, len))
return 0;
/* already have TLLI, but we stille get another one */
} else if (rh->ti) {
uint32_t tlli;
rc = Decoding::tlli_from_ul_data(data, len, &tlli);
if (rc) {
LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI "
"of UL DATA TFI=%d.\n", rh->tfi);
return 0;
}
if (tlli != this->tlli()) {
LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL "
"DATA TFI=%d. (Ignoring due to contention "
"resolution)\n", rh->tfi);
return 0;
}
}
uint32_t new_tlli = 0;
unsigned int block_idx;
/* restart T3169 */
tbf_timer_start(this, 3169, bts_data()->t3169, 0);
T_START(this, T3169, bts_data()->t3169, 0, "acked (data)", true);
/* 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;
uint8_t *rlc_data;
const struct gprs_rlc_data_block_info *rdbi =
&rlc->block_info[block_idx];
bool need_rlc_data = false;
struct gprs_rlc_data *block;
if (!m_window.is_in_window(rh->bsn)) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window "
"%d..%d (it's normal)\n", rh->bsn,
m_window.v_q(),
(m_window.v_q() + ws - 1) & mod_sns);
maybe_schedule_uplink_acknack(rh);
return 0;
LOGPTBFUL(this, LOGL_DEBUG,
"Got %s RLC data block: CV=%d, BSN=%d, SPB=%d, PI=%d, E=%d, TI=%d, bitoffs=%d\n",
rlc->cs.name(),
rdbi->cv, rdbi->bsn, rdbi->spb,
rdbi->pi, rdbi->e, rdbi->ti,
rlc->data_offs_bits[block_idx]);
/* Check whether the block needs to be decoded */
if (!m_window.is_in_window(rdbi->bsn)) {
LOGPTBFUL(this, LOGL_DEBUG, "BSN %d out of window %d..%d (it's normal)\n",
rdbi->bsn,
m_window.v_q(), m_window.mod_sns(m_window.v_q() + ws - 1));
} else if (m_window.is_received(rdbi->bsn)) {
LOGPTBFUL(this, LOGL_DEBUG,
"BSN %d already received\n", rdbi->bsn);
} else {
need_rlc_data = true;
}
if (!is_tlli_valid()) {
if (!rdbi->ti) {
LOGPTBFUL(this, LOGL_NOTICE, "Missing TLLI within UL DATA.\n");
continue;
}
need_rlc_data = true;
}
if (!need_rlc_data)
continue;
/* Store block and meta info to BSN buffer */
LOGPTBFUL(this, LOGL_DEBUG, "BSN %d storing in window (%d..%d)\n",
rdbi->bsn, m_window.v_q(),
m_window.mod_sns(m_window.v_q() + ws - 1));
block = m_rlc.block(rdbi->bsn);
OSMO_ASSERT(rdbi->data_len <= sizeof(block->block));
rlc_data = &(block->block[0]);
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);
}
LOGPTBFUL(this, LOGL_DEBUG,
"data_length=%d, data=%s\n",
block->len, osmo_hexdump(rlc_data, block->len));
/* Get/Handle TLLI */
if (rdbi->ti) {
num_chunks = Decoding::rlc_data_from_ul_data(
rdbi, rlc->cs, rlc_data, NULL, 0, &new_tlli);
if (num_chunks < 0) {
bts->decode_error();
LOGPTBFUL(this, LOGL_NOTICE,
"Failed to decode TLLI of %s UL DATA TFI=%d.\n",
rlc->cs.name(), rlc->tfi);
m_window.invalidate_bsn(rdbi->bsn);
continue;
}
if (!this->is_tlli_valid()) {
if (!new_tlli) {
LOGPTBFUL(this, LOGL_NOTICE,
"TLLI = 0 within UL DATA.\n");
m_window.invalidate_bsn(rdbi->bsn);
continue;
}
LOGPTBFUL(this, LOGL_INFO,
"Decoded premier TLLI=0x%08x of UL DATA TFI=%d.\n",
tlli(), rlc->tfi);
set_tlli_from_ul(new_tlli);
} else if (new_tlli && new_tlli != tlli()) {
LOGPTBFUL(this, LOGL_NOTICE,
"TLLI mismatch on UL DATA TFI=%d. (Ignoring due to contention resolution)\n",
rlc->tfi);
m_window.invalidate_bsn(rdbi->bsn);
continue;
}
}
m_window.receive_bsn(rdbi->bsn);
}
/* Write block to buffer and set receive state array. */
m_rlc.block(rh->bsn)->put_data(data, len);
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n",
rh->bsn, m_window.v_q(),
(m_window.v_q() + ws - 1) & mod_sns);
/* Raise V(Q) if possible, and retrieve LLC frames from blocks.
* This is looped until there is a gap (non received block) or
* the window is empty.*/
const uint16_t v_q_beg = m_window.v_q();
const uint16_t count = m_window.receive_bsn(rh->bsn);
const uint16_t count = m_window.raise_v_q();
/* Retrieve LLC frames from blocks that are ready */
for (uint16_t i = 0; i < count; ++i) {
uint16_t index = (v_q_beg + i) & mod_sns;
uint16_t index = m_window.mod_sns(v_q_beg + i);
assemble_forward_llc(m_rlc.block(index));
}
/* Check CV of last frame in buffer */
if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */
&& this->m_window.v_q() == this->m_window.v_r()) { /* if complete */
struct rlc_ul_header *last_rh = (struct rlc_ul_header *)
m_rlc.block((m_window.v_r() - 1) & mod_sns)->block;
LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, "
"last block: BSN=%d CV=%d\n", last_rh->bsn,
last_rh->cv);
if (last_rh->cv == 0) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL "
"TBF\n");
set_state(GPRS_RLCMAC_FINISHED);
struct gprs_rlc_data *block =
m_rlc.block(m_window.mod_sns(m_window.v_r() - 1));
const struct gprs_rlc_data_block_info *rdbi =
&block->block_info;
LOGPTBFUL(this, LOGL_DEBUG,
"No gaps in received block, last block: BSN=%d CV=%d\n",
rdbi->bsn, rdbi->cv);
if (rdbi->cv == 0) {
LOGPTBFUL(this, LOGL_DEBUG, "Finished with UL TBF\n");
TBF_SET_STATE(this, GPRS_RLCMAC_FINISHED);
/* Reset N3103 counter. */
this->m_n3103 = 0;
this->n_reset(N3103);
}
}
/* If TLLI is included or if we received half of the window, we send
* an ack/nack */
maybe_schedule_uplink_acknack(rh);
maybe_schedule_uplink_acknack(rlc);
return 0;
}
void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh)
void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(
const gprs_rlc_data_info *rlc)
{
if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED)
|| (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
if (rh->si) {
LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
"because MS is stalled.\n");
bool have_ti = rlc->block_info[0].ti ||
(rlc->num_data_blocks > 1 && rlc->block_info[1].ti);
if (rlc->si || have_ti || state_is(GPRS_RLCMAC_FINISHED) ||
(m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0)
{
if (rlc->si) {
LOGPTBFUL(this, LOGL_NOTICE,
"Scheduling Ack/Nack, because MS is stalled.\n");
}
if (rh->ti) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because TLLI is included.\n");
if (have_ti) {
LOGPTBFUL(this, LOGL_DEBUG,
"Scheduling Ack/Nack, because TLLI is included.\n");
}
if (state_is(GPRS_RLCMAC_FINISHED)) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because last block has CV==0.\n");
LOGPTBFUL(this, LOGL_DEBUG,
"Scheduling Ack/Nack, because last block has CV==0.\n");
}
if ((m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because %d frames received.\n",
SEND_ACK_AFTER_FRAMES);
LOGPTBFUL(this, LOGL_DEBUG,
"Scheduling Ack/Nack, because %d frames received.\n",
SEND_ACK_AFTER_FRAMES);
}
if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) {
if (ul_ack_state_is(GPRS_RLCMAC_UL_ACK_NONE)) {
/* trigger sending at next RTS */
ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
TBF_SET_ACK_STATE(this, GPRS_RLCMAC_UL_ACK_SEND_ACK);
} else {
/* already triggered */
LOGP(DRLCMACUL, LOGL_DEBUG, "- Sending Ack/Nack is "
"already triggered, don't schedule!\n");
LOGPTBFUL(this, LOGL_DEBUG,
"Sending Ack/Nack is already triggered, don't schedule!\n");
}
}
}
@@ -423,3 +408,193 @@ 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) {
LOGPTBFUL(this, LOGL_DEBUG,
"Second seg is received first seg is already present set the status to complete\n");
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) {
LOGPTBFUL(this, LOGL_DEBUG,
"Second seg is received first seg is not received set the status to second seg received\n");
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data,
rlc_data + 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) {
LOGPTBFUL(this, LOGL_DEBUG,
"First seg is received second seg is already present set the status to complete\n");
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) {
LOGPTBFUL(this, LOGL_DEBUG,
"First seg is received second seg is not received set the status to first seg received\n");
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];
LOGPTBFUL(this, LOGL_DEBUG,
"Got SPB(%d) cs(%s) data block with BSN (%d), TFI(%d).\n",
rdbi->spb, rlc->cs.name(), rdbi->bsn, rlc->tfi);
egprs_rlc_ul_reseg_bsn_state 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 {
LOGPTBFUL(this, LOGL_ERROR,
"spb(%d) Not supported SPB for this EGPRS configuration\n",
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;
LOGPTBFUL(this, LOGL_DEBUG, "Upgrading to MCS6\n");
break;
case GprsCodingScheme::MCS2 :
block->cs_last = GprsCodingScheme::MCS5;
LOGPTBFUL(this, LOGL_DEBUG, "Upgrading to MCS5\n");
break;
case GprsCodingScheme::MCS1 :
LOGPTBFUL(this, LOGL_DEBUG, "Upgrading to MCS4\n");
block->cs_last = GprsCodingScheme::MCS4;
break;
default:
LOGPTBFUL(this, LOGL_ERROR,
"cs(%s) Error in Upgrading to higher MCS\n",
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::set_window_size()
{
uint16_t ws = egprs_window_size(bts->bts_data(), ul_slots());
LOGPTBFUL(this, LOGL_INFO, "setting EGPRS UL window size to %u, base(%u) slots(%u) ws_pdch(%u)\n",
ws, bts->bts_data()->ws_base, pcu_bitcount(ul_slots()), bts->bts_data()->ws_pdch);
m_window.set_ws(ws);
}

View File

@@ -1,7 +1,7 @@
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/ -I$(top_srcdir)/include/
AM_LDFLAGS = -lrt
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest alloc/MslotTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test edge/EdgeTest bitcomp/BitcompTest fn/FnTest
noinst_PROGRAMS = emu/pcu_emu
rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp
@@ -18,6 +18,14 @@ alloc_AllocTest_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
alloc_MslotTest_SOURCES = alloc/MslotTest.cpp
alloc_MslotTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
tbf_TbfTest_SOURCES = tbf/TbfTest.cpp
tbf_TbfTest_LDADD = \
$(top_builddir)/src/libgprs.la \
@@ -25,6 +33,21 @@ tbf_TbfTest_LDADD = \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
tbf_TbfTest_LDFLAGS = -Wl,--wrap=pcu_sock_send
bitcomp_BitcompTest_SOURCES = bitcomp/BitcompTest.cpp ../src/egprs_rlc_compression.cpp
bitcomp_BitcompTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
edge_EdgeTest_SOURCES = edge/EdgeTest.cpp
edge_EdgeTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
emu_pcu_emu_SOURCES = emu/pcu_emu.cpp emu/test_replay_gprs_attach.cpp \
emu/openbsc_clone.c emu/openbsc_clone.h emu/gprs_tests.h \
@@ -77,6 +100,14 @@ codel_codel_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
fn_FnTest_SOURCES = fn/FnTest.cpp
fn_FnTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
@@ -100,11 +131,14 @@ 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 \
ms/MsTest.ok ms/MsTest.err alloc/MslotTest.ok \
llc/LlcTest.ok llc/LlcTest.err \
llist/LListTest.ok llist/LListTest.err \
codel/codel_test.ok
codel/codel_test.ok \
edge/EdgeTest.ok \
fn/FnTest.ok
DISTCLEANFILES = atconfig

View File

@@ -26,6 +26,7 @@
#include <stdio.h>
extern "C" {
#include "mslot_class.h"
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
@@ -35,38 +36,41 @@ extern "C" {
/* globals used by the code */
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
bool spoof_mnc_3_digits = false;
static gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_bts *bts,
GprsMs *ms, gprs_rlcmac_tbf_direction dir,
uint8_t use_trx,
uint8_t ms_class, uint8_t single_slot)
uint8_t ms_class, uint8_t egprs_ms_class, bool single_slot)
{
if (dir == GPRS_RLCMAC_UL_TBF)
return tbf_alloc_ul_tbf(bts, ms, use_trx, ms_class, single_slot);
return tbf_alloc_ul_tbf(bts, ms, use_trx,
ms_class, egprs_ms_class, single_slot);
else
return tbf_alloc_dl_tbf(bts, ms, use_trx, ms_class, single_slot);
return tbf_alloc_dl_tbf(bts, ms, use_trx,
ms_class, egprs_ms_class, single_slot);
}
static void check_tfi_usage(BTS *the_bts)
{
int pdch_no;
struct gprs_rlcmac_bts *bts = the_bts->bts_data();
struct gprs_rlcmac_tbf *tfi_usage[8][8][2][32] = {{{{NULL}}}};
struct llist_head *tbf_lists[2] = {
&bts->ul_tbfs,
&bts->dl_tbfs
LListHead<gprs_rlcmac_tbf> *tbf_lists[2] = {
&the_bts->ul_tbfs(),
&the_bts->dl_tbfs()
};
LListHead<gprs_rlcmac_tbf> *pos;
gprs_rlcmac_tbf *tbf;
struct llist_pods *lpods;
unsigned list_idx;
struct gprs_rlcmac_tbf **tbf_var;
for (list_idx = 0; list_idx < ARRAY_SIZE(tbf_lists); list_idx += 1)
{
llist_pods_for_each_entry(tbf, tbf_lists[list_idx], list, lpods) {
llist_for_each(pos, tbf_lists[list_idx]) {
tbf = pos->entry();
for (pdch_no = 0; pdch_no < 8; pdch_no += 1) {
struct gprs_rlcmac_pdch *pdch = tbf->pdch[pdch_no];
if (pdch == NULL)
@@ -131,7 +135,7 @@ static void test_alloc_a(gprs_rlcmac_tbf_direction dir,
* least this part is working okay.
*/
for (i = 0; i < (int)ARRAY_SIZE(tbfs); ++i) {
tbfs[i] = tbf_alloc(bts, NULL, dir, -1, 0, 0);
tbfs[i] = tbf_alloc(bts, NULL, dir, -1, 0, 0, 0);
if (tbfs[i] == NULL)
break;
@@ -148,9 +152,9 @@ static void test_alloc_a(gprs_rlcmac_tbf_direction dir,
if (tbfs[i])
tbf_free(tbfs[i]);
tbfs[tfi] = tbf_alloc(bts, NULL, dir, -1, 0, 0);
OSMO_ASSERT(tbfs[tfi]);
tbf_free(tbfs[tfi]);
tbfs[0] = tbf_alloc(bts, NULL, dir, -1, 0, 0, 0);
OSMO_ASSERT(tbfs[0]);
tbf_free(tbfs[0]);
}
static void test_alloc_a()
@@ -164,17 +168,177 @@ static void test_alloc_a()
test_alloc_a(GPRS_RLCMAC_UL_TBF, 0x1e, 28);
}
static void dump_assignment(struct gprs_rlcmac_tbf *tbf, const char *dir)
static void dump_assignment(struct gprs_rlcmac_tbf *tbf, const char *dir, bool verbose)
{
if (!verbose)
return;
for (size_t i = 0; i < ARRAY_SIZE(tbf->pdch); ++i)
if (tbf->pdch[i])
printf("PDCH[%d] is used for %s\n", i, dir);
printf("PDCH[%zu] is used for %s\n", i, dir);
printf("PDCH[%d] is control_ts for %s\n", tbf->control_ts, dir);
printf("PDCH[%d] is first common for %s\n", tbf->first_common_ts, dir);
}
static void test_alloc_b(int ms_class)
#define ENABLE_PDCH(ts_no, enable_flag, trx) \
if (enable_flag) \
trx->pdch[ts_no].enable();
static inline void enable_ts_on_bts(struct gprs_rlcmac_bts *bts,
bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7)
{
struct gprs_rlcmac_trx *trx = &bts->trx[0];
ENABLE_PDCH(0, ts0, trx);
ENABLE_PDCH(1, ts1, trx);
ENABLE_PDCH(2, ts2, trx);
ENABLE_PDCH(3, ts3, trx);
ENABLE_PDCH(4, ts4, trx);
ENABLE_PDCH(5, ts5, trx);
ENABLE_PDCH(6, ts6, trx);
ENABLE_PDCH(7, ts7, trx);
}
static inline bool test_alloc_b_ul_dl(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7,
uint8_t ms_class, bool verbose)
{
BTS the_bts;
struct gprs_rlcmac_bts *bts = the_bts.bts_data();
gprs_rlcmac_ul_tbf *ul_tbf;
gprs_rlcmac_dl_tbf *dl_tbf;
if (verbose)
printf("Testing UL then DL assignment.\n");
bts->alloc_algorithm = alloc_algorithm_b;
enable_ts_on_bts(bts, ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7);
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 0, true);
if (!ul_tbf)
return false;
OSMO_ASSERT(ul_tbf->ms());
OSMO_ASSERT(ul_tbf->ms()->current_trx());
dump_assignment(ul_tbf, "UL", verbose);
/* assume final ack has not been sent */
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), ul_tbf->ms()->current_trx()->trx_no, ms_class, 0,
false);
if (!dl_tbf)
return false;
dump_assignment(dl_tbf, "DL", verbose);
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
return true;
}
static inline bool test_alloc_b_dl_ul(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7,
uint8_t ms_class, bool verbose)
{
BTS the_bts;
struct gprs_rlcmac_bts *bts = the_bts.bts_data();
gprs_rlcmac_ul_tbf *ul_tbf;
gprs_rlcmac_dl_tbf *dl_tbf;
if (verbose)
printf("Testing DL then UL assignment followed by update\n");
bts->alloc_algorithm = alloc_algorithm_b;
enable_ts_on_bts(bts, ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7);
dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 0, true);
if (!dl_tbf)
return false;
dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
OSMO_ASSERT(dl_tbf->ms());
OSMO_ASSERT(dl_tbf->ms()->current_trx());
dump_assignment(dl_tbf, "DL", verbose);
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), dl_tbf->ms()->current_trx()->trx_no, ms_class, 0,
false);
if (!ul_tbf)
return false;
ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
ul_tbf->m_contention_resolution_done = 1;
dump_assignment(ul_tbf, "UL", verbose);
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
/* now update the dl_tbf */
dl_tbf->update();
dump_assignment(dl_tbf, "DL", verbose);
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
return true;
}
static inline bool test_alloc_b_jolly(uint8_t ms_class)
{
BTS the_bts;
struct gprs_rlcmac_bts *bts = the_bts.bts_data();
int tfi;
uint8_t trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
printf("Testing jolly example\n");
bts->alloc_algorithm = alloc_algorithm_b;
enable_ts_on_bts(bts, false, true, true, true, true, false, false, false);
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, .1, ms_class, 0, false);
if (!ul_tbf)
return false;
OSMO_ASSERT(ul_tbf->ms());
OSMO_ASSERT(ul_tbf->ms()->current_trx());
trx_no = ul_tbf->ms()->current_trx()->trx_no;
dump_assignment(ul_tbf, "UL", true);
/* assume final ack has not been sent */
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0,
false);
if (!dl_tbf)
return false;
dump_assignment(dl_tbf, "DL", true);
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
return true;
}
static void test_alloc_b_for_ms(uint8_t ms_class)
{
bool rc;
printf("Going to test multislot assignment MS_CLASS=%d\n", ms_class);
/*
* PDCH is on TS 6,7,8 and we start with a UL allocation and
@@ -182,143 +346,26 @@ static void test_alloc_b(int ms_class)
*
* Uplink assigned and still available..
*/
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
printf("Testing UL then DL assignment.\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 1);
OSMO_ASSERT(ul_tbf);
OSMO_ASSERT(ul_tbf->ms());
OSMO_ASSERT(ul_tbf->ms()->current_trx());
trx_no = ul_tbf->ms()->current_trx()->trx_no;
dump_assignment(ul_tbf, "UL");
/* assume final ack has not been sent */
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0);
OSMO_ASSERT(dl_tbf);
dump_assignment(dl_tbf, "DL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
rc = test_alloc_b_ul_dl(false, false, false, false, false, true, true, true, ms_class, true);
if (!rc)
return;
/**
* Test with the other order.. first DL and then UL
*/
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t trx_no;
gprs_rlcmac_ul_tbf *ul_tbf;
gprs_rlcmac_dl_tbf *dl_tbf;
printf("Testing DL then UL assignment followed by update\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 1);
dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
OSMO_ASSERT(dl_tbf);
OSMO_ASSERT(dl_tbf->ms());
OSMO_ASSERT(dl_tbf->ms()->current_trx());
trx_no = dl_tbf->ms()->current_trx()->trx_no;
dump_assignment(dl_tbf, "DL");
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), trx_no, ms_class, 0);
ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
ul_tbf->m_contention_resolution_done = 1;
OSMO_ASSERT(ul_tbf);
dump_assignment(ul_tbf, "UL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
/* now update the dl_tbf */
dl_tbf->update();
dump_assignment(dl_tbf, "DL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
rc = test_alloc_b_dl_ul(false, false, false, false, false, true, true, true, ms_class, true);
if (!rc)
return;
/* Andreas osmocom-pcu example */
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int tfi;
uint8_t trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
printf("Testing jolly example\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[1].enable();
trx->pdch[2].enable();
trx->pdch[3].enable();
trx->pdch[4].enable();
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, .1, ms_class, 0);
OSMO_ASSERT(ul_tbf);
OSMO_ASSERT(ul_tbf->ms());
OSMO_ASSERT(ul_tbf->ms()->current_trx());
trx_no = ul_tbf->ms()->current_trx()->trx_no;
dump_assignment(ul_tbf, "UL");
/* assume final ack has not been sent */
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0);
OSMO_ASSERT(dl_tbf);
dump_assignment(dl_tbf, "DL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
test_alloc_b_jolly(ms_class);
}
#define ENABLE_PDCH(ts_no, enable_flag, trx) \
if (enable_flag) \
trx->pdch[ts_no].enable();
static void test_alloc_b(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7, int ms_class)
static void test_alloc_mass(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7, int ms_class)
{
bool rc;
/* we can test the allocation failures differently */
if (!ts0 && !ts1 && !ts2 && !ts3 && !ts4 && !ts5 && !ts6 && !ts7)
return;
@@ -334,97 +381,14 @@ static void test_alloc_b(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool
ts7 ? 'O' : 'x', ms_class);
fflush(stdout);
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
ENABLE_PDCH(0, ts0, trx);
ENABLE_PDCH(1, ts1, trx);
ENABLE_PDCH(2, ts2, trx);
ENABLE_PDCH(3, ts3, trx);
ENABLE_PDCH(4, ts4, trx);
ENABLE_PDCH(5, ts5, trx);
ENABLE_PDCH(6, ts6, trx);
ENABLE_PDCH(7, ts7, trx);
ul_tbf = tbf_alloc_ul_tbf(bts, NULL, -1, ms_class, 1);
OSMO_ASSERT(ul_tbf->ms());
OSMO_ASSERT(ul_tbf->ms()->current_trx());
trx_no = ul_tbf->ms()->current_trx()->trx_no;
OSMO_ASSERT(ul_tbf);
/* assume final ack has not been sent */
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), trx_no, ms_class, 0);
OSMO_ASSERT(dl_tbf);
/* verify that both are on the same ts */
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
rc = test_alloc_b_ul_dl(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class, false);
if (!rc)
return;
/**
* Test with the other order.. first DL and then UL
*/
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t trx_no;
gprs_rlcmac_ul_tbf *ul_tbf;
gprs_rlcmac_dl_tbf *dl_tbf;
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
ENABLE_PDCH(0, ts0, trx);
ENABLE_PDCH(1, ts1, trx);
ENABLE_PDCH(2, ts2, trx);
ENABLE_PDCH(3, ts3, trx);
ENABLE_PDCH(4, ts4, trx);
ENABLE_PDCH(5, ts5, trx);
ENABLE_PDCH(6, ts6, trx);
ENABLE_PDCH(7, ts7, trx);
dl_tbf = tbf_alloc_dl_tbf(bts, NULL, -1, ms_class, 1);
OSMO_ASSERT(dl_tbf);
OSMO_ASSERT(dl_tbf->ms());
OSMO_ASSERT(dl_tbf->ms()->current_trx());
trx_no = dl_tbf->ms()->current_trx()->trx_no;
dl_tbf->update_ms(0x23, GPRS_RLCMAC_DL_TBF);
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), trx_no, ms_class, 0);
OSMO_ASSERT(ul_tbf);
ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
ul_tbf->m_contention_resolution_done = 1;
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
/* now update the dl_tbf */
dl_tbf->update();
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
OSMO_ASSERT(ul_tbf->ms_class() == ms_class);
OSMO_ASSERT(dl_tbf->ms_class() == ms_class);
check_tfi_usage(&the_bts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
test_alloc_b_dl_ul(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class, false);
}
static void test_all_alloc_b()
@@ -438,22 +402,20 @@ static void test_all_alloc_b()
for (uint8_t ts5 = 0; ts5 < 2; ++ts5)
for (uint8_t ts6 = 0; ts6 < 2; ++ts6)
for (uint8_t ts7 = 0; ts7 < 2; ++ts7)
for (int ms_class = 0; ms_class < 30; ++ms_class)
test_alloc_b(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class);
for (int ms_class = 0; ms_class < mslot_class_max(); ++ms_class)
test_alloc_mass(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class);
}
static void test_alloc_b()
{
for (int i = 0; i < 30; ++i)
test_alloc_b(i);
for (int i = 0; i < mslot_class_max(); ++i)
test_alloc_b_for_ms(i);
test_all_alloc_b();
}
typedef int (*algo_t)(struct gprs_rlcmac_bts *bts,
struct GprsMs *ms,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single,
int use_trx);
typedef int (*algo_t)(struct gprs_rlcmac_bts *bts, struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, bool single,
int8_t use_trx);
static char get_dir_char(uint8_t mask, uint8_t tx, uint8_t rx, uint8_t busy)
{
@@ -473,6 +435,19 @@ enum test_mode {
TEST_MODE_UL_AFTER_DL,
};
static inline char *test_mode_descr(enum test_mode t)
{
switch (t) {
case TEST_MODE_UL_ONLY: return (char*)"UL only";
case TEST_MODE_DL_ONLY: return (char*)"DL only";
case TEST_MODE_UL_AND_DL: return (char*)"UL and DL";
case TEST_MODE_DL_AND_UL: return (char*)"DL and UL";
case TEST_MODE_DL_AFTER_UL: return (char*)"DL after UL";
case TEST_MODE_UL_AFTER_DL: return (char*)"UL after DL";
default: return NULL;
}
}
static GprsMs *alloc_tbfs(BTS *the_bts, GprsMs *ms, unsigned ms_class,
enum test_mode mode)
{
@@ -495,7 +470,7 @@ static GprsMs *alloc_tbfs(BTS *the_bts, GprsMs *ms, unsigned ms_class,
case TEST_MODE_UL_AND_DL:
if (ms && ms->ul_tbf())
tbf_free(ms->ul_tbf());
tbf = tbf_alloc_ul_tbf(bts, ms, trx_no, ms_class, 0);
tbf = tbf_alloc_ul_tbf(bts, ms, trx_no, ms_class, 0, false);
if (tbf == NULL)
return NULL;
break;
@@ -504,7 +479,7 @@ static GprsMs *alloc_tbfs(BTS *the_bts, GprsMs *ms, unsigned ms_class,
case TEST_MODE_DL_AND_UL:
if (ms && ms->dl_tbf())
tbf_free(ms->dl_tbf());
tbf = tbf_alloc_dl_tbf(bts, ms, trx_no, ms_class, 0);
tbf = tbf_alloc_dl_tbf(bts, ms, trx_no, ms_class, 0, false);
if (tbf == NULL)
return NULL;
}
@@ -600,6 +575,10 @@ static unsigned alloc_many_tbfs(BTS *the_bts, unsigned min_class,
if (dl_tbf->pdch[i])
dl_slots |= 1 << i;
for (i = 0; ul_tbf && i < ARRAY_SIZE(ul_tbf->pdch); i += 1)
if (ul_tbf->pdch[i])
ul_slots |= 1 << i;
for (i = 0; trx && i < ARRAY_SIZE(trx->pdch); i += 1) {
struct gprs_rlcmac_pdch *pdch = &trx->pdch[i];
@@ -607,17 +586,17 @@ static unsigned alloc_many_tbfs(BTS *the_bts, unsigned min_class,
continue;
if (ul_tbf &&
pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) != 0xffffffff)
pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) != NO_FREE_TFI)
continue;
if (dl_tbf &&
pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) != 0xffffffff)
pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) != NO_FREE_TFI)
continue;
busy_slots |= 1 << i;
}
printf(" TBF[%d] class %d reserves %c%c%c%c%c%c%c%c\n",
printf(" TBF[%d] class %d reserves " OSMO_BIT_SPEC "\n",
tfi, ms_class,
get_dir_char(0x01, ul_slots, dl_slots, busy_slots),
get_dir_char(0x02, ul_slots, dl_slots, busy_slots),
@@ -654,7 +633,8 @@ static void test_successive_allocation(algo_t algo, unsigned min_class,
struct gprs_rlcmac_trx *trx;
unsigned counter;
printf("Going to test assignment with many TBF, %s\n", text);
printf("Going to test assignment with many TBF, algorithm %s class %u..%u (%s)\n",
text, min_class, max_class, test_mode_descr(mode));
bts = the_bts.bts_data();
bts->alloc_algorithm = algo;
@@ -668,9 +648,11 @@ static void test_successive_allocation(algo_t algo, unsigned min_class,
counter = alloc_many_tbfs(&the_bts, min_class, max_class, mode);
printf(" Successfully allocated %d UL TBFs\n", counter);
printf(" Successfully allocated %u UL TBFs, algorithm %s class %u..%u (%s)\n",
counter, text, min_class, max_class, test_mode_descr(mode));
if (counter != expect_num)
fprintf(stderr, " Expected %d TBFs for %s\n", expect_num, text);
fprintf(stderr, " Expected %u TBFs (got %u), algorithm %s class %u..%u (%s)\n",
expect_num, counter, text, min_class, max_class, test_mode_descr(mode));
OSMO_ASSERT(counter == expect_num);
@@ -692,7 +674,7 @@ static void test_many_connections(algo_t algo, unsigned expect_num,
TEST_MODE_DL_ONLY,
};
printf("Going to test assignment with many connections, %s\n", text);
printf("Going to test assignment with many connections, algorithm %s\n", text);
bts = the_bts.bts_data();
bts->alloc_algorithm = algo;
@@ -705,7 +687,7 @@ static void test_many_connections(algo_t algo, unsigned expect_num,
trx->pdch[7].enable();
for (i = 0; i < ARRAY_SIZE(mode_seq); i += 1) {
counter1 = alloc_many_tbfs(&the_bts, 1, 29, mode_seq[i]);
counter1 = alloc_many_tbfs(&the_bts, 1, mslot_class_max(), mode_seq[i]);
fprintf(stderr, " Allocated %d TBFs (previously %d)\n",
counter1, counter2);
@@ -727,67 +709,85 @@ static void test_many_connections(algo_t algo, unsigned expect_num,
printf(" Successfully allocated %d TBFs\n", counter1);
if (counter1 != (int)expect_num)
fprintf(stderr, " Expected %d TBFs for %s\n", expect_num, text);
fprintf(stderr, " Expected %d TBFs (got %d) for algorithm %s\n", expect_num, counter1, text);
OSMO_ASSERT(expect_num == (unsigned)counter1);
}
static void test_successive_allocation()
static inline void test_a_b_dyn(enum test_mode mode, uint8_t exp_A, uint8_t exp_B, uint8_t exp_dyn)
{
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_AND_DL,
35, "algorithm A (UL and DL)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_AND_DL,
32, "algorithm B class 10 (UL and DL)");
test_successive_allocation(alloc_algorithm_b, 12, 12, TEST_MODE_UL_AND_DL,
32, "algorithm B class 12 (UL and DL)");
test_successive_allocation(alloc_algorithm_b, 1, 12, TEST_MODE_UL_AND_DL,
32, "algorithm B class 1-12 (UL and DL)");
test_successive_allocation(alloc_algorithm_b, 1, 29, TEST_MODE_UL_AND_DL,
32, "algorithm B class 1-29 (UL and DL)");
test_successive_allocation(alloc_algorithm_dynamic, 1, 29, TEST_MODE_UL_AND_DL,
35, "algorithm dynamic class 1-29 (UL and DL)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_AND_UL,
35, "algorithm A (DL and UL)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_AND_UL,
32, "algorithm B class 10 (DL and UL)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_AND_UL,
32, "algorithm dynamic class 10 (DL and UL)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_AFTER_UL,
160, "algorithm A (DL after UL)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_AFTER_UL,
32, "algorithm B class 10 (DL after UL)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_AFTER_UL,
95, "algorithm dynamic class 10 (DL after UL)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_AFTER_DL,
35, "algorithm A (UL after DL)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_AFTER_DL,
32, "algorithm B class 10 (UL after DL)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_UL_AFTER_DL,
35, "algorithm dynamic class 10 (UL after DL)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_ONLY,
35, "algorithm A (UL only)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_ONLY,
32, "algorithm B class 10 (UL only)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_UL_ONLY,
35, "algorithm dynamic class 10 (UL only)");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_DL_ONLY,
160, "algorithm A (DL ONLY)");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_DL_ONLY,
32, "algorithm B class 10 (DL ONLY)");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, TEST_MODE_DL_ONLY,
101, "algorithm dynamic class 10 (DL ONLY)");
test_successive_allocation(alloc_algorithm_a, 1, 1, mode, exp_A, "A");
test_successive_allocation(alloc_algorithm_b, 10, 10, mode, exp_B, "B");
test_successive_allocation(alloc_algorithm_dynamic, 10, 10, mode, exp_dyn, "dynamic");
}
static void test_many_connections()
static void test_successive_allocations()
{
test_many_connections(alloc_algorithm_a, 160, "algorithm A");
test_many_connections(alloc_algorithm_b, 32, "algorithm B");
test_many_connections(alloc_algorithm_dynamic, 160, "algorithm dynamic");
test_successive_allocation(alloc_algorithm_a, 1, 1, TEST_MODE_UL_AND_DL, 35, "A");
test_successive_allocation(alloc_algorithm_b, 10, 10, TEST_MODE_UL_AND_DL, 32, "B");
test_successive_allocation(alloc_algorithm_b, 12, 12, TEST_MODE_UL_AND_DL, 32, "B");
test_successive_allocation(alloc_algorithm_b, 1, 12, TEST_MODE_UL_AND_DL, 32, "B");
test_successive_allocation(alloc_algorithm_b, 1, mslot_class_max(), TEST_MODE_UL_AND_DL, 32, "B");
test_successive_allocation(alloc_algorithm_dynamic, 1, mslot_class_max(), TEST_MODE_UL_AND_DL, 35, "dynamic");
test_a_b_dyn(TEST_MODE_DL_AND_UL, 35, 32, 32);
test_a_b_dyn(TEST_MODE_DL_AFTER_UL, 160, 32, 95);
test_a_b_dyn(TEST_MODE_UL_AFTER_DL, 35, 32, 35);
test_a_b_dyn(TEST_MODE_UL_ONLY, 35, 32, 35);
test_a_b_dyn(TEST_MODE_DL_ONLY, 160, 32, 101);
}
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,
false);
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,
false);
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)
@@ -796,8 +796,8 @@ int main(int argc, char **argv)
if (!tall_pcu_ctx)
abort();
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
msgb_talloc_ctx_init(tall_pcu_ctx, 0);
osmo_init_logging2(tall_pcu_ctx, &gprs_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
if (getenv("LOGL_DEBUG"))
@@ -805,8 +805,11 @@ int main(int argc, char **argv)
test_alloc_a();
test_alloc_b();
test_successive_allocation();
test_many_connections();
test_successive_allocations();
test_many_connections(alloc_algorithm_a, 160, "A");
test_many_connections(alloc_algorithm_b, 32, "B");
test_many_connections(alloc_algorithm_dynamic, 160, "dynamic");
test_2_consecutive_dl_tbfs();
return EXIT_SUCCESS;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

188
tests/alloc/MslotTest.cpp Normal file
View File

@@ -0,0 +1,188 @@
/* MslotTest.cpp
*
* Copyright (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "gprs_rlcmac.h"
#include "gprs_debug.h"
#include "tbf.h"
#include "bts.h"
#include <string.h>
#include <stdio.h>
#include <errno.h>
extern "C" {
#include "mslot_class.h"
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
}
/* globals used by the code */
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
bool spoof_mnc_3_digits = false;
static inline void test_all_classes(struct gprs_rlcmac_trx *trx, bool clear_masks)
{
int i, rc;
uint8_t dl_slots = 0, ul_slots = 0;
for (i = 0; i < 64; i++) {
rc = find_multi_slots(trx, i, &ul_slots, &dl_slots);
printf(" [%s] multislot class %3u - UL: " OSMO_BIT_SPEC " DL: " OSMO_BIT_SPEC " [%d]\n",
clear_masks ? "SEQ" : "ACC", i, OSMO_BIT_PRINT(ul_slots), OSMO_BIT_PRINT(dl_slots), rc);
if (rc == -EINVAL)
return;
if (clear_masks) {
dl_slots = 0;
ul_slots = 0;
}
}
}
static inline void test_multislot_total_ascending(bool seq)
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int i;
printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
bts = the_bts.bts_data();
trx = &bts->trx[0];
for (i = 0; i < 8; i++) {
printf(" Enabled PDCH %u for multislot tests...\n", i);
trx->pdch[i].enable();
test_all_classes(trx, seq);
}
}
static inline void test_multislot_total_descending(bool seq)
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int i;
printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
bts = the_bts.bts_data();
trx = &bts->trx[0];
for (i = 7; i >= 0; i--) {
printf(" Enabled PDCH %u for multislot tests...\n", i);
trx->pdch[i].enable();
test_all_classes(trx, seq);
}
}
static inline void test_multislot_middle(bool seq)
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
bts = the_bts.bts_data();
trx = &bts->trx[0];
trx->pdch[2].enable();
trx->pdch[3].enable();
trx->pdch[4].enable();
test_all_classes(trx, seq);
}
static inline void test_multislot_ends(bool seq)
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
bts = the_bts.bts_data();
trx = &bts->trx[0];
trx->pdch[0].enable();
trx->pdch[7].enable();
test_all_classes(trx, seq);
}
static inline void test_window_wrapper()
{
uint16_t i;
for (i = 0; i < 256 * 2 + 1; i++)
printf("W[%03u] -> %3u %s\n",
i, mslot_wrap_window(i), mslot_wrap_window(i) < 256 ? "OK" : "FAIL");
}
int main(int argc, char **argv)
{
tall_pcu_ctx = talloc_named_const(NULL, 1, "MslotTest context");
if (!tall_pcu_ctx)
abort();
msgb_talloc_ctx_init(tall_pcu_ctx, 0);
osmo_init_logging2(tall_pcu_ctx, &gprs_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
test_multislot_total_ascending(true);
test_multislot_total_ascending(false);
test_multislot_total_descending(true);
test_multislot_total_descending(false);
test_multislot_middle(true);
test_multislot_middle(false);
test_multislot_ends(true);
test_multislot_ends(false);
test_window_wrapper();
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(); }
}

2245
tests/alloc/MslotTest.ok Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,217 @@
#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
#define CEIL_DIV_8(x) (((x) + 7)/8)
#define _LOG(fmt, args...) \
fprintf(stderr, fmt, ## args)
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 expect_rc;
} 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, 0xc0
},
.ucmp_len = 194,
.expect_rc = 0,
},
{ .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, 0x00
},
.ucmp_len = 182,
.expect_rc = 0,
},
{ .crbb_len = 8, .cc = 1,
.crbb_data = {0x02},
.ucmp_data = {0xff, 0xff, 0xff, 0xf8},
.ucmp_len = 29,
.expect_rc = 0,
},
{ .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,
.expect_rc = 0,
},
/* 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,
.expect_rc = 0,
},
{ .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,
.expect_rc = 0,
},
/* TODO: previously marked as "Invalid inputs" but succeeds */
{ .crbb_len = 18, .cc = 1,
.crbb_data = {0x1E, 0x70, 0xc0},
.ucmp_data = {0xb0, 0x00, 0x00},
.ucmp_len = 19,
.expect_rc = 0,
},
/* Invalid inputs */
{ .crbb_len = 14, .cc = 1,
.crbb_data = {0x00, 0x1E, 0x7c},
.ucmp_data = {0x0},
.ucmp_len = 0,
.expect_rc = -1,
},
{ .crbb_len = 24, .cc = 0,
.crbb_data = {0x00, 0x00, 0x00},
.ucmp_data = {0x0},
.ucmp_len = 0,
.expect_rc = -1,
}
};
bool result_matches(const bitvec &bits, const uint8_t *exp_data, unsigned int exp_len)
{
if (bits.cur_bit != exp_len)
return false;
return (memcmp(exp_data, bits.data, CEIL_DIV_8(exp_len)) == 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;
unsigned 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++) {
memset(bits_data, 0, sizeof(bits_data));
dest.data = bits_data;
dest.data_len = sizeof(bits_data);
dest.cur_bit = 0;
_LOG("\nTest:%d\n"
"Tree based decoding:\n"
"uncompressed data = %s\n"
"len = %d\n",
itr + 1,
osmo_hexdump(test[itr].crbb_data,
CEIL_DIV_8(test[itr].crbb_len)),
test[itr].crbb_len);
rc = egprs_compress::decompress_crbb(test[itr].crbb_len,
test[itr].cc, test[itr].crbb_data, &dest);
_LOG("rc = %d\n", rc);
OSMO_ASSERT(test[itr].expect_rc == rc);
if (rc < 0) {
_LOG("Failed to decode CRBB: length %d, data %s\n",
test[itr].crbb_len,
osmo_hexdump(test[itr].crbb_data,
CEIL_DIV_8(test[itr].crbb_len)));
continue;
}
if (!result_matches(dest, test[itr].ucmp_data,
test[itr].ucmp_len)) {
_LOG("\nTree based decoding: Error\n"
"expected data = %s\n"
"expected len = %d\n",
osmo_hexdump(test[itr].ucmp_data,
CEIL_DIV_8(test[itr].ucmp_len)),
test[itr].ucmp_len);
_LOG("decoded data = %s\n"
"decoded len = %d\n",
osmo_hexdump(dest.data,
CEIL_DIV_8(dest.cur_bit)),
dest.cur_bit);
OSMO_ASSERT(0);
}
_LOG("\nexpected data = %s\n"
"expected len = %d\n",
osmo_hexdump(test[itr].ucmp_data,
CEIL_DIV_8(test[itr].ucmp_len)),
test[itr].ucmp_len);
_LOG("decoded data = %s\n"
"decoded len = %d\n",
osmo_hexdump(dest.data, CEIL_DIV_8(dest.cur_bit)),
dest.cur_bit);
}
printf("=== end %s ===\n", __func__);
}
int main(int argc, char **argv)
{
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile bitcompTest context");
if (!tall_pcu_ctx)
abort();
osmo_init_logging2(tall_pcu_ctx, &gprs_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
log_parse_category_mask(osmo_stderr_target, "DRLCMACUL,1");
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(); }
}

View File

@@ -0,0 +1,131 @@
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
rc = 0
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 c0
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 c0
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
rc = 0
expected data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 00
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 00
decoded len = 182
Test:3
Tree based decoding:
uncompressed data = 02
len = 8
Run_length = 29
rc = 0
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
rc = 0
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
rc = 0
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
rc = 0
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
rc = 0
expected data = b0 00 00
expected len = 19
decoded data = b0 00 00
decoded len = 19
Test:8
Tree based decoding:
uncompressed data = 00 1e
len = 14
rc = -1
Failed to decode CRBB: length 14, data 00 1e
Test:9
Tree based decoding:
uncompressed data = 00 00 00
len = 24
rc = -1
Failed to decode CRBB: length 24, data 00 00 00

View File

@@ -0,0 +1,2 @@
=== start test_EPDAN_decode_tree ===
=== end test_EPDAN_decode_tree ===

View File

@@ -134,7 +134,7 @@ static struct log_info info = {};
int main(int argc, char **argv)
{
osmo_init_logging(&info);
osmo_init_logging2(NULL, &info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
log_set_log_level(osmo_stderr_target, LOGL_INFO);

Some files were not shown because too many files have changed in this diff Show More