Compare commits

...

104 Commits

Author SHA1 Message Date
Pravin Kumarvel b6da682060 Update README with working commit versions of osmo components
Necessary changes for build this pcu.
2016-10-13 19:45:09 +05:30
Pravin Kumarvel 03a4786a22 Fix generation of Compressed bitmap in PUAN
correction for generation of CRBB in PUAN.
2016-10-13 19:40:53 +05:30
Pravin Kumarvel 75b8f8efd8 Fix Ul window calculation for GPRS
The problem was introduced while calculating flexible window
size for EGPRS in UL direction.
Incorrect window calculation for GPRS UL tbf is fixed
2016-10-13 19:39:42 +05:30
pravin dcb2f6b3de Update README with working commit versions of osmo components
Necessary changes for build this pcu.
2016-09-30 16:00:39 +05:30
pravin f15edb81ff EGPRS: fix for EPDAN out of window
Fix for aligning the EPDAN out of RLC transmit window is made
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

Related: OS#1789
2016-09-30 11:30:26 +05:30
pravin 99e78ab60e EGPRS: add test case to show EPDAN BSN out of window bug
This patch adds a test case test_tbf_egprs_epdan 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 ignores EPDAN
The test's expectation is corrected along with the bug
fix in a subsequent commit

Related: OS#1789
2016-09-30 11:09:38 +05:30
pravin 07b05394bd Fix: DL slot allocation based on direction configured
Currently number of TS for second DL TBF is less
compared to first DL TBF because PCU is considering the
combined capacity of DL and UL for TS allocation,
with this there is a difference in throughput between
the 2 DL TBFs. This patch enables the user to maximize
the number of DL TSs for the TBF based on the
direction configured through VTY with cfg_pcu_ts_alloc_maximise_cmd

Related: OS#1792
2016-09-30 10:29:00 +05:30
pravin 92e8b8122f Add test case for testing PUAN
This test case is for testing generation of
EGPRS PUAN. Corresponding log files .ok and .err
are modified.
2016-09-30 10:17:39 +05:30
pravin c73ae31306 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.
Test case is updated for testing the compression with bitmaps of
varying lengths.
2016-09-30 10:09:57 +05:30
pravin 93605f3b4b Fix issues in URBB generation in EGPRS PUAN
Below issue in uncompresed bitmap generation is addressed
1. Corrected the number of bits that is included in URBB
2. If length is not present, 0s are coded as remaining bits
Along with this, code is restructured to enable addition of
compression support
2016-09-30 10:08:13 +05:30
pravin d97a7c5921 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.
2016-09-30 10:06:23 +05:30
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
74 changed files with 8053 additions and 694 deletions

17
.gitignore vendored
View File

@ -14,10 +14,11 @@ config.guess
config.sub
config.status
configure
compile
depcomp
install-sh
missing
libtool
*libtool
ltmain.sh
core
@ -43,3 +44,17 @@ tests/codel/codel_test
tests/emu/pcu_emu
tests/testsuite
tests/testsuite.log
tests/edge/EdgeTest
tests/llc/LlcTest
# ignore debian files
.tarball-version
debian/autoreconf.after
debian/autoreconf.before
debian/files
debian/*.debhelper*
debian/*.substvars
debian/osmo-pcu-dbg/
debian/osmo-pcu.substvars
debian/osmo-pcu/
debian/tmp/

3
.gitreview Normal file
View File

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

9
README
View File

@ -2,14 +2,19 @@ This is an implementation of Packet Control Unit (PCU) according to TS 04.60
The PCU is part of BSS, so it connects directly to SGSN.
For this PCU the dependent libosmocore patch is in contrib which has to be
applied on top of libosmmocore a23817622b28cb1969a73ffd36da501eb29b9cd7.
== Current limitations ==
This PCU enables
* Fix for Flexible window calculation for EGPRS in UL
* Correction for CRBB generation in PUAN
== Crrent limitations ==
* No PFC support
* No fixed allocation support
* No extended dynamic allocation support
* No unacknowledged mode operation
* No PCCCH/PBCCH support
* Only single slot assignment on uplink direction
* No half-duplex class support (only semi-duplex)
* No handover support

View File

@ -35,6 +35,25 @@ AC_ARG_ENABLE(sysmocom-dsp,
AC_MSG_RESULT([$enable_sysmocom_dsp])
AM_CONDITIONAL(ENABLE_SYSMODSP, test "x$enable_sysmocom_dsp" = "xyes")
AC_MSG_CHECKING([whether to enable direct PHY access for PDCH of NuRAN Wireless Litecell 1.5 BTS])
AC_ARG_ENABLE(lc15bts-phy,
AC_HELP_STRING([--enable-lc15bts-phy],
[enable code for Litecell 1.5 PHY [default=no]]),
[enable_lc15bts_phy="$enableval"],[enable_lc15bts_phy="no"])
AC_ARG_WITH([litecell15], [AS_HELP_STRING([--with-litecell15=INCLUDE_DIR], [Location of the litecell 1.5 API header files])],
[litecell15_incdir="$withval"],[litecell15_incdir="$incdir"])
AC_SUBST([LITECELL15_INCDIR], $litecell15_incdir)
AC_MSG_RESULT([$enable_lc15bts_phy])
AM_CONDITIONAL(ENABLE_LC15BTS_PHY, test "x$enable_lc15bts_phy" = "xyes")
if test "$enable_litecell15" = "yes"; then
oldCPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$LITECELL15_INCDIR -I$srcdir/include $LIBOSMOCORE_CFLAGS"
AC_CHECK_HEADER([nrw/litecell15/litecell15.h],[],
[AC_MSG_ERROR([nrw/litecell15/litecell15.h can not be found in $litecell15_incdir])],
[#include <nrw/litecell15/litecell15.h>])
CPPFLAGS=$oldCPPFLAGS
fi
AC_ARG_ENABLE([vty_tests],
AC_HELP_STRING([--enable-vty-tests],
[Include the VTY tests in make check [default=no]]),

View File

@ -0,0 +1,83 @@
From 369a4edfc661a82e0766a63f423b7d2665115183 Mon Sep 17 00:00:00 2001
From: Pravin Kumarvel <pmanohar@radisys.com>
Date: Thu, 13 Oct 2016 16:35:28 +0530
Subject: [PATCH] Add function to get uninterrupted bit run
Function bitvec_rl_curbit added to get number of uninterrupted
bits run in vector starting from the current bit till max number
of bits.
---
include/osmocom/core/bitvec.h | 1 +
src/bitvec.c | 44 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+)
diff --git a/include/osmocom/core/bitvec.h b/include/osmocom/core/bitvec.h
index 19e2af8..0e17ba7 100644
--- a/include/osmocom/core/bitvec.h
+++ b/include/osmocom/core/bitvec.h
@@ -89,6 +89,7 @@ char bit_value_to_char(enum bit_value v);
void bitvec_to_string_r(const struct bitvec *bv, char *str);
void bitvec_zero(struct bitvec *bv);
unsigned bitvec_rl(const struct bitvec *bv, bool b);
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits);
void bitvec_shiftl(struct bitvec *bv, unsigned int n);
int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits);
unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array,
diff --git a/src/bitvec.c b/src/bitvec.c
index 38148ac..d366eb9 100644
--- a/src/bitvec.c
+++ b/src/bitvec.c
@@ -575,6 +575,50 @@ unsigned bitvec_rl(const struct bitvec *bv, bool b)
return bv->cur_bit;
}
+/*! \brief Return number (bits) of uninterrupted bit run in vector starting from the current bit
+ * \param[in] bv The boolean vector to work on
+ * \param[in] b The boolean, sequence of 1's or 0's to be checked
+ * \returns Number of consecutive bits of \p b in \p bv
+ */
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits)
+{
+ unsigned i = 0;
+ int temp_res = 0;
+ int count = 0;
+ unsigned readIndex = bv->cur_bit;
+ if (bv->cur_bit % 8 == 0) {
+ for (i = (bv->cur_bit/8); i < (max_bits % 8 ? max_bits/8 + 1 : max_bits/8); i++, count++) {
+ if ((b ? 0xFF : 0) != bv->data[i]) {
+ bv->cur_bit = (count * 8 + leading_bits(bv->data[i], b) + readIndex);
+ return count * 8 + leading_bits(bv->data[i], b);
+ }
+ }
+ bv->cur_bit = (count * 8) + readIndex ;
+ if (bv->cur_bit > max_bits)
+ bv->cur_bit = max_bits;
+ return (bv->cur_bit - readIndex);
+ } else {
+ int pos = bv->cur_bit/8;
+ while (readIndex < max_bits && bitvec_read_field(bv, &readIndex, 1) == b) {
+ if( bv->cur_bit % 8 >= 0)
+ temp_res++;
+ else {
+ pos++;
+ for (i = pos; i < (max_bits % 8 ? max_bits/8 + 1 : max_bits/8); i++, count++) {
+ if ((b ? 0xFF : 0) != bv->data[i]) {
+ bv->cur_bit = (count * 8 + leading_bits(bv->data[i], b) + temp_res) + readIndex ;
+ return count * 8 + leading_bits(bv->data[i], b) + temp_res;
+ }
+ }
+ bv->cur_bit = (temp_res + (count * 8)) + readIndex;
+ return temp_res + (count * 8);
+ }
+ }
+ bv->cur_bit--;
+ return temp_res;
+ }
+}
+
/*! \brief Shifts bitvec to the left, n MSB bits lost */
void bitvec_shiftl(struct bitvec *bv, unsigned n)
{
--
1.9.1

66
contrib/jenkins.sh Executable file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -ex
if [ -z "$MAKE" ]; then
echo 'The $MAKE variable is not defined, cannot build'
exit 1
fi
base="$PWD"
deps="$base/deps"
inst="$deps/install"
rm -rf "$inst"
mkdir -p "$deps"
# Collect configure options for osmo-pcu
PCU_CONFIG=""
if [ "$with_dsp" = sysmo ]; then
PCU_CONFIG="$PCU_CONFIG --enable-sysmocom-dsp"
# For direct sysmo DSP access, provide the SysmoBTS Layer 1 API
cd "$deps"
if [ ! -d layer1-api ]; then
git clone git://git.sysmocom.de/sysmo-bts/layer1-api.git layer1-api
fi
cd layer1-api
git fetch origin
git reset --hard origin/master
api_incl="$inst/include/sysmocom/femtobts/"
mkdir -p "$api_incl"
cp include/*.h "$api_incl"
elif [ -z "$with_dsp" -o "$with_dsp" = none ]; then
echo "Direct DSP access disabled"
else
echo 'Invalid $with_dsp value:' $with_dsp
exit 1
fi
if [ "$with_vty" = "yes" ]; then
PCU_CONFIG="$PCU_CONFIG --enable-vty-tests"
elif [ -z "$with_vty" -o "$with_vty" = "no" ]; then
echo "VTY tests disabled"
else
echo 'Invalid $with_vty value:' $with_vty
exit 1
fi
# Build deps
cd "$deps"
osmo-deps.sh libosmocore
cd libosmocore
autoreconf --install --force
./configure --prefix="$inst"
$MAKE $PARALLEL_MAKE install
export PKG_CONFIG_PATH="$inst/lib/pkgconfig"
export LD_LIBRARY_PATH="$inst/lib"
# Build osmo-pcu
cd "$base"
autoreconf --install --force
./configure $PCU_CONFIG
$MAKE $PARALLEL_MAKE
DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" AM_DISTCHECK_CONFIGURE_FLAGS="$PCU_CONFIG" $MAKE distcheck

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

5
debian/changelog vendored Normal file
View File

@ -0,0 +1,5 @@
osmo-pcu (0.3) UNRELEASED; urgency=medium
* Initial release.
-- Holger Hans Peter Freyther <holger@moiji-mobile.com> Fri, 01 Apr 2016 18:59:00 +0200

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
7

24
debian/control vendored Normal file
View File

@ -0,0 +1,24 @@
Source: osmo-pcu
Section: net
Priority: optional
Maintainer: Holger Hans Peter Freyther <holger@moiji-mobile.com>
Build-Depends: debhelper (>= 7.0.0~), dh-autoreconf, dh-systemd (>= 1.5), autotools-dev, pkg-config, libosmocore-dev
Standards-Version: 3.8.4
Homepage: http://osmocom.org/projects/osmopcu
Vcs-Git: git://git.osmocom.org/osmo-pcu
Vcs-Browser: http://git.osmocom.org/osmo-pcu/
Package: osmo-pcu
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: osmo-pcu GSM PCU for GPRS and EDGE
osmo-pcu for GPRS and EDGE support in the network
Package: osmo-pcu-dbg
Architecture: any
Section: debug
Priority: extra
Depends: osmo-pcu (= ${binary:Version}), ${misc:Depends}
Description: Debug symbols for the osmo-pcu
Make debugging possible

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

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

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

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

19
debian/rules vendored Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/make -f
DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2)
DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1)
VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g')
#export DH_VERBOSE=1
export DEB_BUILD_HARDENING=1
%:
dh $@ --with=systemd --with autoreconf --fail-missing
override_dh_strip:
dh_strip --dbg-package=osmo-pcu-dbg
override_dh_autoreconf:
echo $(VERSION) > .tarball-version
dh_autoreconf

1
debian/source/format vendored Normal file
View File

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

View File

@ -1,6 +1,24 @@
pcu
egprs only
flow-control-interval 10
cs 2
alloc-algorithm dynamic
flow-control force-bvc-bucket-size 596000
flow-control force-bvc-leak-rate 59600
flow-control force-ms-bucket-size 596000
flow-control force-ms-leak-rate 59600
cs 1
cs max 1
no cs threshold
no cs downgrade-threshold
cs link-quality-ranges cs1 6 cs2 5 8 cs3 7 13 cs4 12
mcs 9 9
mcs max 9 9
window-size 64 104
queue lifetime infinite
queue idle-ack-delay 10
no queue codel
alloc-algorithm b
two-phase-access
alpha 0
gamma 0
dl-tbf-idle-time 2000
maximise-direction dl

View File

@ -18,10 +18,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
AUTOMAKE_OPTIONS = subdir-objects
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)
if ENABLE_SYSMODSP
AM_CPPFLAGS += -DENABLE_SYSMODSP
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
endif
if ENABLE_LC15BTS_PHY
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
endif
AM_CXXFLAGS = -Wall -ldl -pthread
@ -57,18 +62,14 @@ libgprs_la_SOURCES = \
rlc.cpp \
osmobts_sock.cpp \
gprs_codel.c \
gprs_coding_scheme.cpp
gprs_coding_scheme.cpp \
egprs_rlc_compression.cpp
bin_PROGRAMS = \
osmo-pcu
noinst_PROGRAMS =
if ENABLE_SYSMODSP
noinst_PROGRAMS += \
osmo-pcu-remote
endif
noinst_HEADERS = \
gprs_debug.h \
csn1.h \
@ -83,8 +84,6 @@ noinst_HEADERS = \
bitvector.h \
pcu_vty.h \
pcu_vty_functions.h \
sysmo_l1_if.h \
femtobts.h \
tbf.h \
bts.h \
poll_controller.h \
@ -96,19 +95,65 @@ noinst_HEADERS = \
pcu_utils.h \
cxx_linuxlist.h \
gprs_codel.h \
gprs_coding_scheme.h
gprs_coding_scheme.h \
egprs_rlc_compression.h
osmo_pcu_SOURCES = pcu_main.cpp
if ENABLE_SYSMODSP
osmo_pcu_SOURCES += sysmo_l1_if.c \
sysmo_l1_hw.c \
femtobts.c
AM_CPPFLAGS += -I$(srcdir)/osmo-bts-sysmo
osmo_pcu_remote_SOURCES = pcu_main.cpp \
sysmo_l1_if.c \
sysmo_l1_fwd.c \
femtobts.c
EXTRA_DIST = \
osmo-bts-sysmo/sysmo_l1_if.c \
osmo-bts-sysmo/sysmo_l1_if.h \
osmo-bts-sysmo/sysmo_l1_hw.c \
osmo-bts-sysmo/femtobts.c \
osmo-bts-sysmo/femtobts.h
noinst_HEADERS += \
osmo-bts-sysmo/sysmo_l1_if.h \
osmo-bts-sysmo/femtobts.h
noinst_PROGRAMS += \
osmo-pcu-remote
osmo_pcu_SOURCES += \
osmo-bts-sysmo/sysmo_l1_if.c \
osmo-bts-sysmo/sysmo_l1_hw.c \
osmo-bts-sysmo/femtobts.c
osmo_pcu_remote_SOURCES = \
pcu_main.cpp \
osmo-bts-sysmo/sysmo_l1_if.c \
osmo-bts-sysmo/sysmo_l1_fwd.c \
osmo-bts-sysmo/femtobts.c
osmo_pcu_remote_LDADD = \
libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COMMON_LA)
endif
if ENABLE_LC15BTS_PHY
AM_CPPFLAGS += -I$(LITECELL15_INCDIR) -I$(srcdir)/osmo-bts-litecell15
EXTRA_DIST = \
osmo-bts-litecell15/lc15_l1_if.c \
osmo-bts-litecell15/lc15_l1_if.h \
osmo-bts-litecell15/lc15_l1_hw.c \
osmo-bts-litecell15/lc15bts.c \
osmo-bts-litecell15/lc15bts.h
noinst_HEADERS += \
osmo-bts-litecell15/lc15_l1_if.h \
osmo-bts-litecell15/lc15bts.h
osmo_pcu_SOURCES += \
osmo-bts-litecell15/lc15_l1_if.c \
osmo-bts-litecell15/lc15_l1_hw.c \
osmo-bts-litecell15/lc15bts.c
endif
osmo_pcu_LDADD = \
@ -118,13 +163,4 @@ osmo_pcu_LDADD = \
$(LIBOSMOGSM_LIBS) \
$(COMMON_LA)
if ENABLE_SYSMODSP
osmo_pcu_remote_LDADD = \
libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(COMMON_LA)
endif
#MOSTLYCLEANFILES += testSource testDestination

View File

@ -101,23 +101,6 @@ uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len
}
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len)
{
unsigned int i;
int rc;
bv->cur_bit = write_index;
for (i = 0; i < len; i++) {
int bit = 0;
if (val & ((uint64_t)1 << (len - i - 1)))
bit = 1;
rc = bitvec_set_bit(bv, (bit_value)bit);
if (rc)
return rc;
}
write_index += len;
return 0;
}
int bitvec_write_field_lh(struct bitvec *bv, unsigned& write_index,
uint64_t val, unsigned len)
{

View File

@ -38,9 +38,15 @@ int bitvec_unhex(struct bitvec *bv, const char* src);
unsigned int bitvec_pack(struct bitvec *bv, uint8_t *buffer);
unsigned int bitvec_unpack(struct bitvec *bv, uint8_t *buffer);
uint64_t bitvec_read_field(struct bitvec *bv, unsigned& read_index, unsigned len);
int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
int bitvec_write_field_lh(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len);
static inline int bitvec_write_field(struct bitvec *bv, unsigned& write_index, uint64_t val, unsigned len)
{
/* Call the libosmocore variant */
return ::bitvec_write_field(bv, &write_index, val, len);
}
/*! }@ */
#endif // BITVECTOR_H

View File

@ -33,6 +33,7 @@ extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/stats.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
}
#include <arpa/inet.h>
@ -71,6 +72,12 @@ static const struct rate_ctr_desc bts_ctr_description[] = {
{ "rlc.ack.failed", "RLC Ack Failed "},
{ "rlc.rel.timedout", "RLC Release Timeout "},
{ "rlc.late-block", "RLC Late Block "},
{ "rlc.sent-dummy", "RLC Sent Dummy "},
{ "rlc.sent-control", "RLC Sent Control "},
{ "rlc.dl_bytes", "RLC DL Bytes "},
{ "rlc.dl_payload_bytes", "RLC DL Payload Bytes "},
{ "rlc.ul_bytes", "RLC UL Bytes "},
{ "rlc.ul_payload_bytes", "RLC UL Payload Bytes "},
{ "decode.errors", "Decode Errors "},
{ "sba.allocated", "SBA Allocated "},
{ "sba.freed", "SBA Freed "},
@ -78,6 +85,8 @@ static const struct rate_ctr_desc bts_ctr_description[] = {
{ "llc.timeout", "Timedout Frames "},
{ "llc.dropped", "Dropped Frames "},
{ "llc.scheduled", "Scheduled Frames "},
{ "llc.dl_bytes", "RLC encapsulated PDUs"},
{ "llc.ul_bytes", "full PDUs received "},
{ "rach.requests", "RACH requests "},
};
@ -459,7 +468,8 @@ int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn)
return 0;
}
int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
int BTS::rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit,
enum ph_burst_type burst_type)
{
struct gprs_rlcmac_ul_tbf *tbf = NULL;
uint8_t trx_no, ts_no = 0;
@ -470,20 +480,16 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
uint8_t usf = 7;
uint8_t tsc;
uint16_t ta;
uint16_t ms_class = 0;
uint16_t priority = 0;
rach_frame();
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide "
"one:\n");
if ((ra & 0xf8) == 0x70) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
"allocation\n");
sb = 1;
} else if (m_bts.force_two_phase) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single phase access, "
"but we force two phase access\n");
sb = 1;
}
sb = is_single_block(ra, burst_type, is_11bit, &ms_class, &priority);
if (qta < 0)
qta = 0;
if (qta > 252)
@ -505,9 +511,17 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
tsc = m_bts.trx[trx_no].pdch[ts_no].tsc;
} else {
// Create new TBF
#warning "Copy and pate with other routines.."
/* set class to 0, since we don't know the multislot class yet */
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0, 0, 1);
#warning "Copy and paste with other routines.."
if (is_11bit) {
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0,
ms_class, 1);
} else {
/* set class to 0, since we don't know the multislot
* class yet */
tbf = tbf_alloc_ul_tbf(&m_bts, NULL, -1, 0, 0, 1);
}
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
/* FIXME: send reject */
@ -542,7 +556,7 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
plen = Encoding::write_immediate_assignment(
tbf, immediate_assignment, 0, ra, Fn, ta,
m_bts.trx[trx_no].arfcn, ts_no, tsc, usf, 0, sb_fn,
m_bts.alpha, m_bts.gamma, -1);
m_bts.alpha, m_bts.gamma, -1, burst_type, sb);
if (plen >= 0) {
pcu_l1if_tx_agch(immediate_assignment, plen);
@ -555,6 +569,73 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
return 0;
}
uint8_t BTS::is_single_block(uint16_t ra, enum ph_burst_type burst_type,
uint8_t is_11bit, uint16_t *ms_class, uint16_t *priority)
{
uint8_t sb = 0, val = 0;
if (!is_11bit && (burst_type == GSM_L1_BURST_TYPE_ACCESS_0)) {
if ((ra & 0xf8) == 0x70) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
"allocation\n");
sb = 1;
} else if (m_bts.force_two_phase) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single "
"phase access, but we force two phase "
"access\n");
sb = 1;
}
} else if (is_11bit &&
((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
val = !!(ra & (1 << 10));
if (!val) {
if (m_bts.force_two_phase) {
LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS 11 bit RACH "
"received. MS requests single phase "
"access but we force two phase "
"access\n");
sb = 1;
} else {
sb = 0;
*ms_class = (ra & 0x3e0) >> 5;
*priority = (ra & 0x18) >> 3;
}
} else {
LOGP(DRLCMAC, LOGL_DEBUG, "EGPRS 11 bit RACH received."
"MS requests single block allocation\n");
sb = 1;
}
} else if (is_11bit &&
(burst_type == GSM_L1_BURST_TYPE_ACCESS_0)) {
LOGP(DRLCMAC, LOGL_ERROR,
"Error: GPRS 11 bit RACH not supported\n");
} else if (burst_type == GSM_L1_BURST_TYPE_NONE) {
LOGP(DRLCMAC, LOGL_DEBUG, "pcu has not received burst type "
"from bts \n");
if ((ra & 0xf8) == 0x70) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
"allocation\n");
sb = 1;
} else if (m_bts.force_two_phase) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single "
"phase access, but we force two phase "
"access\n");
sb = 1;
}
}
return sb;
}
/* depending on the current TBF, we assign on PACCH or AGCH */
void BTS::trigger_dl_ass(
struct gprs_rlcmac_dl_tbf *dl_tbf,
@ -1120,7 +1201,7 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
uint32_t tlli = request->ID.u.TLLI;
uint8_t ms_class = 0;
uint8_t egprs_ms_class = 0;
uint8_t ta = 0;
uint8_t ta = GSM48_TA_INVALID;
struct pcu_l1_meas meas;
GprsMs *ms = bts()->ms_by_tlli(tlli);
@ -1300,6 +1381,8 @@ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
return -EINVAL;
}
bts()->rlc_ul_bytes(len);
LOGP(DRLCMACUL, LOGL_DEBUG, "Got RLC block, coding scheme: %s, "
"length: %d (%d))\n", cs.name(), len, cs.usedSizeUL());
@ -1315,6 +1398,7 @@ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
return -EINVAL;
}
/*! \brief process egprs and gprs data blocks */
int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint32_t fn,
struct pcu_l1_meas *meas, GprsCodingScheme cs)
{
@ -1333,15 +1417,6 @@ int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint32_t fn,
cs.name());
return -EINVAL;
}
if (!cs.isEgprsGmsk()) {
LOGP(DRLCMACUL, LOGL_ERROR,
"Got %s RLC block but EGPRS is not implemented "
"for 8PSK yet\n",
cs.name());
bts()->decode_error();
return -EINVAL;
}
}
LOGP(DRLCMACUL, LOGL_DEBUG, " UL data: %s\n", osmo_hexdump(data, len));

View File

@ -28,6 +28,7 @@ extern "C" {
#include <osmocom/core/stat_item.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/gsm/l1sap.h>
}
#include "poll_controller.h"
@ -41,6 +42,12 @@ extern "C" {
#define LLC_CODEL_DISABLE 0
#define LLC_CODEL_USE_DEFAULT (-1)
#define MAX_GPRS_CS 9
enum maximise_direction {
DL_ONLY,
NO_MAXIMISE
};
struct BTS;
struct GprsMs;
@ -184,11 +191,17 @@ struct gprs_rlcmac_bts {
uint8_t alpha, gamma;
uint8_t egprs_enabled;
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
/* 0 to support resegmentation in DL, 1 for no reseg */
uint8_t dl_arq_type;
enum maximise_direction maximise_dir;
uint32_t ms_idle_sec;
uint8_t cs_adj_enabled;
uint8_t cs_adj_upper_limit;
uint8_t cs_adj_lower_limit;
struct {int16_t low; int16_t high;} cs_lqual_ranges[4];
struct {int16_t low; int16_t high; } cs_lqual_ranges[MAX_GPRS_CS];
uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
uint16_t ws_base;
uint16_t ws_pdch; /* increase WS by this value per PDCH */
@ -233,6 +246,12 @@ public:
CTR_RLC_ACK_FAILED,
CTR_RLC_REL_TIMEDOUT,
CTR_RLC_LATE_BLOCK,
CTR_RLC_SENT_DUMMY,
CTR_RLC_SENT_CONTROL,
CTR_RLC_DL_BYTES,
CTR_RLC_DL_PAYLOAD_BYTES,
CTR_RLC_UL_BYTES,
CTR_RLC_UL_PAYLOAD_BYTES,
CTR_DECODE_ERRORS,
CTR_SBA_ALLOCATED,
CTR_SBA_FREED,
@ -240,6 +259,8 @@ public:
CTR_LLC_FRAME_TIMEDOUT,
CTR_LLC_FRAME_DROPPED,
CTR_LLC_FRAME_SCHED,
CTR_LLC_DL_BYTES,
CTR_LLC_UL_BYTES,
CTR_RACH_REQUESTS,
};
@ -275,7 +296,10 @@ public:
int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx);
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
uint8_t is_single_block(uint16_t ra, enum ph_burst_type burst_type,
uint8_t is_11bit, uint16_t *ms_class, uint16_t *priority);
int rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit,
enum ph_burst_type burst_type);
void trigger_dl_ass(gprs_rlcmac_dl_tbf *tbf, gprs_rlcmac_tbf *old_tbf);
void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
@ -309,6 +333,12 @@ public:
void rlc_ack_failed();
void rlc_rel_timedout();
void rlc_late_block();
void rlc_sent_dummy();
void rlc_sent_control();
void rlc_dl_bytes(int bytes);
void rlc_dl_payload_bytes(int bytes);
void rlc_ul_bytes(int bytes);
void rlc_ul_payload_bytes(int bytes);
void decode_error();
void sba_allocated();
void sba_freed();
@ -316,6 +346,8 @@ public:
void llc_timedout_frame();
void llc_dropped_frame();
void llc_frame_sched();
void llc_dl_bytes(int bytes);
void llc_ul_bytes(int bytes);
void rach_frame();
void ms_present(int32_t n);
@ -423,6 +455,11 @@ inline struct osmo_stat_item_group *BTS::stat_items() const
return m_statg;
}
#define CREATE_COUNT_ADD_INLINE(func_name, ctr_name) \
inline void BTS::func_name(int inc) {\
rate_ctr_add(&m_ratectrs->ctr[ctr_name], inc); \
}
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
inline void BTS::func_name() {\
rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
@ -449,6 +486,12 @@ CREATE_COUNT_INLINE(rlc_ack_timedout, CTR_RLC_ACK_TIMEDOUT);
CREATE_COUNT_INLINE(rlc_ack_failed, CTR_RLC_ACK_FAILED);
CREATE_COUNT_INLINE(rlc_rel_timedout, CTR_RLC_REL_TIMEDOUT);
CREATE_COUNT_INLINE(rlc_late_block, CTR_RLC_LATE_BLOCK);
CREATE_COUNT_INLINE(rlc_sent_dummy, CTR_RLC_SENT_DUMMY);
CREATE_COUNT_INLINE(rlc_sent_control, CTR_RLC_SENT_CONTROL);
CREATE_COUNT_ADD_INLINE(rlc_dl_bytes, CTR_RLC_DL_BYTES);
CREATE_COUNT_ADD_INLINE(rlc_dl_payload_bytes, CTR_RLC_DL_PAYLOAD_BYTES);
CREATE_COUNT_ADD_INLINE(rlc_ul_bytes, CTR_RLC_UL_BYTES);
CREATE_COUNT_ADD_INLINE(rlc_ul_payload_bytes, CTR_RLC_UL_PAYLOAD_BYTES);
CREATE_COUNT_INLINE(decode_error, CTR_DECODE_ERRORS)
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)
@ -456,6 +499,8 @@ CREATE_COUNT_INLINE(sba_timedout, CTR_SBA_TIMEDOUT)
CREATE_COUNT_INLINE(llc_timedout_frame, CTR_LLC_FRAME_TIMEDOUT);
CREATE_COUNT_INLINE(llc_dropped_frame, CTR_LLC_FRAME_DROPPED);
CREATE_COUNT_INLINE(llc_frame_sched, CTR_LLC_FRAME_SCHED);
CREATE_COUNT_ADD_INLINE(llc_dl_bytes, CTR_LLC_DL_BYTES);
CREATE_COUNT_ADD_INLINE(llc_ul_bytes, CTR_LLC_UL_BYTES);
CREATE_COUNT_INLINE(rach_frame, CTR_RACH_REQUESTS);
#undef CREATE_COUNT_INLINE

View File

@ -1110,22 +1110,21 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector, unsig
{ /* extract bits */
guint8* pui8 = pui8DATA(data, pDescr->offset);
gint16 nB1 = no_of_bits & 0x07;/* no_of_bits Mod 8 */
while (no_of_bits > 0)
while (no_of_bits >= 8)
{
*pui8 = bitvec_read_field(vector, readIndex, 8);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
no_of_bits -= 8;
}
if (nB1 > 0)
if (no_of_bits > 0)
{
*pui8 = bitvec_read_field(vector, readIndex, nB1);
*pui8 = bitvec_read_field(vector, readIndex, no_of_bits);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)*pui8);
pui8++;
no_of_bits -= nB1;
bit_offset += nB1; /* (nB1 is no_of_bits Mod 8) */
bit_offset += no_of_bits;
no_of_bits = 0;
}
}
}
@ -2400,7 +2399,12 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
guint8 bits_to_handle = remaining_bits_len%8;
if (bits_to_handle > 0)
{
guint8 fl = filler&(0xff>>(8-bits_to_handle));
/* section 11 of 44.060
* The padding bits may be the 'null' string. Otherwise, the
* padding bits starts with bit '0', followed by 'spare padding'
* < padding bits > ::= { null | 0 < spare padding > ! < Ignore : 1 bit** = < no string > > } ;
*/
guint8 fl = filler&(0xff>>(8-bits_to_handle + 1));
bitvec_write_field(vector, writeIndex, fl, bits_to_handle);
LOGPC(DCSN1, LOGL_NOTICE, "%u|", fl);
remaining_bits_len -= bits_to_handle;
@ -2499,6 +2503,7 @@ gint16 csnStreamEncoder(csnStream_t* ar, const CSN_DESCR* pDescr, bitvec *vector
bitvec_write_field(vector, writeIndex, !Tag, 1);
LOGPC(DCSN1, LOGL_NOTICE, "%s = %u | ", pDescr->sz , (unsigned)(!Tag));
bit_offset++;
remaining_bits_len--;
pDescr++;
break;

View File

@ -20,10 +20,12 @@
#include <decoding.h>
#include <rlc.h>
#include <gprs_debug.h>
#include <egprs_rlc_compression.h>
extern "C" {
#include <osmocom/core/utils.h>
#include <osmocom/core/bitcomp.h>
#include <osmocom/gprs/protocol/gsm_04_60.h>
}
#include <arpa/inet.h>
@ -32,7 +34,7 @@ extern "C" {
#include <string.h>
#define LENGTH_TO_END 255
/*
/*!
* \returns num extensions fields (num frames == offset) on success,
* -errno otherwise.
*/
@ -66,14 +68,11 @@ static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len,
"but no more chunks possible\n");
return -ENOSPC;
}
if (li->li == 0 && num_chunks == 0 && li->e == 0) {
if (li->li == 0 && num_chunks == 0) {
/* TS 44.060, table 10.4.14a.1, row 2a */
/* TS 44.060, table 10.4.14a.1, row 4 */
chunks[num_chunks].length = 0;
chunks[num_chunks].is_complete = true;
} else if (li->li == 0 && num_chunks == 0 && li->e == 1) {
/* TS 44.060, table 10.4.14a.1, row 4 */
chunks[num_chunks].length = LENGTH_TO_END;
chunks[num_chunks].is_complete = is_last_block;
} else if (li->li == 127 && li->e == 1) {
/* TS 44.060, table 10.4.14a.1, row 3 & 5 */
/* only filling bytes left */
@ -197,6 +196,7 @@ int Decoding::rlc_data_from_ul_data(
e = rdbi->e;
if (e) {
if (chunks_size > 0) {
/* Block without LI means it only contains data of one LLC PDU */
chunks[num_chunks].offset = offs;
chunks[num_chunks].length = LENGTH_TO_END;
chunks[num_chunks].is_complete = is_last_block;
@ -266,7 +266,7 @@ int Decoding::rlc_data_from_ul_data(
* so drop it (this may happen with EGPRS since
* there is no M flag. */
num_chunks -= 1;
break;;
break;
}
chunks[i].length = data_len - offs;
}
@ -344,78 +344,20 @@ void Decoding::extract_rbb(const struct bitvec *rbb, char *show_rbb)
int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
const uint8_t *data, GprsCodingScheme cs)
{
const struct gprs_rlc_ul_header_egprs_3 *egprs3;
const struct rlc_ul_header *gprs;
unsigned int e_ti_header;
unsigned int cur_bit = 0;
int punct, punct2, with_padding, cps;
unsigned int offs;
switch(cs.headerTypeData()) {
case GprsCodingScheme::HEADER_GPRS_DATA:
gprs = static_cast<struct rlc_ul_header *>
((void *)data);
gprs_rlc_data_info_init_ul(rlc, cs, false);
rlc->r = gprs->r;
rlc->si = gprs->si;
rlc->tfi = gprs->tfi;
rlc->cps = 0;
rlc->rsb = 0;
rlc->num_data_blocks = 1;
rlc->block_info[0].cv = gprs->cv;
rlc->block_info[0].pi = gprs->pi;
rlc->block_info[0].bsn = gprs->bsn;
rlc->block_info[0].e = gprs->e;
rlc->block_info[0].ti = gprs->ti;
rlc->block_info[0].spb = 0;
cur_bit += rlc->data_offs_bits[0];
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
case GprsCodingScheme::HEADER_GPRS_DATA :
cur_bit = rlc_parse_ul_data_header_gprs(rlc, data, cs);
break;
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3:
egprs3 = static_cast<struct gprs_rlc_ul_header_egprs_3 *>
((void *)data);
cps = (egprs3->cps_a << 0) | (egprs3->cps_b << 2);
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
rlc->r = egprs3->r;
rlc->si = egprs3->si;
rlc->tfi = (egprs3->tfi_a << 0) | (egprs3->tfi_b << 2);
rlc->cps = cps;
rlc->rsb = egprs3->rsb;
rlc->num_data_blocks = 1;
rlc->block_info[0].cv = egprs3->cv;
rlc->block_info[0].pi = egprs3->pi;
rlc->block_info[0].spb = egprs3->spb;
rlc->block_info[0].bsn =
(egprs3->bsn1_a << 0) | (egprs3->bsn1_b << 5);
cur_bit += rlc->data_offs_bits[0] - 2;
offs = rlc->data_offs_bits[0] / 8;
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1);
e_ti_header = (data[offs-1] + (data[offs] << 8)) >> 7;
rlc->block_info[0].e = !!(e_ti_header & 0x01);
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
cur_bit += 2;
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3 :
cur_bit = rlc_parse_ul_data_header_egprs_type_3(rlc, data, cs);
break;
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2 :
cur_bit = rlc_parse_ul_data_header_egprs_type_2(rlc, data, cs);
break;
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1 :
cur_bit = rlc_parse_ul_data_header_egprs_type_1(rlc, data, cs);
break;
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1:
case GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2:
/* TODO: Support both header types */
/* fall through */
default:
LOGP(DRLCMACDL, LOGL_ERROR,
"Decoding of uplink %s data blocks not yet supported.\n",
@ -426,6 +368,181 @@ int Decoding::rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
return cur_bit;
}
int Decoding::rlc_parse_ul_data_header_egprs_type_3(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs)
{
int punct, punct2, with_padding, cps;
unsigned int e_ti_header, offs, cur_bit = 0;
const struct gprs_rlc_ul_header_egprs_3 *egprs3;
egprs3 = static_cast < struct gprs_rlc_ul_header_egprs_3 * >
((void *)data);
cps = (egprs3->cps_hi << 0) | (egprs3->cps_lo << 2);
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
rlc->r = egprs3->r;
rlc->si = egprs3->si;
rlc->tfi = (egprs3->tfi_hi << 0) | (egprs3->tfi_lo << 2);
rlc->cps = cps;
rlc->rsb = egprs3->rsb;
rlc->num_data_blocks = 1;
rlc->block_info[0].cv = egprs3->cv;
rlc->block_info[0].pi = egprs3->pi;
rlc->block_info[0].spb = egprs3->spb;
rlc->block_info[0].bsn =
(egprs3->bsn1_hi << 0) | (egprs3->bsn1_lo << 5);
cur_bit += rlc->data_offs_bits[0] - 2;
offs = rlc->data_offs_bits[0] / 8;
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1);
e_ti_header = (data[offs-1] + (data[offs] << 8)) >> 7;
rlc->block_info[0].e = !!(e_ti_header & 0x01);
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
cur_bit += 2;
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
return cur_bit;
}
int Decoding::rlc_parse_ul_data_header_egprs_type_2(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs)
{
const struct gprs_rlc_ul_header_egprs_2 *egprs2;
unsigned int e_ti_header, offs, cur_bit = 0;
int punct, punct2, with_padding, cps;
egprs2 = static_cast < struct gprs_rlc_ul_header_egprs_2 * >
((void *)data);
cps = (egprs2->cps_hi << 0) | (egprs2->cps_lo << 2);
gprs_rlc_mcs_cps_decode(cps, cs, &punct, &punct2, &with_padding);
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
rlc->r = egprs2->r;
rlc->si = egprs2->si;
rlc->tfi = (egprs2->tfi_hi << 0) | (egprs2->tfi_lo << 2);
rlc->cps = cps;
rlc->rsb = egprs2->rsb;
rlc->num_data_blocks = 1;
rlc->block_info[0].cv = egprs2->cv;
rlc->block_info[0].pi = egprs2->pi;
rlc->block_info[0].bsn =
(egprs2->bsn1_hi << 0) | (egprs2->bsn1_lo << 5);
cur_bit += rlc->data_offs_bits[0] - 2;
offs = rlc->data_offs_bits[0] / 8;
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 7);
e_ti_header = (data[offs] & 0x60) >> 5;
rlc->block_info[0].e = !!(e_ti_header & 0x01);
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
cur_bit += 2;
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
return cur_bit;
}
int Decoding::rlc_parse_ul_data_header_egprs_type_1(
struct gprs_rlc_data_info *rlc,
const uint8_t *data, const GprsCodingScheme &cs)
{
struct gprs_rlc_ul_header_egprs_1 *egprs1;
unsigned int e_ti_header, cur_bit = 0, offs;
int punct, punct2, with_padding;
egprs1 = static_cast < struct gprs_rlc_ul_header_egprs_1 * >
((void *)data);
gprs_rlc_mcs_cps_decode(egprs1->cps, cs, &punct, &punct2,
&with_padding);
gprs_rlc_data_info_init_ul(rlc, cs, with_padding);
rlc->r = egprs1->r;
rlc->si = egprs1->si;
rlc->tfi = (egprs1->tfi_hi << 0) | (egprs1->tfi_lo << 2);
rlc->cps = egprs1->cps;
rlc->rsb = egprs1->rsb;
rlc->num_data_blocks = 2;
rlc->block_info[0].cv = egprs1->cv;
rlc->block_info[0].pi = egprs1->pi;
rlc->block_info[0].bsn =
(egprs1->bsn1_hi << 0) | (egprs1->bsn1_lo << 5);
cur_bit += rlc->data_offs_bits[0] - 2;
offs = rlc->data_offs_bits[0] / 8;
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 0);
e_ti_header = data[offs - 1] >> 6;
rlc->block_info[0].e = (e_ti_header & 0x01);
rlc->block_info[0].ti = !!(e_ti_header & 0x02);
cur_bit += 2;
rlc->block_info[1].cv = egprs1->cv;
rlc->block_info[1].pi = egprs1->pi;
rlc->block_info[1].bsn = rlc->block_info[0].bsn +
((egprs1->bsn2_hi << 0) | (egprs1->bsn2_lo << 2));
rlc->block_info[1].bsn = rlc->block_info[1].bsn & (RLC_EGPRS_SNS - 1);
if ((rlc->block_info[1].bsn != rlc->block_info[0].bsn) &&
(rlc->block_info[0].cv == 0))
rlc->block_info[0].cv = 1;
cur_bit = rlc->data_offs_bits[1] - 2;
offs = rlc->data_offs_bits[1] / 8;
OSMO_ASSERT(rlc->data_offs_bits[1] % 8 == 2);
e_ti_header = (data[offs] & (0x03));
rlc->block_info[1].e = (e_ti_header & 0x01);
rlc->block_info[1].ti = !!(e_ti_header & 0x02);
cur_bit += 2;
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
return cur_bit;
}
int Decoding::rlc_parse_ul_data_header_gprs(struct gprs_rlc_data_info *rlc,
const uint8_t *data, const GprsCodingScheme &cs)
{
const struct rlc_ul_header *gprs;
unsigned int cur_bit = 0;
gprs = static_cast < struct rlc_ul_header * >
((void *)data);
gprs_rlc_data_info_init_ul(rlc, cs, false);
rlc->r = gprs->r;
rlc->si = gprs->si;
rlc->tfi = gprs->tfi;
rlc->cps = 0;
rlc->rsb = 0;
rlc->num_data_blocks = 1;
rlc->block_info[0].cv = gprs->cv;
rlc->block_info[0].pi = gprs->pi;
rlc->block_info[0].bsn = gprs->bsn;
rlc->block_info[0].e = gprs->e;
rlc->block_info[0].ti = gprs->ti;
rlc->block_info[0].spb = 0;
cur_bit += rlc->data_offs_bits[0];
/* skip data area */
cur_bit += cs.maxDataBlockBytes() * 8;
return cur_bit;
}
/**
* \brief Copy LSB bitstream RLC data block to byte aligned buffer.
*
@ -576,21 +693,17 @@ int Decoding::decode_egprs_acknack_bits(const EGPRS_AckNack_Desc_t *desc,
if (crbb_len > 0) {
int old_len = bits->cur_bit;
struct bitvec crbb;
crbb.data = (uint8_t *)desc->CRBB;
crbb.data_len = sizeof(desc->CRBB);
crbb.cur_bit = desc->CRBB_LENGTH;
rc = osmo_t4_decode(&crbb, desc->CRBB_STARTING_COLOR_CODE,
bits);
LOGP(DRLCMACDL, LOGL_DEBUG, "Compress bitmap exists, "
"CRBB LEN = %d and Starting color code = %d",
desc->CRBB_LENGTH, desc->CRBB_STARTING_COLOR_CODE);
rc = egprs_compress::decompress_crbb(desc->CRBB_LENGTH,
desc->CRBB_STARTING_COLOR_CODE, desc->CRBB, bits);
if (rc < 0) {
LOGP(DRLCMACUL, LOGL_NOTICE,
"Failed to decode CRBB: "
"length %d, data '%s'\n",
desc->CRBB_LENGTH,
osmo_hexdump(crbb.data, crbb.data_len));
"Failed to decode CRBB: length %d, data '%s'\n",
desc->CRBB_LENGTH, osmo_hexdump(
desc->CRBB, (desc->CRBB_LENGTH + 7)/8));
/* We don't know the SSN offset for the URBB,
* return what we have so far and assume the
* bitmap has stopped here */

View File

@ -28,10 +28,11 @@ struct bitvec;
class Decoding {
public:
/* represents (parts) LLC PDUs within one RLC Data block */
struct RlcData {
uint8_t offset;
uint8_t length;
bool is_complete;
bool is_complete; /* if this PDU ends in this block */
};
static int rlc_data_from_ul_data(
@ -43,7 +44,22 @@ public:
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
static void extract_rbb(const struct bitvec *rbb, char *show_rbb);
static int rlc_parse_ul_data_header_egprs_type_3(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs);
static int rlc_parse_ul_data_header_egprs_type_2(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs);
static int rlc_parse_ul_data_header_egprs_type_1(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs);
static int rlc_parse_ul_data_header_gprs(
struct gprs_rlc_data_info *rlc,
const uint8_t *data,
const GprsCodingScheme &cs);
static int rlc_parse_ul_data_header(struct gprs_rlc_data_info *rlc,
const uint8_t *data, GprsCodingScheme cs);
static unsigned int rlc_copy_to_aligned_buffer(

View File

@ -0,0 +1,669 @@
/* egprs_rlc_compression.h
* Routines for EGPRS RLC bitmap compression handling
*/
#include <errno.h>
#include <decoding.h>
#include <arpa/inet.h>
#include <string.h>
#include <gprs_debug.h>
#include <gprs_rlcmac.h>
#include <egprs_rlc_compression.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/stats.h>
}
#define EGPRS_CODEWORDS 79 /* total number of codewords */
extern void *tall_pcu_ctx;
extern const char *one_run_len_code_list[EGPRS_CODEWORDS];
extern const char *zero_run_len_code_list[EGPRS_CODEWORDS];
egprs_compress *egprs_compress::s_instance = 0;
egprs_compress_node *egprs_compress::create_tree_node(void *parent)
{
egprs_compress_node *new_node;
new_node = talloc_zero(parent, egprs_compress_node);
new_node->left = NULL;
new_node->right = NULL;
new_node->run_length = -1;
return new_node;
}
/* The function expands the given tree by incorporating
* the given codewords.
* \param root[in] Root of Ones or Zeros tree
* \param cdwd[in] Code word
*/
void egprs_compress::build_codewords(egprs_compress_node *root, const char *cdwd[])
{
egprs_compress_node *iter;
int len;
int i;
int idx;
for (idx = 0; idx < EGPRS_CODEWORDS; idx++) {
len = strlen((const char *)cdwd[idx]);
iter = root;
for (i = 0; i < len; i++) {
if (cdwd[idx][i] == '0') {
if (!iter->left)
iter->left = create_tree_node(root);
iter = iter->left;
} else {
if (!iter->right)
iter->right = create_tree_node(root);
iter = iter->right;
}
}
if (iter) {
if (idx < 64)
iter->run_length = idx;
else
/* The first 64 run lengths are 0, 1, 2, ..., 63
* and the following ones are 64, 128, 192 described in
* section 9.1.10 of 3gpp 44.060 */
iter->run_length = (idx - 63) * 64;
}
}
}
/*
* Terminating codes for uninterrupted sequences of 0 and 1 up to 64 bit length
* according to TS 44.060 9.1.10
*/
static const unsigned t4_term[2][64] = {
{
0b0000110111,
0b10,
0b11,
0b010,
0b011,
0b0011,
0b0010,
0b00011,
0b000101,
0b000100,
0b0000100,
0b0000101,
0b0000111,
0b00000100,
0b00000111,
0b000011000,
0b0000010111,
0b0000011000,
0b0000001000,
0b00001100111,
0b00001101000,
0b00001101100,
0b00000110111,
0b00000101000,
0b00000010111,
0b00000011000,
0b000011001010,
0b000011001011,
0b000011001100,
0b000011001101,
0b000001101000,
0b000001101001,
0b000001101010,
0b000001101011,
0b000011010010,
0b000011010011,
0b000011010100,
0b000011010101,
0b000011010110,
0b000011010111,
0b000001101100,
0b000001101101,
0b000011011010,
0b000011011011,
0b000001010100,
0b000001010101,
0b000001010110,
0b000001010111,
0b000001100100,
0b000001100101,
0b000001010010,
0b000001010011,
0b000000100100,
0b000000110111,
0b000000111000,
0b000000100111,
0b000000101000,
0b000001011000,
0b000001011001,
0b000000101011,
0b000000101100,
0b000001011010,
0b000001100110,
0b000001100111
},
{
0b00110101,
0b000111,
0b0111,
0b1000,
0b1011,
0b1100,
0b1110,
0b1111,
0b10011,
0b10100,
0b00111,
0b01000,
0b001000,
0b000011,
0b110100,
0b110101,
0b101010,
0b101011,
0b0100111,
0b0001100,
0b0001000,
0b0010111,
0b0000011,
0b0000100,
0b0101000,
0b0101011,
0b0010011,
0b0100100,
0b0011000,
0b00000010,
0b00000011,
0b00011010,
0b00011011,
0b00010010,
0b00010011,
0b00010100,
0b00010101,
0b00010110,
0b00010111,
0b00101000,
0b00101001,
0b00101010,
0b00101011,
0b00101100,
0b00101101,
0b00000100,
0b00000101,
0b00001010,
0b00001011,
0b01010010,
0b01010011,
0b01010100,
0b01010101,
0b00100100,
0b00100101,
0b01011000,
0b01011001,
0b01011010,
0b01011011,
0b01001010,
0b01001011,
0b00110010,
0b00110011,
0b00110100
}
};
static const unsigned t4_term_length[2][64] = {
{10, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
{8, 6, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}
};
static const unsigned t4_min_term_length[] = {2, 4};
static const unsigned t4_min_make_up_length[] = {10, 5};
static const unsigned t4_max_term_length[] = {12, 8};
static const unsigned t4_max_make_up_length[] = {13, 9};
static const unsigned t4_make_up_length[2][15] = {
{10, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13},
{5, 5, 6, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9}
};
static const unsigned t4_make_up_ind[15] = {64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960};
static const unsigned t4_make_up[2][15] = {
{
0b0000001111,
0b000011001000,
0b000011001001,
0b000001011011,
0b000000110011,
0b000000110100,
0b000000110101,
0b0000001101100,
0b0000001101101,
0b0000001001010,
0b0000001001011,
0b0000001001100,
0b0000001001101,
0b0000001110010,
0b0000001110011
},
{
0b11011,
0b10010,
0b010111,
0b0110111,
0b00110110,
0b00110111,
0b01100100,
0b01100101,
0b01101000,
0b01100111,
0b011001100,
0b011001101,
0b011010010,
0b011010011,
0b011010100
}
};
/* The code words for one run length and zero
* run length are described in table 9.1.10.1
* of 3gpp 44.060
*/
const char *one_run_len_code_list[EGPRS_CODEWORDS] = {
"00110101",
"000111",
"0111",
"1000",
"1011",
"1100",
"1110",
"1111",
"10011",
"10100",
"00111",
"01000",
"001000",
"000011",
"110100",
"110101",
"101010",
"101011",
"0100111",
"0001100",
"0001000",
"0010111",
"0000011",
"0000100",
"0101000",
"0101011",
"0010011",
"0100100",
"0011000",
"00000010",
"00000011",
"00011010",
"00011011",
"00010010",
"00010011",
"00010100",
"00010101",
"00010110",
"00010111",
"00101000",
"00101001",
"00101010",
"00101011",
"00101100",
"00101101",
"00000100",
"00000101",
"00001010",
"00001011",
"01010010",
"01010011",
"01010100",
"01010101",
"00100100",
"00100101",
"01011000",
"01011001",
"01011010",
"01011011",
"01001010",
"01001011",
"00110010",
"00110011",
"00110100",
"11011",
"10010",
"010111",
"0110111",
"00110110",
"00110111",
"01100100",
"01100101",
"01101000",
"01100111",
"011001100",
"011001101",
"011010010",
"011010011",
"011010100"
};
const char *zero_run_len_code_list[EGPRS_CODEWORDS] = {
"0000110111",
"10",
"11",
"010",
"011",
"0011",
"0010",
"00011",
"000101",
"000100",
"0000100",
"0000101",
"0000111",
"00000100",
"00000111",
"000011000",
"0000010111",
"0000011000",
"0000001000",
"00001100111",
"00001101000",
"00001101100",
"00000110111",
"00000101000",
"00000010111",
"00000011000",
"000011001010",
"000011001011",
"000011001100",
"000011001101",
"000001101000",
"000001101001",
"000001101010",
"000001101011",
"000011010010",
"000011010011",
"000011010100",
"000011010101",
"000011010110",
"000011010111",
"000001101100",
"000001101101",
"000011011010",
"000011011011",
"000001010100",
"000001010101",
"000001010110",
"000001010111",
"000001100100",
"000001100101",
"000001010010",
"000001010011",
"000000100100",
"000000110111",
"000000111000",
"000000100111",
"000000101000",
"000001011000",
"000001011001",
"000000101011",
"000000101100",
"000001011010",
"000001100110",
"000001100111",
"0000001111",
"000011001000",
"000011001001",
"000001011011",
"000000110011",
"000000110100",
"000000110101",
"0000001101100",
"0000001101101",
"0000001001010",
"0000001001011",
"0000001001100",
"0000001001101",
"0000001110010",
"0000001110011"
};
/* Calculate runlength of a codeword
* \param root[in] Root of Ones or Zeros tree
* \param bmbuf[in] Received compressed bitmap buf
* \param bit_pos[in] The start bit pos to read codeword
* \param len_codewd[in] Length of code word
* \param rlen[out] Caluculate run length
*/
static int search_runlen(
egprs_compress_node *root,
const uint8_t *bmbuf,
uint8_t bit_pos,
uint8_t *len_codewd,
uint16_t *rlen)
{
egprs_compress_node *iter;
uint8_t dir;
iter = root;
*len_codewd = 0;
while (iter->run_length == -1) {
if ((!iter->left) && (!iter->right))
return -1;
/* get the bit value at the bitpos and put it in right most of dir */
dir = ((bmbuf[BITS_TO_BYTES(bit_pos) - 1]
>> (7 - MOD8(bit_pos))) & 0x01);
(bit_pos)++;
(*len_codewd)++;
if (((dir&0x01) == 0) && (iter->left != NULL))
iter = iter->left;
else if (((dir&0x01) == 1) && (iter->right != NULL))
iter = iter->right;
else
return -1;
}
LOGP(DRLCMACUL, LOGL_DEBUG, "Run_length = %d\n", iter->run_length);
(*rlen) = (iter->run_length);
return 1;
}
/* Function to decompress compressed received block bitmap
* \param compress_bmap_len[in] Compressed bitmap length
* \param color_code_bit[in] Color code 1 for Ones runlength 0 for Zero runlength
* \param orig_crbb_buf[in] Received block crbb bitmap
* \param dest[out] Uncompressed bitvector
*/
int egprs_compress::decompress_crbb(
int8_t compress_bmap_len,
bool color_code_bit,
const uint8_t *orig_crbb_buf,
bitvec *dest)
{
uint8_t bit_pos = 0;
uint8_t data = 0x0;
egprs_compress_node *list = NULL;
uint8_t nbits = 0; /* number of bits of codeword */
uint16_t run_length = 0;
uint16_t cbmaplen = 0; /* compressed bitmap part after decompression */
unsigned wp = dest->cur_bit;
int rc = 0;
egprs_compress *compress = egprs_compress::instance();
while (compress_bmap_len > 0) {
if (color_code_bit == 1) {
data = 0xff;
list = compress->ones_list;
} else {
data = 0x00;
list = compress->zeros_list;
}
rc = search_runlen(list, orig_crbb_buf,
bit_pos, &nbits, &run_length);
if (rc == -1)
return -1;
/* If run length > 64, need makeup and terminating code */
if (run_length < 64)
color_code_bit ? color_code_bit = 0 : color_code_bit = 1;
cbmaplen = cbmaplen + run_length;
/* put run length of Ones in uncompressed bitmap */
while (run_length != 0) {
if (run_length > 8) {
bitvec_write_field(dest, wp, data, 8);
run_length = run_length - 8;
} else {
bitvec_write_field(dest, wp, data, run_length);
run_length = 0;
}
}
bit_pos = bit_pos + nbits;
compress_bmap_len = compress_bmap_len - nbits;
}
return 0;
}
void egprs_compress::decode_tree_init()
{
ones_list = create_tree_node(tall_pcu_ctx);
zeros_list = create_tree_node(tall_pcu_ctx);
build_codewords(ones_list, one_run_len_code_list);
build_codewords(zeros_list, zero_run_len_code_list);
}
void compress_bitmap(
uint16_t *run_len_cnt, /* cnt: run length count */
uint16_t *codewrd_bitmap, /* code word */
int16_t *codewrd_len, /* number of bits in the code word */
bitvec *crbb_vec, /* bitmap buffer to put code word in */
uint8_t clr_code)
{
int i = 0;
unsigned writeIndex = crbb_vec->cur_bit;
*codewrd_bitmap = 0;
*codewrd_len = 0;
if ((*run_len_cnt) >= 64) {
for (i = 0; i < 15; i++) {
if (t4_make_up_ind[i] == *run_len_cnt) {
*codewrd_bitmap = t4_make_up[clr_code][i];
*codewrd_len = t4_make_up_length[clr_code][i];
}
}
} else {
*codewrd_bitmap = t4_term[clr_code][*run_len_cnt];
*codewrd_len = t4_term_length[clr_code][*run_len_cnt];
}
bitvec_write_field(crbb_vec, writeIndex, (*codewrd_bitmap), (*codewrd_len));
}
int osmo_t4_compress(struct bitvec *bv)
{
uint8_t crbb_len = 0;
uint8_t uclen_crbb = 0;
uint8_t crbb_bitmap[127] = {'\0'};
uint8_t color_code = (bv->data[0] & 0x80)>>7;
struct bitvec crbb_vec;
crbb_vec.data = crbb_bitmap;
crbb_vec.cur_bit = 0;
crbb_vec.data_len = 127;
bv->data_len = bv->cur_bit;
bv->cur_bit = 0;
if (compress_rbb(bv, &crbb_vec, &uclen_crbb, 23*8)) {
memcpy(bv->data, crbb_bitmap, (crbb_len+7)/8);
bv->cur_bit = crbb_len;
bv->data_len = (crbb_len+7)/8;
return color_code;
}
else
printf("Encode failed\n");
return -1;
}
/*! \brief compression algorithm using T4 encoding
* the compressed bitmap's are copied in crbb_bitmap
* \param[in] rbb_vec bit vector to be encoded
* \return 1 if compression is success or 0 for failure
*/
int compress_rbb(
struct bitvec *urbb_vec,
struct bitvec *crbb_vec,
uint8_t *uclen_crbb, /* Uncompressed bitmap len in CRBB */
uint8_t max_bits) /* max remaining bits */
{
bool run_len_bit;
int buflen = urbb_vec->cur_bit;
int total_bits = urbb_vec->cur_bit;
uint16_t rlen;
uint16_t temprl = 0;
uint16_t cbmap = 0; /* Compressed code word */
int16_t nbits; /* Length of code word */
uint16_t uclen = 0;
int16_t clen = 0;
uint8_t clr_code = 0;
urbb_vec->cur_bit = 0;
run_len_bit = (urbb_vec->data[0] & 0x80)>>7;
while (buflen > 0) {
temprl = 0;
/* Find Run length */
if (run_len_bit == 1)
rlen = bitvec_rl_curbit(urbb_vec, true, total_bits);
else
rlen = bitvec_rl_curbit(urbb_vec, false, total_bits);
buflen = buflen - rlen;
/* if rlen> 64 need 2 code words */
/*Compress the bits */
if (run_len_bit == 0) {
clr_code = 0;
if (rlen >= 64) {
temprl = (rlen/64)*64;
compress_bitmap(&temprl, &cbmap, &nbits,
crbb_vec, clr_code);
clen = clen + nbits;
}
temprl = MOD64(rlen);
compress_bitmap(&temprl, &cbmap, &nbits,
crbb_vec, clr_code);
/* next time the run length will be Ones */
run_len_bit = 1;
} else {
clr_code = 1;
if (rlen >= 64) {
temprl = (rlen/64)*64;
compress_bitmap(&temprl, &cbmap, &nbits,
crbb_vec, clr_code);
clen = clen + nbits;
}
temprl = MOD64(rlen);
compress_bitmap(&temprl, &cbmap, &nbits,
crbb_vec, clr_code);
/* next time the run length will be Zeros */
run_len_bit = 0;
}
uclen = uclen + (rlen);
clen = clen + nbits;
/*compressed bitmap exceeds the buffer space */
if (clen > max_bits) {
uclen = uclen - rlen;
clen = clen - nbits;
break;
}
}
crbb_vec->cur_bit = clen;
*uclen_crbb = uclen;
if (clen >= uclen)
/* No Gain is observed, So no need to compress */
return 0;
else
LOGP(DRLCMACUL, LOGL_DEBUG, "CRBB bitmap = %s\n", osmo_hexdump(crbb_vec->data, (crbb_vec->cur_bit+7)/8));
/* Add compressed bitmap to final buffer */
return 1;
}

View File

@ -0,0 +1,58 @@
/* egprs_rlc_compression.h
* Routines for EGPRS RLC bitmap compression handling
*/
#pragma once
#define BITS_TO_BYTES(X) (((X) ? (X)/8 : 0) + 1)
#define MOD8(X) ((X) & (0x07))
#define MOD64(X) ((X + 64) & 0x3F)
typedef struct egprs_compress_node{
struct egprs_compress_node *left;
struct egprs_compress_node *right;
int run_length;
} egprs_compress_node;
int decompress_crbb(int8_t compress_bmap_len, uint8_t clr_code_bit,
const uint8_t *orig_buf, bitvec *dest);
int osmo_t4_compress(struct bitvec *bv);
int compress_rbb(
struct bitvec *urbb_vec,/*!< input bitvector to compress */
struct bitvec *crbb_vec, /*!< Compressed bitmap len */
uint8_t *uclen_crbb, /*!< Uncompressed bitmap len in CRBB */
uint8_t max_bits /*!< Maximum remaining bits */
);
/* Singleton to manage the EGPRS compression algorithm. */
class egprs_compress
{
public:
static int decompress_crbb(int8_t compress_bmap_len,
bool color_code_bit, const uint8_t *orig_buf,
bitvec *dest);
private:
static egprs_compress *s_instance;
egprs_compress_node *ones_list;
egprs_compress_node *zeros_list;
void decode_tree_init(void);
static egprs_compress *instance()
{
if (!s_instance)
s_instance = new egprs_compress;
return s_instance;
}
egprs_compress()
{
decode_tree_init();
}
egprs_compress_node *create_tree_node(void *);
void build_codewords(egprs_compress_node *root, const char *cdwd[]);
/* singleton class, so this private destructor is left unimplemented. */
~egprs_compress();
};

View File

@ -24,14 +24,22 @@
#include <bts.h>
#include <tbf.h>
#include <gprs_debug.h>
#include <egprs_rlc_compression.h>
extern "C" {
#include <osmocom/gprs/protocol/gsm_04_60.h>
}
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
static int write_ia_rest_downlink(
gprs_rlcmac_dl_tbf *tbf,
bitvec * dest, unsigned& wp,
uint8_t polling, uint32_t fn,
uint8_t polling, bool ta_valid, uint32_t fn,
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
{
if (!tbf) {
@ -54,7 +62,7 @@ static int write_ia_rest_downlink(
}
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
bitvec_write_field(dest, wp,polling,1); // Polling Bit
bitvec_write_field(dest, wp,!polling,1); // TA_VALID ???
bitvec_write_field(dest, wp, ta_valid, 1); // N. B: NOT related to TAI!
if (ta_idx < 0) {
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
} else {
@ -140,11 +148,71 @@ static int write_ia_rest_egprs_uplink(
gprs_rlcmac_ul_tbf *tbf,
bitvec * dest, unsigned& wp,
uint8_t usf, uint32_t fn,
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
enum ph_burst_type burst_type, uint16_t ra)
{
LOGP(DRLCMACUL, LOGL_ERROR,
"EGPRS Packet Uplink Assignment is not yet implemented\n");
return -EINVAL;
unsigned int ws_enc = 0;
uint8_t extended_ra = 0;
extended_ra = (ra & 0x1F);
bitvec_write_field(dest, wp, 1, 2); /* LH */
bitvec_write_field(dest, wp, 0, 2); /* 0 EGPRS Uplink Assignment */
bitvec_write_field(dest, wp, extended_ra, 5); /* Extended RA */
bitvec_write_field(dest, wp, 0, 1); /* Access technology Request */
if (tbf == NULL) {
bitvec_write_field(dest, wp, 0, 1); /* multiblock allocation */
if (alpha) {
bitvec_write_field(dest, wp, 0x1, 1); /* ALPHA =yes */
bitvec_write_field(dest, wp, alpha, 4); /* ALPHA */
} else {
bitvec_write_field(dest, wp, 0x0, 1); /* ALPHA = no */
}
bitvec_write_field(dest, wp, gamma, 5); /* GAMMA power contrl */
bitvec_write_field(dest, wp, (fn / (26 * 51)) % 32, 5);/* T1' */
bitvec_write_field(dest, wp, fn % 51, 6); /* T3 */
bitvec_write_field(dest, wp, fn % 26, 5); /* T2 */
bitvec_write_field(dest, wp, 0, 2); /* Radio block allocation */
bitvec_write_field(dest, wp, 0, 1);
} else {
ws_enc = (tbf->m_window.ws() - 64) / 32;
bitvec_write_field(dest, wp, 1, 1); /* single block alloc */
bitvec_write_field(dest, wp, tbf->tfi(), 5);/* TFI assignment */
bitvec_write_field(dest, wp, 0, 1); /* polling bit */
bitvec_write_field(dest, wp, 0, 1); /* constant */
bitvec_write_field(dest, wp, usf, 3); /* USF bit */
bitvec_write_field(dest, wp, 0, 1); /* USF granularity */
bitvec_write_field(dest, wp, 0, 1); /* P0 */
/* MCS */
bitvec_write_field(dest, wp, tbf->current_cs().to_num()-1, 4);
/* tlli channel block */
bitvec_write_field(dest, wp, tbf->tlli(), 1);
bitvec_write_field(dest, wp, 0, 1); /* BEP period present */
bitvec_write_field(dest, wp, 0, 1); /* resegmentation */
bitvec_write_field(dest, wp, ws_enc, 5);/* egprs window_size */
if (alpha) {
bitvec_write_field(dest, wp, 0x1, 1); /* ALPHA =yes */
bitvec_write_field(dest, wp, alpha, 4); /* ALPHA */
} else {
bitvec_write_field(dest, wp, 0x0, 1); /* ALPHA = no */
}
bitvec_write_field(dest, wp, gamma, 5); /* GAMMA power contrl */
bitvec_write_field(dest, wp, 0, 1); /* TIMING_ADVANCE_INDEX */
bitvec_write_field(dest, wp, 0, 1); /* TBF_STARTING_TIME_FLAG */
bitvec_write_field(dest, wp, 0, 1); /* NULL */
}
return 0;
}
/*
@ -153,10 +221,10 @@ static int write_ia_rest_egprs_uplink(
*/
int Encoding::write_immediate_assignment(
struct gprs_rlcmac_tbf *tbf,
bitvec * dest, uint8_t downlink, uint8_t ra,
bitvec * dest, uint8_t downlink, uint16_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t usf, uint8_t polling, uint32_t fn, uint8_t alpha,
uint8_t gamma, int8_t ta_idx)
uint8_t gamma, int8_t ta_idx, enum ph_burst_type burst_type, uint8_t sb)
{
unsigned wp = 0;
int plen;
@ -182,7 +250,13 @@ int Encoding::write_immediate_assignment(
bitvec_write_field(dest, wp,arfcn,10); // ARFCN
//10.5.2.30 Request Reference
bitvec_write_field(dest, wp,ra,8); // RA
if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
bitvec_write_field(dest, wp, 0x7f, 8); /* RACH value */
} else {
bitvec_write_field(dest, wp, ra, 8); /* RACH value */
}
bitvec_write_field(dest, wp,(ref_fn / (26 * 51)) % 32,5); // T1'
bitvec_write_field(dest, wp,ref_fn % 51,6); // T3
bitvec_write_field(dest, wp,ref_fn % 26,5); // T2
@ -204,12 +278,13 @@ int Encoding::write_immediate_assignment(
if (downlink)
rc = write_ia_rest_downlink(as_dl_tbf(tbf), dest, wp,
polling, fn,
polling, gsm48_ta_is_valid(ta), fn,
alpha, gamma, ta_idx);
else if (as_ul_tbf(tbf) && as_ul_tbf(tbf)->is_egprs_enabled())
else if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2)))
rc = write_ia_rest_egprs_uplink(as_ul_tbf(tbf), dest, wp,
usf, fn,
alpha, gamma, ta_idx);
alpha, gamma, ta_idx, burst_type, ra);
else
rc = write_ia_rest_uplink(as_ul_tbf(tbf), dest, wp,
usf, fn,
@ -258,7 +333,7 @@ void Encoding::write_packet_uplink_assignment(
if (!use_egprs) {
bitvec_write_field(dest, wp,0x0,1); // Message escape
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 2); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 2); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
bitvec_write_field(dest, wp,tbf->ta(),6); // TIMING_ADVANCE_VALUE
@ -276,11 +351,12 @@ void Encoding::write_packet_uplink_assignment(
bitvec_write_field(dest, wp,0x0,1); // No CONTENTION_RESOLUTION_TLLI
bitvec_write_field(dest, wp,0x0,1); // No COMPACT reduced MA
bitvec_write_field(dest, wp,tbf->current_cs().to_num()-1, 4); // EGPRS Modulation and Coding IE
bitvec_write_field(dest, wp,0x0,1); // No RESEGMENT
/* 0: no RESEGMENT, 1: Segmentation*/
bitvec_write_field(dest, wp, 0x1, 1);
bitvec_write_field(dest, wp,ws_enc,5); // EGPRS Window Size
bitvec_write_field(dest, wp,0x0,1); // No Access Technologies Request
bitvec_write_field(dest, wp,0x0,1); // No ARAC RETRANSMISSION REQUEST
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
bitvec_write_field(dest, wp,0x0,1); // No BEP_PERIOD2
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
@ -534,17 +610,44 @@ static void write_packet_uplink_ack_gprs(
static void write_packet_ack_nack_desc_egprs(
struct gprs_rlcmac_bts *bts, bitvec * dest, unsigned& wp,
gprs_rlc_ul_window *window, bool is_final)
gprs_rlc_ul_window *window, bool is_final, unsigned& rest_bits)
{
int urbb_len = 0;
int crbb_len = 0;
int len;
uint8_t crbb_len = 0;
uint8_t len;
bool bow = true;
bool eow = true;
int ssn = window->mod_sns(window->v_q() + 1);
int num_blocks = window->mod_sns(window->v_r() - window->v_q());
int esn_crbb = window->mod_sns(ssn - 1);
int rest_bits = dest->data_len * 8 - wp;
static uint8_t rbb[RLC_EGPRS_MAX_WS] = {'\0'};
uint8_t iter = 0;
int is_compressed = 0;
bool try_compression = false;
uint8_t ucmp_bmplen;
uint8_t crbb_bitmap[23] = {'\0'};
bitvec ucmp_vec;
bitvec crbb_vec;
uint8_t uclen_crbb = 0;
bool len_coded = true;
uint8_t crbb_start_clr_code;
uint8_t i;
#if 0
/* static size of 16 bits*/
..0. .... = ACKNACK: (Union)
Desc
...0 .... = FINAL_ACK_INDICATION: False
.... 1... = BEGINNING_OF_WINDOW: 1
.... .1.. = END_OF_WINDOW: 1
.... ..10 0101 0001 1... .... = STARTING_SEQUENCE_NUMBER: 1187
.0.. .... = CRBB Exist: 0
#endif
rest_bits -= 16;
if (num_blocks > 0)
/* V(Q) is NACK and omitted -> SSN = V(Q) + 1 */
@ -553,31 +656,69 @@ static void write_packet_ack_nack_desc_egprs(
if (num_blocks > window->ws())
num_blocks = window->ws();
/* Try Compression as number of blocks does not fit */
if (num_blocks > rest_bits) {
eow = false;
urbb_len = rest_bits;
/* TODO: use compression, start encoding bits and stop when the
* space is exhausted. Use the first combination that encodes
* all bits. If there is none, use the combination that encodes
* the largest number of bits (e.g. by setting num_blocks to the
* max and repeating the construction).
*/
} else if (num_blocks > rest_bits - 9) {
/* union bit and length field take 9 bits */
eow = false;
urbb_len = rest_bits - 9;
/* TODO: use compression (see above) */
try_compression = true;
}
if (try_compression == true) {
ucmp_bmplen = window->update_egprs_rbb(rbb);
ucmp_vec.data = rbb;
ucmp_vec.cur_bit = ucmp_bmplen;
ucmp_vec.data_len = 127;
crbb_vec.data = crbb_bitmap;
crbb_vec.cur_bit = 0;
crbb_vec.data_len = 127;
LOGP(DRLCMACUL, LOGL_DEBUG,
"rest_bits=%d uncompressed len %d and uncompressed bitmap = %s\n",
rest_bits, ucmp_bmplen,
osmo_hexdump(ucmp_vec.data, (ucmp_bmplen+7)/8));
is_compressed = compress_rbb(&ucmp_vec, /* Uncompressed bitmap*/
&crbb_vec, /*Compressed bitmap vector */
&uclen_crbb,
(rest_bits - 8 - 8));/* CRBBlength:7 colourcode:1 dissector length:8*/
LOGP(DRLCMACUL, LOGL_DEBUG,
"the ucmp len=%d uclen_crbb=%d num_blocks=%d crbb length %d, "
"and the CRBB bitmap = %s\n",
ucmp_bmplen, uclen_crbb, num_blocks, crbb_vec.cur_bit,
osmo_hexdump(crbb_bitmap, (crbb_vec.cur_bit+7)/8));
crbb_len = crbb_vec.cur_bit;
}
if (urbb_len + crbb_len == rest_bits)
len = -1;
else if (crbb_len == 0)
if (is_compressed == 0) {
/* length field takes 8 bits*/
if (num_blocks > rest_bits - 8) {
eow = false;
urbb_len = rest_bits;
len_coded = false;
} else if (num_blocks == rest_bits) {
urbb_len = rest_bits;
len_coded = false;
} else
urbb_len = num_blocks;
len = urbb_len + 15;
else
} else {
if (num_blocks > uclen_crbb) {
eow = false;
urbb_len = num_blocks - uclen_crbb;
}
/* Union bit takes 1 bit */
/* Other fields in descr of compresed bitmap takes 23 bits*/
if (urbb_len > (rest_bits - crbb_len - 8)) {
eow = false;
len_coded = false;
urbb_len = rest_bits - crbb_len - 8;
} else if (urbb_len > (rest_bits - crbb_len - 8 - 8)) {
eow = false;
len_coded = false;
urbb_len = rest_bits - crbb_len - 8 - 8;
}
len = urbb_len + crbb_len + 23;
}
/* EGPRS Ack/Nack Description IE */
if (len < 0) {
if (len_coded == false) {
bitvec_write_field(dest, wp, 0, 1); // 0: don't have length
} else {
bitvec_write_field(dest, wp, 1, 1); // 1: have length
@ -588,17 +729,37 @@ static void write_packet_ack_nack_desc_egprs(
bitvec_write_field(dest, wp, bow, 1); // BEGINNING_OF_WINDOW
bitvec_write_field(dest, wp, eow, 1); // END_OF_WINDOW
bitvec_write_field(dest, wp, ssn, 11); // STARTING_SEQUENCE_NUMBER
bitvec_write_field(dest, wp, 0, 1); // 0: don't have CRBB
/* TODO: Add CRBB support */
if (is_compressed) {
bitvec_write_field(dest, wp, 1, 1); // CRBB_Exist
bitvec_write_field(dest, wp, crbb_len, 7); // CRBB_LENGTH
crbb_start_clr_code = (0x80 & ucmp_vec.data[0])>>7;
bitvec_write_field(dest, wp, crbb_start_clr_code, 1); // CRBB_clr_code
LOGP(DRLCMACUL, LOGL_DEBUG,
"EGPRS CRBB, crbb_len = %d, crbb_start_clr_code = %d\n",
crbb_len, crbb_start_clr_code);
while (crbb_len != 0) {
if (crbb_len > 8) {
bitvec_write_field(dest, wp, crbb_bitmap[iter], 8);
crbb_len = crbb_len - 8;
iter++;
} else {
bitvec_write_field(dest, wp, crbb_bitmap[iter], crbb_len);
crbb_len = 0;
}
}
esn_crbb = window->mod_sns(esn_crbb + uclen_crbb);
} else {
bitvec_write_field(dest, wp, 0, 1); // CRBB_Exist
}
LOGP(DRLCMACUL, LOGL_DEBUG,
" - EGPRS URBB, len = %d, SSN = %d, ESN_CRBB = %d, "
"EGPRS URBB, urbb len = %d, SSN = %d, ESN_CRBB = %d, "
"len present = %s,desc len = %d, "
"SNS = %d, WS = %d, V(Q) = %d, V(R) = %d%s%s\n",
urbb_len, ssn, esn_crbb,
urbb_len, ssn, esn_crbb, len_coded ? "yes" : "No" ,len,
window->sns(), window->ws(), window->v_q(), window->v_r(),
bow ? ", BOW" : "", eow ? ", EOW" : "");
for (int i = urbb_len; i > 0; i--) {
for (i = urbb_len; i > 0; i--) {
/* Set bit at the appropriate position (see 3GPP TS 04.60 12.3.1) */
bool is_ack = window->m_v_n.is_received(esn_crbb + i);
bitvec_write_field(dest, wp, is_ack, 1);
@ -610,9 +771,11 @@ static void write_packet_uplink_ack_egprs(
struct gprs_rlcmac_ul_tbf *tbf, bool is_final)
{
bitvec_write_field(dest, wp, 0, 2); // fixed 00
bitvec_write_field(dest, wp, 2, 4); // CHANNEL_CODING_COMMAND: MCS-3
// bitvec_write_field(dest, wp, tbf->current_cs() - 1, 4); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp, 0, 1); // 0: no RESEGMENT (nyi)
/* CHANNEL_CODING_COMMAND */
bitvec_write_field(dest, wp,
tbf->current_cs().to_num() - 1, 4);
/* 0: no RESEGMENT, 1: Segmentation*/
bitvec_write_field(dest, wp, 1, 1);
bitvec_write_field(dest, wp, 1, 1); // PRE_EMPTIVE_TRANSMISSION, TODO: This resembles GPRS, change it?
bitvec_write_field(dest, wp, 0, 1); // 0: no PRR_RETRANSMISSION_REQUEST, TODO: clarify
bitvec_write_field(dest, wp, 0, 1); // 0: no ARAC_RETRANSMISSION_REQUEST, TODO: clarify
@ -624,7 +787,9 @@ static void write_packet_uplink_ack_egprs(
bitvec_write_field(dest, wp, 0, 1); // 0: don't have Power Control Parameters
bitvec_write_field(dest, wp, 0, 1); // 0: don't have Extension Bits
write_packet_ack_nack_desc_egprs(bts, dest, wp, &tbf->m_window, is_final);
/* -2 for last bit 0 mandatory and REL5 not supported */
unsigned bits_ack_nack = dest->data_len * 8 - wp - 2;
write_packet_ack_nack_desc_egprs(bts, dest, wp, &tbf->m_window, is_final, bits_ack_nack);
bitvec_write_field(dest, wp, 0, 1); // fixed 0
bitvec_write_field(dest, wp, 0, 1); // 0: don't have REL 5
@ -744,20 +909,20 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
egprs1->usf = rlc->usf;
egprs1->es_p = rlc->es_p;
egprs1->rrbp = rlc->rrbp;
egprs1->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
egprs1->tfi_b = rlc->tfi >> 1; /* 4 bits */
egprs1->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
egprs1->tfi_lo = rlc->tfi >> 1; /* 4 bits */
egprs1->pr = rlc->pr;
egprs1->cps = rlc->cps;
egprs1->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs1->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs1->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
egprs1->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs1->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs1->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
bsn_delta = (rlc->block_info[1].bsn - rlc->block_info[0].bsn) &
(RLC_EGPRS_SNS - 1);
egprs1->bsn2_a = bsn_delta >> 0; /* 7 bits LSB */
egprs1->bsn2_b = bsn_delta >> 7; /* 3 bits */
egprs1->bsn2_hi = bsn_delta >> 0; /* 7 bits LSB */
egprs1->bsn2_lo = bsn_delta >> 7; /* 3 bits */
/* first FBI/E header */
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
@ -783,14 +948,14 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
egprs2->usf = rlc->usf;
egprs2->es_p = rlc->es_p;
egprs2->rrbp = rlc->rrbp;
egprs2->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
egprs2->tfi_b = rlc->tfi >> 1; /* 4 bits */
egprs2->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
egprs2->tfi_lo = rlc->tfi >> 1; /* 4 bits */
egprs2->pr = rlc->pr;
egprs2->cps = rlc->cps;
egprs2->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs2->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs2->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
egprs2->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs2->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs2->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
e_fbi_header |= rlc->block_info[0].cv == 0 ? 0x02 : 0; /* FBI */
@ -807,14 +972,14 @@ int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
egprs3->usf = rlc->usf;
egprs3->es_p = rlc->es_p;
egprs3->rrbp = rlc->rrbp;
egprs3->tfi_a = rlc->tfi >> 0; /* 1 bit LSB */
egprs3->tfi_b = rlc->tfi >> 1; /* 4 bits */
egprs3->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
egprs3->tfi_lo = rlc->tfi >> 1; /* 4 bits */
egprs3->pr = rlc->pr;
egprs3->cps = rlc->cps;
egprs3->bsn1_a = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs3->bsn1_b = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs3->bsn1_c = rlc->block_info[0].bsn >> 10; /* 1 bit */
egprs3->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
egprs3->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
egprs3->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
egprs3->spb = rlc->block_info[0].spb;
@ -891,11 +1056,21 @@ unsigned int Encoding::rlc_copy_from_aligned_buffer(
return rdbi->data_len;
}
/*!
* \brief (GPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
* \param rdbi rlc/mac block info
* \param llc llc pdu
* \param offset given offset within the rlc/mac block
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
* \param data_block buffer holds rlc/mac data
* \param is_final if this is the last rlc/mac within a TBF
* \param count_payload if not NULL save the written size of payload in bytes into it
* \return the state of the rlc/mac like if there is more space for another chunk
*/
static Encoding::AppendResult rlc_data_to_dl_append_gprs(
struct gprs_rlc_data_block_info *rdbi,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data_block,
bool is_final)
uint8_t *data_block, bool is_final, int *count_payload)
{
int chunk;
int space;
@ -920,6 +1095,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
*e_pointer |= 0x01;
/* fill only space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
/* return data block as message */
*offset = rdbi->data_len;
(*num_chunks)++;
@ -937,6 +1114,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
*e_pointer |= 0x01;
/* fill space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
*offset = rdbi->data_len;
(*num_chunks)++;
rdbi->cv = 0;
@ -964,6 +1143,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
// no need to set e_pointer nor increase delimiter
/* fill only space, which is 1 octet less than chunk */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
/* return data block as message */
*offset = rdbi->data_len;
(*num_chunks)++;
@ -989,6 +1170,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
(*num_chunks)++;
/* copy (rest of) LLC frame to space and reset later */
llc->consume(data, chunk);
if (count_payload)
*count_payload = chunk;
data += chunk;
space -= chunk;
(*offset) += chunk;
@ -1012,11 +1195,22 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
return Encoding::AR_COMPLETED_BLOCK_FILLED;
}
/*!
* \brief (EGPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
* \param rdbi rlc/mac block info
* \param llc llc pdu
* \param offset given offset within the rlc/mac block
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
* \param data_block buffer holds rlc/mac data
* \param is_final if this is the last rlc/mac within a TBF
* \param count_payload if not NULL save the written size of payload in bytes into it
* \return the state of the rlc/mac like if there is more space for another chunk
*/
static Encoding::AppendResult rlc_data_to_dl_append_egprs(
struct gprs_rlc_data_block_info *rdbi,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data_block,
bool is_final)
bool is_final, int *count_payload)
{
int chunk;
int space;
@ -1040,6 +1234,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
chunk, space);
/* fill only space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
/* return data block as message */
*offset = rdbi->data_len;
(*num_chunks)++;
@ -1054,6 +1250,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
"header, and we are done\n", chunk, space);
/* fill space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
*offset = rdbi->data_len;
(*num_chunks)++;
rdbi->cv = 0;
@ -1068,6 +1266,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
chunk, space);
/* fill space */
llc->consume(data, space);
if (count_payload)
*count_payload = space;
*offset = rdbi->data_len;
(*num_chunks)++;
return Encoding::AR_NEED_MORE_BLOCKS;
@ -1098,6 +1298,8 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
(*num_chunks)++;
/* copy (rest of) LLC frame to space and reset later */
llc->consume(data, chunk);
if (count_payload)
*count_payload = chunk;
data += chunk;
space -= chunk;
(*offset) += chunk;
@ -1154,19 +1356,32 @@ static Encoding::AppendResult rlc_data_to_dl_append_egprs(
return Encoding::AR_COMPLETED_BLOCK_FILLED;
}
/*!
* \brief Encoding::rlc_data_to_dl_append
* \param rdbi rlc/mac block info
* \param cs the coding scheme to use
* \param llc llc pdu
* \param offset given offset within the rlc/mac block
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
* \param data_block buffer holds rlc/mac data
* \param is_final if this is the last rlc/mac within a TBF
* \param count_payload if not NULL save the written size of payload in bytes into it
* \return the state of the rlc/mac like if there is more space for another chunk
*/
Encoding::AppendResult Encoding::rlc_data_to_dl_append(
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data_block,
bool is_final)
uint8_t *data_block, bool is_final, int *count_payload)
{
if (cs.isGprs())
return rlc_data_to_dl_append_gprs(rdbi,
llc, offset, num_chunks, data_block, is_final);
llc, offset, num_chunks, data_block, is_final,
count_payload);
if (cs.isEgprs())
return rlc_data_to_dl_append_egprs(rdbi,
llc, offset, num_chunks, data_block, is_final);
llc, offset, num_chunks, data_block, is_final,
count_payload);
LOGP(DRLCMACDL, LOGL_ERROR, "%s data block encoding not implemented\n",
cs.name());

View File

@ -23,6 +23,9 @@
#include <stdint.h>
#include <gsm_rlcmac.h>
#include <gprs_coding_scheme.h>
extern "C" {
#include <osmocom/gsm/l1sap.h>
}
struct gprs_rlcmac_bts;
struct gprs_rlcmac_tbf;
@ -40,11 +43,14 @@ class Encoding {
public:
static int write_immediate_assignment(
struct gprs_rlcmac_tbf *tbf,
bitvec * dest, uint8_t downlink, uint8_t ra,
bitvec * dest, uint8_t downlink, uint16_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts,
uint8_t tsc, uint8_t usf, uint8_t polling,
uint32_t fn, uint8_t alpha, uint8_t gamma,
int8_t ta_idx);
int8_t ta_idx,
enum ph_burst_type burst_type =
GSM_L1_BURST_TYPE_ACCESS_0,
uint8_t sb = 1);
static void write_packet_uplink_assignment(
struct gprs_rlcmac_bts *bts,
@ -91,6 +97,5 @@ public:
static AppendResult rlc_data_to_dl_append(
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
gprs_llc *llc, int *offset, int *num_chunks,
uint8_t *data,
bool is_final);
uint8_t *data, bool is_final, int *count_payload);
};

View File

@ -21,6 +21,38 @@
#include "gprs_coding_scheme.h"
/*
* 44.060 Table 8.1.1.1 and Table 8.1.1.2
* It has 3 level indexing. 0th level is ARQ type
* 1st level is Original MCS( index 0 corresponds to MCS1 and so on)
* 2nd level is MS MCS (index 0 corresponds to MCS1 and so on)
*/
enum GprsCodingScheme::Scheme GprsCodingScheme::egprs_mcs_retx_tbl[MAX_NUM_ARQ]
[MAX_NUM_MCS][MAX_NUM_MCS] = {
{
{MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1},
{MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2},
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3},
{MCS1, MCS1, MCS1, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4},
{MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7},
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9},
{MCS2, MCS2, MCS2, MCS2, MCS5, MCS5, MCS7, MCS7, MCS7},
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS8, MCS8},
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS6, MCS6, MCS6, MCS9}
},
{
{MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1, MCS1},
{MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2, MCS2},
{MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3, MCS3},
{MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4, MCS4},
{MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7},
{MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9},
{MCS5, MCS5, MCS5, MCS5, MCS5, MCS5, MCS7, MCS7, MCS7},
{MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS8, MCS8},
{MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS6, MCS9}
}
};
static struct {
struct {
unsigned int bytes;

View File

@ -26,6 +26,12 @@
class GprsCodingScheme {
public:
#define MAX_NUM_ARQ 2 /* max. number of ARQ */
#define MAX_NUM_MCS 9 /* max. number of MCS */
#define EGPRS_ARQ1 0x0
#define EGPRS_ARQ2 0x1
enum Scheme {
UNKNOWN,
CS1, CS2, CS3, CS4,
@ -64,6 +70,7 @@ public:
unsigned int to_num() const;
GprsCodingScheme& operator =(Scheme s);
bool operator == (Scheme s) const;
GprsCodingScheme& operator =(GprsCodingScheme o);
bool isValid() const {return UNKNOWN <= m_scheme && m_scheme <= MCS9;}
@ -105,6 +112,12 @@ public:
static GprsCodingScheme getEgprsByNum(unsigned num);
static const char *modeName(Mode mode);
static Scheme get_retx_mcs(const GprsCodingScheme mcs,
const GprsCodingScheme retx_mcs,
const unsigned arq_type);
static enum Scheme egprs_mcs_retx_tbl[MAX_NUM_ARQ]
[MAX_NUM_MCS][MAX_NUM_MCS];
private:
GprsCodingScheme(int s); /* fail on use */
GprsCodingScheme& operator =(int s); /* fail on use */
@ -188,6 +201,11 @@ inline bool operator ==(GprsCodingScheme a, GprsCodingScheme b)
return GprsCodingScheme::Scheme(a) == GprsCodingScheme::Scheme(b);
}
inline bool GprsCodingScheme::operator == (Scheme scheme) const
{
return this->m_scheme == scheme;
}
inline bool operator !=(GprsCodingScheme a, GprsCodingScheme b)
{
return !(a == b);
@ -213,4 +231,11 @@ inline bool operator >=(GprsCodingScheme a, GprsCodingScheme b)
{
return a == b || a > b;
}
inline GprsCodingScheme::Scheme GprsCodingScheme::get_retx_mcs(
const GprsCodingScheme mcs,
const GprsCodingScheme demanded_mcs,
const unsigned arq_type)
{
return egprs_mcs_retx_tbl[arq_type][mcs.to_num() - 1]
[demanded_mcs.to_num() - 1];
}

View File

@ -32,6 +32,7 @@
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
}
#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
@ -95,7 +96,7 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
m_tlli(tlli),
m_new_ul_tlli(0),
m_new_dl_tlli(0),
m_ta(0),
m_ta(GSM48_TA_INVALID),
m_ms_class(0),
m_egprs_ms_class(0),
m_is_idle(true),
@ -237,7 +238,7 @@ void GprsMs::set_mode(GprsCodingScheme::Mode mode)
if (!m_current_cs_ul.isEgprs()) {
m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
m_bts->bts_data()->initial_mcs_ul);
if (!m_current_cs_dl.isValid())
if (!m_current_cs_ul.isValid())
m_current_cs_ul = GprsCodingScheme::MCS1;
}
if (!m_current_cs_dl.isEgprs()) {
@ -464,11 +465,15 @@ void GprsMs::set_ta(uint8_t ta_)
if (ta_ == m_ta)
return;
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
tlli(), m_ta, ta_);
m_ta = ta_;
if (gsm48_ta_is_valid(ta_)) {
LOGP(DRLCMAC, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
tlli(), m_ta, ta_);
m_ta = ta_;
} else
LOGP(DRLCMAC, LOGL_NOTICE,
"MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
"value %d kept)\n", tlli(), ta_, m_ta);
}
void GprsMs::set_ms_class(uint8_t ms_class_)
@ -574,6 +579,11 @@ GprsCodingScheme GprsMs::max_cs_ul() const
return GprsCodingScheme(GprsCodingScheme::MCS4);
}
void GprsMs::set_current_cs_dl(GprsCodingScheme::Scheme scheme)
{
m_current_cs_dl = scheme;
}
GprsCodingScheme GprsMs::max_cs_dl() const
{
struct gprs_rlcmac_bts *bts_data;
@ -632,8 +642,8 @@ void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
} else if (m_current_cs_ul.isEgprs()) {
/* TODO, use separate table */
if (current_cs_num > 4)
current_cs_num = 4;
if (current_cs_num > MAX_GPRS_CS)
current_cs_num = MAX_GPRS_CS;
low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
} else {

View File

@ -86,6 +86,7 @@ public:
uint8_t egprs_ms_class() const;
void set_ms_class(uint8_t ms_class);
void set_egprs_ms_class(uint8_t ms_class);
void set_current_cs_dl(GprsCodingScheme::Scheme scheme);
GprsCodingScheme current_cs_ul() const;
GprsCodingScheme current_cs_dl() const;

View File

@ -90,7 +90,7 @@ int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
const char *imsi);
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint16_t arfcn,
uint8_t trx, uint8_t ts,
uint32_t fn, uint8_t block_nr);
int gprs_alloc_max_dl_slots_per_ms(struct gprs_rlcmac_bts *bts,

View File

@ -132,7 +132,7 @@ static struct msgb *sched_select_ctrl_msg(
/*
* Assignments for the same direction have lower precedence,
* because they may kill the TBF when the CONTOL ACK is
* because they may kill the TBF when the CONTROL ACK is
* received, thus preventing the others from being processed.
*/
@ -289,7 +289,7 @@ static struct msgb *sched_dummy(void)
}
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint16_t arfcn,
uint8_t trx, uint8_t ts,
uint32_t fn, uint8_t block_nr)
{
struct gprs_rlcmac_pdch *pdch;
@ -300,8 +300,6 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
struct msgb *msg = NULL;
uint32_t poll_fn, sba_fn;
#warning "ARFCN... it is already in the TRX..... is it consistent with it?"
if (trx >= 8 || ts >= 8)
return -EINVAL;
pdch = &bts->trx[trx].pdch[ts];
@ -339,18 +337,28 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
/* Prio 1: select control message */
msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
dl_ass_tbf, ul_ack_tbf);
if (msg)
bts->bts->rlc_sent_control();
/* Prio 2: select data message for downlink */
if (!msg)
if (!msg) {
msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
if (msg)
bts->bts->rlc_sent();
}
/* Prio 3: send dummy contol message */
if (!msg)
if (!msg) {
/* increase counter */
msg = sched_dummy();
if (msg)
bts->bts->rlc_sent_dummy();
}
if (!msg)
return -ENOMEM;
/* msg is now available */
bts->bts->rlc_dl_bytes(msg->data_len);
/* set USF */
OSMO_ASSERT(msgb_length(msg) > 0);
@ -360,7 +368,7 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
gprs_bssgp_update_frames_sent();
/* send PDTCH/PACCH to L1 */
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
pcu_l1if_tx_pdtch(msg, trx, ts, bts->trx[trx].arfcn, fn, block_nr);
return 0;
}

View File

@ -775,9 +775,13 @@ static int find_multi_slots(struct gprs_rlcmac_bts *bts,
rx_window & tx_window, 'C'),
capacity);
#endif
if (capacity <= max_capacity)
continue;
if (bts->maximise_dir == DL_ONLY) {
if (rx_window < max_dl_slots)
continue;
} else {
if (capacity <= max_capacity)
continue;
}
max_capacity = capacity;
max_ul_slots = tx_window;

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,393 @@
/* Copyright (C) 2015 by Yves Godin <support@nuranwireless.com>
* based on:
* femto_l1_if.c
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <errno.h>
#include <nrw/litecell15/litecell15.h>
#include <nrw/litecell15/gsml1prim.h>
#include <nrw/litecell15/gsml1const.h>
#include <nrw/litecell15/gsml1types.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <lc15_l1_if.h>
#include <gprs_debug.h>
#include <pcu_l1_if.h>
extern void *tall_pcu_ctx;
uint32_t l1if_ts_to_hLayer2(uint8_t trx, uint8_t ts)
{
return (ts << 16) | (trx << 24);
}
/* allocate a msgb containing a GsmL1_Prim_t */
struct msgb *l1p_msgb_alloc(void)
{
struct msgb *msg = msgb_alloc(sizeof(GsmL1_Prim_t), "l1_prim");
if (msg)
msg->l1h = msgb_put(msg, sizeof(GsmL1_Prim_t));
return msg;
}
static int l1if_req_pdch(struct lc15l1_hdl *fl1h, struct msgb *msg)
{
struct osmo_wqueue *wqueue = &fl1h->write_q[MQ_PDTCH_WRITE];
if (osmo_wqueue_enqueue(wqueue, msg) != 0) {
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
msgb_free(msg);
}
return 0;
}
static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct lc15l1_hdl *gl1)
{
prim->id = id;
switch (id) {
case GsmL1_PrimId_MphInitReq:
//prim->u.mphInitReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_MphCloseReq:
prim->u.mphCloseReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_MphConnectReq:
prim->u.mphConnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_MphDisconnectReq:
prim->u.mphDisconnectReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_MphActivateReq:
prim->u.mphActivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_MphDeactivateReq:
prim->u.mphDeactivateReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_MphConfigReq:
prim->u.mphConfigReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_MphMeasureReq:
prim->u.mphMeasureReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_MphInitCnf:
case GsmL1_PrimId_MphCloseCnf:
case GsmL1_PrimId_MphConnectCnf:
case GsmL1_PrimId_MphDisconnectCnf:
case GsmL1_PrimId_MphActivateCnf:
case GsmL1_PrimId_MphDeactivateCnf:
case GsmL1_PrimId_MphConfigCnf:
case GsmL1_PrimId_MphMeasureCnf:
break;
case GsmL1_PrimId_MphTimeInd:
break;
case GsmL1_PrimId_MphSyncInd:
break;
case GsmL1_PrimId_PhEmptyFrameReq:
prim->u.phEmptyFrameReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_PhDataReq:
prim->u.phDataReq.hLayer1 = (HANDLE)gl1->hLayer1;
break;
case GsmL1_PrimId_PhConnectInd:
break;
case GsmL1_PrimId_PhReadyToSendInd:
break;
case GsmL1_PrimId_PhDataInd:
break;
case GsmL1_PrimId_PhRaInd:
break;
default:
LOGP(DL1IF, LOGL_ERROR, "unknown L1 primitive %u\n", id);
break;
}
return &prim->u;
}
/* connect PDTCH */
int l1if_connect_pdch(void *obj, uint8_t ts)
{
struct lc15l1_hdl *fl1h = obj;
struct msgb *msg = l1p_msgb_alloc();
GsmL1_MphConnectReq_t *cr;
cr = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConnectReq, fl1h);
cr->u8Tn = ts;
cr->logChComb = GsmL1_LogChComb_XIII;
return l1if_req_pdch(fl1h, msg);
}
static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1h,
GsmL1_PhReadyToSendInd_t *rts_ind)
{
struct gsm_time g_time;
int rc = 0;
gsm_fn2gsmtime(&g_time, rts_ind->u32Fn);
DEBUGP(DL1IF, "Rx PH-RTS.ind %02u/%02u/%02u SAPI=%s\n",
g_time.t1, g_time.t2, g_time.t3,
get_value_string(lc15bts_l1sapi_names, rts_ind->sapi));
switch (rts_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
rts_ind->u32Fn, rts_ind->u8BlockNbr);
case GsmL1_Sapi_Ptcch:
// FIXME
default:
break;
}
return rc;
}
static void get_meas(struct pcu_l1_meas *meas, const GsmL1_MeasParam_t *l1_meas)
{
meas->rssi = (int8_t) (l1_meas->fRssi);
meas->have_rssi = 1;
meas->ber = (uint8_t) (l1_meas->fBer * 100);
meas->have_ber = 1;
meas->bto = (int16_t) (l1_meas->i16BurstTiming);
meas->have_bto = 1;
meas->link_qual = (int16_t) (l1_meas->fLinkQuality);
meas->have_link_qual = 1;
}
static int handle_ph_data_ind(struct lc15l1_hdl *fl1h,
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
{
int rc = 0;
struct pcu_l1_meas meas = {0};
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s\n",
get_value_string(lc15bts_l1sapi_names, data_ind->sapi),
data_ind->hLayer2,
osmo_hexdump(data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size));
/*
* TODO: Add proper bad frame handling here. This could be used
* to switch the used CS. Avoid a crash with the PCU right now
* feed "0 - 1" amount of data.
*/
if (data_ind->msgUnitParam.u8Size == 0)
return -1;
gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
data_ind->msgUnitParam.u8Size-1);
get_meas(&meas, &data_ind->measParam);
switch (data_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
/* drop incomplete UL block */
if (data_ind->msgUnitParam.u8Buffer[0]
!= GsmL1_PdtchPlType_Full)
break;
/* PDTCH / PACCH frame handling */
pcu_rx_data_ind_pdtch(fl1h->trx_no, data_ind->u8Tn,
data_ind->msgUnitParam.u8Buffer + 1,
data_ind->msgUnitParam.u8Size - 1,
data_ind->u32Fn,
&meas);
break;
case GsmL1_Sapi_Ptcch:
// FIXME
break;
default:
LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %s\n",
get_value_string(lc15bts_l1sapi_names, data_ind->sapi));
break;
}
return rc;
}
#define MIN_QUAL_RACH 5.0f
static int handle_ph_ra_ind(struct lc15l1_hdl *fl1h, GsmL1_PhRaInd_t *ra_ind)
{
uint8_t acc_delay;
if (ra_ind->measParam.fLinkQuality < MIN_QUAL_RACH)
return 0;
DEBUGP(DL1IF, "Rx PH-RA.ind");
/* check for under/overflow / sign */
if (ra_ind->measParam.i16BurstTiming < 0)
acc_delay = 0;
else
acc_delay = ra_ind->measParam.i16BurstTiming >> 2;
LOGP(DL1IF, LOGL_NOTICE, "got (P)RACH request, TA = %u (ignored)\n",
acc_delay);
#warning "The (P)RACH request is just dropped here"
#if 0
if (acc_delay > bts->max_ta) {
LOGP(DL1C, LOGL_INFO, "ignoring RACH request %u > max_ta(%u)\n",
acc_delay, btsb->max_ta);
return 0;
}
#endif
return 0;
}
/* handle any random indication from the L1 */
int l1if_handle_l1prim(int wq, struct lc15l1_hdl *fl1h, struct msgb *msg)
{
GsmL1_Prim_t *l1p = msgb_l1prim(msg);
int rc = 0;
LOGP(DL1IF, LOGL_DEBUG, "Rx L1 prim %s on queue %d\n",
get_value_string(lc15bts_l1prim_names, l1p->id), wq);
switch (l1p->id) {
#if 0
case GsmL1_PrimId_MphTimeInd:
rc = handle_mph_time_ind(fl1h, &l1p->u.mphTimeInd);
break;
case GsmL1_PrimId_MphSyncInd:
break;
case GsmL1_PrimId_PhConnectInd:
break;
#endif
case GsmL1_PrimId_PhReadyToSendInd:
rc = handle_ph_readytosend_ind(fl1h, &l1p->u.phReadyToSendInd);
break;
case GsmL1_PrimId_PhDataInd:
rc = handle_ph_data_ind(fl1h, &l1p->u.phDataInd, msg);
break;
case GsmL1_PrimId_PhRaInd:
rc = handle_ph_ra_ind(fl1h, &l1p->u.phRaInd);
break;
default:
break;
}
msgb_free(msg);
return rc;
}
int l1if_handle_sysprim(struct lc15l1_hdl *fl1h, struct msgb *msg)
{
return -ENOTSUP;
}
/* send packet data request to L1 */
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
{
struct lc15l1_hdl *fl1h = obj;
struct msgb *msg;
GsmL1_Prim_t *l1p;
GsmL1_PhDataReq_t *data_req;
GsmL1_MsgUnitParam_t *msu_param;
struct gsm_time g_time;
gsm_fn2gsmtime(&g_time, fn);
DEBUGP(DL1IF, "TX packet data %02u/%02u/%02u is_ptcch=%d ts=%d "
"block_nr=%d, arfcn=%d, len=%d\n", g_time.t1, g_time.t2,
g_time.t3, is_ptcch, ts, block_nr, arfcn, len);
msg = l1p_msgb_alloc();
l1p = msgb_l1prim(msg);
l1p->id = GsmL1_PrimId_PhDataReq;
data_req = &l1p->u.phDataReq;
data_req->hLayer1 = (HANDLE)fl1h->hLayer1;
data_req->sapi = (is_ptcch) ? GsmL1_Sapi_Ptcch : GsmL1_Sapi_Pdtch;
data_req->subCh = GsmL1_SubCh_NA;
data_req->u8BlockNbr = block_nr;
data_req->u8Tn = ts;
data_req->u32Fn = fn;
msu_param = &data_req->msgUnitParam;
msu_param->u8Size = len;
memcpy(msu_param->u8Buffer, data, len);
gsmtap_send(fl1h->gsmtap, arfcn, data_req->u8Tn, GSMTAP_CHANNEL_PACCH,
0, data_req->u32Fn, 0, 0,
data_req->msgUnitParam.u8Buffer,
data_req->msgUnitParam.u8Size);
/* transmit */
if (osmo_wqueue_enqueue(&fl1h->write_q[MQ_PDTCH_WRITE], msg) != 0) {
LOGP(DL1IF, LOGL_ERROR, "PDTCH queue full. dropping message.\n");
msgb_free(msg);
}
return 0;
}
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1)
{
struct lc15l1_hdl *fl1h;
int rc;
fl1h = talloc_zero(tall_pcu_ctx, struct lc15l1_hdl);
if (!fl1h)
return NULL;
fl1h->hLayer1 = hlayer1;
fl1h->trx_no = trx_no;
/* hardware queues are numbered starting from 0 */
fl1h->hw_info.trx_nr = trx_no;
DEBUGP(DL1IF, "PCU: Using TRX HW#%u\n", fl1h->hw_info.trx_nr);
rc = l1if_transport_open(MQ_PDTCH_WRITE, fl1h);
if (rc < 0) {
talloc_free(fl1h);
return NULL;
}
fl1h->gsmtap = gsmtap_source_init("localhost", GSMTAP_UDP_PORT, 1);
if (fl1h->gsmtap)
gsmtap_source_add_sink(fl1h->gsmtap);
return fl1h;
}
int l1if_close_pdch(void *obj)
{
struct lc15l1_hdl *fl1h = obj;
if (fl1h)
l1if_transport_close(MQ_PDTCH_WRITE, fl1h);
talloc_free(fl1h);
return 0;
}

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

@ -109,23 +109,6 @@ static void *prim_init(GsmL1_Prim_t *prim, GsmL1_PrimId_t id, struct femtol1_hdl
return &prim->u;
}
struct sapi_dir {
GsmL1_Sapi_t sapi;
GsmL1_Dir_t dir;
};
static const struct sapi_dir pdtch_sapis[] = {
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Pdtch, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_TxDownlink },
{ GsmL1_Sapi_Prach, GsmL1_Dir_RxUplink },
#if 0
{ GsmL1_Sapi_Ptcch, GsmL1_Dir_RxUplink },
{ GsmL1_Sapi_Pacch, GsmL1_Dir_TxDownlink },
#endif
};
/* connect PDTCH */
int l1if_connect_pdch(void *obj, uint8_t ts)
{
@ -155,8 +138,8 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
switch (rts_ind->sapi) {
case GsmL1_Sapi_Pdtch:
case GsmL1_Sapi_Pacch:
rc = pcu_rx_rts_req_pdtch((long)fl1h->priv, rts_ind->u8Tn,
rts_ind->u16Arfcn, rts_ind->u32Fn, rts_ind->u8BlockNbr);
rc = pcu_rx_rts_req_pdtch(fl1h->trx_no, rts_ind->u8Tn,
rts_ind->u32Fn, rts_ind->u8BlockNbr);
case GsmL1_Sapi_Ptcch:
// FIXME
default:
@ -215,7 +198,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
!= GsmL1_PdtchPlType_Full)
break;
/* PDTCH / PACCH frame handling */
pcu_rx_data_ind_pdtch((long)fl1h->priv, data_ind->u8Tn,
pcu_rx_data_ind_pdtch(fl1h->trx_no, data_ind->u8Tn,
data_ind->msgUnitParam.u8Buffer + 1,
data_ind->msgUnitParam.u8Size - 1,
data_ind->u32Fn,
@ -357,7 +340,7 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
return 0;
}
void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
{
struct femtol1_hdl *fl1h;
int rc;
@ -367,7 +350,7 @@ void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
return NULL;
fl1h->hLayer1 = hlayer1;
fl1h->priv = priv;
fl1h->trx_no = trx_no;
fl1h->clk_cal = 0;
/* default clock source: OCXO */
fl1h->clk_src = SuperFemto_ClkSrcId_Ocxo;

View File

@ -38,7 +38,7 @@ struct femtol1_hdl {
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_sapi_mask;
void *priv; /* user reference */
uint8_t trx_no;
struct osmo_timer_list alive_timer;
unsigned int alive_prim_cnt;

View File

@ -100,7 +100,7 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost)
/* disable all slots, kick all TBFs */
for (trx = 0; trx < 8; trx++) {
#ifdef ENABLE_SYSMODSP
#ifdef ENABLE_DIRECT_PHY
if (bts->trx[trx].fl1h) {
l1if_close_pdch(bts->trx[trx].fl1h);
bts->trx[trx].fl1h = NULL;

View File

@ -44,7 +44,8 @@ extern "C" {
// FIXME: move this, when changed from c++ to c.
extern "C" {
void *l1if_open_pdch(void *priv, uint32_t hlayer1, struct gsmtap_inst *gsmtap);
void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1,
struct gsmtap_inst *gsmtap);
int l1if_connect_pdch(void *obj, uint8_t ts);
int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
@ -128,7 +129,7 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
{
struct gprs_rlcmac_bts *bts = bts_main_data();
#ifdef ENABLE_SYSMODSP
#ifdef ENABLE_DIRECT_PHY
if (bts->trx[trx].fl1h) {
l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
msg->data, msg->len);
@ -147,7 +148,7 @@ void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
{
struct gprs_rlcmac_bts *bts = bts_main_data();
#ifdef ENABLE_SYSMODSP
#ifdef ENABLE_DIRECT_PHY
if (bts->trx[trx].fl1h) {
l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr,
msg->data, msg->len);
@ -213,7 +214,15 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind, struct gsmtap_inst
int rc;
pcu_l1_meas meas;
meas.set_rssi(data_ind->rssi);
#ifndef ENABLE_DIRECT_PHY
/* convert BER to % value */
meas.set_ber(data_ind->ber10k / 100);
meas.set_bto(data_ind->ta_offs_qbits);
meas.set_link_qual(data_ind->lqual_cb / 10);
LOGP(DL1IF, LOGL_DEBUG, "Data indication with raw measurements "
"received: BER10k = %d, BTO = %d, Q = %d\n", data_ind->ber10k,
data_ind->ta_offs_qbits, data_ind->lqual_cb);
#endif
LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
"block=%d data=%s\n", data_ind->sapi,
data_ind->arfcn, data_ind->block_nr,
@ -263,11 +272,11 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
}
// FIXME: remove this, when changed from c++ to c.
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
uint32_t fn, uint8_t block_nr)
{
return gprs_rlcmac_rcv_rts_block(bts_main_data(),
trx, ts, arfcn, fn, block_nr);
trx, ts, fn, block_nr);
}
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
@ -281,7 +290,7 @@ static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
switch (rts_req->sapi) {
case PCU_IF_SAPI_PDTCH:
pcu_rx_rts_req_pdtch(rts_req->trx_nr, rts_req->ts_nr,
rts_req->arfcn, rts_req->fn, rts_req->block_nr);
rts_req->fn, rts_req->block_nr);
break;
case PCU_IF_SAPI_PTCCH:
/* FIXME */
@ -313,7 +322,8 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
case PCU_IF_SAPI_RACH:
rc = BTS::main_bts()->rcv_rach(
rach_ind->ra, rach_ind->fn,
rach_ind->qta);
rach_ind->qta, rach_ind->is_11bit,
(ph_burst_type)rach_ind->burst_type);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
@ -330,8 +340,8 @@ static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
struct gprs_bssgp_pcu *pcu;
struct gprs_rlcmac_pdch *pdch;
struct in_addr ia;
int rc = 0;
int trx, ts;
int rc = 0, ts;
uint8_t trx;
int i;
if (info_ind->version != PCU_IF_VERSION) {
@ -445,12 +455,12 @@ bssgp_failed:
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
if ((info_ind->flags & PCU_IF_FLAG_SYSMO)
&& info_ind->trx[trx].hlayer1) {
#ifdef ENABLE_SYSMODSP
#ifdef ENABLE_DIRECT_PHY
LOGP(DL1IF, LOGL_DEBUG, " TRX %d hlayer1=%x\n", trx,
info_ind->trx[trx].hlayer1);
if (!bts->trx[trx].fl1h)
bts->trx[trx].fl1h = l1if_open_pdch(
(void *)trx,
trx,
info_ind->trx[trx].hlayer1,
bts->gsmtap);
if (!bts->trx[trx].fl1h) {
@ -471,7 +481,7 @@ bssgp_failed:
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
/* FIXME: activate dynamically at RLCMAC */
if (!pdch->is_enabled()) {
#ifdef ENABLE_SYSMODSP
#ifdef ENABLE_DIRECT_PHY
if ((info_ind->flags &
PCU_IF_FLAG_SYSMO))
l1if_connect_pdch(

View File

@ -124,7 +124,7 @@ int pcu_sock_send(struct msgb *msg);
#ifdef __cplusplus
extern "C" {
#endif
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts,
uint32_t fn, uint8_t block_nr);
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,

View File

@ -32,6 +32,7 @@ extern "C" {
#include "pcu_vty.h"
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/ports.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
@ -46,7 +47,7 @@ void *tall_pcu_ctx;
extern void *bv_tall_ctx;
static int quit = 0;
static int rt_prio = -1;
static char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
static const char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
static void print_help()
{
@ -189,8 +190,8 @@ int main(int argc, char *argv[])
bts->cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
bts->max_cs_ul = 4;
bts->max_cs_dl = 4;
bts->max_mcs_ul = 4;
bts->max_mcs_dl = 4;
bts->max_mcs_ul = MAX_GPRS_CS;
bts->max_mcs_dl = MAX_GPRS_CS;
/* CS-1 to CS-4 */
bts->cs_lqual_ranges[0].low = -256;
bts->cs_lqual_ranges[0].high = 6;
@ -210,6 +211,14 @@ int main(int argc, char *argv[])
bts->dl_tbf_idle_msec = 2000;
bts->llc_idle_ack_csec = 10;
/*
* By default resegmentation is supported in DL
* can also be configured through VTY
*/
bts->dl_arq_type = EGPRS_ARQ1;
bts->maximise_dir = NO_MAXIMISE;
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
@ -244,7 +253,8 @@ int main(int argc, char *argv[])
fprintf(stderr, "No config file: '%s' Using default config.\n",
config_file);
rc = telnet_init(tall_pcu_ctx, NULL, 4240);
rc = telnet_init_dynif(tall_pcu_ctx, NULL, vty_get_bind_addr(),
OSMO_VTY_PORT_PCU);
if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n");
exit(1);

View File

@ -129,6 +129,10 @@ static int config_write_pcu(struct vty *vty)
vty_out(vty, " window-size %d %d%s", bts->ws_base, bts->ws_pdch,
VTY_NEWLINE);
if (bts->dl_arq_type)
vty_out(vty, " egprs dl arq-type arq2%s",
VTY_NEWLINE);
if (bts->force_llc_lifetime == 0xffff)
vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
else if (bts->force_llc_lifetime)
@ -187,12 +191,6 @@ DEFUN(cfg_pcu_egprs,
bts->egprs_enabled = 1;
vty_out(vty, "%%Note that EGPRS support is in an experimental state "
"and the PCU will currently fail to use a TBF if the MS is capable "
"to do EGPRS. You may want to disable this feature by entering "
"the \"no egprs\" command. "
"Do not use this in production!%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -480,6 +478,51 @@ DEFUN(cfg_pcu_no_mcs_max,
return CMD_SUCCESS;
}
#define DL_STR "downlink specific configuration\n"
DEFUN(cfg_pcu_dl_arq_type,
cfg_pcu_dl_arq_cmd,
"egprs dl arq-type (spb|arq2)",
EGPRS_STR DL_STR "ARQ options\n"
"enable SPB(ARQ1) support\n"
"enable ARQ2 support")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
if (!strcmp(argv[0], "arq2"))
bts->dl_arq_type = 1;
else
bts->dl_arq_type = 0;
return CMD_SUCCESS;
}
#define MAXIMISE_STR "Maximise TS allocation based on configuration\n"
DEFUN(cfg_pcu_ts_alloc_maximise_type,
cfg_pcu_ts_alloc_maximise_cmd,
"maximise-direction dl",
MAXIMISE_STR "Maximise DL capacity\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->maximise_dir = DL_ONLY;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_maximise_direction,
cfg_pcu_no_maximise_direction_cmd,
"no maximise-direction",
NO_STR "Maximise direction configuration\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->maximise_dir = NO_MAXIMISE;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_window_size,
cfg_pcu_window_size_cmd,
"window-size <0-1024> [<0-256>]",
@ -954,6 +997,9 @@ int pcu_vty_init(const struct log_info *cat)
install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
install_element(PCU_NODE, &cfg_pcu_mcs_cmd);
install_element(PCU_NODE, &cfg_pcu_dl_arq_cmd);
install_element(PCU_NODE, &cfg_pcu_ts_alloc_maximise_cmd);
install_element(PCU_NODE, &cfg_pcu_no_maximise_direction_cmd);
install_element(PCU_NODE, &cfg_pcu_no_mcs_cmd);
install_element(PCU_NODE, &cfg_pcu_mcs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_no_mcs_max_cmd);

View File

@ -102,7 +102,7 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
llist_for_each(ms_iter, &bts->ms_store().ms_list()) {
GprsMs *ms = ms_iter->entry();
vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%s, CS-DL=%s, LLC=%d, "
vty_out(vty, "MS TLLI=%08x, TA=%d, CS-UL=%s, CS-DL=%s, LLC=%zd, "
"IMSI=%s%s",
ms->tlli(),
ms->ta(), ms->current_cs_ul().name(),
@ -136,9 +136,9 @@ static int show_ms(struct vty *vty, GprsMs *ms)
if (slots & (1 << i))
vty_out(vty, "%d ", i);
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " LLC queue length: %d%s", ms->llc_queue()->size(),
vty_out(vty, " LLC queue length: %zd%s", ms->llc_queue()->size(),
VTY_NEWLINE);
vty_out(vty, " LLC queue octets: %d%s", ms->llc_queue()->octets(),
vty_out(vty, " LLC queue octets: %zd%s", ms->llc_queue()->octets(),
VTY_NEWLINE);
if (ms->l1_meas()->have_rssi)
vty_out(vty, " RSSI: %d dBm%s",

View File

@ -1,7 +1,9 @@
#ifndef _PCUIF_PROTO_H
#define _PCUIF_PROTO_H
#define PCU_IF_VERSION 0x05
#include <osmocom/gsm/l1sap.h>
#define PCU_IF_VERSION 0x07
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
@ -50,6 +52,9 @@ struct gsm_pcu_if_data {
uint8_t ts_nr;
uint8_t block_nr;
int8_t rssi;
uint16_t ber10k; /*!< \brief BER in units of 0.01% */
int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */
int16_t lqual_cb; /* !< \brief Link quality in centiBel */
} __attribute__ ((packed));
struct gsm_pcu_if_rts_req {
@ -64,10 +69,12 @@ struct gsm_pcu_if_rts_req {
struct gsm_pcu_if_rach_ind {
uint8_t sapi;
uint8_t ra;
uint16_t ra;
int16_t qta;
uint32_t fn;
uint16_t arfcn;
uint8_t is_11bit;
uint8_t burst_type;
} __attribute__ ((packed));
struct gsm_pcu_if_info_trx {

View File

@ -33,6 +33,9 @@ uint8_t *gprs_rlc_data::prepare(size_t block_data_len)
memset(block, 0x0, sizeof(block));
memset(block, 0x2b, block_data_len);
/* Initial value of puncturing scheme */
next_ps = EGPRS_PS_1;
return block;
}
@ -80,6 +83,32 @@ int gprs_rlc_dl_window::mark_for_resend()
return resend;
}
/* Update the receive block bitmap */
uint16_t gprs_rlc_ul_window::update_egprs_rbb(uint8_t *rbb)
{
int i;
uint16_t bsn;
uint16_t bitmask = 0x80;
int8_t pos = 0;
int8_t bit_pos = 0;
for (i = 0, bsn = (v_q()+1); ((bsn < (v_r())) && (i < ws())); i++,
bsn = this->mod_sns(bsn + 1)) {
if (m_v_n.is_received(bsn)) {
rbb[pos] = rbb[pos] | bitmask;
} else {
rbb[pos] = rbb[pos] & (~bitmask);
}
bitmask = bitmask >> 1;
bit_pos++;
bit_pos = bit_pos % 8;
if(bit_pos == 0) {
pos++;
bitmask = 0x80;
}
}
return i;
}
int gprs_rlc_dl_window::count_unacked()
{
uint16_t unacked = 0;
@ -102,7 +131,8 @@ void gprs_rlc_dl_window::update(BTS *bts, const struct bitvec *rbb,
uint16_t first_bsn, uint16_t *lost,
uint16_t *received)
{
unsigned num_blocks = rbb->cur_bit;
unsigned num_blocks = rbb->cur_bit > (unsigned)distance()
? distance() : rbb->cur_bit;
unsigned bsn;
/* first_bsn is in range V(A)..V(S) */
@ -214,6 +244,8 @@ void gprs_rlc_window::set_sns(uint16_t sns)
void gprs_rlc_window::set_ws(uint16_t ws)
{
LOGP(DRLCMAC, LOGL_ERROR, "ws(%d)\n",
ws);
OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
m_ws = ws;
@ -224,7 +256,7 @@ void gprs_rlc_ul_window::update_rbb(char *rbb)
{
int i;
for (i=0; i < ws(); i++) {
if (m_v_n.is_received(ssn()-1-i))
if (m_v_n.is_received((ssn()-1-i) & mod_sns()))
rbb[ws()-1-i] = 'R';
else
rbb[ws()-1-i] = 'I';
@ -282,7 +314,8 @@ bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
}
static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding, unsigned int header_bits)
GprsCodingScheme cs, bool with_padding, unsigned int header_bits,
const unsigned int spb)
{
unsigned int i;
unsigned int padding_bits = with_padding ? cs.optionalPaddingBits() : 0;
@ -297,7 +330,7 @@ static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
for (i = 0; i < rlc->num_data_blocks; i++) {
gprs_rlc_data_block_info_init(&rlc->block_info[i], cs,
with_padding);
with_padding, spb);
rlc->data_offs_bits[i] =
header_bits + padding_bits +
@ -307,21 +340,25 @@ static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
}
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding)
GprsCodingScheme cs, bool with_padding, const unsigned int spb)
{
return gprs_rlc_data_header_init(rlc, cs, with_padding,
cs.numDataHeaderBitsDL());
cs.numDataHeaderBitsDL(), spb);
}
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding)
{
/*
* last parameter is sent as 0 since common function used
* for both DL and UL
*/
return gprs_rlc_data_header_init(rlc, cs, with_padding,
cs.numDataHeaderBitsUL());
cs.numDataHeaderBitsUL(), 0);
}
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
GprsCodingScheme cs, bool with_padding)
GprsCodingScheme cs, bool with_padding, const unsigned int spb)
{
unsigned int data_len = cs.maxDataBlockBytes();
if (with_padding)
@ -333,24 +370,35 @@ void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
rdbi->e = 1;
rdbi->cv = 15;
rdbi->pi = 0;
rdbi->spb = 0;
rdbi->spb = spb;
}
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
int with_padding)
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs,
enum egprs_puncturing_values punct,
enum egprs_puncturing_values punct2, int with_padding)
{
switch (GprsCodingScheme::Scheme(cs)) {
case GprsCodingScheme::MCS1: return 0b1011 + punct % 2;
case GprsCodingScheme::MCS2: return 0b1001 + punct % 2;
case GprsCodingScheme::MCS1: return 0b1011 +
punct % EGPRS_MAX_PS_NUM_2;
case GprsCodingScheme::MCS2: return 0b1001 +
punct % EGPRS_MAX_PS_NUM_2;
case GprsCodingScheme::MCS3: return (with_padding ? 0b0110 : 0b0011) +
punct % 3;
case GprsCodingScheme::MCS4: return 0b0000 + punct % 3;
case GprsCodingScheme::MCS5: return 0b100 + punct % 2;
punct % EGPRS_MAX_PS_NUM_3;
case GprsCodingScheme::MCS4: return 0b0000 +
punct % EGPRS_MAX_PS_NUM_3;
case GprsCodingScheme::MCS5: return 0b100 +
punct % EGPRS_MAX_PS_NUM_2;
case GprsCodingScheme::MCS6: return (with_padding ? 0b010 : 0b000) +
punct % 2;
case GprsCodingScheme::MCS7: return 0b10100 + 3 * (punct % 3) + punct2 % 3;
case GprsCodingScheme::MCS8: return 0b01011 + 3 * (punct % 3) + punct2 % 3;
case GprsCodingScheme::MCS9: return 0b00000 + 4 * (punct % 3) + punct2 % 3;
punct % EGPRS_MAX_PS_NUM_2;
case GprsCodingScheme::MCS7: return 0b10100 +
3 * (punct % EGPRS_MAX_PS_NUM_3) +
punct2 % EGPRS_MAX_PS_NUM_3;
case GprsCodingScheme::MCS8: return 0b01011 +
3 * (punct % EGPRS_MAX_PS_NUM_3) +
punct2 % EGPRS_MAX_PS_NUM_3;
case GprsCodingScheme::MCS9: return 0b00000 +
4 * (punct % EGPRS_MAX_PS_NUM_3) +
punct2 % EGPRS_MAX_PS_NUM_3;
default: ;
}
@ -385,3 +433,87 @@ void gprs_rlc_mcs_cps_decode(unsigned int cps,
default: ;
}
}
/*
* Finds the PS value for retransmission with MCS change,
* retransmission with no MCS change, fresh transmission cases.
* The return value shall be used for current transmission only
* 44.060 9.3.2.1 defines the PS selection for MCS change case
* cs_current is the output of MCS selection algorithm for retx
* cs is coding scheme of previous transmission of RLC data block
*/
enum egprs_puncturing_values gprs_get_punct_scheme(
enum egprs_puncturing_values punct,
const GprsCodingScheme &cs,
const GprsCodingScheme &cs_current,
const enum egprs_rlcmac_dl_spb spb)
{
/*
* 10.4.8b of TS 44.060
* If it is second segment of the block
* dont change the puncturing scheme
*/
if (spb == EGPRS_RLCMAC_DL_SEC_SEG)
return punct;
/* TS 44.060 9.3.2.1.1 */
if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS9) &&
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS6)) {
if ((punct == EGPRS_PS_1) || (punct == EGPRS_PS_3))
return EGPRS_PS_1;
else if (punct == EGPRS_PS_2)
return EGPRS_PS_2;
} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6) &&
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS9)) {
if (punct == EGPRS_PS_1)
return EGPRS_PS_3;
else if (punct == EGPRS_PS_2)
return EGPRS_PS_2;
} else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS7) &&
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS5))
return EGPRS_PS_1;
else if ((GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS5) &&
(GprsCodingScheme::Scheme(cs_current) == GprsCodingScheme::MCS7))
return EGPRS_PS_2;
else if (cs != cs_current)
return EGPRS_PS_1;
/* TS 44.060 9.3.2.1.1 ends here */
/*
* Below else will handle fresh transmission, retransmission with no
* MCS change case
*/
else
return punct;
return EGPRS_PS_INVALID;
}
/*
* This function calculates puncturing scheme for retransmission of a RLC
* block with same MCS. The computed value shall be used for next transmission
* of the same RLC block
* TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
*/
void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
const GprsCodingScheme &cs)
{
switch (GprsCodingScheme::Scheme(cs)) {
case GprsCodingScheme::MCS1 :
case GprsCodingScheme::MCS2 :
case GprsCodingScheme::MCS5 :
case GprsCodingScheme::MCS6 :
*punct = ((enum egprs_puncturing_values)((*punct + 1) %
EGPRS_MAX_PS_NUM_2));
break;
case GprsCodingScheme::MCS3 :
case GprsCodingScheme::MCS4 :
case GprsCodingScheme::MCS7 :
case GprsCodingScheme::MCS8 :
case GprsCodingScheme::MCS9 :
*punct = ((enum egprs_puncturing_values)((*punct + 1) %
EGPRS_MAX_PS_NUM_3));
break;
default:
break;
}
}

208
src/rlc.h
View File

@ -27,7 +27,6 @@
#define RLC_GPRS_SNS 128 /* GPRS, must be power of 2 */
#define RLC_GPRS_WS 64 /* max window size */
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
#define RLC_EGPRS_MIN_WS 64 /* min window size */
#define RLC_EGPRS_MAX_WS 1024 /* min window size */
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
@ -56,6 +55,88 @@ enum gprs_rlc_dl_bsn_state {
GPRS_RLC_DL_BSN_MAX,
};
/*
* EGPRS resegment status information for UL
* When only first split block is received bsn state
* will be set to EGPRS_RESEG_FIRST_SEG_RXD and when
* only second segment is received the state will be
* set to EGPRS_RESEG_SECOND_SEG_RXD. When both Split
* blocks are received the state will be set to
* EGPRS_RESEG_DEFAULT
* The EGPRS resegmentation feature allows MS to retransmit
* RLC blocks of HeaderType1, HeaderType2 by segmenting
* them to 2 HeaderType3 blocks(Example MCS5 will be
* retransmitted as 2 MCS2 blocks). Table 10.4.8b.1 of 44.060
* explains the possible values of SPB in HeadrType3 for UL
* direction. When the MCS is changed at the PCU, PCU directs the
* changed MCS to MS by PUAN or UPLINK ASSIGNMENT message along
* with RESEGMENT flag, Then MS may decide to retransmit the
* blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
* The retransmission MCS is calculated based on current MCS of
* the Block and demanded MCS by PCU. Section 10.3a.4.3 of 44.060
* shows the HeadrType3 with SPB field present in it
*/
enum egprs_rlc_ul_reseg_bsn_state {
EGPRS_RESEG_DEFAULT = 0,
EGPRS_RESEG_FIRST_SEG_RXD = 0x01,
EGPRS_RESEG_SECOND_SEG_RXD = 0x02,
EGPRS_RESEG_INVALID = 0x04
};
/*
* EGPRS resegment status information for DL
* When only first segment is sent, bsn state
* will be set to EGPRS_RESEG_FIRST_SEG_SENT and when
* second segment is sent the state will be
* set to EGPRS_RESEG_SECOND_SEG_SENT.
* EGPRS_RESEG_DL_INVALID is set to 8 considering there is a scope for
* 3rd segment according to Table 10.4.8b.2 of 44.060
* The EGPRS resegmentation feature allows PCU to retransmit
* RLC blocks of HeaderType1, HeaderType2 by segmenting
* them to 2 HeaderType3 blocks(Example MCS5 will be
* retransmitted as 2 MCS2 blocks). Table 10.4.8b.2 of 44.060
* explains the possible values of SPB in HeadrType3 for DL
* direction.The PCU decides to retransmit the
* blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
* The retransmission MCS is calculated based on current MCS of
* the Block and demanded MCS by PCU. Section 10.3a.3.3 of 44.060
* shows the HeadrType3 with SPB field present in it
*/
enum egprs_rlc_dl_reseg_bsn_state {
EGPRS_RESEG_DL_DEFAULT = 0,
EGPRS_RESEG_FIRST_SEG_SENT = 0x01,
EGPRS_RESEG_SECOND_SEG_SENT = 0x02,
EGPRS_RESEG_DL_INVALID = 0x08
};
/* Table 10.4.8b.2 of 44.060 */
enum egprs_rlcmac_dl_spb {
EGPRS_RLCMAC_DL_NO_RETX = 0,
EGPRS_RLCMAC_DL_FIRST_SEG = 2,
EGPRS_RLCMAC_DL_SEC_SEG = 3,
};
/*
* Valid puncturing scheme values
* TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
*/
enum egprs_puncturing_values {
EGPRS_PS_1,
EGPRS_PS_2,
EGPRS_PS_3,
EGPRS_PS_INVALID,
};
/*
* EGPRS_MAX_PS_NUM_2 is valid for MCS 1,2,5,6.
* And EGPRS_MAX_PS_NUM_3 is valid for MCS 3,4,7,8,9
* TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
*/
enum egprs_puncturing_types {
EGPRS_MAX_PS_NUM_2 = 2,
EGPRS_MAX_PS_NUM_3,
EGPRS_MAX_PS_NUM_INVALID,
};
static inline uint16_t mod_sns_half()
{
@ -89,30 +170,64 @@ struct gprs_rlc_data_info {
struct gprs_rlc_data_block_info block_info[2];
};
/* holds the current status of the block w.r.t UL/DL split blocks */
union split_block_status {
egprs_rlc_ul_reseg_bsn_state block_status_ul;
egprs_rlc_dl_reseg_bsn_state block_status_dl;
};
struct gprs_rlc_data {
uint8_t *prepare(size_t block_data_length);
void put_data(const uint8_t *data, size_t len);
/* block history */
/* block data including LI headers */
uint8_t block[RLC_MAX_LEN];
/* block len of history */
/* block data len including LI headers*/
uint8_t len;
struct gprs_rlc_data_block_info block_info;
GprsCodingScheme cs;
/*
* cs_current_trans is variable to hold the cs_last value for
* current transmission. cs_current_trans is same as cs_last during
* transmission case. during retransmission cs_current_trans is
* fetched from egprs_mcs_retx_tbl table based on
* cs and demanded cs.reference is 44.060 Table
* 8.1.1.1 and Table 8.1.1.2
* For UL. cs_last shall be used everywhere.
*/
GprsCodingScheme cs_current_trans;
GprsCodingScheme cs_last;
/*
* The MCS of initial transmission of a BSN
* This variable is used for split block
* processing in DL
*/
GprsCodingScheme cs_init;
/* puncturing scheme value to be used for next transmission*/
enum egprs_puncturing_values next_ps;
/* holds the status of the block w.r.t UL/DL split blocks*/
union split_block_status spb_status;
};
void gprs_rlc_data_info_init_dl(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding);
GprsCodingScheme cs, bool with_padding, const unsigned int spb);
void gprs_rlc_data_info_init_ul(struct gprs_rlc_data_info *rlc,
GprsCodingScheme cs, bool with_padding);
void gprs_rlc_data_block_info_init(struct gprs_rlc_data_block_info *rdbi,
GprsCodingScheme cs, bool with_padding);
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, int punct, int punct2,
int with_padding);
GprsCodingScheme cs, bool with_padding, const unsigned int spb);
unsigned int gprs_rlc_mcs_cps(GprsCodingScheme cs, enum egprs_puncturing_values
punct, enum egprs_puncturing_values punct2, int with_padding);
void gprs_rlc_mcs_cps_decode(unsigned int cps, GprsCodingScheme cs,
int *punct, int *punct2, int *with_padding);
enum egprs_puncturing_values gprs_get_punct_scheme(enum egprs_puncturing_values
punct, const GprsCodingScheme &cs,
const GprsCodingScheme &cs_current_trans,
const enum egprs_rlcmac_dl_spb spb);
void gprs_update_punct_scheme(enum egprs_puncturing_values *punct,
const GprsCodingScheme &cs);
/*
* I hold the currently transferred blocks and will provide
* the routines to manipulate these arrays.
@ -187,6 +302,9 @@ struct gprs_rlc_dl_window: public gprs_rlc_window {
const uint16_t v_a() const;
const int16_t distance() const;
void set_v_s(int);
void set_v_a(int);
/* Methods to manage reception */
int resend_needed();
int mark_for_resend();
@ -232,6 +350,7 @@ struct gprs_rlc_ul_window: public gprs_rlc_window {
bool is_received(uint16_t bsn) const;
void update_rbb(char *rbb);
uint16_t update_egprs_rbb(uint8_t *rbb);
void raise_v_r_to(int moves);
void raise_v_r(const uint16_t bsn);
uint16_t raise_v_q();
@ -287,67 +406,6 @@ struct rlc_li_field_egprs {
uint8_t e:1,
li:7;
} __attribute__ ((packed));
struct gprs_rlc_ul_header_egprs_3 {
uint8_t r:1,
si:1,
cv:4,
tfi_a:2;
uint8_t tfi_b:3,
bsn1_a:5;
uint8_t bsn1_b:6,
cps_a:2;
uint8_t cps_b:2,
spb:2,
rsb:1,
pi:1,
spare:1,
dummy:1;
} __attribute__ ((packed));
struct gprs_rlc_dl_header_egprs_1 {
uint8_t usf:3,
es_p:2,
rrbp:2,
tfi_a:1;
uint8_t tfi_b:4,
pr:2,
bsn1_a:2;
uint8_t bsn1_b:8;
uint8_t bsn1_c:1,
bsn2_a:7;
uint8_t bsn2_b:3,
cps:5;
} __attribute__ ((packed));
struct gprs_rlc_dl_header_egprs_2 {
uint8_t usf:3,
es_p:2,
rrbp:2,
tfi_a:1;
uint8_t tfi_b:4,
pr:2,
bsn1_a:2;
uint8_t bsn1_b:8;
uint8_t bsn1_c:1,
cps:3,
dummy:4;
} __attribute__ ((packed));
struct gprs_rlc_dl_header_egprs_3 {
uint8_t usf:3,
es_p:2,
rrbp:2,
tfi_a:1;
uint8_t tfi_b:4,
pr:2,
bsn1_a:2;
uint8_t bsn1_b:8;
uint8_t bsn1_c:1,
cps:4,
spb:2,
dummy:1;
} __attribute__ ((packed));
#else
# error "Only little endian headers are supported yet. TODO: add missing structs"
#endif
@ -455,6 +513,16 @@ inline const uint16_t gprs_rlc_dl_window::v_s() const
return m_v_s;
}
inline void gprs_rlc_dl_window::set_v_s(int v_s)
{
m_v_s = v_s;
}
inline void gprs_rlc_dl_window::set_v_a(int v_a)
{
m_v_a = v_a;
}
inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
{
return mod_sns(m_v_s + offset);

View File

@ -26,6 +26,7 @@
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
}
#include <errno.h>
@ -55,6 +56,9 @@ int SBAController::alloc(
if (!sba)
return -ENOMEM;
if (!gsm48_ta_is_valid(ta))
return -EINVAL;
for (trx = 0; trx < 8; trx++) {
for (ts = 7; ts >= 0; ts--) {
pdch = &m_bts.bts_data()->trx[trx].pdch[ts];

View File

@ -74,7 +74,7 @@ gprs_rlcmac_tbf::gprs_rlcmac_tbf(BTS *bts_, gprs_rlcmac_tbf_direction dir) :
m_tfi(0),
m_created_ts(0),
m_ms(NULL),
m_ta(0),
m_ta(GSM48_TA_INVALID),
m_ms_class(0),
m_list(this),
m_ms_list(this),
@ -151,7 +151,8 @@ void gprs_rlcmac_tbf::set_ta(uint8_t ta)
if (ms())
ms()->set_ta(ta);
m_ta = ta;
if (gsm48_ta_is_valid(ta))
m_ta = ta;
}
uint8_t gprs_rlcmac_tbf::ms_class() const
@ -375,6 +376,12 @@ int gprs_rlcmac_tbf::update()
return -rc;
}
if (is_egprs_enabled()) {
gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(this);
if (dl_tbf)
dl_tbf->egprs_calc_window_size();
}
return 0;
}
@ -687,14 +694,16 @@ struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
if (egprs_ms_class > 0 && bts->egprs_enabled) {
tbf->enable_egprs();
tbf->m_window.set_sns(RLC_EGPRS_SNS);
/* TODO: Allow bigger UL windows when CRBB encoding is supported */
tbf->m_window.set_ws(RLC_EGPRS_MIN_WS);
setup_egprs_mode(bts, ms);
setup_egprs_mode(bts, ms);
LOGP(DRLCMAC, LOGL_INFO, "Enabled EGPRS for %s, mode %s\n",
tbf->name(), GprsCodingScheme::modeName(ms->mode()));
}
rc = setup_tbf(tbf, ms, use_trx, ms_class, egprs_ms_class, single_slot);
if (tbf->is_egprs_enabled())
tbf->egprs_calc_ulwindow_size();
/* if no resource */
if (rc < 0) {
talloc_free(tbf);
@ -780,20 +789,8 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
return NULL;
}
if (tbf->is_egprs_enabled()) {
unsigned int num_pdch = pcu_bitcount(tbf->dl_slots());
unsigned int ws = bts->ws_base + num_pdch * bts->ws_pdch;
ws = (ws / 32) * 32;
ws = OSMO_MAX(64, ws);
if (num_pdch == 1)
ws = OSMO_MIN(192, ws);
else
ws = OSMO_MIN(128 * num_pdch, ws);
LOGP(DRLCMAC, LOGL_INFO, "%s: Setting EGPRS window size to %d\n",
tbf->name(), ws);
tbf->m_window.set_ws(ws);
}
if (tbf->is_egprs_enabled())
tbf->egprs_calc_window_size();
llist_add(&tbf->list(), &bts->bts->dl_tbfs());
tbf->bts->tbf_dl_created();
@ -1176,14 +1173,11 @@ int gprs_rlcmac_tbf::set_tlli_from_ul(uint32_t new_tlli)
const char *tbf_name(gprs_rlcmac_tbf *tbf)
{
return tbf->name();
return tbf ? tbf->name() : "(no TBF)";
}
const char *gprs_rlcmac_tbf::name() const
{
if (this == NULL)
return "(no TBF)";
snprintf(m_name_buf, sizeof(m_name_buf) - 1,
"TBF(TFI=%d TLLI=0x%08x DIR=%s STATE=%s%s)",
m_tfi, tlli(),

View File

@ -372,6 +372,8 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
int release();
int abort();
void egprs_calc_window_size();
/* TODO: add the gettimeofday as parameter */
struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
@ -421,6 +423,11 @@ protected:
int analyse_errors(char *show_rbb, uint8_t ssn, ana_result *res);
void schedule_next_frame();
enum egprs_rlc_dl_reseg_bsn_state egprs_dl_get_data
(int bsn, uint8_t **block_data);
unsigned int get_egprs_dl_spb_status(int bsn);
enum egprs_rlcmac_dl_spb get_egprs_dl_spb(int bsn);
struct osmo_timer_list m_llc_timer;
};
@ -439,6 +446,23 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
int assemble_forward_llc(const gprs_rlc_data *data);
int snd_ul_ud();
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_spb(
const struct gprs_rlc_data_info *rlc,
struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx);
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_first_seg(
const struct gprs_rlc_data_info *rlc,
struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx);
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_second_seg(
const struct gprs_rlc_data_info *rlc,
struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx);
void egprs_calc_ulwindow_size();
/* Please note that all variables here will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
* All states that need reset must be in this struct, so this is why

View File

@ -34,6 +34,7 @@
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gprs/gprs_bssgp_bss.h>
}
#include <errno.h>
@ -43,11 +44,6 @@ extern "C" {
/* After sending these frames, we poll for ack/nack. */
#define POLL_ACK_AFTER_FRAMES 20
extern "C" {
int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
uint8_t num_frames, uint32_t num_octets);
}
static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf,
const uint8_t ms_class)
{
@ -124,7 +120,7 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
{
uint8_t ss;
int8_t use_trx;
uint16_t ta = 0;
uint16_t ta = GSM48_TA_INVALID;
struct gprs_rlcmac_ul_tbf *ul_tbf = NULL, *old_ul_tbf;
struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
GprsMs *ms;
@ -218,7 +214,7 @@ int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts,
LOGP(DRLCMAC, LOGL_NOTICE,
"%s IMSI %s: "
"moving DL TBF to new MS object\n",
dl_tbf->name(), imsi);
ms_old->dl_tbf()->name(), imsi);
dl_tbf = ms_old->dl_tbf();
/* Move the DL TBF to the new MS */
dl_tbf->set_ms(ms);
@ -359,13 +355,12 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
{
int bsn;
int data_len2, force_data_len = -1;
GprsCodingScheme cs2;
GprsCodingScheme force_cs;
bsn = m_window.resend_needed();
if (previous_bsn >= 0) {
force_cs = m_rlc.block(previous_bsn)->cs;
force_cs = m_rlc.block(previous_bsn)->cs_current_trans;
if (!force_cs.isEgprs())
return -1;
force_data_len = m_rlc.block(previous_bsn)->len;
@ -379,7 +374,35 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
m_window.mod_sns(bsn - previous_bsn) > RLC_EGPRS_MAX_BSN_DELTA)
return -1;
cs2 = m_rlc.block(bsn)->cs;
if (is_egprs_enabled()) {
/* Table 8.1.1.2 and Table 8.1.1.1 of 44.060 */
m_rlc.block(bsn)->cs_current_trans =
GprsCodingScheme::get_retx_mcs(
m_rlc.block(bsn)->cs_init,
ms()->current_cs_dl(),
bts->bts_data()->dl_arq_type);
LOGP(DRLCMACDL, LOGL_DEBUG,
"- initial_cs_dl(%d) last_mcs(%d)"
" demanded_mcs(%d) cs_trans(%d)"
" arq_type(%d) bsn(%d)\n",
m_rlc.block(bsn)->cs_init.to_num(),
m_rlc.block(bsn)->cs_last.to_num(),
ms()->current_cs_dl().to_num(),
m_rlc.block(bsn)->cs_current_trans.to_num(),
bts->bts_data()->dl_arq_type, bsn);
/* TODO: Need to remove this check when MCS-8 -> MCS-6
* transistion is handled.
* Refer commit be881c028fc4da00c4046ecd9296727975c206a3
*/
if (m_rlc.block(bsn)->cs_init == GprsCodingScheme::MCS8)
m_rlc.block(bsn)->cs_current_trans =
GprsCodingScheme::MCS8;
} else
m_rlc.block(bsn)->cs_current_trans =
m_rlc.block(bsn)->cs_last;
data_len2 = m_rlc.block(bsn)->len;
if (force_data_len > 0 && force_data_len != data_len2)
return -1;
@ -402,13 +425,14 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
if (restart_bsn_cycle())
return take_next_bsn(fn, previous_bsn, may_combine);
} else if (have_data()) {
GprsCodingScheme new_cs;
/* New blocks may be send */
cs2 = force_cs ? force_cs : current_cs();
new_cs = force_cs ? force_cs : current_cs();
LOGP(DRLCMACDL, LOGL_DEBUG,
"- Sending new block at BSN %d, CS=%s\n",
m_window.v_s(), cs2.name());
m_window.v_s(), new_cs.name());
bsn = create_new_bsn(fn, cs2);
bsn = create_new_bsn(fn, new_cs);
} else if (!m_window.window_empty()) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
"because all blocks have been transmitted (FLOW).\n",
@ -422,7 +446,7 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
"- Sending new dummy block at BSN %d, CS=%s\n",
m_window.v_s(), current_cs().name());
bsn = create_new_bsn(fn, current_cs());
/* Don't send a second block, so don't set cs2 */
/* Don't send a second block, so don't set cs_current_trans */
}
if (bsn < 0) {
@ -433,7 +457,7 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
bts->rlc_resent();
}
*may_combine = cs2.numDataBlocks() > 1;
*may_combine = m_rlc.block(bsn)->cs_current_trans.numDataBlocks() > 1;
return bsn;
}
@ -504,7 +528,13 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
/* now we still have untransmitted LLC data, so we fill mac block */
rlc_data = m_rlc.block(bsn);
data = rlc_data->prepare(block_data_len);
rlc_data->cs = cs;
rlc_data->cs_last = cs;
rlc_data->cs_current_trans = cs;
/* Initialise the variable related to DL SPB */
rlc_data->spb_status.block_status_dl = EGPRS_RESEG_DL_DEFAULT;
rlc_data->cs_init = cs;
rlc_data->len = block_data_len;
rdbi = &(rlc_data->block_info);
@ -517,8 +547,11 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
do {
bool is_final;
int payload_written = 0;
if (m_llc.frame_length() == 0) {
/* nothing to sent - delay the release of the TBF */
int space = block_data_len - write_offset;
/* A header will need to by added, so we just need
* space-1 octets */
@ -542,7 +575,10 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
is_final = llc_queue()->size() == 0 && !keep_open(fn);
ar = Encoding::rlc_data_to_dl_append(rdbi, cs,
&m_llc, &write_offset, &num_chunks, data, is_final);
&m_llc, &write_offset, &num_chunks, data, is_final, &payload_written);
if (payload_written > 0)
bts->rlc_dl_payload_bytes(payload_written);
if (ar == Encoding::AR_NEED_MORE_BLOCKS)
break;
@ -550,6 +586,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s"
"len=%d\n", tbf_name(this), m_llc.frame_length());
gprs_rlcmac_dl_bw(this, m_llc.frame_length());
bts->llc_dl_bytes(m_llc.frame_length());
m_llc.reset();
if (is_final) {
@ -562,7 +599,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
} while (ar == Encoding::AR_COMPLETED_SPACE_LEFT);
LOGP(DRLCMACDL, LOGL_DEBUG, "data block (BSN %d, %s): %s\n",
bsn, rlc_data->cs.name(),
bsn, rlc_data->cs_last.name(),
osmo_hexdump(rlc_data->block, block_data_len));
/* raise send state and set ack state array */
m_window.m_v_b.mark_unacked(bsn);
@ -589,9 +626,10 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
GprsCodingScheme cs;
int bsns[ARRAY_SIZE(rlc.block_info)];
unsigned num_bsns;
int punct[ARRAY_SIZE(rlc.block_info)];
enum egprs_puncturing_values punct[ARRAY_SIZE(rlc.block_info)];
bool need_padding = false;
enum egprs_rlcmac_dl_spb spb = EGPRS_RLCMAC_DL_NO_RETX;
unsigned int spb_status = get_egprs_dl_spb_status(index);
/*
* TODO: This is an experimental work-around to put 2 BSN into
* MSC-7 to MCS-9 encoded messages. It just sends the same BSN
@ -600,7 +638,8 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
* be put into the data area, even if the resulting CS is higher than
* the current limit.
*/
cs = m_rlc.block(index)->cs;
cs = m_rlc.block(index)->cs_current_trans;
GprsCodingScheme &cs_init = m_rlc.block(index)->cs_init;
bsns[0] = index;
num_bsns = 1;
@ -609,14 +648,36 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
num_bsns += 1;
}
if (num_bsns == 1) {
/*
* if the intial mcs is 8 and retransmission mcs is either 6 or 3
* we have to include the padding of 6 octets in first segment
*/
if ((GprsCodingScheme::Scheme(cs_init) == GprsCodingScheme::MCS8) &&
(GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS6 ||
GprsCodingScheme::Scheme(cs) == GprsCodingScheme::MCS3)) {
if (spb_status == EGPRS_RESEG_DL_DEFAULT ||
spb_status == EGPRS_RESEG_SECOND_SEG_SENT)
need_padding = true;
} else if (num_bsns == 1) {
/* TODO: remove the conditional when MCS-6 padding isn't
* failing to be decoded by MEs anymore */
/* TODO: support of MCS-8 -> MCS-6 transition should be
* handled
* Refer commit be881c028fc4da00c4046ecd9296727975c206a3
* dated 2016-02-07 23:45:40 (UTC)
*/
if (cs != GprsCodingScheme(GprsCodingScheme::MCS8))
cs.decToSingleBlock(&need_padding);
}
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding);
spb = get_egprs_dl_spb(index);
LOGP(DRLCMACDL, LOGL_DEBUG, "- need_padding %d spb_status %d spb %d"
"(BSN1 %d BSN2 %d)\n",
need_padding,
spb_status, spb, index, index2);
gprs_rlc_data_info_init_dl(&rlc, cs, need_padding, spb);
rlc.usf = 7; /* will be set at scheduler */
rlc.pr = 0; /* FIXME: power reduction */
@ -630,14 +691,17 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
msg_data = msgb_put(dl_msg, msg_len);
OSMO_ASSERT(rlc.num_data_blocks <= ARRAY_SIZE(rlc.block_info));
OSMO_ASSERT(rlc.num_data_blocks > 0);
/* Copy block(s) to RLC message */
for (data_block_idx = 0; data_block_idx < rlc.num_data_blocks;
data_block_idx++)
{
int bsn;
GprsCodingScheme cs_enc;
uint8_t *block_data;
gprs_rlc_data_block_info *rdbi, *block_info;
enum egprs_rlc_dl_reseg_bsn_state reseg_status;
/* Check if there are more blocks than BSNs */
if (data_block_idx < num_bsns)
@ -645,25 +709,45 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
else
bsn = bsns[0];
cs_enc = m_rlc.block(bsn)->cs;
/* Get current puncturing scheme from block */
/* get data and header from current block */
block_data = m_rlc.block(bsn)->block;
m_rlc.block(bsn)->next_ps = gprs_get_punct_scheme(
m_rlc.block(bsn)->next_ps,
m_rlc.block(bsn)->cs_last, cs, spb);
/* TODO: Use real puncturing values */
punct[data_block_idx] = data_block_idx;
if (cs.isEgprs()) {
OSMO_ASSERT(m_rlc.block(bsn)->next_ps >= EGPRS_PS_1);
OSMO_ASSERT(m_rlc.block(bsn)->next_ps <= EGPRS_PS_3);
}
punct[data_block_idx] = m_rlc.block(bsn)->next_ps;
rdbi = &rlc.block_info[data_block_idx];
block_info = &m_rlc.block(bsn)->block_info;
if(rdbi->data_len != m_rlc.block(bsn)->len) {
LOGP(DRLCMACDL, LOGL_ERROR,
"ERROR: Expected len = %d for %s instead of "
"%d in data unit %d (BSN %d, %s)\n",
rdbi->data_len, cs.name(), m_rlc.block(bsn)->len,
data_block_idx, bsn, cs_enc.name());
OSMO_ASSERT(rdbi->data_len == m_rlc.block(bsn)->len);
/*
* get data and header from current block
* function returns the reseg status
*/
reseg_status = egprs_dl_get_data(bsn, &block_data);
m_rlc.block(bsn)->spb_status.block_status_dl = reseg_status;
/*
* If it is first segment of the split block set the state of
* bsn to nacked. If it is the first segment dont update the
* next ps value of bsn. since next segment also needs same cps
*/
if (spb == EGPRS_RLCMAC_DL_FIRST_SEG)
m_window.m_v_b.mark_nacked(bsn);
else {
/*
* TODO: Need to handle 2 same bsns
* in header type 1
*/
gprs_update_punct_scheme(&m_rlc.block(bsn)->next_ps,
cs);
}
m_rlc.block(bsn)->cs_last = cs;
rdbi->e = block_info->e;
rdbi->cv = block_info->cv;
rdbi->bsn = bsn;
@ -742,8 +826,6 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
/* Increment TX-counter */
m_tx_counter++;
bts->rlc_sent();
return dl_msg;
}
@ -763,7 +845,8 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
uint16_t bsn = 0;
unsigned received_bytes = 0, lost_bytes = 0;
unsigned received_packets = 0, lost_packets = 0;
unsigned num_blocks = strlen(show_rbb);
unsigned num_blocks = strlen(show_rbb) > (unsigned)m_window.distance()
? m_window.distance() : strlen(show_rbb);
/* SSN - 1 is in range V(A)..V(S)-1 */
for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
@ -796,7 +879,7 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn,
/* Get statistics for current CS */
if (rlc_data->cs != current_cs()) {
if (rlc_data->cs_last != current_cs()) {
/* This block has already been encoded with a different
* CS, so it doesn't help us to decide, whether the
* current CS is ok. Ignore it. */
@ -843,7 +926,8 @@ int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
char show_rbb[RLC_MAX_SNS + 1];
int error_rate;
struct ana_result ana_res;
unsigned num_blocks = rbb->cur_bit;
unsigned num_blocks = rbb->cur_bit > (unsigned)m_window.distance()
? m_window.distance() : rbb->cur_bit;
unsigned behind_last_bsn = m_window.mod_sns(first_bsn + num_blocks);
Decoding::extract_rbb(rbb, show_rbb);
@ -866,8 +950,7 @@ int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
* TODO: check whether this FIXME still makes sense
*/
LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of "
"V(A)..V(S) range %s Free TBF!\n", tbf_name(this));
return 1; /* indicate to free TBF */
"V(A)..V(S) range %s\n", tbf_name(this));
}
}
@ -1125,3 +1208,138 @@ bool gprs_rlcmac_dl_tbf::keep_open(unsigned fn) const
keep_time_frames = msecs_to_frames(bts_data()->dl_tbf_idle_msec);
return frames_since_last_drain(fn) <= keep_time_frames;
}
/*
* This function returns the pointer to data which needs
* to be copied. Also updates the status of the block related to
* Split block handling in the RLC/MAC block.
*/
enum egprs_rlc_dl_reseg_bsn_state
gprs_rlcmac_dl_tbf::egprs_dl_get_data(int bsn, uint8_t **block_data)
{
gprs_rlc_data *rlc_data = m_rlc.block(bsn);
egprs_rlc_dl_reseg_bsn_state *block_status_dl =
&rlc_data->spb_status.block_status_dl;
GprsCodingScheme &cs_current_trans = m_rlc.block(bsn)->cs_current_trans;
GprsCodingScheme &cs_init = m_rlc.block(bsn)->cs_init;
*block_data = &rlc_data->block[0];
/*
* Table 10.3a.0.1 of 44.060
* MCS6,9: second segment starts at 74/2 = 37
* MCS5,7: second segment starts at 56/2 = 28
* MCS8: second segment starts at 31
* MCS4: second segment starts at 44/2 = 22
*/
if (cs_current_trans.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3) {
if (*block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
switch (GprsCodingScheme::Scheme(cs_init)) {
case GprsCodingScheme::MCS6 :
case GprsCodingScheme::MCS9 :
*block_data = &rlc_data->block[37];
break;
case GprsCodingScheme::MCS7 :
case GprsCodingScheme::MCS5 :
*block_data = &rlc_data->block[28];
break;
case GprsCodingScheme::MCS8 :
*block_data = &rlc_data->block[31];
break;
case GprsCodingScheme::MCS4 :
*block_data = &rlc_data->block[22];
break;
default:
LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
"--%s hit invalid condition. headerType(%d) "
" blockstatus(%d) cs(%s) PLEASE FIX!\n", name(),
cs_current_trans.headerTypeData(),
*block_status_dl, cs_init.name());
break;
}
return EGPRS_RESEG_SECOND_SEG_SENT;
} else if ((cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
(cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
return EGPRS_RESEG_FIRST_SEG_SENT;
} else if ((GprsCodingScheme::Scheme(cs_init) ==
GprsCodingScheme::MCS4) &&
(GprsCodingScheme::Scheme(cs_current_trans) ==
GprsCodingScheme::MCS1)) {
return EGPRS_RESEG_FIRST_SEG_SENT;
}
}
return EGPRS_RESEG_DL_DEFAULT;
}
/*
* This function returns the status of split block
* for RLC/MAC block.
*/
unsigned int gprs_rlcmac_dl_tbf::get_egprs_dl_spb_status(const int bsn)
{
const gprs_rlc_data *rlc_data = m_rlc.block(bsn);
return rlc_data->spb_status.block_status_dl;
}
/*
* This function returns the spb value to be sent OTA
* for RLC/MAC block.
*/
enum egprs_rlcmac_dl_spb gprs_rlcmac_dl_tbf::get_egprs_dl_spb(const int bsn)
{
struct gprs_rlc_data *rlc_data = m_rlc.block(bsn);
egprs_rlc_dl_reseg_bsn_state block_status_dl =
rlc_data->spb_status.block_status_dl;
GprsCodingScheme &cs_current_trans = m_rlc.block(bsn)->cs_current_trans;
GprsCodingScheme &cs_init = m_rlc.block(bsn)->cs_init;
/* Table 10.4.8b.1 of 44.060 */
if (cs_current_trans.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_3) {
/*
* if we are sending the second segment the spb should be 3
* other wise it should be 2
*/
if (block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
return EGPRS_RLCMAC_DL_SEC_SEG;
} else if ((cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_1) ||
(cs_init.headerTypeData() ==
GprsCodingScheme::HEADER_EGPRS_DATA_TYPE_2)) {
return EGPRS_RLCMAC_DL_FIRST_SEG;
} else if ((GprsCodingScheme::Scheme(cs_init) ==
GprsCodingScheme::MCS4) &&
(GprsCodingScheme::Scheme(cs_current_trans) ==
GprsCodingScheme::MCS1)) {
return EGPRS_RLCMAC_DL_FIRST_SEG;
}
}
/* Non SPB cases 0 is reurned */
return EGPRS_RLCMAC_DL_NO_RETX;
}
void gprs_rlcmac_dl_tbf::egprs_calc_window_size()
{
struct gprs_rlcmac_bts *bts_data = bts->bts_data();
unsigned int num_pdch = pcu_bitcount(dl_slots());
unsigned int ws = bts_data->ws_base + num_pdch * bts_data->ws_pdch;
ws = (ws / 32) * 32;
ws = OSMO_MAX(64, ws);
if (num_pdch == 1)
ws = OSMO_MIN(192, ws);
else
ws = OSMO_MIN(128 * num_pdch, ws);
LOGP(DRLCMAC, LOGL_INFO, "%s: Setting EGPRS window size to %d\n",
name(), ws);
m_window.set_ws(ws);
}

View File

@ -29,6 +29,8 @@
#include <decoding.h>
#include <pcu_l1_if.h>
#include "pcu_utils.h"
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
@ -51,7 +53,7 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
const uint8_t *data = _data->block;
uint8_t len = _data->len;
const struct gprs_rlc_data_block_info *rdbi = &_data->block_info;
GprsCodingScheme cs = _data->cs;
GprsCodingScheme cs = _data->cs_last;
Decoding::RlcData frames[16], *frame;
int i, num_frames = 0;
@ -60,25 +62,32 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
num_frames = Decoding::rlc_data_from_ul_data(
rdbi, cs, data, &(frames[0]), sizeof(frames),
rdbi, cs, data, &(frames[0]), ARRAY_SIZE(frames),
&dummy_tlli);
/* create LLC frames */
for (i = 0; i < num_frames; i++) {
frame = frames + i;
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset %d, "
"length=%d, is_complete=%d\n",
i + 1, frame->offset, frame->length, frame->is_complete);
if (frame->length) {
bts->rlc_ul_payload_bytes(frame->length);
m_llc.append_frame(data + frame->offset, frame->length);
m_llc.consume(frame->length);
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d "
"starts at offset %d, "
"length=%d, is_complete=%d\n",
i + 1, frame->offset, frame->length,
frame->is_complete);
m_llc.append_frame(data + frame->offset, frame->length);
m_llc.consume(frame->length);
}
if (frame->is_complete) {
/* send frame to SGSN */
LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n",
tbf_name(this) , m_llc.frame_length());
snd_ul_ud();
bts->llc_ul_bytes(m_llc.frame_length());
m_llc.reset();
}
}
@ -138,6 +147,7 @@ struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn, uint8_t ts)
return msg;
}
/*! \brief receive data from PDCH/L1 */
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
const struct gprs_rlc_data_info *rlc,
uint8_t *data, struct pcu_l1_meas *meas)
@ -219,36 +229,28 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
rdbi->bsn, m_window.v_q(),
m_window.mod_sns(m_window.v_q() + ws - 1));
block = m_rlc.block(rdbi->bsn);
block->block_info = *rdbi;
block->cs = rlc->cs;
OSMO_ASSERT(rdbi->data_len < sizeof(block->block));
OSMO_ASSERT(rdbi->data_len <= sizeof(block->block));
rlc_data = &(block->block[0]);
/* TODO: Handle SPB != 0 -> Set length to 2*len, add offset if
* 2nd part. Note that resegmentation is currently disabled
* within the UL assignment.
*/
if (rdbi->spb) {
LOGP(DRLCMACUL, LOGL_NOTICE,
"Got SPB != 0 but resegmentation has been "
"disabled, skipping %s data block with BSN %d, "
"TFI=%d.\n", rlc->cs.name(), rdbi->bsn,
rlc->tfi);
continue;
}
block->len =
Decoding::rlc_copy_to_aligned_buffer(rlc, block_idx, data,
rlc_data);
if (rdbi->spb) {
egprs_rlc_ul_reseg_bsn_state assemble_status;
assemble_status = handle_egprs_ul_spb(rlc,
block, data, block_idx);
if (assemble_status != EGPRS_RESEG_DEFAULT)
return 0;
} else {
block->block_info = *rdbi;
block->cs_last = rlc->cs;
block->len =
Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data);
}
LOGP(DRLCMACUL, LOGL_DEBUG,
"%s: data_length=%d, data=%s\n",
name(), block->len, osmo_hexdump(rlc_data, block->len));
/* TODO: Handle SPB != 0 -> set state to partly received
* (upper/lower) and continue with the loop, unless the other
* part is already present.
*/
/* Get/Handle TLLI */
if (rdbi->ti) {
num_chunks = Decoding::rlc_data_from_ul_data(
@ -389,3 +391,150 @@ int gprs_rlcmac_ul_tbf::snd_ul_ud()
return 0;
}
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_second_seg(
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx)
{
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
union split_block_status *spb_status = &block->spb_status;
uint8_t *rlc_data = &block->block[0];
if (spb_status->block_status_ul &
EGPRS_RESEG_FIRST_SEG_RXD) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"---%s: Second seg is received "
"first seg is already present "
"set the status to complete\n", name());
spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data + block->len);
block->block_info.data_len += rdbi->data_len;
} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"---%s: Second seg is received "
"first seg is not received "
"set the status to second seg received\n",
name());
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data,
rlc_data + rlc->block_info[block_idx].data_len);
spb_status->block_status_ul = EGPRS_RESEG_SECOND_SEG_RXD;
block->block_info = *rdbi;
}
return spb_status->block_status_ul;
}
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_first_seg(
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx)
{
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
uint8_t *rlc_data = &block->block[0];
union split_block_status *spb_status = &block->spb_status;
if (spb_status->block_status_ul & EGPRS_RESEG_SECOND_SEG_RXD) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"---%s: First seg is received "
"second seg is already present "
"set the status to complete\n", name());
block->len += Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data);
block->block_info.data_len = block->len;
spb_status->block_status_ul = EGPRS_RESEG_DEFAULT;
} else if (spb_status->block_status_ul == EGPRS_RESEG_DEFAULT) {
LOGP(DRLCMACUL, LOGL_DEBUG,
"---%s: First seg is received "
"second seg is not received "
"set the status to first seg "
"received\n", name());
spb_status->block_status_ul = EGPRS_RESEG_FIRST_SEG_RXD;
block->len = Decoding::rlc_copy_to_aligned_buffer(rlc,
block_idx, data, rlc_data);
block->block_info = *rdbi;
}
return spb_status->block_status_ul;
}
egprs_rlc_ul_reseg_bsn_state gprs_rlcmac_ul_tbf::handle_egprs_ul_spb(
const struct gprs_rlc_data_info *rlc, struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx)
{
const gprs_rlc_data_block_info *rdbi = &rlc->block_info[block_idx];
LOGP(DRLCMACUL, LOGL_DEBUG,
"--%s: Got SPB(%d) "
"cs(%s) data block with BSN (%d), "
"TFI(%d).\n", name(), rdbi->spb, rlc->cs.name(), rdbi->bsn,
rlc->tfi);
egprs_rlc_ul_reseg_bsn_state assemble_status = EGPRS_RESEG_INVALID;
/* Section 10.4.8b of 44.060*/
if (rdbi->spb == 2)
assemble_status = handle_egprs_ul_first_seg(rlc,
block, data, block_idx);
else if (rdbi->spb == 3)
assemble_status = handle_egprs_ul_second_seg(rlc,
block, data, block_idx);
else {
LOGP(DRLCMACUL, LOGL_ERROR,
"--%s: spb(%d) Not supported SPB for this EGPRS "
"configuration\n",
name(), rdbi->spb);
}
/*
* When the block is successfully constructed out of segmented blocks
* upgrade the MCS to the type 2
*/
if (assemble_status == EGPRS_RESEG_DEFAULT) {
switch (GprsCodingScheme::Scheme(rlc->cs)) {
case GprsCodingScheme::MCS3 :
block->cs_last = GprsCodingScheme::MCS6;
LOGP(DRLCMACUL, LOGL_DEBUG,
"--%s: Upgrading to MCS6\n", name());
break;
case GprsCodingScheme::MCS2 :
block->cs_last = GprsCodingScheme::MCS5;
LOGP(DRLCMACUL, LOGL_DEBUG,
"--%s: Upgrading to MCS5\n", name());
break;
case GprsCodingScheme::MCS1 :
LOGP(DRLCMACUL, LOGL_DEBUG,
"--%s: Upgrading to MCS4\n", name());
block->cs_last = GprsCodingScheme::MCS4;
break;
default:
LOGP(DRLCMACUL, LOGL_ERROR,
"--%s: cs(%s) Error in Upgrading to higher MCS\n",
name(), rlc->cs.name());
break;
}
}
return assemble_status;
}
void gprs_rlcmac_ul_tbf::egprs_calc_ulwindow_size()
{
struct gprs_rlcmac_bts *bts_data = bts->bts_data();
unsigned int num_pdch = pcu_bitcount(ul_slots());
unsigned int ws = bts_data->ws_base + num_pdch * bts_data->ws_pdch;
ws = (ws / 32) * 32;
ws = OSMO_MAX(64, ws);
if (num_pdch == 1)
ws = OSMO_MIN(192, ws);
else
ws = OSMO_MIN(128 * num_pdch, ws);
LOGP(DRLCMAC, LOGL_ERROR, "%s: Setting EGPRS window size to %d, base(%d) slots(%d) ws_pdch(%d)\n",
name(), ws, bts_data->ws_base, num_pdch, bts_data->ws_pdch);
m_window.set_ws(ws);
}

View File

@ -1,7 +1,7 @@
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/
AM_LDFLAGS = -lrt
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test edge/EdgeTest
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test edge/EdgeTest bitcomp/BitcompTest
noinst_PROGRAMS = emu/pcu_emu
rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp
@ -26,6 +26,11 @@ tbf_TbfTest_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
bitcomp_BitcompTest_SOURCES = bitcomp/BitcompTest.cpp ../src/egprs_rlc_compression.cpp
bitcomp_BitcompTest_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
edge_EdgeTest_SOURCES = edge/EdgeTest.cpp
edge_EdgeTest_LDADD = \
$(top_builddir)/src/libgprs.la \
@ -108,6 +113,7 @@ EXTRA_DIST = \
rlcmac/RLCMACTest.ok rlcmac/RLCMACTest.err \
alloc/AllocTest.ok alloc/AllocTest.err \
tbf/TbfTest.ok tbf/TbfTest.err \
bitcomp/BitcompTest.ok bitcomp/BitcompTest.err \
types/TypesTest.ok types/TypesTest.err \
ms/MsTest.ok ms/MsTest.err \
llc/LlcTest.ok llc/LlcTest.err \

View File

@ -118,6 +118,7 @@ static void test_alloc_a(gprs_rlcmac_tbf_direction dir,
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_a;
bts->maximise_dir = NO_MAXIMISE;
struct gprs_rlcmac_trx *trx = &bts->trx[0];
for (i = 0; i < 8; i += 1)
@ -196,6 +197,7 @@ static void test_alloc_b(int ms_class)
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
bts->maximise_dir = NO_MAXIMISE;
trx = &bts->trx[0];
trx->pdch[5].enable();
@ -238,6 +240,7 @@ static void test_alloc_b(int ms_class)
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
bts->maximise_dir = NO_MAXIMISE;
trx = &bts->trx[0];
trx->pdch[5].enable();
@ -285,6 +288,7 @@ static void test_alloc_b(int ms_class)
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
bts->maximise_dir = NO_MAXIMISE;
trx = &bts->trx[0];
trx->pdch[1].enable();
@ -660,6 +664,7 @@ static void test_successive_allocation(algo_t algo, unsigned min_class,
bts = the_bts.bts_data();
bts->alloc_algorithm = algo;
bts->maximise_dir = NO_MAXIMISE;
trx = &bts->trx[0];
trx->pdch[3].enable();
@ -698,6 +703,7 @@ static void test_many_connections(algo_t algo, unsigned expect_num,
bts = the_bts.bts_data();
bts->alloc_algorithm = algo;
bts->maximise_dir = NO_MAXIMISE;
trx = &bts->trx[0];
trx->pdch[3].enable();
@ -792,6 +798,52 @@ static void test_many_connections()
test_many_connections(alloc_algorithm_dynamic, 160, "algorithm dynamic");
}
static void test_2_consecutive_dl_tbfs()
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
uint8_t ms_class = 11;
uint8_t egprs_ms_class = 11;
gprs_rlcmac_tbf *dl_tbf1, *dl_tbf2;
uint8_t numTs1 = 0, numTs2 = 0;
printf("Testing DL TS allocation for Multi UEs\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
bts->maximise_dir = DL_ONLY;
trx = &bts->trx[0];
trx->pdch[4].enable();
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
dl_tbf1 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class, 0);
OSMO_ASSERT(dl_tbf1);
for (int i = 0; i < 8; i++) {
if (dl_tbf1->pdch[i])
numTs1++;
}
OSMO_ASSERT(numTs1 == 4);
printf("TBF1: numTs(%d)\n", numTs1);
dl_tbf2 = tbf_alloc_dl_tbf(bts, NULL, 0, ms_class, egprs_ms_class, 0);
OSMO_ASSERT(dl_tbf2);
for (int i = 0; i < 8; i++) {
if (dl_tbf2->pdch[i])
numTs2++;
}
printf("TBF2: numTs(%d)\n", numTs2);
OSMO_ASSERT(numTs2 == 4);
tbf_free(dl_tbf1);
tbf_free(dl_tbf2);
}
int main(int argc, char **argv)
{
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile AllocTest context");
@ -809,6 +861,7 @@ int main(int argc, char **argv)
test_alloc_b();
test_successive_allocation();
test_many_connections();
test_2_consecutive_dl_tbfs();
return EXIT_SUCCESS;
}

View File

@ -10793,3 +10793,6 @@ Going to test assignment with many connections, algorithm dynamic
TBF[31] class 14 reserves ......C.
TBF[31] class 15 reserves .......C
Successfully allocated 160 TBFs
Testing DL TS allocation for Multi UEs
TBF1: numTs(4)
TBF2: numTs(4)

View File

@ -0,0 +1,305 @@
#include <stdint.h>
#include <string.h>
#include "rlc.h"
#include "gprs_debug.h"
#include <gprs_rlcmac.h>
#include "egprs_rlc_compression.h"
extern "C" {
#include <osmocom/core/logging.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
}
#define NEW 1
#define MASK(n) (0xFF << (8-n))
#define MAX_CRBB_LEN 23
#define MAX_URBB_LEN 40
void *tall_pcu_ctx;
struct test_data {
int8_t crbb_len;
uint8_t cc;
uint8_t crbb_data[MAX_CRBB_LEN]; /* compressed data */
uint8_t ucmp_data[MAX_URBB_LEN]; /* uncompressed data */
unsigned int ucmp_len;
int verify;
} test[] = {
{ .crbb_len = 67, .cc = 1,
.crbb_data = {
0x02, 0x0c, 0xa0, 0x30, 0xcb, 0x1a, 0x0c, 0xe3, 0x6c
},
.ucmp_data = {
0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x01, 0xff, 0xff,
0xff, 0xf8, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe,
0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xdb
},
.ucmp_len = 194, .verify = 1
},
{ .crbb_len = 40, .cc = 1,
.crbb_data = {
0x53, 0x06, 0xc5, 0x40, 0x6d
},
.ucmp_data = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00,
0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
0x00, 0x00, 0x00, 0x00, 0x03
},
.ucmp_len = 182, .verify = 1
},
{ .crbb_len = 8, .cc = 1,
.crbb_data = {0x02},
.ucmp_data = {0xff, 0xff, 0xff, 0xf8},
.ucmp_len = 29, .verify = 1
},
{ .crbb_len = 103, .cc = 1,
.crbb_data = {
0x02, 0x0c, 0xe0, 0x41, 0xa0, 0x0c, 0x36, 0x0d, 0x03,
0x71, 0xb0, 0x6e, 0x24
},
.ucmp_data = {
0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xff, 0xff,
0xf8, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xfe, 0x00, 0x00,
0x0f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x7f, 0xff,
0xff, 0xff, 0x80, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff
},
.ucmp_len = 288, .verify = 1
},
/* Test vector from libosmocore test */
{ .crbb_len = 18, .cc = 1,
.crbb_data = {0xdd, 0x41, 0x00},
.ucmp_data = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x00
},
.ucmp_len = 90, .verify = 1
},
{ .crbb_len = 35, .cc = 0,
.crbb_data = {0xde, 0x88, 0x75, 0x65, 0x80},
.ucmp_data = {0x37, 0x47, 0x81, 0xf0},
.ucmp_len = 28, .verify = 1
},
/*Invalid inputs*/
{ .crbb_len = 18, .cc = 1,
.crbb_data = {0x1E, 0x70, 0xc0},
.ucmp_data = {0x0},
.ucmp_len = 0, .verify = 0
},
{ .crbb_len = 14, .cc = 1,
.crbb_data = {0x00, 0x1E, 0x7c},
.ucmp_data = {0x0},
.ucmp_len = 0, .verify = 0
},
{ .crbb_len = 24, .cc = 0,
.crbb_data = {0x00, 0x00, 0x00},
.ucmp_data = {0x0},
.ucmp_len = 0, .verify = 0
}
};
static const struct log_info_cat default_categories[] = {
{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_DEBUG, 1},
{"DRLCMAC", "\033[0;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_DEBUG, 1},
{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_DEBUG, 1},
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Downlink (RLCMAC)", LOGL_DEBUG, 1},
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_DEBUG, 1},
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_DEBUG, 1},
{"DRLCMACMEAS", "\033[1;31m", "GPRS RLC/MAC layer Measurements (RLCMAC)", LOGL_INFO, 1},
{"DNS", "\033[1;34m", "GPRS Network Service Protocol (NS)", LOGL_INFO, 1},
{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO, 1},
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
};
static int filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
return 1;
}
/* To verify the result with expected result */
int check_result(bitvec bits, uint8_t *exp_data, int exp_len)
{
if (bits.cur_bit != exp_len)
return 0;
size_t n = (exp_len / 8);
int rem = (exp_len % 8);
if (memcmp(exp_data, bits.data, n) == 0) {
if (rem == 0)
return 1;
if ((bits.data[n] & MASK(rem)) == ((*(exp_data + n)) & MASK(rem)))
return 1;
else
return 0;
} else
return 0;
}
/* To test decoding of compressed bitmap by Tree based method
* and to verify the result with expected result
* for invalid input verfication is suppressed
*/
static void test_EPDAN_decode_tree(void)
{
bitvec dest;
int init_flag = 1;
int itr;
int rc;
uint8_t bits_data[RLC_EGPRS_MAX_WS/8];
printf("=== start %s ===\n", __func__);
for (itr = 0 ; itr < (sizeof(test) / sizeof(test_data)) ; itr++) {
dest.data = bits_data;
dest.data_len = sizeof(bits_data);
dest.cur_bit = 0;
memset(dest.data, 0, sizeof(bits_data));
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTest:%d\nTree based decoding:"
"\nuncompressed data = %s\nlen = %d\n", itr + 1,
osmo_hexdump(test[itr].crbb_data,
(test[itr].crbb_len + 7)/8), test[itr].crbb_len
);
rc = egprs_compress::decompress_crbb(test[itr].crbb_len,
test[itr].cc, test[itr].crbb_data, &dest);
LOGP (DRLCMACDL, LOGL_DEBUG, "PRAVIN %s\n", osmo_hexdump (dest.data, (dest.cur_bit + 7)/8));
if (rc < 0) {
LOGP(DRLCMACUL, LOGL_NOTICE,
"\nFailed to decode CRBB: length %d, data %s",
test[itr].crbb_len, osmo_hexdump(
test[itr].crbb_data, (test[itr].crbb_len + 7)/8));
}
if (init_flag)
init_flag = 0;
if (test[itr].verify) {
if (check_result(dest, test[itr].ucmp_data,
test[itr].ucmp_len) == 0) {
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTree based decoding"
":Error\nexpected data = %s\nexpected"
" len = %d\ndecoded data = %s\n"
"decoded len = %d\n",
osmo_hexdump(test[itr].ucmp_data,
(test[itr].ucmp_len + 7)/8),
test[itr].ucmp_len, osmo_hexdump(dest.data,
(dest.cur_bit + 7)/8), dest.cur_bit
);
OSMO_ASSERT(0);
}
}
LOGP(DRLCMACDL, LOGL_DEBUG, "\nexpected data = %s\nexpected len = %d"
"\ndecoded data = %s\ndecoded len = %d\n",
osmo_hexdump(test[itr].ucmp_data,
(test[itr].ucmp_len + 7)/8),
test[itr].ucmp_len, osmo_hexdump(dest.data,
(dest.cur_bit + 7)/8), dest.cur_bit
);
if (test[itr].verify) {
if (check_result(dest, test[itr].ucmp_data,
test[itr].ucmp_len) == 0) {
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTree based decoding"
":Error\nexpected data = %s\nexpected"
" len = %d\ndecoded data = %s\n"
"decoded len = %d\n",
osmo_hexdump(test[itr].ucmp_data,
(test[itr].ucmp_len + 7)/8),
test[itr].ucmp_len, osmo_hexdump(dest.data,
(dest.cur_bit + 7)/8), dest.cur_bit
);
OSMO_ASSERT(0);
}
}
}
printf("=== end %s ===\n", __func__);
}
void puan_test()
{
printf("=== start %s ===\n", __func__);
int is_compressed, itr;
bitvec ucmp_vec;
unsigned int ucmp_bmplen;
uint8_t crbb_bitmap[23] = {'\0'};
uint8_t uclen_crbb = 0;
bitvec crbb_vec;
unsigned int rest_bits = 94;
for (itr = 0 ; itr < (sizeof(test) / sizeof(test_data)) - 4 ; itr++) {
LOGP(DRLCMACDL, LOGL_DEBUG, "\nTest:%d\n"
"\nuncompressed data = %s\nlen = %d\n", itr + 1,
osmo_hexdump(test[itr].ucmp_data,
(test[itr].ucmp_len + 7)/8), test[itr].ucmp_len
);
if (rest_bits < test[itr].crbb_len)
rest_bits = test[itr].crbb_len + 16;
ucmp_bmplen = test[itr].ucmp_len;
ucmp_vec.data = test[itr].ucmp_data;
ucmp_bmplen = test[itr].ucmp_len;
ucmp_vec.cur_bit = ucmp_bmplen;
ucmp_vec.data_len = test[itr].ucmp_len;
crbb_vec.data = crbb_bitmap;
crbb_vec.cur_bit = 0;
crbb_vec.data_len = test[itr].crbb_len;
is_compressed = compress_rbb(&ucmp_vec, /* Uncompressed bitmap*/
&crbb_vec, /*Compressed bitmap vector */
&uclen_crbb,
(rest_bits - 8 - 8));/* CRBBlength:7 colourcode:1 dissector length:8*/
if (test[itr].verify) {
if (check_result(crbb_vec, test[itr].crbb_data,
test[itr].crbb_len) == 0) {
LOGP(DRLCMACDL, LOGL_DEBUG, "\nencoding"
":Error\nexpected data = %s\nexpected"
" len = %d\ndecoded data = %s\n"
"encoded len = %d\n",
osmo_hexdump(test[itr].crbb_data,
(test[itr].crbb_len + 7)/8),
test[itr].crbb_len, osmo_hexdump(crbb_vec.data,
(crbb_vec.cur_bit + 7)/8), crbb_vec.cur_bit
);
OSMO_ASSERT(0);
}
}
}
printf("=== end %s ===\n", __func__);
}
const struct log_info debug_log_info = {
filter_fn,
(struct log_info_cat *)default_categories,
ARRAY_SIZE(default_categories),
};
int main(int argc, char **argv)
{
osmo_init_logging(&debug_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile bitcompTest context");
if (!tall_pcu_ctx)
abort();
test_EPDAN_decode_tree();
puan_test();
if (getenv("TALLOC_REPORT_FULL"))
talloc_report_full(tall_pcu_ctx, stderr);
talloc_free(tall_pcu_ctx);
return EXIT_SUCCESS;
}
/*
* stubs that should not be reached
*/
extern "C" {
void l1if_pdch_req() { abort(); }
void l1if_connect_pdch() { abort(); }
void l1if_close_pdch() { abort(); }
void l1if_open_pdch() { abort(); }
}

View File

@ -0,0 +1,171 @@
Test:1
Tree based decoding:
uncompressed data = 02 0c a0 30 cb 1a 0c e3 6c
len = 67
Run_length = 29
Run_length = 26
Run_length = 30
Run_length = 27
Run_length = 31
Run_length = 19
Run_length = 32
PRAVIN ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff c0
expected data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff db
expected len = 194
decoded data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff db
decoded len = 194
Test:2
Tree based decoding:
uncompressed data = 53 06 c5 40 6d
len = 40
Run_length = 50
Run_length = 40
Run_length = 51
Run_length = 41
PRAVIN ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 00
expected data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 03
expected len = 182
decoded data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 03
decoded len = 182
Test:3
Tree based decoding:
uncompressed data = 02
len = 8
Run_length = 29
PRAVIN ff ff ff f8
expected data = ff ff ff f8
expected len = 29
decoded data = ff ff ff f8
decoded len = 29
Test:4
Tree based decoding:
uncompressed data = 02 0c e0 41 a0 0c 36 0d 03 71 b0 6e 24
len = 103
Run_length = 29
Run_length = 19
Run_length = 29
Run_length = 20
Run_length = 30
Run_length = 21
Run_length = 31
Run_length = 22
Run_length = 32
Run_length = 22
Run_length = 33
PRAVIN ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
expected data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
expected len = 288
decoded data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
decoded len = 288
Test:5
Tree based decoding:
uncompressed data = dd 41 00
len = 18
Run_length = 64
Run_length = 16
Run_length = 10
PRAVIN ff ff ff ff ff ff ff ff ff ff 00 00
expected data = ff ff ff ff ff ff ff ff ff ff 00 00
expected len = 90
decoded data = ff ff ff ff ff ff ff ff ff ff 00 00
decoded len = 90
Test:6
Tree based decoding:
uncompressed data = de 88 75 65 80
len = 35
Run_length = 2
Run_length = 2
Run_length = 1
Run_length = 3
Run_length = 1
Run_length = 1
Run_length = 3
Run_length = 4
Run_length = 6
Run_length = 5
PRAVIN 37 47 81 f0
expected data = 37 47 81 f0
expected len = 28
decoded data = 37 47 81 f0
decoded len = 28
Test:7
Tree based decoding:
uncompressed data = 1e 70 c0
len = 18
Run_length = 1
Run_length = 1
Run_length = 2
Run_length = 15
PRAVIN b0 00 00
expected data =
expected len = 0
decoded data =
decoded len = 19
Test:8
Tree based decoding:
uncompressed data = 00 1e
len = 14
PRAVIN
Failed to decode CRBB: length 14, data 00 1e
expected data =
expected len = 0
decoded data =
decoded len = 0
Test:9
Tree based decoding:
uncompressed data = 00 00 00
len = 24
PRAVIN
Failed to decode CRBB: length 24, data 00 00 00
expected data =
expected len = 0
decoded data =
decoded len = 0
Test:1
uncompressed data = ff ff ff f8 00 00 01 ff ff ff f8 00 00 00 ff ff ff fe 00 00 3f ff ff ff db
len = 194
CRBB bitmap = 02 0c a0 30 cb 1a 0c e3 60
Test:2
uncompressed data = ff ff ff ff ff ff c0 00 00 00 00 3f ff ff ff ff ff f8 00 00 00 00 03
len = 182
CRBB bitmap = 53 06 c5 40 6d
Test:3
uncompressed data = ff ff ff f8
len = 29
CRBB bitmap = 02
Test:4
uncompressed data = ff ff ff f8 00 00 ff ff ff f8 00 00 7f ff ff fe 00 00 0f ff ff ff e0 00 00 7f ff ff ff 80 00 01 ff ff ff ff
len = 288
CRBB bitmap = 02 0c e0 41 a0 0c 36 0d 03 71 b0 6e 24
Test:5
uncompressed data = ff ff ff ff ff ff ff ff ff ff 00 00
len = 90
CRBB bitmap = dd 41 20

View File

@ -0,0 +1,4 @@
=== start test_EPDAN_decode_tree ===
=== end test_EPDAN_decode_tree ===
=== start puan_test ===
=== end puan_test ===

View File

@ -26,7 +26,7 @@
#include "encoding.h"
#include "rlc.h"
#include "llc.h"
#include "bts.h"
extern "C" {
#include "pcu_vty.h"
@ -35,6 +35,7 @@ extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/vty/vty.h>
#include <osmocom/gprs/protocol/gsm_04_60.h>
}
#include <errno.h>
@ -495,6 +496,24 @@ static void test_rlc_unit_decoder()
OSMO_ASSERT(chunks[2].length == 1);
OSMO_ASSERT(!chunks[2].is_complete);
rdbi.e = 0;
rdbi.ti = 0;
rdbi.cv = 1;
tlli = 0;
offs = 0;
data[offs++] = 1;
num_chunks = Decoding::rlc_data_from_ul_data(&rdbi, cs, data,
chunks, ARRAY_SIZE(chunks), &tlli);
OSMO_ASSERT(num_chunks == 2);
OSMO_ASSERT(chunks[0].offset == 1);
OSMO_ASSERT(chunks[0].length == 0);
OSMO_ASSERT(chunks[0].is_complete);
OSMO_ASSERT(chunks[1].offset == 1);
OSMO_ASSERT(chunks[1].length == 43);
OSMO_ASSERT(!chunks[1].is_complete);
printf("=== end %s ===\n", __func__);
}
@ -506,6 +525,7 @@ static void test_rlc_unit_encoder()
uint8_t llc_data[1500] = {0,};
int num_chunks = 0;
int write_offset;
int count_payload;
struct gprs_llc llc;
Encoding::AppendResult ar;
@ -515,43 +535,49 @@ static void test_rlc_unit_encoder()
/* TS 44.060, B.1 */
cs = GprsCodingScheme::CS4;
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, 11);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 11);
OSMO_ASSERT(count_payload == 11);
OSMO_ASSERT(num_chunks == 1);
llc.reset();
llc.put_frame(llc_data, 26);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 2 + 11 + 26);
OSMO_ASSERT(count_payload == 26);
OSMO_ASSERT(num_chunks == 2);
llc.reset();
llc.put_frame(llc_data, 99);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(rdbi.cv != 0);
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
OSMO_ASSERT(count_payload == 11);
OSMO_ASSERT(num_chunks == 3);
OSMO_ASSERT(data[0] == ((11 << 2) | (1 << 1) | (0 << 0)));
@ -562,50 +588,57 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::CS1;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, 20);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 19);
OSMO_ASSERT(count_payload == 19);
OSMO_ASSERT(num_chunks == 1);
OSMO_ASSERT(data[0] == ((0 << 2) | (0 << 1) | (1 << 0)));
OSMO_ASSERT(data[1] == 0);
/* Block 2 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
OSMO_ASSERT(llc.chunk_size() == 1);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 1);
OSMO_ASSERT(count_payload == 1);
OSMO_ASSERT(num_chunks == 1);
llc.reset();
llc.put_frame(llc_data, 99);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 1 + 18);
OSMO_ASSERT(count_payload == 18);
OSMO_ASSERT(num_chunks == 2);
OSMO_ASSERT(data[0] == ((1 << 2) | (1 << 1) | (1 << 0)));
@ -615,31 +648,35 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::CS1;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, 7);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 7);
OSMO_ASSERT(count_payload == 7);
OSMO_ASSERT(num_chunks == 1);
llc.reset();
llc.put_frame(llc_data, 11);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 2 + 7 + 11);
OSMO_ASSERT(count_payload == 11);
OSMO_ASSERT(num_chunks == 2);
OSMO_ASSERT(data[0] == ((7 << 2) | (1 << 1) | (0 << 0)));
@ -650,20 +687,22 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::CS1;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, 99);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
OSMO_ASSERT(rdbi.e == 1);
OSMO_ASSERT(write_offset == 20);
OSMO_ASSERT(count_payload == 20);
OSMO_ASSERT(num_chunks == 1);
OSMO_ASSERT(rdbi.cv != 0);
@ -673,20 +712,22 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::CS1;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, 20);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, true);
&llc, &write_offset, &num_chunks, data, true, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
OSMO_ASSERT(rdbi.e == 1);
OSMO_ASSERT(write_offset == 20);
OSMO_ASSERT(count_payload == 20);
OSMO_ASSERT(num_chunks == 1);
OSMO_ASSERT(rdbi.cv == 0);
@ -696,49 +737,55 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::CS1;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, 30);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
OSMO_ASSERT(rdbi.e == 1);
OSMO_ASSERT(write_offset == 20);
OSMO_ASSERT(count_payload == 20);
OSMO_ASSERT(num_chunks == 1);
OSMO_ASSERT(data[0] == 0);
/* Block 2 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
OSMO_ASSERT(llc.chunk_size() == 10);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 10);
OSMO_ASSERT(count_payload == 10);
OSMO_ASSERT(num_chunks == 1);
llc.reset();
llc.put_frame(llc_data, 99);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 10 + 9);
OSMO_ASSERT(count_payload == 9);
OSMO_ASSERT(num_chunks == 2);
OSMO_ASSERT(data[0] == ((10 << 2) | (1 << 1) | (1 << 0)));
@ -748,43 +795,49 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::MCS4;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, 11);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 11);
OSMO_ASSERT(count_payload == 11);
OSMO_ASSERT(num_chunks == 1);
llc.reset();
llc.put_frame(llc_data, 26);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 2 + 11 + 26);
OSMO_ASSERT(count_payload == 26);
OSMO_ASSERT(num_chunks == 2);
llc.reset();
llc.put_frame(llc_data, 99);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(rdbi.cv != 0);
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
OSMO_ASSERT(count_payload == 5);
OSMO_ASSERT(num_chunks == 3);
OSMO_ASSERT(data[0] == ((11 << 1) | (0 << 0)));
@ -800,75 +853,86 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::MCS2;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, 15);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 15);
OSMO_ASSERT(count_payload == 15);
OSMO_ASSERT(num_chunks == 1);
llc.reset();
llc.put_frame(llc_data, 12);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
/* no LI here, becaues there are exact 12 bytes left. Put LI into next frame */
OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(rdbi.cv != 0);
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
OSMO_ASSERT(count_payload == 12);
OSMO_ASSERT(num_chunks == 2);
OSMO_ASSERT(data[0] == ((15 << 1) | (1 << 0)));
OSMO_ASSERT(data[1] == 0);
/* Block 2 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
OSMO_ASSERT(llc.chunk_size() == 0);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 0);
OSMO_ASSERT(count_payload == 0);
OSMO_ASSERT(num_chunks == 1);
llc.reset();
llc.put_frame(llc_data, 7);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(rdbi.cv != 0);
OSMO_ASSERT(write_offset == 2 + 0 + 7);
OSMO_ASSERT(count_payload == 7);
OSMO_ASSERT(num_chunks == 2);
llc.reset();
llc.put_frame(llc_data, 18);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(rdbi.cv != 0);
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
OSMO_ASSERT(count_payload == 18);
OSMO_ASSERT(num_chunks == 3);
OSMO_ASSERT(data[0] == ((0 << 1) | (0 << 0)));
@ -877,32 +941,36 @@ static void test_rlc_unit_encoder()
OSMO_ASSERT(data[3] == 0);
/* Block 3 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, 6);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, false);
&llc, &write_offset, &num_chunks, data, false, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(write_offset == 1 + 6);
OSMO_ASSERT(count_payload == 6);
OSMO_ASSERT(num_chunks == 1);
llc.reset();
llc.put_frame(llc_data, 12);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, true);
&llc, &write_offset, &num_chunks, data, true, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(rdbi.cv == 0);
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
OSMO_ASSERT(count_payload == 12);
OSMO_ASSERT(num_chunks == 3);
OSMO_ASSERT(data[0] == ((6 << 1) | (0 << 0)));
@ -917,21 +985,23 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::MCS2;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, rdbi.data_len);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, true);
&llc, &write_offset, &num_chunks, data, true, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
OSMO_ASSERT(rdbi.e == 1);
OSMO_ASSERT(rdbi.cv == 0);
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
OSMO_ASSERT(count_payload == rdbi.data_len);
OSMO_ASSERT(num_chunks == 1);
OSMO_ASSERT(data[0] == 0);
@ -941,21 +1011,23 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::MCS2;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, rdbi.data_len - 1);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, true);
&llc, &write_offset, &num_chunks, data, true, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(rdbi.cv == 0);
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
OSMO_ASSERT(count_payload == rdbi.data_len - 1);
OSMO_ASSERT(num_chunks == 1);
OSMO_ASSERT(data[0] == (((rdbi.data_len-1) << 1) | (1 << 0)));
@ -966,21 +1038,23 @@ static void test_rlc_unit_encoder()
cs = GprsCodingScheme::MCS2;
/* Block 1 */
gprs_rlc_data_block_info_init(&rdbi, cs, false);
gprs_rlc_data_block_info_init(&rdbi, cs, false, 0);
num_chunks = 0;
write_offset = 0;
memset(data, 0, sizeof(data));
llc.reset();
llc.put_frame(llc_data, rdbi.data_len - 2);
count_payload = -1;
ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
&llc, &write_offset, &num_chunks, data, true);
&llc, &write_offset, &num_chunks, data, true, &count_payload);
OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
OSMO_ASSERT(rdbi.e == 0);
OSMO_ASSERT(rdbi.cv == 0);
OSMO_ASSERT(write_offset == (int)rdbi.data_len);
OSMO_ASSERT(count_payload == rdbi.data_len - 2);
OSMO_ASSERT(num_chunks == 2);
OSMO_ASSERT(data[0] == (((rdbi.data_len-2) << 1) | (0 << 0)));
@ -1018,7 +1092,7 @@ static void test_rlc_unaligned_copy()
block_idx++)
{
struct gprs_rlc_data_info rlc;
gprs_rlc_data_info_init_dl(&rlc, cs, false);
gprs_rlc_data_info_init_dl(&rlc, cs, false, 0);
memset(bits, pattern, sizeof(bits));
Decoding::rlc_copy_to_aligned_buffer(
@ -1062,12 +1136,14 @@ static void test_rlc_info_init()
struct gprs_rlc_data_info rlc;
printf("=== start %s ===\n", __func__);
gprs_rlc_data_info_init_dl(&rlc, GprsCodingScheme(GprsCodingScheme::CS1), false);
gprs_rlc_data_info_init_dl(&rlc,
GprsCodingScheme(GprsCodingScheme::CS1), false, 0);
OSMO_ASSERT(rlc.num_data_blocks == 1);
OSMO_ASSERT(rlc.data_offs_bits[0] == 24);
OSMO_ASSERT(rlc.block_info[0].data_len == 20);
gprs_rlc_data_info_init_dl(&rlc, GprsCodingScheme(GprsCodingScheme::MCS1), false);
gprs_rlc_data_info_init_dl(&rlc,
GprsCodingScheme(GprsCodingScheme::MCS1), false, 0);
OSMO_ASSERT(rlc.num_data_blocks == 1);
OSMO_ASSERT(rlc.data_offs_bits[0] == 33);
OSMO_ASSERT(rlc.block_info[0].data_len == 22);
@ -1101,6 +1177,250 @@ const struct log_info debug_log_info = {
ARRAY_SIZE(default_categories),
};
static void setup_bts(BTS *the_bts, uint8_t ts_no, uint8_t cs = 1)
{
gprs_rlcmac_bts *bts;
gprs_rlcmac_trx *trx;
bts = the_bts->bts_data();
bts->egprs_enabled = true;
bts->alloc_algorithm = alloc_algorithm_a;
bts->initial_cs_dl = cs;
bts->initial_cs_ul = cs;
trx = &bts->trx[0];
trx->pdch[ts_no].enable();
}
static void uplink_header_type_2_parsing_test(BTS *the_bts,
uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
uint8_t ms_class)
{
GprsMs *ms;
struct pcu_l1_meas meas;
int tfi = 0;
gprs_rlcmac_bts *bts;
RlcMacUplink_t ulreq = {0};
uint8_t data[79] = {0};
struct gprs_rlc_ul_header_egprs_2 *egprs2 = NULL;
egprs2 = (struct gprs_rlc_ul_header_egprs_2 *) data;
bts = the_bts->bts_data();
tfi = 1;
struct gprs_rlc_data_info rlc;
GprsCodingScheme cs;
int rc, offs;
/*without padding*/
cs = GprsCodingScheme::MCS5;
egprs2 = (struct gprs_rlc_ul_header_egprs_2 *) data;
egprs2->r = 1;
egprs2->si = 1;
egprs2->cv = 7;
egprs2->tfi_hi = tfi & 0x03;
egprs2->tfi_lo = (tfi & 0x1c) >> 2;
egprs2->bsn1_hi = 0;
egprs2->bsn1_lo = 0;
egprs2->cps_hi = 3;
egprs2->cps_lo = 0;
egprs2->rsb = 0;
egprs2->pi = 0;
data[4] = 0x20; /* Setting E field */
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
offs = rlc.data_offs_bits[0] / 8;
OSMO_ASSERT(offs == 4);
OSMO_ASSERT(rlc.tfi == 1);
OSMO_ASSERT(rlc.num_data_blocks == 1);
OSMO_ASSERT(rlc.block_info[0].e == 1);
OSMO_ASSERT(rlc.block_info[0].ti == 0);
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
/* with padding case */
cs = GprsCodingScheme::MCS6;
egprs2 = (struct gprs_rlc_ul_header_egprs_2 *) data;
egprs2->r = 1;
egprs2->si = 1;
egprs2->cv = 7;
egprs2->tfi_hi = tfi & 0x03;
egprs2->tfi_lo = (tfi & 0x1c) >> 2;
egprs2->bsn1_hi = 0;
egprs2->bsn1_lo = 0;
egprs2->cps_hi = 3;
egprs2->cps_lo = 0;
egprs2->rsb = 0;
egprs2->pi = 0;
data[10] = 0x20; /* Setting E field */
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
offs = rlc.data_offs_bits[0] / 8;
OSMO_ASSERT(offs == 10);
OSMO_ASSERT(rlc.num_data_blocks == 1);
OSMO_ASSERT(rlc.tfi == 1);
OSMO_ASSERT(rlc.block_info[0].e == 1);
OSMO_ASSERT(rlc.block_info[0].ti == 0);
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
egprs2->r = 1;
egprs2->si = 1;
egprs2->cv = 7;
egprs2->tfi_hi = tfi & 0x03;
egprs2->tfi_lo = (tfi & 0x1c) >> 2;
egprs2->bsn1_hi = 1;
egprs2->bsn1_lo = 0;
egprs2->cps_hi = 2;
egprs2->cps_lo = 0;
egprs2->rsb = 0;
egprs2->pi = 0;
data[10] = 0x20; /* Setting E field */
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
offs = rlc.data_offs_bits[0] / 8;
OSMO_ASSERT(offs == 10);
OSMO_ASSERT(rlc.tfi == 1);
OSMO_ASSERT(rlc.num_data_blocks == 1);
OSMO_ASSERT(rlc.block_info[0].e == 1);
OSMO_ASSERT(rlc.block_info[0].ti == 0);
OSMO_ASSERT(rlc.block_info[0].bsn == 1);
}
static void uplink_header_type2_test(void)
{
BTS the_bts;
int ts_no = 7;
uint32_t fn = 2654218;
uint16_t qta = 31;
uint32_t tlli = 0xf1223344;
const char *imsi = "0011223344";
uint8_t ms_class = 1;
gprs_rlcmac_ul_tbf *ul_tbf;
GprsMs *ms;
printf("=== start %s ===\n", __func__);
setup_bts(&the_bts, ts_no, 10);
uplink_header_type_2_parsing_test(&the_bts, ts_no,
tlli, &fn, qta, ms_class);
printf("=== end %s ===\n", __func__);
}
static void uplink_header_type_1_parsing_test(BTS *the_bts,
uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
uint8_t ms_class)
{
uint8_t trx_no = 0;
int tfi = 0;
struct gprs_rlcmac_pdch *pdch;
gprs_rlcmac_bts *bts;
uint8_t data[155] = {0};
struct gprs_rlc_ul_header_egprs_1 *egprs1 = NULL;
struct gprs_rlc_data_info rlc;
GprsCodingScheme cs;
int rc, offs;
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
bts = the_bts->bts_data();
tfi = 1;
/* MCS 7 */
cs = GprsCodingScheme::MCS7;
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
egprs1->si = 1;
egprs1->r = 1;
egprs1->cv = 7;
egprs1->tfi_hi = tfi & 0x03;
egprs1->tfi_lo = (tfi & 0x1c) >> 2;
egprs1->bsn1_hi = 0;
egprs1->bsn1_lo = 0;
egprs1->bsn2_hi = 1;
egprs1->bsn2_lo = 0;
egprs1->cps = 15;
egprs1->rsb = 0;
egprs1->pi = 0;
data[5] = 0xc0;
data[5 + 57] = 1;
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
OSMO_ASSERT(rlc.num_data_blocks == 2);
OSMO_ASSERT(rlc.block_info[0].e == 1);
OSMO_ASSERT(rlc.block_info[0].ti == 1);
OSMO_ASSERT(rlc.block_info[1].e == 1);
OSMO_ASSERT(rlc.block_info[1].ti == 0);
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
OSMO_ASSERT(rlc.block_info[1].bsn == 1);
OSMO_ASSERT(rlc.tfi == 1);
/* MCS 8 */
cs = GprsCodingScheme::MCS8;
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
egprs1->si = 1;
egprs1->r = 1;
egprs1->cv = 7;
egprs1->tfi_hi = tfi & 0x03;
egprs1->tfi_lo = (tfi & 0x1c) >> 2;
egprs1->bsn1_hi = 0;
egprs1->bsn1_lo = 0;
egprs1->bsn2_hi = 1;
egprs1->bsn2_lo = 0;
egprs1->cps = 15;
egprs1->rsb = 0;
egprs1->pi = 0;
data[5] = 0xc0;
data[5 + 69] = 1;
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
OSMO_ASSERT(rlc.num_data_blocks == 2);
OSMO_ASSERT(rlc.block_info[0].e == 1);
OSMO_ASSERT(rlc.block_info[0].ti == 1);
OSMO_ASSERT(rlc.block_info[1].e == 1);
OSMO_ASSERT(rlc.block_info[1].ti == 0);
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
OSMO_ASSERT(rlc.block_info[1].bsn == 1);
OSMO_ASSERT(rlc.tfi == 1);
/* MCS 9 */
cs = GprsCodingScheme::MCS9;
egprs1 = (struct gprs_rlc_ul_header_egprs_1 *) data;
egprs1->si = 1;
egprs1->r = 1;
egprs1->cv = 7;
egprs1->tfi_hi = tfi & 0x03;
egprs1->tfi_lo = (tfi & 0x1c) >> 2;
egprs1->bsn1_hi = 0;
egprs1->bsn1_lo = 0;
egprs1->bsn2_hi = 1;
egprs1->bsn2_lo = 0;
egprs1->cps = 15;
egprs1->rsb = 0;
egprs1->pi = 0;
data[5] = 0xc0;
data[5 + 75] = 1;
rc = Decoding::rlc_parse_ul_data_header(&rlc, data, cs);
OSMO_ASSERT(rlc.num_data_blocks == 2);
OSMO_ASSERT(rlc.block_info[0].e == 1);
OSMO_ASSERT(rlc.block_info[0].ti == 1);
OSMO_ASSERT(rlc.block_info[1].e == 1);
OSMO_ASSERT(rlc.block_info[1].ti == 0);
OSMO_ASSERT(rlc.block_info[0].bsn == 0);
OSMO_ASSERT(rlc.block_info[1].bsn == 1);
OSMO_ASSERT(rlc.tfi == 1);
}
void uplink_header_type1_test(void)
{
BTS the_bts;
int ts_no = 7;
uint32_t fn = 2654218;
uint16_t qta = 31;
uint32_t tlli = 0xf1223344;
const char *imsi = "0011223344";
uint8_t ms_class = 1;
gprs_rlcmac_ul_tbf *ul_tbf;
GprsMs *ms;
printf("=== start %s ===\n", __func__);
setup_bts(&the_bts, ts_no, 12);
uplink_header_type_1_parsing_test(&the_bts, ts_no, tlli, &fn,
qta, ms_class);
printf("=== end %s ===\n", __func__);
}
int main(int argc, char **argv)
{
struct vty_app_info pcu_vty_info = {0};
@ -1123,6 +1443,9 @@ int main(int argc, char **argv)
test_rlc_unaligned_copy();
test_rlc_unit_encoder();
uplink_header_type2_test();
uplink_header_type1_test();
if (getenv("TALLOC_REPORT_FULL"))
talloc_report_full(tall_pcu_ctx, stderr);
return EXIT_SUCCESS;

View File

@ -6,3 +6,7 @@
=== end test_rlc_unit_decoder ===
=== start test_rlc_unit_encoder ===
=== end test_rlc_unit_encoder ===
=== start uplink_header_type2_test ===
=== end uplink_header_type2_test ===
=== start uplink_header_type1_test ===
=== end uplink_header_type1_test ===

View File

@ -30,6 +30,7 @@ extern const struct log_info gprs_log_info;
#include "pcu_vty.h"
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
}
using namespace std;
@ -89,9 +90,12 @@ void testRlcMacDownlink()
std::string testData[] = {
"4e082500e3f1a81d080820800b2b2b2b2b2b2b2b2b2b2b", // Packet Downlink Assignment
"48282407a6a074227201000b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
"48282407a6a07422720100032b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
"47240c00400000000000000079eb2ac9402b2b2b2b2b2b", // Packet Uplink Ack Nack
"47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b" // Packet Uplink Assignment
"47283c367513ba333004242b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Assignment
"400820001a3904df0680efb3300b2b2b2b2b2b2b2b2b2b", // Packet Downlink Assignment (EGPRS)
"40284f0000001009810c826f4406809dcecb2b2b2b2b2b", // Packet Uplink Assignment (EGPRS)
"4024030f2f0000000087b0042b2b2b2b2b2b2b2b2b2b2b" // Packet Uplink Ack Nack (EGPRS)
"4913e00850884013a8048b2b2b2b2b2b2b2b2b2b2b2b2b"
"412430007fffffffffffffffefd19c7ba12b2b2b2b2b2b"
"41942b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
@ -152,9 +156,10 @@ void testRlcMacUplink()
bitvec_unhex(resultVector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
std::string testData[] = {
"400e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Dummy Control Block
"400b8020000000000000002480e00b2b2b2b2b2b2b2b2b", // Packet Downlink Ack/Nack
"4016713dc094270ca2ae57ef909006aa0fc0001f80222b" // Packet Resource Request
"400e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // Packet Uplink Dummy Control Block
"400b8020000000000000002480e0032b2b2b2b2b2b2b2b", // Packet Downlink Ack/Nack
"4016713dc094270ca2ae57ef909006aa0fc0001f80222b", // Packet Resource Request
"40200ffc0021ec010b2b2b2b2b2b2b2b2b2b2b2b2b2b2b", // EPDAN
"400a9020000000000000003010012a0800132b2b2b2b2b"
};
@ -207,6 +212,21 @@ void testRlcMacUplink()
bitvec_free(resultVector);
}
void testCsnLeftAlignedVarBmpBounds()
{
bitvec *vector = bitvec_alloc(23);
bitvec_unhex(vector, "40200bffd161003e0e519ffffffb800000000000000000");
RlcMacUplink_t data;
EGPRS_AckNack_Desc_t *urbb =
&data.u.Egprs_Packet_Downlink_Ack_Nack.EGPRS_AckNack.Desc;
decode_gsm_rlcmac_uplink(vector, &data);
OSMO_ASSERT(!strcmp(osmo_hexdump(urbb->URBB, 13),
"7f ff ff ee 00 00 00 00 00 00 00 00 00 "));
}
int main(int argc, char *argv[])
{
osmo_init_logging(&gprs_log_info);
@ -214,5 +234,5 @@ int main(int argc, char *argv[])
//printSizeofRLCMAC();
testRlcMacDownlink();
testRlcMacUplink();
testCsnLeftAlignedVarBmpBounds();
}

View File

@ -7,13 +7,13 @@ vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
vector1 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
vector2 = 4e8250e3f1a81d882080b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
vector1 = 4828247a6a07422721032b2b2b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
vector2 = 4828247a6a074227210b2b2b2b2b2b2b2b2b2b2b2b
vector1 = 4828247a6a07422721032b2b2b2b2b2b2b2b2b2b2b
vector2 = 4828247a6a07422721032b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 4724c040000000079eb2ac9402b2b2b2b2b2b
=========Start DECODE===========
@ -31,22 +31,46 @@ vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
vector1 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
vector2 = 47283c367513ba33304242b2b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
UPLINK
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
vector1 = 4082001a394df680efb330b2b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
vector2 = 40e1e61d11f2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
vector1 = 4082001a394df680efb330b2b2b2b2b2b2b2b2b2b
vector2 = 4082001a394df680efb330b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
vector1 = 40284f00010981c826f446809dcecb2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
vector2 = 40b802000000002480e0b2b2b2b2b2b2b2b2b
vector1 = 40284f00010981c826f446809dcecb2b2b2b2b2b
vector2 = 40284f00010981c826f446809dcecb2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 40243f2f000087b042b2b2b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 40243f2f000087b042b2b2b2b2b2b2b2b2b2b2b
vector2 = 40243f2f000087b042b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
UPLINK
vector1 = 40e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 40e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
vector2 = 40e1e61d11d2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 40b802000000002480e032b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 40b802000000002480e032b2b2b2b2b2b2b2b
vector2 = 40b802000000002480e032b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
=========Start DECODE===========
@ -56,3 +80,19 @@ vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
vector1 = 4016713dc09427ca2ae57ef90906aafc001f80222b
vector2 = 4016713dc09427ca2ae57ef90906aafc001f80222b
vector1 == vector2 : TRUE
vector1 = 4020ffc021ec1b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 4020ffc021ec1b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
vector2 = 4020ffc021ec1b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
vector1 == vector2 : TRUE
vector1 = 40a90200000000301012a80132b2b2b2b2b
=========Start DECODE===========
+++++++++Finish DECODE++++++++++
=========Start ENCODE=============
+++++++++Finish ENCODE+++++++++++
vector1 = 40a90200000000301012a80132b2b2b2b2b
vector2 = 40a90200000000301012a80132b2b2b2b2b
vector1 == vector2 : TRUE

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,8 @@
=== end test_tbf_ws ===
=== start test_tbf_egprs_two_phase ===
=== end test_tbf_egprs_two_phase ===
=== start test_tbf_egprs_two_phase_spb ===
=== end test_tbf_egprs_two_phase_spb ===
=== start test_tbf_egprs_dl ===
Testing MCS-1
Testing MCS-2
@ -43,3 +45,26 @@ Testing MCS-7
Testing MCS-8
Testing MCS-9
=== end test_tbf_egprs_dl ===
=== start test_tbf_egprs_retx_dl ===
Testing retx for MCS 6 - 6
Testing retx for MCS 1 - 9
Testing retx for MCS 2 - 8
Testing retx for MCS 5 - 7
Testing retx for MCS 6 - 9
Testing retx for MCS 7 - 5
Testing retx for MCS 9 - 6
=== end test_tbf_egprs_retx_dl ===
=== start test_tbf_egprs_spb_dl ===
Testing retx for MCS 6 to reseg_mcs 3
Testing retx for MCS 5 to reseg_mcs 2
Testing retx for MCS 4 to reseg_mcs 1
Testing retx for MCS 6 to reseg_mcs 3
=== end test_tbf_egprs_spb_dl ===
=== start test_tbf_puan_urbb_len ===
=== end test_tbf_puan_urbb_len ===
=== start test_tbf_update_ws ===
=== end test_tbf_update_ws ===
=== start test_tbf_li_decoding ===
=== end test_tbf_li_decoding ===
=== start test_tbf_egprs_epdan ===
=== end test_tbf_egprs_epdan ===

View File

@ -23,6 +23,13 @@ cat $abs_srcdir/tbf/TbfTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/tbf/TbfTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([bitcomp])
AT_KEYWORDS([bitcomp])
cat $abs_srcdir/bitcomp/BitcompTest.ok > expout
cat $abs_srcdir/bitcomp/BitcompTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/bitcomp/BitcompTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([edge])
AT_KEYWORDS([edge])
cat $abs_srcdir/edge/EdgeTest.ok > expout

View File

@ -322,6 +322,23 @@ static void test_rlc_dl_ul_basic()
ul_win.receive_bsn(4);
count = ul_win.raise_v_q();
OSMO_ASSERT(count == 0);
/*
* SSN wrap around case
* Should not expect any BSN as nacked.
*/
rbb = "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR";
for (int i = 0; i < 128; ++i) {
ul_win.receive_bsn(i);
ul_win.raise_v_q();
}
ul_win.receive_bsn(0);
ul_win.raise_v_q();
ul_win.receive_bsn(1);
ul_win.raise_v_q();
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
OSMO_ASSERT(ul_win.ssn() == 2);
}
{